Skip to content

Commit

Permalink
Merge pull request #13 from nodes-ios/async_tokens
Browse files Browse the repository at this point in the history
Make async token refresh mechanism
  • Loading branch information
jakobmygind authored Feb 7, 2023
2 parents 27fb06c + 403be69 commit e1c7ea1
Show file tree
Hide file tree
Showing 112 changed files with 1,510 additions and 2,266 deletions.
20 changes: 20 additions & 0 deletions App/APITokensProtocolConformance.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// APITokensProtocolConformance.swift
// PROJECT_NAME
//
// Created by Jakob Mygind on 30/01/2023.
//

import MLTokenHandler
import Model
import Foundation

extension APITokensEnvelope: APITokensEnvelopeProtocol {
public var getAccessToken: String {
token.rawValue
}

public var getRefreshToken: String {
refreshToken.token.rawValue
}
}
76 changes: 23 additions & 53 deletions App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,38 @@
import APIClientLive
import AppFeature
import Combine
import Dependencies
import Localizations
import Model
import NStackSDK
import PersistenceClient
import Style
import SwiftUI
import XCTestDynamicOverlay

@main
final class AppDelegate: NSObject, UIApplicationDelegate {

func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {

return true
}
}

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
var localizations: ObservableLocalizations = .init(.bundled)
var tokenCancellable: AnyCancellable?


func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let scene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: scene)

self.setupNStack()

self.registerFonts()
self.startAppView()
}
Expand All @@ -47,60 +48,29 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// MARK: - Dependencies setup

extension SceneDelegate {

/// Allows using project specific fonts int the same way you use any of the iOS-provided fonts.
/// Custom fonts should be located on the `Style`.
fileprivate func registerFonts() {
CustomFonts.registerCustomFonts()
}

/// Handles setup of [NStack](https://nstack.io) services.
/// This example demonstrates [Localization](https://nstack-io.github.io/docs/docs/features/localize.html) service activation.
fileprivate func setupNStack() {
localizations = startNStackSDK(
appId: <#appID#>,
restAPIKey: <#restAPIKey#>
)
}

}

/// Defines content view of the window assigned to the scene with the required dependencies.
/// This is the entry point for the app which defines the source of truth for related environment settings.
fileprivate func startAppView() {
let baseURL = Configuration.API.baseURL

let persistenceClient = PersistenceClient.live(keyPrefix: Bundle.main.bundleIdentifier!)

let authHandler = AuthenticationHandler(
refreshURL: <#refreshURL#>,
tokens: persistenceClient.tokens.load()
)
tokenCancellable = authHandler.tokenUpdatePublisher.sink(
receiveValue: persistenceClient.tokens.save)

let environment = AppEnvironment(
mainQueue: DispatchQueue.main.eraseToAnyScheduler(),
apiClient: .live(baseURL: baseURL, authenticationHandler: authHandler),
date: Date.init,
calendar: .autoupdatingCurrent,
localizations: localizations,
appVersion: .live,
persistenceClient: persistenceClient,
tokenUpdatePublisher: authHandler.tokenUpdatePublisher.eraseToAnyPublisher(),
networkMonitor: .live(queue: .main)
)


@Dependency(\.localizations) var localizations

#if RELEASE
AppView(viewModel: .init())
.environmentObject(localizations)
#else
let apiEnvironments = Configuration.API.environments

let vm = AppViewModel(environment: environment)
#if RELEASE
let appView = AppView(viewModel: vm)
.environmentObject(localizations)
#else
let appView = AppView(viewModel: vm)
.environmentObject(localizations)
.environment(\.apiEnvironments, apiEnvironments)
#endif

let appView = AppView(viewModel: .init())
.environmentObject(localizations)
.environment(\.apiEnvironments, apiEnvironments)
#endif

self.window?.rootViewController = UIHostingController(rootView: appView)
self.window?.makeKeyAndVisible()
}
Expand Down
58 changes: 58 additions & 0 deletions App/EnvVars.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// EnvVars.swift
// PROJECT_NAME
//
// Created by Jakob Mygind on 24/01/2023.
//

import Dependencies
import Foundation
import NStackSDK
import PersistenceClient

public struct EnvVars {

public struct NStackVars {
let appId: String
let restAPIKey: String
let environment: NStackSDK.Configuration.NStackEnvironment
}

var baseURL: URL
var refreshURL: URL
var persistenceKeyPrefix: String
var nstackVars: NStackVars
}

