Swift(iOS),在返回之前等待所有图像完成下载

我正在写一个Swift iOS应用程序(我的第一个,所以请耐心等待),我使用Swifter HTTP服务器来处理各种请求。 一个这样的请求是一个HTTP POST,带有一个JSON数组,指定要从网上下载的图像(以及其他一些与手头问题无关的东西)。

我使用Alamofire下载图像(这工作正常),但我正在寻找好的(最好是简单的)的方式来等待所有的图像完成下载之前返回上述POST请求的响应(因为响应必须包含JSON指示结果,包括任何失败的下载)。

什么是一个很好的方法来完成这个(最好没有阻塞主线程)?

这里有一些片段来说明:

public func webServer(publicDir: String?) -> HttpServer { let server = HttpServer() server.POST["/images/update"] = { r in let images = ...(from JSON array in body) let updateResult = ImageUtil.updateImages(images) let resultJson: String = Mapper().toJSONString(updateResult, prettyPrint: true)! if updateResult.success { return .OK(.Text(resultJson)) } return HttpResponse.RAW(500, "Error", nil, { $0.write([UInt8](updateResult.errorMessage.utf8)) }) } } static func updateImages(images: [ImageInfo]) -> UpdateResult { let updateResult = UpdateResult() for image in images { Alamofire.download(.GET, serverFile.imageUrl) { temporaryURL, response in return destinationPath } .validate() .response{_, _, _, error in if let error = error { Log.error?.message("Error downloading file \(image.imageUrl) to \(image.fileName): \(error)") } else { updateResult.filesDownloaded++ Log.info?.message("Downloaded file \(image.imageUrl) to \(image.fileName)") }} } return updateResult // It obviously returns before any images finish downloading. I need to wait until all images have downloaded before I can return an accurate result. } 

更新2016年1月23日,使用每个皮皮调度员

这是尝试使用调度机制,但对updateImages的调用仍然立即返回(即使使用dispatch_sync)。

在将HTTP响应返回给调用者之前,如何等待完成所有下载?

 public func webServer(publicDir: String?) -> HttpServer { let server = HttpServer() server.POST["/images/update"] = { r in let imageDownloader = ImageDownloader() imageDownloader.updateimageFiles(adFilesOnServer) let resultJson: String = Mapper().toJSONString(imageDownloader.updateResult, prettyPrint: true)! if imageDownloader.updateResult.success { return .OK(.Text(resultJson)) } return HttpResponse.RAW(500, "Error", nil, { $0.write([UInt8](imageDownloader.updateResult.errorMessage.utf8)) }) } } class ImageDownloader { var updateResult = AdUpdateResult() private var imageFilesOnServer = [ImageFile]() private let fileManager = NSFileManager.defaultManager() private let imageDirectoryURL = NSURL(fileURLWithPath: Settings.imageDirectory, isDirectory: true) private let semaphore = dispatch_semaphore_create(4) private let downloadQueue = dispatch_queue_create("com.acme.downloader", DISPATCH_QUEUE_SERIAL) func updateimageFiles(imageFilesOnServer: [ImageFile]) { self.imageFilesOnServer = imageFilesOnServer dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) for serverFile in imageFilesOnServer { downloadImageFileFromServer(serverFile) } dispatch_sync(downloadQueue) { dispatch_sync(dispatch_get_main_queue()) { print("done") // It gets here before images have downloaded. } } } private func downloadImageFileFromServer(serverFile: ImageFile) { let destinationPath = imageDirectoryURL.URLByAppendingPathComponent(serverFile.fileName) Alamofire.download(.GET, serverFile.imageUrl) { temporaryURL, response in return destinationPath } .validate() .response { _, _, _, error in if let error = error { Log.error?.message("Error downloading file \(serverFile.imageUrl) to \(serverFile.fileName): \(error)") } else { self.updateResult.filesDownloaded++ Log.info?.message("Downloaded file \(serverFile.imageUrl) to \(serverFile.fileName)") } dispatch_semaphore_signal(self.semaphore) } } } 

首先,你真的不想在没有某种节stream的情况下点燃每个图像的请求。 信号量对于这样的事情工作得很好。

其次,你需要基本上统计未完成的操作的数量,然后在全部完成时启动完成处理程序。 或者,如果可以随时启动新的操作,则可能需要对操作进行分组。

那么,伪代码:

 sema = dispatch_semaphore_create(4) // 4 being # of concurrent operations allowed serialQ = dispatch_queue_create(.., SERIAL) dispatch_async(serialQ) { dispatch_semaphore_wait(sema, FOREVER) // will block if there are 4 in flight already for image in images { downloader.downloadAsync(image, ...) { // completion dispatch_semaphore_signal(sema) // signal that we are done with one ... handle downloaded image or error ... ... add downloaded images to downloadedImages ... } } } dispatch_async(serialQ) { // since serialQ is serial, this will be executed after the downloads are done dispatch_async(main_queue()) { yo_main_queue_here_be_yer_images(... downloadedImages ...) } }