Skip to content

Commit e0cb32b

Browse files
authored
Merge pull request #65 from 3a4oT/sendable
Conform Navigator to @unchecked Sendable.
2 parents b142ded + 28d9e90 commit e0cb32b

File tree

2 files changed

+75
-62
lines changed

2 files changed

+75
-62
lines changed

Sources/Navigator.swift

+70-61
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Created by Freek (github.com/frzi) 2021
44
//
55

6+
import Dispatch
67
import SwiftUI
78

89
/// EnvironmentObject storing the state of a Router.
@@ -15,7 +16,8 @@ import SwiftUI
1516
/// ```swift
1617
/// @EnvironmentObject var navigator: Navigator
1718
/// ```
18-
public final class Navigator: ObservableObject {
19+
public final class Navigator: ObservableObject, @unchecked Sendable {
20+
private let serialAccessQueue = DispatchQueue(label: "serialAccessQueue", qos: .default)
1921

2022
@Published private var historyStack: [String]
2123
@Published private var forwardStack: [String] = []
@@ -84,29 +86,31 @@ public final class Navigator: ObservableObject {
8486
/// - Parameter path: Path of the new location to navigate to.
8587
/// - Parameter replace: if `true` will replace the last path in the history stack with the new path.
8688
public func navigate(_ path: String, replace: Bool = false) {
87-
let path = resolvePaths(self.path, path)
88-
let previousPath = self.path
89-
90-
guard path != previousPath else {
91-
#if DEBUG
92-
print("SwiftUIRouter: Navigating to the same path ignored.")
93-
#endif
94-
return
95-
}
96-
97-
forwardStack.removeAll()
98-
99-
if replace && !historyStack.isEmpty {
100-
historyStack[historyStack.endIndex - 1] = path
101-
}
102-
else {
103-
historyStack.append(path)
104-
}
105-
106-
lastAction = NavigationAction(
107-
currentPath: path,
108-
previousPath: previousPath,
109-
action: .push)
89+
serialAccessQueue.sync {
90+
let path = resolvePaths(self.path, path)
91+
let previousPath = self.path
92+
93+
guard path != previousPath else {
94+
#if DEBUG
95+
print("SwiftUIRouter: Navigating to the same path ignored.")
96+
#endif
97+
return
98+
}
99+
100+
forwardStack.removeAll()
101+
102+
if replace && !historyStack.isEmpty {
103+
historyStack[historyStack.endIndex - 1] = path
104+
}
105+
else {
106+
historyStack.append(path)
107+
}
108+
109+
lastAction = NavigationAction(
110+
currentPath: path,
111+
previousPath: previousPath,
112+
action: .push)
113+
}
110114
}
111115

112116
/// Go back *n* steps in the navigation history.
@@ -118,49 +122,54 @@ public final class Navigator: ObservableObject {
118122
guard canGoBack else {
119123
return
120124
}
121-
122-
let previousPath = path
123-
124-
let total = min(total, historyStack.count)
125-
let start = historyStack.count - total
126-
forwardStack.append(contentsOf: historyStack[start...].reversed())
127-
historyStack.removeLast(total)
128-
129-
lastAction = NavigationAction(
130-
currentPath: path,
131-
previousPath: previousPath,
132-
action: .back)
125+
serialAccessQueue.sync {
126+
let previousPath = path
127+
128+
let total = min(total, historyStack.count)
129+
let start = historyStack.count - total
130+
forwardStack.append(contentsOf: historyStack[start...].reversed())
131+
historyStack.removeLast(total)
132+
133+
lastAction = NavigationAction(
134+
currentPath: path,
135+
previousPath: previousPath,
136+
action: .back)
137+
}
133138
}
134139

135140
/// Go forward *n* steps in the navigation history.
136141
///
137142
/// `total` will always be clamped and thus prevent from going out of bounds.
138143
///
139144
/// - Parameter total: Total steps to go forward.
140-
public func goForward(total: Int = 1) {
141-
guard canGoForward else {
142-
return
143-
}
144-
145-
let previousPath = path
146-
147-
let total = min(total, forwardStack.count)
148-
let start = forwardStack.count - total
149-
historyStack.append(contentsOf: forwardStack[start...])
150-
forwardStack.removeLast(total)
151-
152-
lastAction = NavigationAction(
153-
currentPath: path,
154-
previousPath: previousPath,
155-
action: .forward)
156-
}
145+
public func goForward(total: Int = 1) {
146+
guard canGoForward else {
147+
return
148+
}
149+
150+
serialAccessQueue.sync {
151+
let previousPath = path
152+
153+
let total = min(total, forwardStack.count)
154+
let start = forwardStack.count - total
155+
historyStack.append(contentsOf: forwardStack[start...])
156+
forwardStack.removeLast(total)
157+
158+
lastAction = NavigationAction(
159+
currentPath: path,
160+
previousPath: previousPath,
161+
action: .forward)
162+
}
163+
}
157164

158165
/// Clear the entire navigation history.
159-
public func clear() {
160-
forwardStack.removeAll()
161-
historyStack = [path]
162-
lastAction = nil
163-
}
166+
public func clear() {
167+
serialAccessQueue.sync {
168+
forwardStack.removeAll()
169+
historyStack = [path]
170+
lastAction = nil
171+
}
172+
}
164173
}
165174

166175
extension Navigator: Equatable {
@@ -180,9 +189,9 @@ extension Navigator {
180189

181190
// MARK: -
182191
/// Information about a navigation that occurred.
183-
public struct NavigationAction: Equatable {
192+
public struct NavigationAction: Equatable, Sendable {
184193
/// Directional difference between the current path and the previous path.
185-
public enum Direction {
194+
public enum Direction: Sendable {
186195
/// The new path is higher up in the hierarchy *or* a completely different path.
187196
/// Example: `/user/settings` → `/user`. Or `/favorite/music` → `/news/latest`.
188197
case higher
@@ -193,7 +202,7 @@ public struct NavigationAction: Equatable {
193202
}
194203

195204
/// The kind of navigation that occurred.
196-
public enum Action {
205+
public enum Action: Sendable {
197206
/// Navigated to a new path.
198207
case push
199208
/// Navigated back in the stack.

Sources/Router.swift

+5-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ public struct Router<Content: View>: View {
6363

6464
// MARK: - Relative path environment key
6565
struct RelativeRouteEnvironment: EnvironmentKey {
66-
static var defaultValue = "/"
66+
#if swift(>=5.10)
67+
static nonisolated(unsafe) var defaultValue = "/"
68+
#else
69+
static var defaultValue = "/"
70+
#endif
6771
}
6872

6973
public extension EnvironmentValues {

0 commit comments

Comments
 (0)