2017-03-20 3 views
0

私は熱い写真と普通の写真(RGB)を作成できるアプリケーション(Xcode 8.2.1、iOS 10、swift 3.0)を作っています。その後、Amazon S3にアップロードすることができます。 しかし、画像や複数の画像をアップロードする瞬間にインターネットがないとどうなりますか?その問題をどうやって解決するのですか? 私はcoredataまたはsqliteという2つのソリューションしか考えられませんが、どちらにアドバイスしますか?iOS 10 - インターネットなしで写真を保存する - Swift 3.0とXcode 8.2

一方、電話機にインターネット接続がないことを検出すると、

+0

いいアイデアです:)!私はそれについて考えなかった。 私はGETとPOSTを実行するREST APIを使って作業していて、Almofireフレームワークとコールバックを使って作業していることを伝えたいと思います。私の質問は、Almofire(コールバック付き)で電話をすれば、URLSessionと同じ結果になるのでしょうか? しかし、ユーザーがインターネットなしで複数の写真を撮ったら、それらはすべてアップロードされますか? –

答えて

0

インターネット接続が確立されたら、ファイルをアップロードするURLSessionの背景を使用することもできます。また、ユーザーがその時点でオンラインであっても、アップロードの進行中にアプリを離れると、アプリを離してもアプリが終了するまで続行されます(ただし、アプリを手動で削除するとホームボタン)。

残念なことに、バックグラウンドセッションは、必然的に対処するのがはるかに面倒です。重要な問題は、アップロードが完了したときにアプリが実行されていない可能性があることです。その場合にアプリを再起動すると、アップロードを作成したときに渡されたクロージャは、間に合わなくなります。つまり、完了ハンドラパターンはバックグラウンドセッションでは機能しません。 URLSession APIのデリゲートベースのレンディションを使用する必要があります。アプリケーションデリゲートなどに追加のメソッドを実装する必要があります。ただし、「background URLSession tutorial swift 3」をgoogleに設定すると、いくつかの例が見つかります。

コメントでは、Alamofireを使用していると言います。 GET/POSTリクエストを作成するのは素晴らしいですが、バックグラウンドリクエストを処理する場合はそれほど悪くないかもしれません。

  1. SessionManagerを作成する必要があります。

  2. メモリからファイルをアップロードすることはできません(アプリが削除されている可能性があるため)。ファイルからアップロードする必要があります。つまり、複雑なPOSTリクエスト(JSONまたはmultipart/form-dataなど)を作成している場合、これを作成してファイルに保存してからアップロードする必要があります。

  3. あなたのアップロードの完了ハンドラに頼ることができないので、あなたがSessionManagerdelegatetaskDidCompletedataTaskDidReceiveDatasessionDidFinishEventsForBackgroundURLSessionなど、閉鎖であなたのすべての処理をしなければなりません。例えば

、スウィフト3:

// BackgroundSession.swift 

import Foundation 
import Alamofire 
import MobileCoreServices 
import UserNotifications 

class BackgroundSession { 
    private var sessionManager: SessionManager! 

    var completionHandler: (() -> Void)? 

    static private(set) var shared = BackgroundSession() 

    var responseBodies = [Int: Data]() 

    private init() { 
     let configuration = URLSessionConfiguration.background(withIdentifier: "com.example.app.background") 
     sessionManager = Alamofire.SessionManager(configuration: configuration) 

     // for giggles and grins, let's monitor uploads (while app is active, at least) 

     sessionManager.delegate.taskDidSendBodyData = { session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend in 
      print("\(totalBytesSent) of \(totalBytesExpectedToSend)") 
     } 

     // if app delegate captured completion handler, let's call it here 

     sessionManager.delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in 
      self?.completionHandler?() 
      self?.completionHandler = nil 
     } 

     // we probably want to capture body of response from server 

     sessionManager.delegate.dataTaskDidReceiveData = { [weak self] session, task, data in 
      if self?.responseBodies[task.taskIdentifier] == nil { 
       self?.responseBodies[task.taskIdentifier] = Data() 
      } 
      self?.responseBodies[task.taskIdentifier]?.append(data) 
     } 

     // what to do when task completes 
     // 
     // I'm posting `UNNotificationRequest` (in case app wasn't running when upload finished), 
     // but you'd probably want to post NotificationCenter so your view controller could update 
     // itself accordingly. 

     sessionManager.delegate.taskDidComplete = { [weak self] session, task, error in 
      var title: String 

      if error != nil { 
       print("error = \(error!)") 
       title = error!.localizedDescription 
      } else { 
       // parse your self?.responseBodies[task.taskIdentifier] to make sure request succeeded 
       title = ... 
      } 
      self?.responseBodies[task.taskIdentifier] = nil 

      let content = UNMutableNotificationContent() 
      content.title = title 
      content.body = "Whoo, hoo!" 
      let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false) 
      let notification = UNNotificationRequest(identifier: "timer", content: content, trigger: trigger) 
      UNUserNotificationCenter.current().add(notification) 
     } 
    } 

    @discardableResult func upload(_ data: Data, name: String, filename: String, to url: URL) throws -> UploadRequest { 
     // create multipart body 

     let multipart = MultipartFormData() 
     multipart.append(data, withName: name, fileName: filename, mimeType: URL(fileURLWithPath: filename).mimeType) 
     let fileURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) 
      .appendingPathComponent(temporaryFileName()) 
     try multipart.writeEncodedData(to: fileURL) 

     // create request 

     var request = try! URLRequest(url: url, method: .post) 
     request.setValue("multipart/form-data; boundary=\(multipart.boundary)", forHTTPHeaderField: "Content-Type") 

     // initiate upload 

     let uploadRequest = sessionManager.upload(fileURL, with: request) 
     uploadRequest.resume() 
     return uploadRequest 
    } 

    private func temporaryFileName() -> String { 
     return UUID().uuidString 
    } 

} 

extension URL { 

    /// Determine mime type on the basis of extension of a file. 
    /// 
    /// This requires MobileCoreServices framework. 
    /// 
    /// - parameter url: The file `URL` of the local file for which we are going to determine the mime type. 
    /// 
    /// - returns:  Returns the mime type if successful. Returns application/octet-stream if unable to determine mime type. 

    var mimeType: String { 
     if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as NSString, nil)?.takeRetainedValue() { 
      if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() { 
       return mimetype as String 
      } 
     } 
     return "application/octet-stream"; 
    } 

} 

そして、あなたはまた、それがバックグラウンドセッション処理のために目覚めたかどうかに目を光らせて維持するためにアプリのデリゲートを伝え、完了ハンドラをキャプチャする必要がありもしそうであれば:残念ながら

func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping() -> Void) { 
    BackgroundSession.shared.completionHandler = completionHandler 
} 

、上にある、それはあなたがおそらくもっぱらに依存したくないとして、実際の実装は、どのように見えるかの総簡素化だとして毛深いですではなく、独自のUIをそれに応じて更新する必要があるかもしれないので、おそらくNotificationCenterメッセージを処理したり、タスク識別子などを結びつけるコールバッククロージャの配列を追跡したりしているはずです。

0

通常、このような場合は、何らかのエラーメッセージでアップロードに失敗したことをユーザーに通知し、後でもう一度やり直すように要求する必要があります。しかし、イメージを保存して後で送信する必要がある場合は、CoreDataを使用してイメージを保存するか、NSKeyedArchiverを直列化オブジェクトとして使用してドキュメントフォルダに保存してください。どちらも長所と短所があります。

関連する問題