extension EnvVars: DependencyKey {
#warning("Set up environment variables here")
public static var liveValue: EnvVars {
.init(
baseURL: Configuration.API.baseURL,
refreshURL: unimplemented(),
persistenceKeyPrefix: Bundle.main.bundleIdentifier!,
nstackVars: .init(
appId: unimplemented(),
restAPIKey: unimplemented(),
environment: currentNStackEnvironment()
)
)
}

static func currentNStackEnvironment() -> NStackSDK.Configuration.NStackEnvironment {
#if RELEASE
return .production
#elseif TEST
return .test
#else
return .debug
#endif
}
}

extension DependencyValues {
public var envVars: EnvVars {
get { self[EnvVars.self] }
set { self[EnvVars.self] = newValue }
}
}
4 changes: 2 additions & 2 deletions App/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>API_BASE_URL_DEV</key>
<string>$(API_BASE_URL_DEV)</string>
<key>API_BASE_URL_STAGING</key>
<string>$(API_BASE_URL_STAGING)</string>
<key>API_BASE_URL_PROD</key>
<string>$(API_BASE_URL_PROD)</string>
<key>DEFAULT_BASE_URL</key>
Expand Down
82 changes: 82 additions & 0 deletions App/LiveDependencies.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// Environment.swift
// PROJECT_NAME
//
// Created by Jakob Mygind on 24/01/2023.
//

import APIClient
import APIClientLive
import AppVersion
import Dependencies
import Foundation
import Localizations
import Model
import NetworkClient
import NStackSDK
import PersistenceClient
import MLTokenHandler

extension APIClient: DependencyKey {
public static var liveValue: APIClient {

@Dependency(\.envVars) var envVars
@Dependency(\.persistenceClient) var persistenceClient

var continuation: AsyncStream<APITokensEnvelope?>.Continuation!

let tokenValuesStream: AsyncStream<APITokensEnvelope?> = .init { cont in
continuation = cont
}
let authHandler = AuthenticationHandlerAsync<APITokensEnvelope>(
refreshURL: envVars.refreshURL,
getTokens: persistenceClient.tokens.load,
saveTokens: { tokens in
persistenceClient.tokens.save(tokens)
continuation.yield(tokens)
}
)

return APIClient.live(
baseURL: envVars.baseURL,
authenticationHandler: authHandler,
tokensUpdateStream: tokenValuesStream
)
}
}

extension PersistenceClient: DependencyKey {

public static var liveValue: PersistenceClient {
@Dependency(\.envVars) var envVars

return .live(keyPrefix: envVars.persistenceKeyPrefix)
}
}

extension AppVersion: DependencyKey {
public static var liveValue: AppVersion {
.live
}
}

/// Handles setup of [NStack](https://nstack.io) services.
/// This example demonstrates [Localization](https://nstack-io.github.io/docs/docs/features/localize.html) service activation.
extension ObservableLocalizations: DependencyKey {
public static var liveValue: ObservableLocalizations {

@Dependency(\.envVars) var envVars
return startNStackSDK(
appId: envVars.nstackVars.appId,
restAPIKey: envVars.nstackVars.restAPIKey,
environment: envVars.nstackVars.environment
)
}
}

extension NetworkClient: DependencyKey {
public static var liveValue: NetworkClient {
.live(queue: .main)
}
}

2 changes: 1 addition & 1 deletion App/ProjectConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public enum Configuration {
public enum API {
enum Key: String, CaseIterable {
#if !RELEASE
case dev = "API_BASE_URL_DEV"
case dev = "API_BASE_URL_STAGING"
#endif
case prod = "API_BASE_URL_PROD"
static var defaultKey = "DEFAULT_BASE_URL"
Expand Down
2 changes: 1 addition & 1 deletion App/Test.xcconfig → App/Staging.xcconfig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include "Production.xcconfig"

API_BASE_URL_DEV = some.apiurl.dev.com
API_BASE_URL_STAGING = some.apiurl.dev.com

DEFAULT_BASE_URL = API_BASE_URL_PROD
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "LoginFeature"
BuildableName = "LoginFeature"
BlueprintName = "LoginFeature"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "LoginFeatureTests"
BuildableName = "LoginFeatureTests"
BlueprintName = "LoginFeatureTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "LoginFeature"
BuildableName = "LoginFeature"
BlueprintName = "LoginFeature"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
Loading

0 comments on commit e1c7ea1

Please sign in to comment.