2016-07-25 37 views
3

我想创建一个Upload Manager Singleton,每10分钟上传一次多部分数据。上传本身是明确的,但我怎么能做一个上传在这段时间内的数据在后台的数据?Swift后台同步管理器

只是为了说明:

,我要上传的数据是有对象的数组模型。每个对象都有一个标志,当该标志被设置时,该对象已准备好上传。整个“同步功能”应该被调用一次,并且每10分钟重复一次,而不管在哪个ViewController上。有谁知道我该怎么做?

回答

4

的代码使用一些外部框架。它基于递归。

  1. Alamofire //联网

*上述框架并不重要。我只是用它来加快开发过程。

同步管理

import Foundation 
import Alamofire 


let SyncMangerIdentifier = "com.example.background.syncmanger" 

class SyncManager: Alamofire.Manager{ 

    static let instance = SyncManager() 
    private var pendingTasks = [SyncTask]() // SyncTask is a class with 3 variables [image,audio,[tags]] that are being uploading to server 
    private var request: Request? 
    private var isSyncing = false 


    private init(){ 
     let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(SyncMangerIdentifier) 
     configuration.allowsCellularAccess = Config.cellularAccess 
     super.init(configuration: configuration) 
    } 

    // CALL THIS FUNCTION TO START THE SYNC 
    // variable isSyncing guards multiple execution of syncManager 
    func start(){ 
     guard !isSyncing else { 
      // WE ARE ALREADY SYNCING 
      return 
     } 

     // CALL TO PREPARE FUNCTION TO EVALUATE WHETHER WE CAN SYNC OR NOT 
     prepare() 
    } 


    /* 
    initialize the syncItem variable with the first entry from SyncTask 
    if we are stopping return 
    if syncTask isEmpty stop 
    if there are no items in first syncTask remove the task and restart the process. 

    */ 
    private func prepare(){ 

     // I use a database query to store & retrieve pendingTasks 

     guard !pendingTasks.isEmpty else{ 
      // todo no more data to sync 
      isSyncing = false // syncing process ended 

      // Notify app that your long running task has finished 
      (UIApplication.sharedApplication().delegate as? AppDelegate)?.endBackgroundSyncTask() 
      return 
     } 

     isSyncing = true // we are in syncing process 

     // Notify app that our long running task has begun 
     (UIApplication.sharedApplication().delegate as? AppDelegate)?.beginBackgroundRestoreTask() 

     // Call function to start the first upload 
     uploadFileOrData() 
     } 
    } 


    /** 
    upload the files & data from array recursively 
    */  
    private func uploadFileOrData(){ 
     var task = pendingTasks[0] 

     let imageUrl = task.imageUrl 
     let audioUrl = task.audioUrl 
     let tags = task.tags.reduce(""){ prev, next in 
      if prev.isEmpty{ 
       return next.text 
      } 
      return "\(prev),\(next.text)" 
     } 

     let form : (MultipartFormData) ->() = { data in 

      if imageUrl.checkResourceIsReachableAndReturnError(nil){ 
       data.appendBodyPart(fileURL: imageUrl, name: "image") 
      } 

      if audioUrl.checkResourceIsReachableAndReturnError(nil){ 
       data.appendBodyPart(fileURL: audioUrl, name: "audio") 
      } 
      data.appendBodyPart(data: tags.dataUsingEncoding(NSUTF8StringEncoding,allowLossyConversion: false)!, name: "tags") 

     } 

     upload(.POST, Api.fileUploadUrl, multipartFormData: form ,encodingCompletion: { 

     // Call function to process the response 
      self.processUploadFileResponse($0) 
     }) 
    } 

    private func processUploadFileResponse(result: Manager.MultipartFormDataEncodingResult){ 
     switch result { 
     case .Success(let upload, _, _): 

      // YOUR LOGIN ON SUCCESS 

      // MOVE TO NEXT LOCATION 
      self.moveToNextTask() 

     case .Failure(_): 
      // YOUR LOGIN ON FALIURE 

      // MOVE TO NEXT LOCATION 
      self.moveToNextTask() 
     } 
    } 




    private func moveToNextTask(){ 
     // DELETE pendingTasks[0] & CALL prepare() function 

     // If you want to repeat after every 10 MINUTE 
     // Then wrap your function call 'prepare()' inside dispatch_after 


     dispatch_after(
        dispatch_time(
         DISPATCH_TIME_NOW, 
         Int64(10 * 60 * Double(NSEC_PER_SEC)) // 10 * 60 to convert seconds into minute 
        ), 
        dispatch_get_main_queue(), { 
         self.prepare() 
       }) 

      } 

AppDelegate类

// bind the alamofire backgroundCompletionHandler 
func application(application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler:() -> Void) { 
    //  NSLog("handle events for background: \(identifier)") 
    if identifier == SyncMangerIdentifier{ 
     SyncManager.instance.backgroundCompletionHandler = completionHandler 
    } 
} 

// Identifier for long running background task for SyncManager class 
var backgroundSyncTask: UIBackgroundTaskIdentifier? 

// Call this at the beginning of syncing 
func beginBackgroundSyncTask() { 

    backgroundRestoreTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({ 
     self.endBackgroundRestoreTask() 
    }) 
} 

// Call this when syncing process ends 
func endBackgroundSyncTask() { 

    guard backgroundSyncTask != nil else { 
     return 
    } 
    UIApplication.sharedApplication().endBackgroundTask(self.backgroundSyncTask!) 
    self.backgroundSyncTask = UIBackgroundTaskInvalid 
} 

为了当你的应用程序进入后台,你可能需要从应用程序的功能部分使BackGroundFetchMode

0

既然你要上传的方法在所有ViewControllers(VCS)被调用一个方法是: -

extension UIViewController 
{ 
    func uploadData(parameters) ->Bool 
    { 
    return true/false; 
    } 
} 

然后在所有ViewControllers(VCS),你可以调用uploadData方法viewDidLoad中或在特定的功能,如: -

if(self.uploadData(parameters)) // if true method is called i.e. new objects available to upload or 10mins have passed as per your requirement 
{ 
} 

第二种方法是定义它检查是否10分钟,在AppDelegate中已通过的NSTimer部分并创建一个空的雨燕文件,该文件确实上传并在AppDelegate中调用该方法didFinishLaunchingWithOptions。

有很多方法可以解决这个问题,但这取决于您的应用需要如何进行流动。

注: -使用 NSURLSessionUploadTask - >上载并 的NSTimer - >要检查10分钟是否通过

+0

感谢您的回应,继续要求!但我不想在每个ViewController中调用该函数。我想在第一个ViewController中调用它,然后每10分钟调用一次 – Thomas

+0

然后你可以用第二种方法 – MShah

+0

这是为了同步的目的,我认为这种方法最终会消耗大量的用户资源和带宽,如果它继续每10分钟上传一次。 – MShah