Skip to content

Commit 7d45641

Browse files
authored
Merge pull request #192 from mattrubin/view-model-parameters
Refactor TokenList
2 parents 5905042 + 5d14098 commit 7d45641

File tree

9 files changed

+227
-188
lines changed

9 files changed

+227
-188
lines changed

Authenticator/Source/AppController.swift

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ class AppController {
3333
private let store: TokenStore
3434
private var component: Root {
3535
didSet {
36+
let viewModel = currentViewModel()
3637
// TODO: Fix the excessive updates of bar button items so that the tick can run while they are on screen.
37-
if case .none = component.viewModel.modal {
38+
if case .none = viewModel.modal {
3839
if displayLink == nil {
3940
startTick()
4041
}
@@ -43,12 +44,12 @@ class AppController {
4344
stopTick()
4445
}
4546
}
46-
view.updateWithViewModel(component.viewModel)
47+
view.updateWithViewModel(viewModel)
4748
}
4849
}
4950
private lazy var view: RootViewController = {
5051
return RootViewController(
51-
viewModel: self.component.viewModel,
52+
viewModel: self.currentViewModel(),
5253
dispatchAction: self.handleAction
5354
)
5455
}()
@@ -71,15 +72,15 @@ class AppController {
7172

7273
// If this is a demo, show the scanner even in the simulator.
7374
let deviceCanScan = QRScanner.deviceCanScan || CommandLine.isDemo
74-
component = Root(
75-
persistentTokens: store.persistentTokens,
76-
displayTime: .currentDisplayTime(),
77-
deviceCanScan: deviceCanScan
78-
)
75+
component = Root(deviceCanScan: deviceCanScan)
7976

8077
startTick()
8178
}
8279

80+
private func currentViewModel() -> Root.ViewModel {
81+
return component.viewModel(for: store.persistentTokens, at: .currentDisplayTime())
82+
}
83+
8384
// MARK: - Tick
8485

8586
private var displayLink: CADisplayLink?
@@ -97,8 +98,8 @@ class AppController {
9798

9899
@objc
99100
func tick() {
100-
// Dispatch an event to trigger a view model update.
101-
handleEvent(.updateDisplayTime(.currentDisplayTime()))
101+
// Update the view with a new view model for the current display time.
102+
view.updateWithViewModel(currentViewModel())
102103
}
103104

104105
// MARK: - Update
@@ -126,35 +127,39 @@ class AppController {
126127
case let .addToken(token, success, failure):
127128
do {
128129
try store.addToken(token)
129-
handleEvent(success(store.persistentTokens))
130+
handleEvent(success)
130131
} catch {
131132
handleEvent(failure(error))
132133
}
133134

134135
case let .saveToken(token, persistentToken, success, failure):
135136
do {
136137
try store.saveToken(token, toPersistentToken: persistentToken)
137-
handleEvent(success(store.persistentTokens))
138+
handleEvent(success)
138139
} catch {
139140
handleEvent(failure(error))
140141
}
141142

142-
case let .updatePersistentToken(persistentToken, success, failure):
143+
case let .updatePersistentToken(persistentToken, failure):
143144
do {
144145
try store.updatePersistentToken(persistentToken)
145-
handleEvent(success(store.persistentTokens))
146+
view.updateWithViewModel(currentViewModel())
146147
} catch {
147148
handleEvent(failure(error))
148149
}
149150

150-
case let .moveToken(fromIndex, toIndex, success):
151-
store.moveTokenFromIndex(fromIndex, toIndex: toIndex)
152-
handleEvent(success(store.persistentTokens))
151+
case let .moveToken(fromIndex, toIndex, failure):
152+
do {
153+
try store.moveTokenFromIndex(fromIndex, toIndex: toIndex)
154+
view.updateWithViewModel(currentViewModel())
155+
} catch {
156+
handleEvent(failure(error))
157+
}
153158

154-
case let .deletePersistentToken(persistentToken, success, failure):
159+
case let .deletePersistentToken(persistentToken, failure):
155160
do {
156161
try store.deletePersistentToken(persistentToken)
157-
handleEvent(success(store.persistentTokens))
162+
view.updateWithViewModel(currentViewModel())
158163
} catch {
159164
handleEvent(failure(error))
160165
}

Authenticator/Source/Component.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
protocol Component {
2727
// MARK: View
2828
associatedtype ViewModel
29-
var viewModel: ViewModel { get }
3029

3130
// MARK: Update
3231
associatedtype Action

Authenticator/Source/Demo.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ struct DemoTokenStore: TokenStore {
7777
throw Error()
7878
}
7979

80-
func moveTokenFromIndex(_ origin: Int, toIndex destination: Int) {
81-
return
80+
func moveTokenFromIndex(_ origin: Int, toIndex destination: Int) throws {
81+
throw Error()
8282
}
8383

8484
func deletePersistentToken(_ persistentToken: PersistentToken) throws {

Authenticator/Source/Root.swift

Lines changed: 28 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ struct Root: Component {
5454
}
5555
}
5656

57-
init(persistentTokens: [PersistentToken], displayTime: DisplayTime, deviceCanScan: Bool) {
58-
tokenList = TokenList(persistentTokens: persistentTokens, displayTime: displayTime)
57+
init(deviceCanScan: Bool) {
58+
tokenList = TokenList()
5959
modal = .none
6060
self.deviceCanScan = deviceCanScan
6161
}
@@ -66,9 +66,9 @@ struct Root: Component {
6666
extension Root {
6767
typealias ViewModel = RootViewModel
6868

69-
var viewModel: ViewModel {
69+
func viewModel(for persistentTokens: [PersistentToken], at displayTime: DisplayTime) -> ViewModel {
7070
return ViewModel(
71-
tokenList: tokenList.viewModel,
71+
tokenList: tokenList.viewModel(for: persistentTokens, at: displayTime),
7272
modal: modal.viewModel
7373
)
7474
}
@@ -89,35 +89,32 @@ extension Root {
8989
}
9090

9191
enum Event {
92-
case tokenListEvent(TokenList.Event)
93-
case updateDisplayTime(DisplayTime)
94-
95-
case addTokenFromURLSucceeded([PersistentToken])
96-
97-
case tokenFormSucceeded([PersistentToken])
92+
case addTokenFromURLSucceeded
93+
case tokenFormSucceeded
9894

9995
case addTokenFailed(Error)
10096
case saveTokenFailed(Error)
97+
case updateTokenFailed(Error)
98+
case moveTokenFailed(Error)
99+
case deleteTokenFailed(Error)
101100
}
102101

103102
enum Effect {
104103
case addToken(Token,
105-
success: ([PersistentToken]) -> Event,
104+
success: Event,
106105
failure: (Error) -> Event)
107106

108107
case saveToken(Token, PersistentToken,
109-
success: ([PersistentToken]) -> Event,
108+
success: Event,
110109
failure: (Error) -> Event)
111110

112111
case updatePersistentToken(PersistentToken,
113-
success: ([PersistentToken]) -> Event,
114112
failure: (Error) -> Event)
115113

116114
case moveToken(fromIndex: Int, toIndex: Int,
117-
success: ([PersistentToken]) -> Event)
115+
failure: (Error) -> Event)
118116

119117
case deletePersistentToken(PersistentToken,
120-
success: ([PersistentToken]) -> Event,
121118
failure: (Error) -> Event)
122119

123120
case showErrorMessage(String)
@@ -166,31 +163,24 @@ extension Root {
166163

167164
mutating func update(_ event: Event) -> Effect? {
168165
switch event {
169-
case .tokenListEvent(let event):
170-
return handleTokenListEvent(event)
171-
172-
case .updateDisplayTime(let displayTime):
173-
return handleTokenListEvent(.updateDisplayTime(displayTime))
174-
175-
case .addTokenFromURLSucceeded(let persistentTokens):
176-
return handleTokenListEvent(.tokenChangeSucceeded(persistentTokens))
166+
case .addTokenFromURLSucceeded:
167+
return nil
177168

178-
case .tokenFormSucceeded(let persistentTokens):
169+
case .tokenFormSucceeded:
179170
// Dismiss the modal form.
180171
modal = .none
181-
return handleTokenListEvent(.tokenChangeSucceeded(persistentTokens))
172+
return nil
182173

183174
case .addTokenFailed:
184175
return .showErrorMessage("Failed to add token.")
185176
case .saveTokenFailed:
186177
return .showErrorMessage("Failed to save token.")
187-
}
188-
}
189-
190-
private mutating func handleTokenListEvent(_ event: TokenList.Event) -> Effect? {
191-
let effect = tokenList.update(event)
192-
return effect.flatMap { effect in
193-
handleTokenListEffect(effect)
178+
case .updateTokenFailed:
179+
return .showErrorMessage("Failed to update token.")
180+
case .moveTokenFailed:
181+
return .showErrorMessage("Failed to move token.")
182+
case .deleteTokenFailed:
183+
return .showErrorMessage("Failed to delete token.")
194184
}
195185
}
196186

@@ -209,19 +199,17 @@ extension Root {
209199
modal = .editForm(form)
210200
return nil
211201

212-
case let .updateToken(persistentToken, success, failure):
202+
case let .updateToken(persistentToken):
213203
return .updatePersistentToken(persistentToken,
214-
success: compose(success, Event.tokenListEvent),
215-
failure: compose(failure, Event.tokenListEvent))
204+
failure: Event.updateTokenFailed)
216205

217-
case let .moveToken(fromIndex, toIndex, success):
206+
case let .moveToken(fromIndex, toIndex):
218207
return .moveToken(fromIndex: fromIndex, toIndex: toIndex,
219-
success: compose(success, Event.tokenListEvent))
208+
failure: Event.moveTokenFailed)
220209

221-
case let .deletePersistentToken(persistentToken, success, failure):
210+
case let .deletePersistentToken(persistentToken):
222211
return .deletePersistentToken(persistentToken,
223-
success: compose(success, Event.tokenListEvent),
224-
failure: compose(failure, Event.tokenListEvent))
212+
failure: Event.deleteTokenFailed)
225213

226214
case .showErrorMessage(let message):
227215
return .showErrorMessage(message)
@@ -349,7 +337,3 @@ private extension Root.Modal {
349337
return result
350338
}
351339
}
352-
353-
private func compose<A, B, C>(_ transform: @escaping (A) -> B, _ handler: @escaping (B) -> C) -> (A) -> C {
354-
return { handler(transform($0)) }
355-
}

0 commit comments

Comments
 (0)