Latest1.0.1
Homepagehttps://github.com/BottleRocketStudios/iOS-SessionTools
LicenseApache
Platformsios 9.0
FrameworksFoundation
Authors

CI Status
Version
License
Platform

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.
let config = KeychainContainerConfig(keychainName: "your.keychain.name")
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, clone the repo, and run pod install from the Example directory first.

Requirements

  • iOS 9.0+
  • Swift 4.1

Installation

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'

Contributing

See the CONTRIBUTING document. Thank you, contributors!

Latest podspec

{
    "name": "SessionTools",
    "version": "1.0.1",
    "summary": "Provides a simple way to make "session" objects for storing, deleting, and refreshing data.",
    "description": "Provides a simple way to create "session" objects for use in your own session manager setup. It can store, delete, and refresh any info you want. You can also broadcast notifications when your info changes.",
    "homepage": "https://github.com/BottleRocketStudios/iOS-SessionTools",
    "license": {
        "type": "Apache",
        "file": "LICENSE"
    },
    "authors": {
        "Bottle Rocket Studios": "[email protected]"
    },
    "source": {
        "git": "https://github.com/bottlerocketstudios/iOS-SessionTools.git",
        "tag": "1.0.1"
    },
    "platforms": {
        "ios": "9.0"
    },
    "frameworks": "Foundation",
    "source_files": [
        "SessionTools/Classes/Base/*",
        "SessionTools/Classes/KeychainStorage/*"
    ],
    "subspecs": [
        {
            "name": "Base",
            "source_files": "SessionTools/Classes/Base/*"
        },
        {
            "name": "KeychainStorage",
            "dependencies": {
                "KeychainAccess": []
            }
        }
    ]
}

Pin It on Pinterest

Share This