iOS后台下载

iOS后台下载,ios,background-thread,Ios,Background Thread,我有一个应用程序,当用户登录时需要下载大量数据。我想将其下载部分移动到后台线程,这样用户就可以在应用程序中导航,而无需等待下载完成。我尝试过以下方法,但其中一些方法仍然会锁定应用程序,因此用户无法单击任何内容 dispatch_async(dispatch_get_main_queue(), ^{ }); 也尝试过 [self performSelectorInBackground:@selector(loadDataThatToBeFetchedInThread:)

我有一个应用程序,当用户登录时需要下载大量数据。我想将其下载部分移动到后台线程,这样用户就可以在应用程序中导航,而无需等待下载完成。我尝试过以下方法,但其中一些方法仍然会锁定应用程序,因此用户无法单击任何内容

dispatch_async(dispatch_get_main_queue(), ^{

});
也尝试过

[self performSelectorInBackground:@selector(loadDataThatToBeFetchedInThread:) 
                      withObject:objectArrayThatNeedToFetchData];
如果我在活动之间移动,这一个似乎就停止了。我已经尝试将其移动到AppDelegate方法,但是当我尝试保存到SQlite DB时,我得到了一些错误。我做错什么了吗?能找个人帮忙吗


提前感谢

好吧,
dispatch\u get\u main\u queue()
将为您提供主线程,因此这可能不是您想要的

相反,您应该使用以下方法获取后台队列:

dispatch_async (dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ ... });
然后,通常会发送一些通知,甚至直接回调主线程以(在UI中)报告成功:

dispatch_async (dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    // Do the download...
    // Download finishes...
    dispatch_async(dispatch_get_main_queue(), ^{
        // Call a UI-updating method, or similar
    });
});
抬头看看。这是苹果最新和最棒的产品

从和观看核心网络视频(核心网络的新增功能)

也是一个很好的资源

NSURLSession是一种开箱即用的异步会话,这正是您需要的


作为一个额外的奖励,NSURLSessionDownloadTask可以在应用程序更改为后台状态(这与后台线程大不相同)时轻松地继续下载。它还允许您轻松地取消和/或恢复下载。

我建议使用
NSOperation
NSOperationQueue
保持整洁

阅读和观看更多:

这里有一个基本设置,您可以根据自己的需要进行定制

免责声明:虽然看起来很多,但它弥补了一个更好的API

首先,让我们定义一个接口来处理API端点:

// Endpoints.swift

let api_base = "https://myserver.com/"
let api_path = "api/"

protocol EndpointGenerator {
    func URL() -> NSURL
}

extension EndpointGenerator {
    func URL() -> NSURL {
        return NSURL(string: api_base)!
    }
}

// Represents a null endpoint. It will fail.
struct NullEndpoint: EndpointGenerator { }

enum Endpoint: String, EndpointGenerator {
    case Login = "login"
    case SignUp = "signup"

    func URL() -> NSURL {
        return NSURL(string: api_base + api_path + self.rawValue)!
    }
}
接下来,让我们构建自定义的
NSOperation

// Operation.swift
public class Operation: NSOperation {
    public typealias Completion = Operation -> ()
    public typealias Error = NSError -> ()

    var endpoint: EndpointGenerator {
        return NullEndpoint()
    }

    var headerParams: [String:String]? {
        return nil
    }

    var requestBody: [String:AnyObject]? {
        return nil
    }

    var method: HTTPMethod {
        return .GET
    }

    var networkTask: NSURLSessionTask?

    var completion: Completion?
    var error: Error?
    public var parsedObject = [String:AnyObject]()

    override public init() { }

    public init(completion: Completion, error: Error) {
        self.completion = completion
        self.error = error
    }

    override public func start() {
        NSURLSessionImplementaion.execute(self)
    }

    override public func cancel() {
        networkTask?.cancel()
        networkTask = nil
    }
}
接下来,让我们来处理实际队列:

// OperationQueue.swift
public class OperationQueue: NSOperationQueue {
        public static let internalQueue = OperationQueue()

        public static func addOperation(operation: NSOperation) {
            internalQueue.addOperation(operation)
        }

        public static func addOperations(operations: NSOperation...) {
            for operation in operations {
                addOperation(operation)
            }
        }

        public static func cancellAllOperations() {
            internalQueue.cancelAllOperations()
        }
}
最后,下载部分:

// NSURLSessionImplementation.swift
enum HTTPMethod: String {
    case POST = "POST"
    case GET = "GET"
    case PATCH = "PATCH"
}

public let OSNetworkingErrorDomain = "com.swanros.errordomain"

class NSURLSessionImplementaion {
    class func execute(operation: Operation) {
        let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
        let request = NSMutableURLRequest(URL: operation.endpoint.URL())
        if let headerParams = operation.headerParams {
            for element in headerParams {
                request.setValue(element.1, forHTTPHeaderField: element.0)
            }
        }

        if let body = operation.requestBody {
            do {
                request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(body, options: .PrettyPrinted)
            } catch {
                return
            }
        }

        request.HTTPMethod = operation.method.rawValue

        let task = session.dataTaskWithRequest(request) { data, response, error in
            if let e = error {
                operation.error?(e)
                return
            }

            guard let d = data else {
                operation.error?(errorWithDescription("No data"))
                return
            }

            do {
                let json = try NSJSONSerialization.JSONObjectWithData(d, options: .MutableLeaves) as? [String:AnyObject]
                guard let j = json else {
                    operation.error?(errorWithDescription("Error parsing JSON."))
                    return
                }

                if let errorMessage = string(j, key: "error") {
                    operation.error?(errorWithDescription(errorMessage))
                    return
                }

                operation.parsedObject = j
                operation.completion?(operation)
            } catch let jsonError as NSError {
                operation.error?(jsonError)
            }
        }

        operation.networkTask = task
        task.resume()
    }
}

func errorWithDescription(desc: String) -> NSError {
    return NSError(domain: OSNetworkingErrorDomain, code: 0, userInfo: [NSLocalizedDescriptionKey:desc])
}
如何实现这一点?假设您想点击
/login
端点。子类
操作
如下:

// LogInOperation.swift
public class LogInOperation: Operation {
    override var endpoint: EndpointGenerator {
        // A nice way to represent endpoints: use enums and protocols!
        return Endpoint.Login
    }

    // The headers for this particular request. Maybe you need a token here!
    override var headerParams: [String:String]? {
        return [
            "Content-Type": "application/json",
            "Application-Id": "bAAvLosWNeSTHrlYilysdeEYoJHUXs88"
        ]
    }

    // The HTTP request body!
    override var requestBody: [String:AnyObject]? {
        return [
            "mail": mail,
            "password": password
        ]
    }

    // .GET is default
    override var method: HTTPMethod {
        return .POST
    }

    private var mail: String
    private var password: String

    public init(mail m: String, password p: String, completion: Completion, error: Error) {
        mail = m
        password = p

        super.init(completion: completion, error: error)
    }
}
你是这样使用它的:

// ViewController.swift

let loginOperation = LogInOperation(
                         mail: "mail@example.com", 
                         password: "123123", 
                         completion: { op in
                             // parsedObject would be the user's info 
                             print(op.parsedObject?)
                         }, error: { error in 
                             print(error.localizedDescription)
                         }
                     )
OperationQueue.addOperation(loginOperation)

+1用于提及
NSURLSessionDownloadTask
类。虽然与OP尝试的策略不同,但它几乎肯定是更好的解决方案。