djk3000 / ME

4 stars 2 forks source link

Swift-HTTP的3种异步回调方法 #115

Open djk3000 opened 1 year ago

djk3000 commented 1 year ago

异步回调

平时接口拿一些数据的时候,一般都是异步的,我们需要拿到之后传出来。 下面就用一个简单的下载图片的例子来说明一下常用的异步回调传数据的方法。

方法一:逃逸闭包

class DownloadImageAsyncImageLoder {
    func downloadWithEscaping(completionHandler: @escaping (_ image: UIImage? , _ error :Error?) -> ()) {
        URLSession.shared.dataTask(with: url) {[weak self] data, response, error in
            let image = self?.handleResponse(data: data, response: response)
            completionHandler(image, error)
        }
        .resume()
    }

    func handleResponse(data: Data?, response: URLResponse?) -> UIImage? {
        guard let data = data,
              let image = UIImage(data: data),
              let response = response as? HTTPURLResponse,
              response.statusCode >= 200 && response.statusCode < 300 else {
            return nil
        }
        return image
    }
}

使用

            let loader = DownloadImageAsyncImageLoder()
            func fetchImage() {
                loader.downloadWithEscaping { [weak self] image, error in
                    DispatchQueue.main.async {
                        if let image = image {
                            self?.image = image
                        }
                    }
                }
            }

方法二:Combine

class DownloadImageAsyncImageLoder {
func downloadWithCombine() -> AnyPublisher<UIImage?, Error> {
        URLSession.shared.dataTaskPublisher(for: url)
            .map(handleResponse)
            .mapError({ $0 })
            .eraseToAnyPublisher()
    }

    func handleResponse(data: Data?, response: URLResponse?) -> UIImage? {
        guard let data = data,
              let image = UIImage(data: data),
              let response = response as? HTTPURLResponse,
              response.statusCode >= 200 && response.statusCode < 300 else {
            return nil
        }
        return image
    }
}

使用

      let loader = DownloadImageAsyncImageLoder()
      func fetchImage() {
                loader.downloadWithCombine()
                    .receive(on: DispatchQueue.main)
                    .sink { _ in

                    } receiveValue: {[weak self] image in
                        if let image = image {
                            self?.image = image
                        }
                    }
                    .store(in: &cancelable)
                }

方法三:async / await

这是ios15新增的一套体系,如果需要在API中使用的话需要ios15以上。

class DownloadImageAsyncImageLoder {
    func downloadWithAsync() async throws -> UIImage? {
        do {
            let (data, response) = try await URLSession.shared.data(from: url, delegate: nil)
            return handleResponse(data: data, response: response)
        } catch {
            throw error
        }
    }

    func handleResponse(data: Data?, response: URLResponse?) -> UIImage? {
        guard let data = data,
              let image = UIImage(data: data),
              let response = response as? HTTPURLResponse,
              response.statusCode >= 200 && response.statusCode < 300 else {
            return nil
        }
        return image
    }
}

使用

func fetchImage() async {
        let image = try? await loader.downloadWithAsync()

        await MainActor.run(body: {
            self.image = image
        })
    }

//在调用的时候需要用Task来使用它
        .onAppear{
//            vm.fetchImage()
            Task {
               await vm.fetchImage()
            }
        }

参考资料