SessionTools 1.3.0

SessionTools 1.3.0

Earl GaspardWill McGintyTyler MilnerRyan Gant维护。



  • Bottle Rocket Studios

SessionTools

CI Status Version Carthage compatible License Platform codecov codebeat badge

目的

这个库使会话管理变得更简单。有几个主要目标

  • 提供一种简单的方式来创建“会话”对象,用于存储、更新、删除和刷新与会话相关的数据(证书、令牌等)。
  • 提供一个预构建的 UserSession 以简化处理用户登录/注销所需的工作。
  • 当您的模型对象更改时,广播登录/注销/更新的通知。
  • 由于通常包含敏感信息,因此将模型对象存储在安全存储机制中。

关键概念

  • 会话 - 创建一个可以存储、检索和删除 SessionContainer 中的项的基类。《可以发布通知通过提供符合《《通知发布》的某种东西》`《(《通知中心》按默认`)符合这个规范>`。》《
  • 会话容器 - 存储到密钥链的容器。
  • 可刷新的 - 表示可刷新的。在我们的用例中,是《`《Session```。
  • 通知发布 - 表示可以发布通知的东西。
  • 用户会话 - 处理当前用户的存储、删除和检索。当用户会话状态更改时,广播通知。如果提供,可以调用《《《代码`《《RefreshHandler`阻塞。
  • 密钥链存储容器 - 使用密钥链作为后备存储的容器。《您可以`违背`的`《`者`文````》《`子类`《`《语`《`成为一个自定义容器通过`《`存储`《`SessionContainer````。
  • 密钥链容器配置 - 一个用于配置`《`《`KeychainStorageContainer`````的类,以供使用。

使用

默认情况下,SessionTools使用密钥链来存储会话数据,以提供最大灵活性。您可以使用SessionTools/Base subspec在不含密钥链依赖的情况下集成SessionTools。假设您正在iOS上使用此功能并希望使用密钥链。

准备

1. 创建一个符合Codable的模型对象。

您可能已经在代码库中创建了此对象的某种变体。

struct Model: Codable {
    let firstName: String
    let lastName: String
    let email: String
    let token: String
}
2. 创建一个包含keychainNameKeychainContainerConfig

默认容器配置使用非托管密钥链容器。这意味着框架将不会尝试为您删除会话数据,您将需要通过调用Session.deleteItem()来负责在需要时删除此会话的数据。由于不同操作系统版本之间的差异,我们无法担保密钥链中的数据将保持多久,超出现有安装的内容。更多信息,请参阅以下内容。

let config = KeychainContainerConfig(keychainName: "your.keychain.name")

如果您只想让当前安装的会话数据持久化,请使用具有生命周期 KeychainLifecycle.currentInstall()KeychainContainerConfig 实例化,并传入一个安装标识符。这个标识符应保持稳定,直到当前安装为止,但应该在不同的安装之间改变。

此安装标识符在运行密钥链操作之前附加在密钥链名称之前。因为标识符在不同的安装之间会改变,所以之前的密钥将不再匹配。理论上,如果您重用以前的安装标识符,仍然可以恢复该密钥,但由于操作系统版本之间的差异,我们无法保证数据将在当前安装之外的密钥链中持续多长时间。有关更多讨论,请参阅下方

let managedConfig = KeychainContainerConfig(keychainName: "com.app.name", lifecycle: .currentInstall(identifier: installationIdentifier))
3. 创建一个带有您的 KeychainContainerConfigKeychainStorageContainer
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 {
        // ...
    }
}
4. 将您的存储容器包裹在 AnySessionContainer 类型擦除容器中。
let anyContainer = AnySessionContainer(container)

现在您可以通过几种不同的方式使用 Session

选项 1 - 直接使用 Session<T> 类。

您只需提供模型对象的类型、存储它的容器以及与对象关联的密钥即可。

let session = Session<Model>(container: anyContainer, storageIdentifier: "identifier.for.your.model.object")
选项2 - 创建一个 Session<T> 的子类,为泛型占位符类型提供您的模型。

可选地,如果您想自动处理模型过期时(例如API令牌)的刷新操作,则可协商 Refreshable

class ModelSession: Session<Model>, Refreshable {
    // your class code here

    // MARK: - Refreshable

    func refresh(completion: @escaping RefreshCompletion) {
        // your refresh code here
        completion(nil)
    }
}
选项3(最常用)- 使用 UserSession<T>,一个已经为您设置的 Session<T> 子类来处理常见的登录/注销操作。
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.frameworkKeychainAccess.framework 依赖添加到您的项目中。

密钥链讨论

过去,您从应用中添加的密钥链数据在安装后仍然存在。尽管目前情况仍是如此,但我们无法保证这一点会在未来版本中保持不变。有关这一事实的总结可以参考以下帖子:这里。在 iOS 10.3 Beta 2 中,苹果增加了一个在卸载时删除所有应用密钥链数据的功能,但后来由于与现有应用的问题而撤销。如果苹果正式化这一行为,我们也将在此正式化。

贡献

查看贡献文档。感谢贡献者