Giter VIP home page Giter VIP logo

ios-sessiontools's Introduction

SessionTools

CI Status Version Carthage compatible License Platform codecov codebeat badge

Purpose

This library makes session management easier. There are a few main goals:

  • Provide a simple way to create "session" objects for storing, updating, deleting, and refreshing session-related data (credentials, tokens, etc.).
  • Provide a pre-built UserSession to simplify the work needed to deal with user login/logout.
  • Broadcast login/logout/update notifications when your model object changes.
  • Store your model object in a secure storage mechanism since it usually contains sensitive information.

Key Concepts

  • Session - A base class for creating something that can store, retrieve, and delete an item in a SessionContainer. Can post notifications by providing something that conforms to NotificationPosting (NotificationCenter conforms to this by default).
  • SessionContainer - A container for storing data to the keychain.
  • Refreshable - Represents something that can be refreshable. In our use case, a Session<T>.
  • NotficationPosting - Represents something that can post a notification.
  • UserSession - Handles storage, deletion, and retrieval of the current user. Broadcasts notifications when user session state changes. Can call a RefreshHandler block if provided.
  • KeychainStorageContainer - A container that uses the keychain as the backing store. You can make your own container by subclassing SessionContainer.
  • KeychainContainerConfig - A class to configure the KeychainStorageContainer for use.

Usage

SessionTools out of the box uses the keychain to store your session data. To allow for maximum flexibility, you can use the SessionTools/Base subspec to integrate SessionTools without the keychain dependencies. Going forward, we are assuming you are using this on iOS and want to use the keychain.

Preparation

1. Create a model object that conforms to Codable.

You've probably already created some variation of this in your codebase.

struct Model: Codable {
    let firstName: String
    let lastName: String
    let email: String
    let token: String
}
2. Create a KeychainContainerConfig supplied with a keychainName.

The default container configuration uses an unmanaged keychain container. This means the framework will make no attempt to remove the session's data on your behalf and you will be responsible for removing this session's data by calling Session.deleteItem() when needed. Because of differences between OS versions, we cannot make any guarantees on how long the data will persist in the keychain beyond the current install. For more discussion, see below.

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

If you only want the session's data to persist for the current installation, instantiate your KeychainContainerConfig with lifecycle KeychainLifecycle.currentInstall() and pass in an installation identifier. This identifier should remain stable for the current installation but change between installations.

This installation indentifier is prepended to the keychain name before running keychain operations. Because the identifier changes between installations the previous key will no longer match. You could theoretically still get that key back if you reuse a previous installation identifier, but because of differences between OS versions, we cannot make any guarantees on how long the data will persist in the keychain beyond the current install. For more discussion, see below.

let managedConfig = KeychainContainerConfig(keychainName: "com.app.name", lifecycle: .currentInstall(identifier: installationIdentifier))
3. Create a KeychainStorageContainer supplied with your KeychainContainerConfig.
let container = KeychainStorageContainer<Model>(config: config)

You can also create your own object conforming to SessionContainer and instantiate it if you're not wanting to use the default keychain storage mechanism.

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. Wrap your storage container in the AnySessionContainer type erased container.
let anyContainer = AnySessionContainer(container)

Now you can make use of a Session in a few different ways.

Option 1 - Use the Session<T> class as-is.

You just need to supply your model object's type, the container to store it in, and the key that will be associated with your object in the storage container.

let session = Session<Model>(container: anyContainer, storageIdentifier: "identifier.for.your.model.object")
Option 2 - Create a subclass of Session<T>, supplying your model for the generic placeholder type.

Optionally, conform to Refreshable if you want to automatically handle refreshing your model when it's expired (e.g. an API token).

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

    // MARK: - Refreshable

    func refresh(completion: @escaping RefreshCompletion) {
        // your refresh code here
        completion(nil)
    }
}
Option 3 (Most Common) - Use UserSession<T>, a Session<T> subclass already setup for you to deal with common log in/log out operations.
let userSession = UserSession<Model>(container: anyContainer, storageIdentifier: "identifier.for.your.model.object", notificationPoster: NotificationCenter.default)

You can also supply a refreshHandler to the UserSession initializer if you want to automatically handle refreshing your model when it's expired (e.g. an API token).

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)

Now you can easily get a reference to your app's current user.

let currentUser = userSession.currentUser

You can also check if there is currently a user logged in.

let isUserLoggedIn = userSession.isLoggedIn

UserSession<T> also contains methods that can be called to log in, log out, or update the information when you deem appropriate.

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
}

Parts of your code can optionally observe these log in/out/update events by subscribing to the Notification.Name.userSessionStateDidChange notification.

NotificationCenter.default.addObserver(self, selector: #selector(didUpdateUser:), name: .userSessionStateDidChange, object: nil)

Access the userSessionState property on the notification to easily get the state change that occurred.

@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:
        // ...
    }
}

Example

To run the example project, you'll first need to use Carthage to install SessionTool's dependency (KeychainAccess.

After installing Carthage, clone the repo:

git clone https://github.com/BottleRocketStudios/iOS-SessionTools.git

Next, use Carthage to install the dependencies:

carthage update

From here, you can open up SessionTools.xcworkspace and run the examples:

Requirements

  • iOS 9.0+
  • watchOS 2.0+
  • tvOS 9.0+
  • macOS 10.9+
  • Swift 5.0

Installation

CocoaPods

SessionTools is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'SessionTools'

Or if you're not working in an environment with access to the keychain, use the base subspec:

pod 'SessionTools/Base'

Carthage

Add the following to your Cartfile:

github "BottleRocketStudios/iOS-SessionTools"

Run carthage update and follow the steps as described in Carthage's README.

NOTE: Don't forget to add both SessionTools.framework and the KeychainAccess.framework dependency to your project if your environment has access to the keychain.

Keychain Discussion

In the past, the keychain data you add from your app persists across installs. While this is still the case, we can't guarantee this will remain the case in future versions. This post summarizes that fact. In iOS 10.3 Beta 2, Apple added a feature to remove all application keychain data on uninstall, but reverted when it caused issues with existing apps. When/if Apple formalizes the behavoir, we will formalize here as well.

Contributing

See the CONTRIBUTING document. Thank you, contributors!

ios-sessiontools's People

Contributors

br-earl-gaspard avatar br-tyler-milner avatar earlgaspard avatar ganttastic avatar tylermilner avatar wmcginty avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.