Can I somehow do a synchronous HTTP request via NSURLSession in Swift

前端 未结 5 1321
有刺的猬
有刺的猬 2020-11-29 01:01

Can I somehow do a synchronous HTTP request via NSURLSession in Swift?

I can do an asynchronous request via the following code:

if let u         


        
相关标签:
5条回答
  • 2020-11-29 01:24

    Apple thread discussing the same issue.

    + (NSData *)sendSynchronousRequest:(NSURLRequest *)request  
        returningResponse:(__autoreleasing NSURLResponse **)responsePtr  
        error:(__autoreleasing NSError **)errorPtr {  
        dispatch_semaphore_t    sem;  
        __block NSData *        result;  
    
        result = nil;  
    
        sem = dispatch_semaphore_create(0);  
    
        [[[NSURLSession sharedSession] dataTaskWithRequest:request  
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {  
            if (errorPtr != NULL) {  
                *errorPtr = error;  
            }  
            if (responsePtr != NULL) {  
                *responsePtr = response;  
            }  
            if (error == nil) {  
                result = data;  
            }  
            dispatch_semaphore_signal(sem);  
        }] resume];  
    
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);  
    
       return result;  
    }  
    

    Answer by Quinn "The Eskimo!" Apple Developer Relations, Developer Technical Support, Core OS/Hardware

    0 讨论(0)
  • 2020-11-29 01:24

    Updated one of the answers to use a URLRequest instead, so we can use PUT etc instead.

    extension URLSession {
        func synchronousDataTask(urlrequest: URLRequest) -> (data: Data?, response: URLResponse?, error: Error?) {
            var data: Data?
            var response: URLResponse?
            var error: Error?
    
            let semaphore = DispatchSemaphore(value: 0)
    
            let dataTask = self.dataTask(with: urlrequest) {
                data = $0
                response = $1
                error = $2
    
                semaphore.signal()
            }
            dataTask.resume()
    
            _ = semaphore.wait(timeout: .distantFuture)
    
            return (data, response, error)
        }
    }
    

    I'm calling like this.

    var request = URLRequest(url: url1)
    request.httpBody = body
    request.httpMethod = "PUT"
    let (_, _, error) = URLSession.shared.synchronousDataTask(urlrequest: request)
    if let error = error {
        print("Synchronous task ended with error: \(error)")
    }
    else {
        print("Synchronous task ended without errors.")
    }
    
    0 讨论(0)
  • 2020-11-29 01:29

    You can use this NSURLSession extension to add a synchronous method:

    extension NSURLSession {
        func synchronousDataTaskWithURL(url: NSURL) -> (NSData?, NSURLResponse?, NSError?) {
            var data: NSData?, response: NSURLResponse?, error: NSError?
    
            let semaphore = dispatch_semaphore_create(0)
    
            dataTaskWithURL(url) {
                data = $0; response = $1; error = $2
                dispatch_semaphore_signal(semaphore)
            }.resume()
    
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
    
            return (data, response, error)
        }
    }
    

    Update for Swift 3:

    extension URLSession {
        func synchronousDataTask(with url: URL) -> (Data?, URLResponse?, Error?) {
            var data: Data?
            var response: URLResponse?
            var error: Error?
    
            let semaphore = DispatchSemaphore(value: 0)
    
            let dataTask = self.dataTask(with: url) {
                data = $0
                response = $1
                error = $2
    
                semaphore.signal()
            }
            dataTask.resume()
    
            _ = semaphore.wait(timeout: .distantFuture)
    
            return (data, response, error)
        }
    }
    
    0 讨论(0)
  • 2020-11-29 01:31

    Be careful with synchronous requests because it can result in bad user experience but I know sometimes its necessary. For synchronous requests use NSURLConnection:

    func synchronousRequest() -> NSDictionary {
    
            //creating the request
            let url: NSURL! = NSURL(string: "exampledomain/...")
            var request = NSMutableURLRequest(URL: url)
            request.HTTPMethod = "GET"
            request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    
    
            var error: NSError?
    
            var response: NSURLResponse?
    
            let urlData = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)
    
            error = nil
            let resultDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(urlData!, options: NSJSONReadingOptions.MutableContainers, error: &error) as! NSDictionary
    
            return resultDictionary
        }
    
    0 讨论(0)
  • 2020-11-29 01:34

    Duplicated answer from https://stackoverflow.com/a/58392835/246776


    If semaphore based approach doesn't work for you, try polling based approach.

    var reply = Data()
    /// We need to make a session object.
    /// This is key to make this work. This won't work with shared session.
    let conf = URLSessionConfiguration.ephemeral
    let sess = URLSession(configuration: conf)
    let task = sess.dataTask(with: u) { data, _, _ in
        reply = data ?? Data()
    }
    task.resume()
    while task.state != .completed {
        Thread.sleep(forTimeInterval: 0.1)
    }
    FileHandle.standardOutput.write(reply)
    
    0 讨论(0)
提交回复
热议问题