实现与 URLSession 发布者的重新连接和结合

Lex*_*Lex 6 swift combine

我想知道是否有一种方法可以使用新的Apple框架实现重新连接机制结合使用URLSession发布者

  • 试图在 WWDC 2019 中找到一些例子
  • 尝试waitsForConnectivity不走运(它甚至没有在自定义会话中调用委托)
  • 尝试过,URLSession.background但在发布过程中崩溃了。

我也不明白我们如何以这种方式跟踪进度
有没有人已经尝试过这样做?

更新:
似乎在 Xcode 11 Beta 中waitsForConnectivity 不起作用

upd2:
Xcode 11 GM -waitsForConnectivity正在运行,但只能在设备上运行。使用默认会话,设置标志并实现会话委托。task is waiting for connectivity无论您是否使用带回调的 init 任务,都会调用方法。

public class DriverService: NSObject, ObservableObject {

    public var decoder = JSONDecoder()
    public private(set) var isOnline = CurrentValueSubject<Bool, Never>(true)

    private var subs = Set<AnyCancellable>()
    private var base: URLComponents
    private  lazy var session: URLSession = {
        let config = URLSessionConfiguration.default
        config.waitsForConnectivity = true
        return URLSession(configuration: config, delegate: self, delegateQueue: nil)
    }()

    public init(host: String, port: Int) {

        base = URLComponents()
        base.scheme = "http"
        base.host = host
        base.port = port

        super.init()

//      Simulate online/offline state
//
//        let pub = Timer.publish(every: 3.0, on: .current, in: .default)
//        pub.sink { _ in
//            let rnd = Int.random(in: 0...1)
//            self.isOnline.send(rnd == 1)
//        }.store(in: &subs)
//        pub.connect()
    }

    public func publisher<T>(for driverRequest: Request<T>) -> AnyPublisher<T, Error> {

        var components = base
        components.path = driverRequest.path

        var request = URLRequest(url: components.url!)
        request.httpMethod = driverRequest.method

        return Future<(data: Data, response: URLResponse), Error> { (complete) in
            let task = self.session.dataTask(with: request) { (data, response, error) in
                if let err = error {
                    complete(.failure(err))
                } else {
                    complete(.success((data!, response!)))
                }
                self.isOnline.send(true)
            }
            task.resume()
        }
        .map({ $0.data })
        .decode(type: T.self, decoder: decoder)
        .eraseToAnyPublisher()
    }
}

extension DriverService: URLSessionTaskDelegate {

    public func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {
        self.isOnline.send(false)
    }

}
Run Code Online (Sandbox Code Playgroud)

Lot*_*lla 3

你试过retry(_:)了吗?它\xe2\x80\x99s 可在Publisher,并在失败时重新运行请求。

\n\n

如果您不\xe2\x80\x99 不希望请求针对所有失败立即重新运行,那么您可以使用catch(_:)并决定哪些故障需要重新运行。

\n\n

这是一些获取进度的代码。

\n\n
enum Either<Left, Right> {\n    case left(Left)\n    case right(Right)\n\n    var left: Left? {\n        switch self {\n        case let .left(value):\n            return value\n        case .right:\n            return nil\n        }\n    }\n\n    var right: Right? {\n        switch self {\n        case let .right(value):\n            return value\n        case .left:\n            return nil\n        }\n    }\n}\n\nextension URLSession {\n    func dataTaskPublisherWithProgress(for url: URL) -> AnyPublisher<Either<Progress, (data: Data, response: URLResponse)>, URLError> {\n        typealias TaskEither = Either<Progress, (data: Data, response: URLResponse)>\n        let completion = PassthroughSubject<(data: Data, response: URLResponse), URLError>()\n        let task = dataTask(with: url) { data, response, error in\n            if let data = data, let response = response {\n                completion.send((data, response))\n                completion.send(completion: .finished)\n            } else if let error = error as? URLError {\n                completion.send(completion: .failure(error))\n            } else {\n                fatalError("This should be unreachable, something is clearly wrong.")\n            }\n\n        }\n        task.resume()\n        return task.publisher(for: \\.progress.completedUnitCount)\n            .compactMap { [weak task] _ in task?.progress }\n            .setFailureType(to: URLError.self)\n            .map(TaskEither.left)\n            .merge(with: completion.map(TaskEither.right))\n            .eraseToAnyPublisher()\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n