2016-11-12 19 views
1

リクエストにエラーがない場合は、メッセージが表示されず、コマンドラインツールアプリケーションであり、私はplistを持っています。 HTTP要求を許可するために、私はいつも完了ブロックを見る。Swift 3で同時httpsリクエストを作成する方法

typealias escHandler = (URLResponse?, Data?) -> Void 

func getRequest(url : URL, _ handler : @escaping escHandler){  
let session = URLSession.shared 
var request = URLRequest(url:url) 
request.cachePolicy = .reloadIgnoringLocalCacheData 
request.httpMethod = "GET" 
let task = session.dataTask(with: url){ (data,response,error) in 
     handler(response,data) 
} 

task.resume() 
} 


func startOp(action : @escaping() -> Void) -> BlockOperation{ 

let exOp = BlockOperation(block: action)  
exOp.completionBlock = { 

print("Finished") 

} 
return exOp 
} 

    for sUrl in textFile.components(separatedBy: "\n"){ 
    let url = URL(string: sUrl)! 

     let queu = startOp { 
      getRequest(url: url){ response, data in 

       print("REACHED") 



      } 

     } 
     operationQueue.addOperation(queu) 
     operationQueue.waitUntilAllOperationsAreFinished() 
+0

https://github.com/ankitthakur/SwiftNetworkをご覧ください。それはswift3で同時にHTTPリクエストをサポートしています。 –

答えて

3

一つの問題は、あなたの操作は、単に要求を開始しているが、要求は非同期に実行されているため、操作はすぐに実際に終了する要求を待っていない、完成されていることです。非同期要求が完了するまで操作を完了したくない場合。

オペレーションキューでこれを行う場合は、NSOperationをサブクラス化し、trueisAsynchronousに戻す必要があります。そして、要求を開始するときにisExecutingを変更し、要求を完了したときにisFinishedを変更し、両方の要求に必要なKVOを行います。これはすべてConcurrency Programming Guide: Defining a Custom Operation Object、特にConfiguring Operations for Concurrent Executionセクションに記載されています。 (このガイドはもう古くなっています(のプロパティは、isAsynchronousであり、Objective-Cなどに焦点を当てています)。

とにかく、これは私は、この非同期操作の愚かさのすべてをカプセル化するために使用する抽象クラスです:

// AsynchronousOperation.swift 

import Foundation 

/// Asynchronous Operation base class 
/// 
/// This class performs all of the necessary KVN of `isFinished` and 
/// `isExecuting` for a concurrent `NSOperation` subclass. So, to developer 
/// a concurrent NSOperation subclass, you instead subclass this class which: 
/// 
/// - must override `main()` with the tasks that initiate the asynchronous task; 
/// 
/// - must call `completeOperation()` function when the asynchronous task is done; 
/// 
/// - optionally, periodically check `self.cancelled` status, performing any clean-up 
/// necessary and then ensuring that `completeOperation()` is called; or 
/// override `cancel` method, calling `super.cancel()` and then cleaning-up 
/// and ensuring `completeOperation()` is called. 

public class AsynchronousOperation : Operation { 

    override public var isAsynchronous: Bool { return true } 

    private let stateLock = NSLock() 

    private var _executing: Bool = false 
    override private(set) public var isExecuting: Bool { 
     get { 
      return stateLock.withCriticalScope { _executing } 
     } 
     set { 
      willChangeValue(forKey: "isExecuting") 
      stateLock.withCriticalScope { _executing = newValue } 
      didChangeValue(forKey: "isExecuting") 
     } 
    } 

    private var _finished: Bool = false 
    override private(set) public var isFinished: Bool { 
     get { 
      return stateLock.withCriticalScope { _finished } 
     } 
     set { 
      willChangeValue(forKey: "isFinished") 
      stateLock.withCriticalScope { _finished = newValue } 
      didChangeValue(forKey: "isFinished") 
     } 
    } 

    /// Complete the operation 
    /// 
    /// This will result in the appropriate KVN of isFinished and isExecuting 

    public func completeOperation() { 
     if isExecuting { 
      isExecuting = false 
     } 

     if !isFinished { 
      isFinished = true 
     } 
    } 

    override public func start() { 
     if isCancelled { 
      isFinished = true 
      return 
     } 

     isExecuting = true 

     main() 
    } 
} 

そして私は、私は上記の状態変化を同期させることを確認するNSLockに、このアップルの拡張機能を使用する:

extension NSLock { 

    /// Perform closure within lock. 
    /// 
    /// An extension to `NSLock` to simplify executing critical code. 
    /// 
    /// - parameter block: The closure to be performed. 

    func withCriticalScope<T>(block:() -> T) -> T { 
     lock() 
     let value = block() 
     unlock() 
     return value 
    } 
} 

次に、例えば、私は今、ネットワーク要求のための操作を作成することができることを行ってた、とにかく

class NetworkOperation: AsynchronousOperation { 

    let url: URL 
    let session: URLSession 
    let requestCompletionHandler: (Data?, URLResponse?, Error?) ->() 

    init(session: URLSession, url: URL, requestCompletionHandler: @escaping (Data?, URLResponse?, Error?) ->()) { 
     self.session = session 
     self.url = url 
     self.requestCompletionHandler = requestCompletionHandler 

     super.init() 
    } 

    private weak var task: URLSessionTask? 

    override func main() { 
     let task = session.dataTask(with: url) { data, response, error in 
      self.requestCompletionHandler(data, response, error) 
      self.completeOperation() 
     } 
     task.resume() 
     self.task = task 
    } 

    override func cancel() { 
     task?.cancel() 
     super.cancel() 
    } 

} 

:私はそれを使用していますNetworkOperationを作成することができ

let queue = OperationQueue() 
queue.name = "com.domain.app.network" 

let url = URL(string: "http://...")! 
let operation = NetworkOperation(session: URLSession.shared, url: url) { data, response, error in 
    guard let data = data, error == nil else { 
     print("\(error)") 
     return 
    } 

    let string = String(data: data, encoding: .utf8) 
    print("\(string)") 
    // do something with `data` here 
} 

let operation2 = BlockOperation { 
    print("done") 
} 

operation2.addDependency(operation) 

queue.addOperations([operation, operation2], waitUntilFinished: false) // if you're using command line app, you'd might use `true` for `waitUntilFinished`, but with standard Cocoa apps, you generally would not 

注意を、上記の例では、私が追加しました最初の操作がネットワーク要求が完了するまで完了していないことを示すために、何かを印刷した第2の操作を第1の操作に依存させる。

私の例では、元の例のwaitUntilAllOperationsAreFinishedwaitUntilFinishedのオプションをaddOperationsと使用しないことは明らかです。しかし、これらの要求が完了するまで終了したくないコマンドラインアプリを扱っているので、このパターンは受け入れられます。 (私は、フリーホイール使用で驚いている将来の読者のためにこれを言及していますが、これは一般的にお勧めできません)。

+0

もう一度ありがとう、ロブ、完璧に働いた。 – eduardo

関連する問題