Skip to content

Commit

Permalink
Swift 6: Main actor isolated store view helpers (#3283)
Browse files Browse the repository at this point in the history
* `@preconcurrency @MainActor` isolation of `Store`

* Remove unneeded `@MainActor`s

* Remove thread checking code

* Remove unneeded `@MainActor`s

* Swift 5.10 compatibility fixes

* wip

* More 5.10 fixes

* wip

* fixes

* wip

* wip

* up the timeout

* wip

* Fixes

* wip

* wip

* wip

* wip

* wip

* wip

* Main actor logger

* wip

* wip

* wip

* fix

* fix

* fix

* fix compilation error

* wip

* wip

* bring back yield

---------

Co-authored-by: Brandon Williams <mbrandonw@hey.com>
  • Loading branch information
stephencelis and mbrandonw authored Aug 27, 2024
1 parent dfa29e0 commit 0d8980f
Show file tree
Hide file tree
Showing 28 changed files with 375 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-clocks",
"state" : {
"revision" : "3581e280bf0d90c3fb9236fb23e75a5d8c46b533",
"version" : "1.0.4"
"revision" : "b9b24b69e2adda099a1fa381cda1eeec272d5b53",
"version" : "1.0.5"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections",
"state" : {
"revision" : "3d2dc41a01f9e49d84f0a3925fb858bed64f702d",
"version" : "1.1.2"
"revision" : "9bf03ff58ce34478e66aaee630e491823326fd06",
"version" : "1.1.3"
}
},
{
Expand All @@ -77,23 +77,23 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-dependencies",
"state" : {
"revision" : "21660b042cd8fd0bdd45cc39050cacd4e91a63a4",
"version" : "1.3.8"
"revision" : "3ef38bb702a1a2f39c7e19fc0578403b8ee52b17",
"version" : "1.3.9"
}
},
{
"identity" : "swift-docc-plugin",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-docc-plugin",
"state" : {
"revision" : "26ac5758409154cc448d7ab82389c520fa8a8247",
"version" : "1.3.0"
"revision" : "0510d9160330025fb5823f7845c26af3cd56a405",
"version" : "1.4.1"
}
},
{
"identity" : "swift-docc-symbolkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-docc-symbolkit",
"location" : "https://github.com/swiftlang/swift-docc-symbolkit",
"state" : {
"revision" : "b45d1f2ed151d057b54504d653e0da5552844e34",
"version" : "1.0.0"
Expand Down Expand Up @@ -122,17 +122,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-navigation",
"state" : {
"revision" : "4d04eb04807dc3176515184abe08c3adcbb04713",
"version" : "2.1.0"
"revision" : "e834b3760731160d7d448509ee6a1408c8582a6b",
"version" : "2.2.0"
}
},
{
"identity" : "swift-perception",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-perception",
"state" : {
"revision" : "1552c8f722ac256cc0b8daaf1a7073217d4fcdfb",
"version" : "1.3.4"
"revision" : "bc67aa8e461351c97282c2419153757a446ae1c9",
"version" : "1.3.5"
}
},
{
Expand All @@ -149,8 +149,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-syntax",
"state" : {
"revision" : "06b5cdc432e93b60e3bdf53aff2857c6b312991a",
"version" : "600.0.0-prerelease-2024-07-30"
"revision" : "515f79b522918f83483068d99c68daeb5116342d",
"version" : "600.0.0-prerelease-2024-08-20"
}
},
{
Expand All @@ -167,8 +167,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
"state" : {
"revision" : "357ca1e5dd31f613a1d43320870ebc219386a495",
"version" : "1.2.2"
"revision" : "96beb108a57f24c8476ae1f309239270772b2940",
"version" : "1.2.5"
}
}
],
Expand Down
19 changes: 8 additions & 11 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,11 @@ let package = Package(
]
)

