iOS 10 - Save pictures without internet - Swift 3.0 and Xcode 8.2

南笙酒味 提交于 2019-12-08 11:08:34

问题


I'm making an application (with Xcode 8.2.1, iOS 10 and swift 3.0) where the user can make thermal photos and normal photos (RGB). Then he can upload them to Amazon s3. But what happens if the user does not have internet at the moment of uploading an image or several images? How do I solve that problem? I can only think of two solutions, with coredata or sqlite, which one do you advise me?

On the other hand, as I detect that the phone has no Internet connection.


回答1:


You can, alternatively, use a background URLSession, which will upload the files once the Internet connection is established. It also has the benefit that even if the user is online at the time, if they leave your app while the upload is in progress, it will continue even after they leave your app (though, not if they manually kill app by double tapping the home button).

Unfortunately, background sessions are, by necessity, a lot more cumbersome to deal with. The key issue is that your app may not be running when the uploads finish. And when the app is restarted in that case, obviously any closures you passed when creating the uploads are long gone. This means that completion-handler patterns just don't work for background sessions. You have to use the delegate-based rendition of URLSession API, you have to implement additional method in your app delegate, etc. But if you google "background URLSession tutorial swift 3", you'll find some examples.

In comments, you mention that you're using Alamofire. That's great at creating GET/POST requests, but it's no better (maybe even worse) when dealing with background requests.

  1. You have to create a background SessionManager.

  2. You can't upload a file from memory (because your app may be gone), so you have to upload from a file. This means that if you're creating a complicated POST request (either JSON or multipart/form-data or whatever), that you have to create this, save it to a file, and then upload that.

  3. You can't rely on completion handlers of your upload, so you have to do all of your processing in the taskDidComplete, dataTaskDidReceiveData, sessionDidFinishEventsForBackgroundURLSession, etc., closures of the delegate of the SessionManager.

For example, in Swift 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";
    }

}

And, you also have to tell your app delegate to keep an eye out for whether it was awaken for background session processing and capture the completion handler, if so:

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

Unfortunately, as hairy as the above is, it's a gross simplification of what the actual implementation looks like, as you probably don't want to rely solely on the UNUserNotificationCenter, but rather you probably want to update your own UI accordingly, so you're probably dealing with NotificationCenter messages or keeping track of arrays of callback closures which you'll tie back through task identifiers and the like.




回答2:


Normally in such cases a user should be informed about a failed upload by an error message of some sort and asked to do it again later. But if you really need to save the image and send it later, you could save it using CoreData or save it in your documents folder using NSKeyedArchiver as a serialized object, which may be a bit simpler for just one or few images. Both have their pros and cons.



来源:https://stackoverflow.com/questions/42912645/ios-10-save-pictures-without-internet-swift-3-0-and-xcode-8-2

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!