Skip to content

Commit

Permalink
fix(refresh): ensure authenticator always reflects current state (#30)
Browse files Browse the repository at this point in the history
* fix(refresh): ensure authenticator always reflects current state

* fix(refresh): ensure authenticator always reflects current state

* fix(refresh): ensure authenticator always reflects current state

* fix(refresh): ensure authenticator always reflects current state

* chore: cleanup

* chore: cleanup
  • Loading branch information
mhamann committed Nov 14, 2022
1 parent b2efef3 commit e3a9856
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
<key>Rownd.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
<integer>1</integer>
</dict>
<key>RowndFrameworkTestApp.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
<integer>2</integer>
</dict>
</dict>
</dict>
Expand Down
10 changes: 7 additions & 3 deletions Sources/Rownd/Models/Context/Context.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,19 +77,23 @@ func rowndStateReducer(action: Action, state: RowndState?) -> RowndState {
auth: authReducer(action: action, state: state?.auth),
user: userReducer(action: action, state: state?.user)
)

RowndState.save(state: newState)
}

return newState
}

let thunkMiddleware: Middleware<RowndState> = createThunkMiddleware()
let authenticatorMiddleware: Middleware<RowndState> = AuthenticatorSubscription.createAuthenticatorMiddleware()

let store = Store(
reducer: rowndStateReducer,
state: RowndState(),
middleware: [thunkMiddleware]
middleware: [
thunkMiddleware,
authenticatorMiddleware
]
)

struct StateError: Error, CustomStringConvertible {
Expand Down
2 changes: 0 additions & 2 deletions Sources/Rownd/Rownd.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ public class Rownd: NSObject {
private static var appleSignUpCoordinator: AppleSignUpCoordinator? = AppleSignUpCoordinator(inst)
internal static var apiClient = RowndApi().client
internal static var authenticator = Authenticator()
// private static let authenticatorSubscription = AuthenticatorSubscription()

private override init() {
super.init()
AuthenticatorSubscription.subscribeToAuthState()
}

public static func configure(launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil, appKey: String?) async {
Expand Down
39 changes: 28 additions & 11 deletions Sources/Rownd/framework/Authenticator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
//

import Foundation
import Combine
import Get
import Factory
import ReSwift

enum AuthenticationError: Error {
case noAccessTokenPresent
Expand Down Expand Up @@ -61,21 +61,38 @@ fileprivate class TokenApiClientDelegate : APIClientDelegate {
// which leads to memmory corruption and weird app crashes.
class AuthenticatorSubscription: NSObject {
private static let inst: AuthenticatorSubscription = AuthenticatorSubscription()
private var stateListeners = Set<AnyCancellable>()

@Published private var authState: ObservableState<AuthState> = store.subscribe { $0.auth }

private override init() {}

internal static func subscribeToAuthState() {
inst.authState
.$current
.sink { authState in
Task {
await Rownd.authenticator.setAuthState(authState)
/// This checks the incoming action to determine whether it contains an AuthState payload and pushes that
/// to the Authenticator if present. This prevents race conditions between the internal Rownd state and any
/// external subscribers. The Authenticator MUST always reflect the correct state in order to prevent race conditions.
internal static func createAuthenticatorMiddleware<State>() -> Middleware<State> {
return { dispatch, getState in
return { next in
return { action in
var authState: AuthState?

switch(action) {
case let action as SetAuthState:
authState = action.payload
case let action as InitializeRowndState:
authState = action.payload.auth
default:
break
}

guard let authState = authState else {
return next(action)
}

Task {
await Rownd.authenticator.setAuthState(authState)
next(action)
}
}
}
.store(in: &inst.stateListeners)
}
}
}

Expand Down

0 comments on commit e3a9856

Please sign in to comment.