//for target in package.targets where target.type != .system {
// target.swiftSettings = target.swiftSettings ?? []
// target.swiftSettings?.append(
// .unsafeFlags([
// "-c", "release",
// "-emit-module-interface", "-enable-library-evolution",
// "-Xfrontend", "-warn-concurrency",
// "-Xfrontend", "-enable-actor-data-race-checks",
// ])
// )
//}
#if compiler(>=6)
for target in package.targets where target.type != .system {
target.swiftSettings = target.swiftSettings ?? []
target.swiftSettings?.append(contentsOf: [
.enableExperimentalFeature("StrictConcurrency"),
])
}
#endif
2 changes: 1 addition & 1 deletion Sources/ComposableArchitecture/Dependencies/Dismiss.swift
Original file line number Diff line number Diff line change
Expand Up @@ -152,5 +152,5 @@ extension DismissEffect {

private enum DismissKey: DependencyKey {
static let liveValue = DismissEffect()
static var testValue = DismissEffect()
static let testValue = DismissEffect()
}
7 changes: 6 additions & 1 deletion Sources/ComposableArchitecture/Internal/Logger.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import OSLog

@_spi(Logging)
#if swift(<5.10)
@MainActor(unsafe)
#else
@preconcurrency @MainActor
#endif
public final class Logger {
public static let shared = Logger()
public var isEnabled = false
Expand Down Expand Up @@ -35,5 +40,5 @@ public final class Logger {
#endif
}

private var isRunningForPreviews =
private let isRunningForPreviews =
ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
13 changes: 10 additions & 3 deletions Sources/ComposableArchitecture/Observation/Alert+Observation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import SwiftUI
@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *)
extension View {
/// Presents an alert when a piece of optional state held in a store becomes non-`nil`.
#if swift(<5.10)
@MainActor(unsafe)
#else
@preconcurrency @MainActor
#endif
public func alert<Action>(_ item: Binding<Store<AlertState<Action>, Action>?>) -> some View {
let store = item.wrappedValue
let alertState = store?.withState { $0 }
Expand Down Expand Up @@ -33,11 +38,13 @@ extension View {
}
)
}
}

