Cobalt 是 E-sites iOS Suite 的一部分。
该 E-sites Swift iOS API 客户端用于标准的 RESTful API,默认支持 OAuth2。
package.swift 依赖项
.package(url: "https://github.com/e-sites/cobalt.git", from: "7.0.0"),
将 "Cobalt"
添加到您的应用程序/库目标的 dependencies
中,例如这样
.target(name: "BestExampleApp", dependencies: ["Cobalt"]),
扩展 Cobalt
类以便在您自己的 API 客户端中使用它。
import Cobalt
class APIClient: CobaltClient {
static let `default` = APIClient()
private init() {
let config = CobaltConfig {
$0.authentication.path = "/oauth/v2/token"
$0.authentication.authorizationPath = "/oauth/v2/connect"
$0.authentication.clientID = "my_oauth_client_id"
$0.authentication.clientSecret = "my_oauth_client_secret"
$0.authentication.pkceEnabled = false // Disabled by default
$0.host = "https://api.domain.com"
}
super.init(config: config)
}
}
APIClient 内部使用 Swift 的 Combine 框架来处理请求的响应。
class APIClient: CobaltClient {
// ...
func users() -> AnyPublisher<[User], CobaltError> {
let request = CobaltRequest {
$0.path = "/users"
$0.parameters = [
"per_page": 10
]
}
return self.request(request).tryMap { response in
return try response.map(to: [User].self)
}.eraseToAnyPublisher()
}
}
要直接使用磁盘缓存,请将以下行添加到您的 Podfile
pod 'Cobalt/Cache'
并像这样实现它
class APIClient: CobaltClient {
// ...
func users() -> AnyPublisher<[User], CobaltError> {
let request = CobaltRequest {
$0.path = "/users"
$0.parameters = [
"per_page": 10
]
$0.diskCachePolicy = .expires(seconds: 60 * 60 * 24) // expires after 1 day
}
return self.request(request).tryMap { response in
return try response.map(to: [User].self)
}.eraseToAnyPublisher()
}
}
要清除整个缓存
APIClientInstance.cache.clearAll()
如果您想使用 OAuth2 协议登录用户,请使用 Cobalt
类中的 login()
函数。 在内部,它将处理提供的 access_token
的检索和刷新
func login(email: String, password: String) -> AnyPublisher<Void, CobaltError>
您还可以使用其他身份验证选项
如果您想检索用户资料,您需要 .oauth2(.password)
身份验证,这样只有在用户通过 login()
函数请求了 access_token 时,请求才会成功。
如果 access_token 过期,Cobalt 将使用 refresh_token 自动刷新它
class APIClient: CobaltClient {
// ...
func profile() -> AnyPublisher<User, CobaltError> {
let request = CobaltRequest {
$0.authentication = .oauth2(.password)
$0.path = "/user/profile"
}
return request(request).tryCompactMap { response in
if let dictionary = response as? [String: Any], let data = dictionary["data"] as? CobaltResponse {
return try data.map(to: User.self)
}
}.eraseToAnyPublisher()
}
}
此授权类型需要用户在 webview 或浏览器中登录。 要启用这种类型的身份验证,请将 .oauth2(.authorizationCode)
添加到 Cobalt.Request
。 如果 access_token 过期,Cobalt 将使用 refresh_token 自动刷新它。
class APIClient: CobaltClient {
// ...
func profile() -> Promise<User> {
let request = Cobalt.Request({
$0.authentication = .oauth2(.authorizationCode)
$0.path = "/user/profile"
})
return request(request).then { json -> Promise<User> in
let user = try json["data"].map(to: User.self)
return Promise(user)
}
}
}
在请求个人资料之前,用户需要登录。 为了简化,Cobalt 可以为您创建一个 AuthorizationCodeRequest
,其中包含您需要将用户重定向到的 URL
public struct AuthorizationCodeRequest {
public var url: URL
public var redirectUri: String
public var state: String?
public var codeVerifier: String?
}
class OAuthAuthenticator {
// ...
private var presentedViewController: UIViewController?
func login() {
// Cobalt uses the credentials you provided in the config
// When you enabled PKCE, Cobalt will also create the code challenge and verifier for you
// The code verifier is returned to you in the AuthorizationCodeRequest
client.startAuthorizationFlow(
scope: ["openid", "profile", "email", "offline_access"],
redirectUri: "app://oauth/authorized"
).subscribe(onSuccess: { [weak self] request in
self?.request = request
let safariController = SFSafariViewController(url: request.url)
self?.presentedViewController = UINavigationController(rootViewController: safariController)
self?.presentedViewController!.setNavigationBarHidden(true, animated: false)
viewController.present(
(self?.presentedViewController)!,
animated: true,
completion: nil
)
}, onError: { error in
print("error: \(error)")
}).disposed(by: disposeBag)
}
// You execute this when receiving the callback from: "app://oauth/authorized?code=code&scope=scope&state=state"
func getAccessToken(from code: String, scope: String? = nil, state: String? = nil) -> Single<Void> {
defer {
presentedViewController = nil
}
if let presentedViewController = presentedViewController {
presentedViewController.dismiss(animated: true, completion: nil)
}
// Validate that the state of the callback equals the state created by Cobalt
// Perform some extra validation by your needs
if request.state != state {
return Single<Void>.error(Error.invalidUrl)
}
client.requestTokenFromAuthorizationCode(initialRequest: request, code: code).subscribe(onSuccess: {
// The user is signed in successfully
}, onError: { error in
// Something went wrong, notify the user
})
}
}
您必须为 Cobalt.Request
提供 .oauth2(.clientCredentials)
身份验证
class APIClient: CobaltClient {
// ...
func register(email: String, password: String) -> AnyPublisher<Void, CobaltError> {
let request = CobaltRequest {
$0.httpMethod = .post
$0.path = "/register"
$0.authentication = .oauth2(.clientCredentials)
$0.parameters = [
"email": email,
"password": password
]
}
return request(request).map { _ in }
.eraseToAnyPublisher()
}
这样 Cobalt 就会知道该请求需要具有 access-token 的 client_credentials
grant_type。
如果用户已经具有带有该 grant_type 的 access_token,Cobalt 将使用它。 否则,它将为您请求一个新的 access_token
要从其内存和钥匙串中删除 access_token,请使用
func clearAccessToken()
只需打开 Cobalt.xcodeproj