自前篇https://akringblog.com/2019/01/31/ios-moya-oauth-20/ 写成已经过了一年余,随着 Swift 5 和 Alamofire 5 的发布,实现 Moya + Oauth 2.0 的方法也有了一些变化。大概框架依然遵循前文的内容,仅更新部分需要处理的内容。

1. Alamofire 部分

Alamofire 已经废弃了 SessionManager,改用 Session,因此需处理替换一下相关的代码:


func getManager() -> SessionManager {
        let oauth2 = OAuth2CodeGrant(settings: [
            "client_id": "my_swift_app",
            "client_secret": "C7447242",
            "authorize_uri": "https://github.com/login/oauth/authorize",
            "token_uri": "https://github.com/login/oauth/access_token",   // code grant only
            "redirect_uris": ["myapp://oauth/callback"],   // register your own "myapp" scheme in Info.plist
            "scope": "user repo:status",
            "secret_in_body": true,    // Github needs this
            "keychain": false,         // if you DON'T want keychain integration
            ] as OAuth2JSON)

        let oauthHandler = OAuth2Handler(oauth2: oauth2)
        let interceptor = Interceptor(adapter: oauthHandler, retrier: oauthHandler)
        let session = Alamofire.Session(interceptor: interceptor)
        return session
}

2. Oauth 部分

修改 OAuth2Handler 代码,由于 Alamofire 5 中 RequestRetrier / RequestAdapter 协议签名有变化,因此需重新实现 RequestRetrier / RequestAdapter 协议:


extension OAuth2Handler: RequestRetrier {
    func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
        
        if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401, let req = request.request {
            var dataRequest = OAuth2DataRequest(request: req, callback: { _ in })
            dataRequest.context = completion
            loader.enqueue(request: dataRequest)
            loader.attemptToAuthorize { authParams, _ in
                self.loader.dequeueAndApply { req in
                    if let comp = req.context as? ((RetryResult) -> Void) {
                        let result = (authParams != nil) ? RetryResult.retry : RetryResult.doNotRetry
                        comp(result)
                    }
                }
            }
        } else {
            completion(.doNotRetry)
        }
    }
}

extension OAuth2Handler: RequestAdapter {

    func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) {
        
        guard loader.oauth2.accessToken != nil else {
            completion(.failure(NSError(domain:"", code:0, userInfo:nil)))
            return
        }
        
        if let signedRequest = try? urlRequest.signed(with: loader.oauth2) {
            completion(.success(signedRequest))
        } else {
            completion(.failure(NSError(domain:"", code:0, userInfo:nil)))
        }
    }
}

3. Catalyst 部分

原有方案可直接套用到 Catalyst 项目中,但我在实际测试中发现 iOS 项目(iPhone / iPad)中可以正常请求,而 macOS 中则认证完成后无法正常请求数据。经排查后发现是因为使用了 Keychain 保存对应的 Oauth token:"keychain": true,如果想在 macOS 中使用,则需开启「Keychain Sharing」