@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *)
extension View {
/// Presents an alert when a piece of optional state held in a store becomes non-`nil`.
#if swift(<5.10)
@MainActor(unsafe)
#else
@preconcurrency @MainActor
#endif
public func confirmationDialog<Action>(
_ item: Binding<Store<ConfirmationDialogState<Action>, Action>?>
) -> some View {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@
///
/// - Parameter action: An action for the binding to send values through.
/// - Returns: A binding.
#if swift(<5.10)
@MainActor(unsafe)
#else
@preconcurrency @MainActor
#endif
public func sending(_ action: CaseKeyPath<Action, Value>) -> Binding<Value> {
self.binding[state: self.keyPath, action: action]
}
Expand Down Expand Up @@ -220,6 +225,11 @@
///
/// - Parameter action: An action for the binding to send values through.
/// - Returns: A binding.
#if swift(<5.10)
@MainActor(unsafe)
#else
@preconcurrency @MainActor
#endif
public func sending(_ action: CaseKeyPath<Action, Value>) -> Binding<Value> {
self.bindable[state: self.keyPath, action: action]
}
Expand Down Expand Up @@ -248,6 +258,11 @@
///
/// - Parameter action: An action for the binding to send values through.
/// - Returns: A binding.
#if swift(<5.10)
@MainActor(unsafe)
#else
@preconcurrency @MainActor
#endif
public func sending(_ action: CaseKeyPath<Action, Value>) -> Binding<Value> {
self.bindable[state: self.keyPath, action: action]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ import SwiftUI
/// }
/// }
/// ```
#if swift(>=5.10)
@preconcurrency @MainActor
#else
@MainActor(unsafe)
#endif
public func scope<State: ObservableState, Action, ElementState, ElementAction>(
state: KeyPath<State, StackState<ElementState>>,
action: CaseKeyPath<Action, StackAction<ElementState, ElementAction>>
Expand All @@ -72,6 +77,11 @@ import SwiftUI
///
/// See ``SwiftUI/Binding/scope(state:action:fileID:filePath:line:column:)`` defined on `Binding` for more
/// information.
#if swift(>=5.10)
@preconcurrency @MainActor
#else
@MainActor(unsafe)
#endif
public func scope<State: ObservableState, Action, ElementState, ElementAction>(
state: KeyPath<State, StackState<ElementState>>,
action: CaseKeyPath<Action, StackAction<ElementState, ElementAction>>
Expand Down Expand Up @@ -104,6 +114,11 @@ import SwiftUI
///
/// See ``SwiftUI/Binding/scope(state:action:fileID:filePath:line:column:)`` defined on `Binding` for more
/// information.
#if swift(>=5.10)
@preconcurrency @MainActor
#else
@MainActor(unsafe)
#endif
public func scope<State: ObservableState, Action, ElementState, ElementAction>(
state: KeyPath<State, StackState<ElementState>>,
action: CaseKeyPath<Action, StackAction<ElementState, ElementAction>>
Expand Down
19 changes: 17 additions & 2 deletions Sources/ComposableArchitecture/Observation/Store+Observation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@
}

extension Store: Equatable {
public static func == (lhs: Store, rhs: Store) -> Bool {
public static nonisolated func == (lhs: Store, rhs: Store) -> Bool {
lhs === rhs
}
}

extension Store: Hashable {
public func hash(into hasher: inout Hasher) {
public nonisolated func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
}
Expand Down Expand Up @@ -155,6 +155,11 @@
/// - state: A key path to optional child state.
/// - action: A case key path to presentation child actions.
/// - Returns: A binding of an optional child store.
#if swift(>=5.10)
@preconcurrency @MainActor
#else
@MainActor(unsafe)
#endif
public func scope<State: ObservableState, Action, ChildState, ChildAction>(
state: KeyPath<State, ChildState?>,
action: CaseKeyPath<Action, PresentationAction<ChildAction>>,
Expand Down Expand Up @@ -225,6 +230,11 @@
/// - state: A key path to optional child state.
/// - action: A case key path to presentation child actions.
/// - Returns: A binding of an optional child store.
#if swift(>=5.10)
@preconcurrency @MainActor
#else
@MainActor(unsafe)
#endif
public func scope<State: ObservableState, Action, ChildState, ChildAction>(
state: KeyPath<State, ChildState?>,
action: CaseKeyPath<Action, PresentationAction<ChildAction>>,
Expand Down Expand Up @@ -321,6 +331,11 @@
}

extension UIBindable {
#if swift(>=5.10)
@preconcurrency @MainActor
#else
@MainActor(unsafe)
#endif
public func scope<State: ObservableState, Action, ChildState, ChildAction>(
state: KeyPath<State, ChildState?>,
action: CaseKeyPath<Action, PresentationAction<ChildAction>>,
Expand Down
5 changes: 4 additions & 1 deletion Sources/ComposableArchitecture/Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,10 @@ public final class Store<State, Action> {
}

deinit {
Logger.shared.log("\(storeTypeName(of: self)).deinit")
guard Thread.isMainThread else { return }
MainActor._assumeIsolated {
Logger.shared.log("\(storeTypeName(of: self)).deinit")
}
}

/// Calls the given closure with a snapshot of the current state of the store.
Expand Down
15 changes: 15 additions & 0 deletions Sources/ComposableArchitecture/SwiftUI/Alert.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ extension View {
/// - Parameters:
/// - store: A store that is focused on ``PresentationState`` and ``PresentationAction`` for an
/// alert.
#if swift(<5.10)
@MainActor(unsafe)
#else
@preconcurrency @MainActor
#endif
public func alert<ButtonAction>(
store: Store<PresentationState<AlertState<ButtonAction>>, PresentationAction<ButtonAction>>
) -> some View {
Expand Down Expand Up @@ -43,6 +48,11 @@ extension View {
message:
"Further scope the store into the 'state' and 'action' cases, instead. For more information, see the following article: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.5#Enum-driven-navigation-APIs"
)
#if swift(<5.10)
@MainActor(unsafe)
#else
@preconcurrency @MainActor
#endif
public func alert<State, Action, ButtonAction>(
store: Store<PresentationState<State>, PresentationAction<Action>>,
state toDestinationState: @escaping (_ state: State) -> AlertState<ButtonAction>?,
Expand All @@ -51,6 +61,11 @@ extension View {
self._alert(store: store, state: toDestinationState, action: fromDestinationAction)
}

#if swift(<5.10)
@MainActor(unsafe)
#else
@preconcurrency @MainActor
#endif
private func _alert<State, Action, ButtonAction>(
store: Store<PresentationState<State>, PresentationAction<Action>>,
state toDestinationState: @escaping (_ state: State) -> AlertState<ButtonAction>?,
Expand Down
15 changes: 15 additions & 0 deletions Sources/ComposableArchitecture/SwiftUI/ConfirmationDialog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ extension View {
/// - Parameters:
/// - store: A store that is focused on ``PresentationState`` and ``PresentationAction`` for a
/// dialog.
#if swift(<5.10)
@MainActor(unsafe)
#else
@preconcurrency @MainActor
#endif
public func confirmationDialog<ButtonAction>(
store: Store<
PresentationState<ConfirmationDialogState<ButtonAction>>,
Expand Down Expand Up @@ -46,6 +51,11 @@ extension View {
message:
"Further scope the store into the 'state' and 'action' cases, instead. For more information, see the following article: https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/migratingto1.5#Enum-driven-navigation-APIs"
)
#if swift(<5.10)
@MainActor(unsafe)
#else
@preconcurrency @MainActor
#endif
public func confirmationDialog<State, Action, ButtonAction>(
store: Store<PresentationState<State>, PresentationAction<Action>>,
state toDestinationState: @escaping (_ state: State) -> ConfirmationDialogState<ButtonAction>?,
Expand All @@ -54,6 +64,11 @@ extension View {
self._confirmationDialog(store: store, state: toDestinationState, action: fromDestinationAction)
}

#if swift(<5.10)
@MainActor(unsafe)
#else
@preconcurrency @MainActor
#endif
private func _confirmationDialog<State, Action, ButtonAction>(
store: Store<PresentationState<State>, PresentationAction<Action>>,
state toDestinationState: @escaping (_ state: State) -> ConfirmationDialogState<ButtonAction>?,
Expand Down
Loading

0 comments on commit 0d8980f

Please sign in to comment.