SessionTools
目的
这个库使会话管理变得更简单。有几个主要目标
- 提供一种简单的方式来创建“会话”对象,用于存储、更新、删除和刷新与会话相关的数据(证书、令牌等)。
- 提供一个预构建的
UserSession
以简化处理用户登录/注销所需的工作。 - 当您的模型对象更改时,广播登录/注销/更新的通知。
- 由于通常包含敏感信息,因此将模型对象存储在安全存储机制中。
关键概念
- 会话 - 创建一个可以存储、检索和删除
SessionContainer
中的项的基类。《可以发布通知通过提供符合《《通知发布》的某种东西》`《(《通知中心》按默认`)符合这个规范>`。》《 - 会话容器 - 存储到密钥链的容器。
- 可刷新的 - 表示可刷新的。在我们的用例中,是《`《
Session
```。 - 通知发布 - 表示可以发布通知的东西。
- 用户会话 - 处理当前用户的存储、删除和检索。当用户会话状态更改时,广播通知。如果提供,可以调用《《《代码`《《
RefreshHandler
`阻塞。 - 密钥链存储容器 - 使用密钥链作为后备存储的容器。《您可以`违背`的`《`者`文````》《`子类`《`《语`《`成为一个自定义容器通过`《`存储`《`
SessionContainer
````。 - 密钥链容器配置 - 一个用于配置`《`《`
KeychainStorageContainer
`````的类,以供使用。
使用
SessionTools/Base
subspec在不含密钥链依赖的情况下集成SessionTools。假设您正在iOS上使用此功能并希望使用密钥链。
默认情况下,SessionTools使用密钥链来存储会话数据,以提供最大灵活性。您可以使用准备
Codable
的模型对象。
1. 创建一个符合您可能已经在代码库中创建了此对象的某种变体。
struct Model: Codable {
let firstName: String
let lastName: String
let email: String
let token: String
}
keychainName
的KeychainContainerConfig
。
2. 创建一个包含默认容器配置使用非托管密钥链容器。这意味着框架将不会尝试为您删除会话数据,您将需要通过调用Session.deleteItem()
来负责在需要时删除此会话的数据。由于不同操作系统版本之间的差异,我们无法担保密钥链中的数据将保持多久,超出现有安装的内容。更多信息,请参阅以下内容。
let config = KeychainContainerConfig(keychainName: "your.keychain.name")
如果您只想让当前安装的会话数据持久化,请使用具有生命周期 KeychainLifecycle.currentInstall()
的 KeychainContainerConfig
实例化,并传入一个安装标识符。这个标识符应保持稳定,直到当前安装为止,但应该在不同的安装之间改变。
此安装标识符在运行密钥链操作之前附加在密钥链名称之前。因为标识符在不同的安装之间会改变,所以之前的密钥将不再匹配。理论上,如果您重用以前的安装标识符,仍然可以恢复该密钥,但由于操作系统版本之间的差异,我们无法保证数据将在当前安装之外的密钥链中持续多长时间。有关更多讨论,请参阅下方。
let managedConfig = KeychainContainerConfig(keychainName: "com.app.name", lifecycle: .currentInstall(identifier: installationIdentifier))
KeychainContainerConfig
的 KeychainStorageContainer
。
3. 创建一个带有您的 let container = KeychainStorageContainer<Model>(config: config)
如果您不想使用默认的密钥链存储机制,您还可以创建自己的符合 SessionContainer
的对象,并实例化它。
struct MyStorageContainer: SessionContainer {
func hasItem(forIdentifier identifier: String) -> Bool {
// ...
}
func item(forIdentifier identifier: String, jsonDecoder: JSONDecoder) throws -> Item? {
// ...
}
func removeItem(forIdentifier identifier: String) throws {
// ...
}
func storeItem(_ item: Item, forIdentifier identifier: String, jsonEncoder: JSONEncoder) throws {
// ...
}
}
AnySessionContainer
类型擦除容器中。
4. 将您的存储容器包裹在 let anyContainer = AnySessionContainer(container)
Session
。
现在您可以通过几种不同的方式使用 Session<T>
类。
选项 1 - 直接使用 您只需提供模型对象的类型、存储它的容器以及与对象关联的密钥即可。
let session = Session<Model>(container: anyContainer, storageIdentifier: "identifier.for.your.model.object")
Session<T>
的子类,为泛型占位符类型提供您的模型。
选项2 - 创建一个 可选地,如果您想自动处理模型过期时(例如API令牌)的刷新操作,则可协商 Refreshable
。
class ModelSession: Session<Model>, Refreshable {
// your class code here
// MARK: - Refreshable
func refresh(completion: @escaping RefreshCompletion) {
// your refresh code here
completion(nil)
}
}
UserSession<T>
,一个已经为您设置的 Session<T>
子类来处理常见的登录/注销操作。
选项3(最常用)- 使用 let userSession = UserSession<Model>(container: anyContainer, storageIdentifier: "identifier.for.your.model.object", notificationPoster: NotificationCenter.default)
如果希望在模型过期时自动处理刷新操作(例如API令牌),也可以向 UserSession 初始化器提供 refreshHandler
。
private static func userRefreshHandler(_ completion: @escaping RefreshCompletion) -> Void {
// your refresh code
completion(nil)
}
let userSession = UserSession<Model>(container: container, storageIdentifier: "identifier.for.your.model.object", notificationPoster: NotificationCenter.default, refreshHandler: userRefreshHandler)
现在您可以轻松获取应用当前用户的引用。
let currentUser = userSession.currentUser
您还可以检查当前是否已登录用户。
let isUserLoggedIn = userSession.isLoggedIn
UserSession<T>
还包含在适当的时候可以调用的登录、注销或更新信息的方法。
do {
try userSession.didLogIn(model)
try userSession.didLogOut(nil) // Optionally provide the error that triggered the logout
try userSession.didUpdate(model)
} catch {
// Handle container read/write errors here
}
您的代码的部分可以可选地通过订阅 Notification.Name.userSessionStateDidChange
通知来观察这些登录/注销/更新事件。
NotificationCenter.default.addObserver(self, selector: #selector(didUpdateUser:), name: .userSessionStateDidChange, object: nil)
要通过通知访问 userSessionState
属性,以便轻松获取发生的状态变化。
@objc private func didUpdateUser(_ notification: Notification) {
guard let sessionState = notification.userSessionState else { return }
// Do something with the state
switch sessionState {
case .loggedIn:
// ...
case .loggedOut(let error): // Optionally get a reference to the error that triggered the logout
// ...
case .updated:
// ...
}
}
示例
要运行示例项目,您首先需要使用 Carthage 安装 SessionTool 的依赖(KeychainAccess)。
在 安装 Carthage 之后,克隆存储库
git clone https://github.com/BottleRocketStudios/iOS-SessionTools.git
接下来,使用 Carthage 安装依赖项
carthage update
从这里,您可以打开 SessionTools.xcworkspace
并运行示例
要求
- iOS 9.0+
- watchOS 2.0+
- tvOS 9.0+
- macOS 10.9+
- Swift 5.0
安装
Swift 包管理器
dependencies: [
.package(url: "https://github.com/BottleRocketStudios/iOS-SessionTools.git", from: "1.2.0")
]
CocoaPods
SessionTools 可通过 CocoaPods 获取。要安装它,只需在您的 Podfile 中添加以下行
pod 'SessionTools'
或者如果您在不具备访问密钥链的环境下工作,使用基本子规范
pod 'SessionTools/Base'
Carthage
在您的 Cartfile 中添加以下内容
github "BottleRocketStudios/iOS-SessionTools"
运行 carthage update
,并按照 Carthage 的 README 中描述的步骤操作。
注意:如果您所在的环境可以访问密钥链,别忘了将 SessionTools.framework
和 KeychainAccess.framework
依赖添加到您的项目中。
密钥链讨论
过去,您从应用中添加的密钥链数据在安装后仍然存在。尽管目前情况仍是如此,但我们无法保证这一点会在未来版本中保持不变。有关这一事实的总结可以参考以下帖子:这里。在 iOS 10.3 Beta 2 中,苹果增加了一个在卸载时删除所有应用密钥链数据的功能,但后来由于与现有应用的问题而撤销。如果苹果正式化这一行为,我们也将在此正式化。