Skip to content

Commit 1bc6282

Browse files
Use enum for events
1 parent 804b5aa commit 1bc6282

16 files changed

+89
-108
lines changed

Fyreplace/Commands/DestinationCommands.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct DestinationCommandsContent: View {
2222
ForEach(Destination.all) { destination in
2323
Button(destination.titleKey) {
2424
Task {
25-
eventBus.send(.navigationShortcut(to: destination))
25+
eventBus.send(.navigationShortcut(destination: destination))
2626
}
2727
}
2828
.disabled(destination.requiresAuthentication && token.isEmpty)

Fyreplace/Events/Event.swift

+13-48
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,25 @@
11
import SwiftUI
22

33
@MainActor
4-
protocol Event: Sendable {}
5-
6-
@MainActor
7-
protocol UnfortunateEvent: Event {}
4+
enum Event: Sendable {
5+
case error(description: LocalizedStringResource = CriticalError.defaultDescription)
6+
case failure(title: LocalizedStringResource, text: LocalizedStringResource)
7+
case authorizationIssue
8+
case navigationShortcut(destination: Destination)
9+
case randomCode(_ code: String)
10+
}
811

9-
struct ErrorEvent: UnfortunateEvent, LocalizedError {
10-
static let defaultDescription: String.LocalizationValue = "Error.Unknown"
12+
struct CriticalError: LocalizedError {
13+
static let defaultDescription: LocalizedStringResource = "Error.Unknown"
1114

12-
var description = defaultDescription
15+
var description: LocalizedStringResource
1316

1417
var errorDescription: String {
1518
.init(localized: description)
1619
}
1720
}
1821

19-
extension Event {
20-
typealias error = ErrorEvent
21-
}
22-
23-
struct FailureEvent: UnfortunateEvent {
24-
let title: LocalizedStringKey
25-
let text: LocalizedStringKey
26-
}
27-
28-
extension Event {
29-
typealias failure = FailureEvent
30-
}
31-
32-
struct AuthorizationIssueEvent: UnfortunateEvent {}
33-
34-
extension Event {
35-
typealias authorizationIssue = AuthorizationIssueEvent
36-
}
37-
38-
struct NavigationShortcutEvent: Event {
39-
let destination: Destination
40-
41-
init(to destination: Destination) {
42-
self.destination = destination
43-
}
44-
}
45-
46-
extension Event {
47-
typealias navigationShortcut = NavigationShortcutEvent
48-
}
49-
50-
struct RandomCodeEvent: Event {
51-
let randomCode: String
52-
53-
init(_ randomCode: String) {
54-
self.randomCode = randomCode
55-
}
56-
}
57-
58-
extension Event {
59-
typealias randomCode = RandomCodeEvent
22+
struct Failure: Sendable {
23+
let title: LocalizedStringResource
24+
let text: LocalizedStringResource
6025
}

Fyreplace/Views/MainView.swift

+16-10
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ struct MainView: View, MainViewProtocol {
1111
var showFailure = false
1212

1313
@State
14-
var errors: [ErrorEvent] = []
14+
var errors: [CriticalError] = []
1515

1616
@State
17-
var failures: [FailureEvent] = []
17+
var failures: [Failure] = []
1818

1919
@Environment(\.api)
2020
private var api
@@ -41,7 +41,7 @@ struct MainView: View, MainViewProtocol {
4141
}
4242
}
4343
.alert(
44-
failures.first?.title ?? "",
44+
String(localized: failures.first?.title ?? ""),
4545
isPresented: $showFailure,
4646
presenting: failures.first,
4747
actions: { _ in
@@ -51,16 +51,22 @@ struct MainView: View, MainViewProtocol {
5151
}
5252
}
5353
},
54-
message: { (failure: FailureEvent) in
54+
message: { (failure: Failure) in
5555
Text(failure.text)
5656
}
5757
)
58-
.onReceive(eventBus.events.compactMap { ($0 as? ErrorEvent) }, perform: addError)
59-
.onReceive(eventBus.events.compactMap { ($0 as? FailureEvent) }, perform: addFailure)
60-
.onReceive(eventBus.events.filter { $0 is AuthorizationIssueEvent }) { _ in
61-
token = ""
62-
eventBus.send(
63-
.failure(title: "Error.Unauthorized.Title", text: "Error.Unauthorized.Text"))
58+
.onReceive(eventBus.events) {
59+
switch $0 {
60+
case let .error(description):
61+
addError(.init(description: description))
62+
case let .failure(title, text):
63+
addFailure(.init(title: title, text: text))
64+
case .authorizationIssue:
65+
token = ""
66+
eventBus.send(.failure(title: "Error.Unauthorized.Title", text: "Error.Unauthorized.Text"))
67+
default:
68+
break
69+
}
6470
}
6571
#if os(macOS)
6672
.task { await keepRefreshingToken() }

Fyreplace/Views/MainViewProtocol.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import Foundation
44
protocol MainViewProtocol: ViewProtocol {
55
var showError: Bool { get nonmutating set }
66
var showFailure: Bool { get nonmutating set }
7-
var errors: [ErrorEvent] { get nonmutating set }
8-
var failures: [FailureEvent] { get nonmutating set }
7+
var errors: [CriticalError] { get nonmutating set }
8+
var failures: [Failure] { get nonmutating set }
99
}
1010

1111
@MainActor
1212
extension MainViewProtocol {
13-
func addError(_ error: ErrorEvent) {
13+
func addError(_ error: CriticalError) {
1414
errors.append(error)
1515
tryShowSomething()
1616
}
@@ -21,7 +21,7 @@ extension MainViewProtocol {
2121
tryShowSomething()
2222
}
2323

24-
func addFailure(_ failure: FailureEvent) {
24+
func addFailure(_ failure: Failure) {
2525
failures.append(failure)
2626
tryShowSomething()
2727
}

Fyreplace/Views/Navigation/RegularNavigation.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ struct RegularNavigation: View, NavigationProtocol {
5151
}
5252
}
5353
}
54-
.onReceive(
55-
eventBus.events
56-
.filter { _ in isInForeground }
57-
.compactMap { $0 as? NavigationShortcutEvent }
58-
) {
59-
selectedDestination = $0.destination
54+
.onReceive(eventBus.events) {
55+
guard isInForeground else { return }
56+
57+
if case let .navigationShortcut(destination) = $0 {
58+
selectedDestination = destination
59+
}
6060
}
6161
.onDeepLink(perform: handle)
6262
}

Fyreplace/Views/Screens/EmailsScreenProtocol.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ extension EmailsScreenProtocol {
3737
)
3838

3939
case .unauthorized:
40-
return .authorizationIssue()
40+
return .authorizationIssue
4141

4242
case .forbidden, .default:
4343
return .error()

Fyreplace/Views/Screens/LoginScreen.swift

+7-7
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,13 @@ struct LoginScreen: View, LoginScreenProtocol {
9393
}
9494
.disabled(isLoading)
9595
.animation(.default, value: isWaitingForRandomCode)
96-
.onReceive(
97-
eventBus.events
98-
.filter { _ in isWaitingForRandomCode }
99-
.compactMap { $0 as? RandomCodeEvent }
100-
) {
101-
randomCode = $0.randomCode
102-
submit()
96+
.onReceive(eventBus.events) {
97+
guard isWaitingForRandomCode else { return }
98+
99+
if case let .randomCode(code) = $0 {
100+
randomCode = code
101+
submit()
102+
}
103103
}
104104
}
105105

Fyreplace/Views/Screens/LoginScreenProtocol.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ extension LoginScreenProtocol {
2323
randomCode = ""
2424
}
2525

26-
func sendEmail() async throws -> UnfortunateEvent? {
26+
func sendEmail() async throws -> Event? {
2727
let response = try await api.createNewToken(body: .json(.init(identifier: identifier)))
2828

2929
switch response {
@@ -55,7 +55,7 @@ extension LoginScreenProtocol {
5555
}
5656
}
5757

58-
func createToken() async throws -> UnfortunateEvent? {
58+
func createToken() async throws -> Event? {
5959
let response = try await api.createToken(
6060
body: .json(.init(identifier: identifier, secret: randomCode))
6161
)

Fyreplace/Views/Screens/RegisterScreen.swift

+7-7
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,13 @@ struct RegisterScreen: View, RegisterScreenProtocol {
156156
}
157157
.disabled(isLoading)
158158
.animation(.default, value: isWaitingForRandomCode)
159-
.onReceive(
160-
eventBus.events
161-
.filter { _ in isWaitingForRandomCode }
162-
.compactMap { $0 as? RandomCodeEvent }
163-
) {
164-
randomCode = $0.randomCode
165-
submit()
159+
.onReceive(eventBus.events) {
160+
guard isWaitingForRandomCode else { return }
161+
162+
if case let .randomCode(code) = $0 {
163+
randomCode = code
164+
submit()
165+
}
166166
}
167167
}
168168

Fyreplace/Views/Screens/RegisterScreenProtocol.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ extension RegisterScreenProtocol {
3030
isRegistering = false
3131
}
3232

33-
func sendEmail() async throws -> UnfortunateEvent? {
33+
func sendEmail() async throws -> Event? {
3434
let response = try await api.createUser(
3535
body: .json(.init(email: email, username: username))
3636
)
@@ -88,7 +88,7 @@ extension RegisterScreenProtocol {
8888
}
8989
}
9090

91-
func createToken() async throws -> UnfortunateEvent? {
91+
func createToken() async throws -> Event? {
9292
let response = try await api.createToken(
9393
body: .json(.init(identifier: email, secret: randomCode))
9494
)

Fyreplace/Views/Screens/SettingsScreenProtocol.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ extension SettingsScreenProtocol {
3030
return nil
3131

3232
case .unauthorized:
33-
return .authorizationIssue()
33+
return .authorizationIssue
3434

3535
case .forbidden, .default:
3636
return .error()
@@ -66,7 +66,7 @@ extension SettingsScreenProtocol {
6666
)
6767

6868
case .unauthorized:
69-
return .authorizationIssue()
69+
return .authorizationIssue
7070

7171
case .forbidden, .default:
7272
return .error()
@@ -88,7 +88,7 @@ extension SettingsScreenProtocol {
8888
return nil
8989

9090
case .unauthorized:
91-
return .authorizationIssue()
91+
return .authorizationIssue
9292

9393
case .forbidden, .default:
9494
return .error()
@@ -121,7 +121,7 @@ extension SettingsScreenProtocol {
121121
)
122122

123123
case .unauthorized:
124-
return .authorizationIssue()
124+
return .authorizationIssue
125125

126126
case .forbidden, .default:
127127
return .error()

Fyreplace/Views/ViewProtocol.swift

+10-11
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ protocol LoadingViewProtocol: APIViewProtocol {
1818

1919
@MainActor
2020
extension ViewProtocol {
21-
func call(action: () async throws -> UnfortunateEvent?) async {
22-
let unfortunateEvent: UnfortunateEvent?
21+
func call(action: () async throws -> Event?) async {
22+
let unfortunateEvent: Event?
2323

2424
do {
2525
unfortunateEvent = try await action()
@@ -29,21 +29,20 @@ extension ViewProtocol {
2929
unfortunateEvent = .error()
3030
}
3131

32-
if let event = unfortunateEvent {
33-
eventBus.send(event)
34-
35-
if let event = unfortunateEvent as? ErrorEvent,
36-
event.description == ErrorEvent.defaultDescription
37-
{
38-
SentrySDK.capture(error: event)
39-
}
32+
guard let unfortunateEvent else { return }
33+
eventBus.send(unfortunateEvent)
34+
35+
if case let .error(error) = unfortunateEvent,
36+
error == CriticalError.defaultDescription
37+
{
38+
SentrySDK.capture(error: CriticalError(description: error))
4039
}
4140
}
4241
}
4342

4443
@MainActor
4544
extension LoadingViewProtocol {
46-
func callWhileLoading(action: () async throws -> UnfortunateEvent?) async {
45+
func callWhileLoading(action: () async throws -> Event?) async {
4746
isLoading = true
4847
await call(action: action)
4948
isLoading = false

FyreplaceTests/Screens/LoginScreenTests.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ struct LoginScreenTests {
3838
screen.identifier = FakeClient.badUsername
3939
await screen.submit()
4040
#expect(eventBus.storedEvents.count == 1)
41-
#expect(eventBus.storedEvents.first is FailureEvent)
41+
#expect(eventBus.storedEvents.first?.isFailure == true)
4242
#expect(!screen.isWaitingForRandomCode)
4343
}
4444

@@ -82,7 +82,7 @@ struct LoginScreenTests {
8282
screen.isWaitingForRandomCode = true
8383
await screen.submit()
8484
#expect(eventBus.storedEvents.count == 1)
85-
#expect(eventBus.storedEvents.first is FailureEvent)
85+
#expect(eventBus.storedEvents.first?.isFailure == true)
8686
}
8787

8888
@Test("Valid random code produces no failures")

FyreplaceTests/Screens/RegisterScreenTests.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ struct RegisterScreenTests {
8686
screen.username = FakeClient.badUsername
8787
await screen.submit()
8888
#expect(eventBus.storedEvents.count == 1)
89-
#expect(eventBus.storedEvents.first is FailureEvent)
89+
#expect(eventBus.storedEvents.first?.isFailure == true)
9090
#expect(!screen.isWaitingForRandomCode)
9191
}
9292

@@ -98,7 +98,7 @@ struct RegisterScreenTests {
9898
screen.email = FakeClient.badEmail
9999
await screen.submit()
100100
#expect(eventBus.storedEvents.count == 1)
101-
#expect(eventBus.storedEvents.first is FailureEvent)
101+
#expect(eventBus.storedEvents.first?.isFailure == true)
102102
#expect(!screen.isWaitingForRandomCode)
103103
}
104104

@@ -136,7 +136,7 @@ struct RegisterScreenTests {
136136
screen.isWaitingForRandomCode = true
137137
await screen.submit()
138138
#expect(eventBus.storedEvents.count == 1)
139-
#expect(eventBus.storedEvents.first is FailureEvent)
139+
#expect(eventBus.storedEvents.first?.isFailure == true)
140140
}
141141

142142
@Test("Valid random code produces no failures")

FyreplaceTests/Screens/SettingsScreenTests.swift

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ struct SettingsScreenTests {
4040
with: try await .init(collecting: FakeClient.notImageBody, upTo: 64)
4141
)
4242
#expect(eventBus.storedEvents.count == 1)
43+
#expect(eventBus.storedEvents.first?.isFailure == true)
4344
#expect(screen.currentUser?.avatar == "")
4445
}
4546

0 commit comments

Comments
 (0)