iOS 10-无需互联网即可保存图片-Swift 3.0和Xcode 8.2 [英] iOS 10 - Save pictures without internet - Swift 3.0 and Xcode 8.2

查看:77
本文介绍了iOS 10-无需互联网即可保存图片-Swift 3.0和Xcode 8.2的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作一个应用程序(使用Xcode 8.2.1,iOS 10和swift 3.0),用户可以在其中制作热敏照片和普通照片(RGB)。然后,他可以将它们上传到Amazon s3。
但是,如果用户在上传一张图片或多张图片时没有互联网,会发生什么?我该如何解决这个问题?
我只能想到两种解决方案,使用coredata或sqlite,您向我建议哪一种?

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?

另一方面,当我检测到电话没有Internet连接。

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

推荐答案

您也可以使用背景 URLSession ,一旦建立Internet连接,它将上传文件。它还有一个好处,即使用户当时在线,如果他们在上载过程中离开了您的应用程序,即使他们离开了您的应用程序,该操作也将继续进行(不过,如果他们通过双击主页按钮)。

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).

不幸的是,后台会话必然要麻烦得多。关键问题是,上传完成后,您的应用可能无法运行。而且,在这种情况下重新启动应用程序时,显然,您在创建上传文件时通过的关闭操作都早已一去不复返了。这意味着完成处理程序模式仅对后台会话无效。您必须使用 URLSession API的基于委托的表示形式,必须在应用程序委托中实现其他方法,等等。但是,如果您使用Google背景URLSession教程swift 3 ,您会找到一些示例。

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.

在评论中,您提到您正在使用Alamofire。创建GET / POST请求非常好,但是处理后台请求也没有更好(甚至更糟)。

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. 您必须创建背景 SessionManager

您无法从内存中上传文件(因为您的应用可能已消失),因此您必须从文件中上传。这意味着,如果您要创建复杂的POST请求(JSON或 multipart / form-data 或其他),则必须创建此请求,然后将其保存到文件中,然后上传。

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.

您不能依赖上传的完成处理程序,因此您必须在<$ c中进行所有处理$ c> taskDidComplete , dataTaskDidReceiveData sessionDidFinishEventsForBackgroundURLSession 等,<$的关闭 SessionManager 的c $ c> delegate 。

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.

例如,在Swift 3中:

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
}

不幸的是,与上面的内容一样,这实际上简化了实际的实现方式,因为您可能不想依赖仅在 UNUserNotificationCenter 上,但是您可能想相应地更新自己的UI,因此您可能正在处理 NotificationCenter 消息或跟踪回调闭包的数组,您将通过任务标识符等将其关闭。

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.

这篇关于iOS 10-无需互联网即可保存图片-Swift 3.0和Xcode 8.2的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