Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swift 6 compatibility #108

Merged
merged 20 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ jobs:
scheme: Spezi-Package
resultBundle: Spezi-Package-iOS.xcresult
artifactname: Spezi-Package-iOS.xcresult
buildandtest_ios_latest:
name: Build and Test Swift Package iOS Latest
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@feature/specify-swift-version
Supereg marked this conversation as resolved.
Show resolved Hide resolved
with:
runsonlabels: '["macOS", "self-hosted"]'
scheme: Spezi-Package
xcodeversion: latest
swiftVersion: 6
resultBundle: Spezi-Package-iOS-Latest.xcresult
artifactname: Spezi-Package-iOS-Latest.xcresult
buildandtest_watchos:
name: Build and Test Swift Package watchOS
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import class Foundation.ProcessInfo
import PackageDescription

#if swift(<6)
let swiftConcurrency: SwiftSetting = .enableExperimentalFeature("SwiftConcurrency")
let swiftConcurrency: SwiftSetting = .enableExperimentalFeature("StrictConcurrency")
#else
let swiftConcurrency: SwiftSetting = .enableUpcomingFeature("SwiftConcurrency")
let swiftConcurrency: SwiftSetting = .enableUpcomingFeature("StrictConcurrency")
#endif


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ import XCTRuntimeAssertions
protocol CollectionBasedProvideProperty {
func collectArrayElements<Repository: SharedRepository<SpeziAnchor>>(into repository: inout Repository)

func clearValues()
func clearValues(isolated: Bool)
}


/// A protocol that identifies a ``_ProvidePropertyWrapper`` which `Value` type is a `Optional`.
protocol OptionalBasedProvideProperty {
func collectOptional<Repository: SharedRepository<SpeziAnchor>>(into repository: inout Repository)

func clearValues()
func clearValues(isolated: Bool)
}


Expand Down Expand Up @@ -69,7 +69,7 @@ public class _ProvidePropertyWrapper<Value> {


deinit {
clear()
clear(isolated: false)
}
}

Expand Down Expand Up @@ -143,15 +143,32 @@ extension _ProvidePropertyWrapper: StorageValueProvider {
collected = true
}

@MainActor
func clear() {
clear(isolated: true)
}

private func clear(isolated: Bool) {
collected = false

if let wrapperWithOptional = self as? OptionalBasedProvideProperty {
wrapperWithOptional.clearValues()
wrapperWithOptional.clearValues(isolated: isolated)
} else if let wrapperWithArray = self as? CollectionBasedProvideProperty {
wrapperWithArray.clearValues()
wrapperWithArray.clearValues(isolated: isolated)
} else {
performClear(isolated: isolated, of: Value.self)
}
}

private func performClear<V>(isolated: Bool, of type: V.Type) {
if isolated {
MainActor.assumeIsolated { [spezi, id] in
spezi?.handleCollectedValueRemoval(for: id, of: type)
}
} else {
spezi?.handleCollectedValueRemoval(for: id, of: Value.self)
Task { @MainActor [spezi, id] in
spezi?.handleCollectedValueRemoval(for: id, of: type)
}
}
}
}
Expand All @@ -162,8 +179,8 @@ extension _ProvidePropertyWrapper: CollectionBasedProvideProperty where Value: A
repository.setValues(for: id, storedValue.unwrappedArray)
}

func clearValues() {
spezi?.handleCollectedValueRemoval(for: id, of: Value.Element.self)
func clearValues(isolated: Bool) {
performClear(isolated: isolated, of: Value.Element.self)
}
}

Expand All @@ -175,8 +192,8 @@ extension _ProvidePropertyWrapper: OptionalBasedProvideProperty where Value: Any
}
}

func clearValues() {
spezi?.handleCollectedValueRemoval(for: id, of: Value.Wrapped.self)
func clearValues(isolated: Bool) {
performClear(isolated: isolated, of: Value.Wrapped.self)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import SpeziFoundation
protocol StorageValueCollector: SpeziPropertyWrapper {
/// This method is called to retrieve all the requested values from the given ``SpeziStorage`` repository.
/// - Parameter repository: Provides access to the ``SpeziStorage`` repository for read access.
@MainActor
func retrieve<Repository: SharedRepository<SpeziAnchor>>(from repository: Repository)
}

Expand All @@ -25,6 +26,7 @@ extension Module {
retrieveProperties(ofType: StorageValueCollector.self)
}

@MainActor
func injectModuleValues<Repository: SharedRepository<SpeziAnchor>>(from repository: Repository) {
for collector in storageValueCollectors {
collector.retrieve(from: repository)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import SpeziFoundation
protocol StorageValueProvider: SpeziPropertyWrapper {
/// This method is called to collect all provided values into the given ``SpeziStorage`` repository.
/// - Parameter repository: Provides access to the ``SpeziStorage`` repository.
@MainActor
func collect<Repository: SharedRepository<SpeziAnchor>>(into repository: inout Repository)
}

Expand All @@ -25,6 +26,7 @@ extension Module {
retrieveProperties(ofType: StorageValueProvider.self)
}

@MainActor
func collectModuleValues<Repository: SharedRepository<SpeziAnchor>>(into repository: inout Repository) {
for provider in storageValueProviders {
provider.collect(into: &repository)
Expand Down
18 changes: 6 additions & 12 deletions Sources/Spezi/Capabilities/Lifecycle/LifecycleHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,28 +138,22 @@ public protocol LifecycleHandler {
)
extension LifecycleHandler {
#if os(iOS) || os(visionOS) || os(tvOS)
// A documentation for this method exists in the `LifecycleHandler` type which SwiftLint doesn't recognize.
// swiftlint:disable:next missing_docs
/// Empty default implementation.
public func willFinishLaunchingWithOptions(_ application: UIApplication, launchOptions: [UIApplication.LaunchOptionsKey: Any]) {}

// A documentation for this method exists in the `LifecycleHandler` type which SwiftLint doesn't recognize.
// swiftlint:disable:next missing_docs
/// Empty default implementation.
public func sceneWillEnterForeground(_ scene: UIScene) { }

// A documentation for this method exists in the `LifecycleHandler` type which SwiftLint doesn't recognize.
// swiftlint:disable:next missing_docs
/// Empty default implementation.
public func sceneDidBecomeActive(_ scene: UIScene) { }

// A documentation for this method exists in the `LifecycleHandler` type which SwiftLint doesn't recognize.
// swiftlint:disable:next missing_docs
/// Empty default implementation.
public func sceneWillResignActive(_ scene: UIScene) { }

// A documentation for this method exists in the `LifecycleHandler` type which SwiftLint doesn't recognize.
// swiftlint:disable:next missing_docs
/// Empty default implementation.
public func sceneDidEnterBackground(_ scene: UIScene) { }

// A documentation for this method exists in the `LifecycleHandler` type which SwiftLint doesn't recognize.
// swiftlint:disable:next missing_docs
/// Empty default implementation.
public func applicationWillTerminate(_ application: UIApplication) { }
#endif
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
import SwiftUI


private final class RemoteNotificationContinuation: DefaultProvidingKnowledgeSource {
private final class RemoteNotificationContinuation: DefaultProvidingKnowledgeSource, Sendable {
typealias Anchor = SpeziAnchor

static let defaultValue = RemoteNotificationContinuation()

@MainActor
var continuation: CheckedContinuation<Data, Error>?

Check warning on line 19 in Sources/Spezi/Capabilities/Notifications/RegisterRemoteNotificationsAction.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package macOS / Test using xcodebuild or run fastlane

stored property 'continuation' of 'Sendable'-conforming class 'RemoteNotificationContinuation' is mutable

Check warning on line 19 in Sources/Spezi/Capabilities/Notifications/RegisterRemoteNotificationsAction.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package iOS / Test using xcodebuild or run fastlane

stored property 'continuation' of 'Sendable'-conforming class 'RemoteNotificationContinuation' is mutable

Check warning on line 19 in Sources/Spezi/Capabilities/Notifications/RegisterRemoteNotificationsAction.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package visionOS / Test using xcodebuild or run fastlane

stored property 'continuation' of 'Sendable'-conforming class 'RemoteNotificationContinuation' is mutable

Check warning on line 19 in Sources/Spezi/Capabilities/Notifications/RegisterRemoteNotificationsAction.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package tvOS / Test using xcodebuild or run fastlane

stored property 'continuation' of 'Sendable'-conforming class 'RemoteNotificationContinuation' is mutable

Check warning on line 19 in Sources/Spezi/Capabilities/Notifications/RegisterRemoteNotificationsAction.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package watchOS / Test using xcodebuild or run fastlane

stored property 'continuation' of 'Sendable'-conforming class 'RemoteNotificationContinuation' is mutable

Check warning on line 19 in Sources/Spezi/Capabilities/Notifications/RegisterRemoteNotificationsAction.swift

View workflow job for this annotation

GitHub Actions / Build and Test UI Tests visionOS / Test using xcodebuild or run fastlane

stored property 'continuation' of 'Sendable'-conforming class 'RemoteNotificationContinuation' is mutable

Check warning on line 19 in Sources/Spezi/Capabilities/Notifications/RegisterRemoteNotificationsAction.swift

View workflow job for this annotation

GitHub Actions / Build and Test UI Tests iOS / Test using xcodebuild or run fastlane

stored property 'continuation' of 'Sendable'-conforming class 'RemoteNotificationContinuation' is mutable

init() {}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public protocol EnvironmentAccessible: AnyObject, Observable {}


extension EnvironmentAccessible {
var viewModifierInitialization: any ViewModifierInitialization {
ModelModifierInitialization(model: self)
@MainActor var viewModifier: any ViewModifier {
ModelModifier(model: self)
}
}
11 changes: 8 additions & 3 deletions Sources/Spezi/Capabilities/Observable/ModelPropertyWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ public class _ModelPropertyWrapper<Model: Observable & AnyObject> {


deinit {
clear()
// mirrors implementation of clear, however in a compiler proven, concurrency safe way (:
collected = false
Task { @MainActor [spezi, id] in
spezi?.handleViewModifierRemoval(for: id)
}
}
}

Expand All @@ -61,6 +65,7 @@ extension _ModelPropertyWrapper: SpeziPropertyWrapper {
spezi?.handleViewModifierRemoval(for: id)
}


func inject(spezi: Spezi) {
self.spezi = spezi
}
Expand Down Expand Up @@ -97,15 +102,15 @@ extension Module {


extension _ModelPropertyWrapper: ViewModifierProvider {
var viewModifierInitialization: (any ViewModifierInitialization)? {
var viewModifier: (any ViewModifier)? {
collected = true

guard let storedValue else {
assertionFailure("@Model with type \(Model.self) was collected but no value was provided!")
return nil
}

return ModelModifierInitialization(model: storedValue)
return ModelModifier(model: storedValue)
}

var placement: ModifierPlacement {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ public class _ModifierPropertyWrapper<Modifier: ViewModifier> {
}

deinit {
clear()
// mirrors implementation of clear, however in a compiler proven, concurrency safe way (:
collected = false
Task { @MainActor [spezi, id] in
spezi?.handleViewModifierRemoval(for: id)
}
}
}

Expand Down Expand Up @@ -97,14 +101,14 @@ extension Module {


extension _ModifierPropertyWrapper: ViewModifierProvider {
var viewModifierInitialization: (any ViewModifierInitialization)? {
var viewModifier: (any ViewModifier)? {
collected = true

guard let storedValue else {
assertionFailure("@Modifier with type \(Modifier.self) was collected but no value was provided!")
return nil
}

return WrappedViewModifier(modifier: storedValue)
return storedValue
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ protocol ViewModifierProvider {
/// The view modifier instance that should be injected into the SwiftUI view hierarchy.
///
/// Does nothing if `nil` is provided.
var viewModifierInitialization: (any ViewModifierInitialization)? { get }
@MainActor var viewModifier: (any ViewModifier)? { get }

/// Defines the placement order of this view modifier.
///
Expand All @@ -48,13 +48,14 @@ extension ViewModifierProvider {

extension Module {
/// All SwiftUI `ViewModifier` the module wants to modify the global view hierarchy with.
var viewModifierEntires: [(UUID, any ViewModifierInitialization)] {
@MainActor
var viewModifierEntires: [(UUID, any ViewModifier)] {
retrieveProperties(ofType: ViewModifierProvider.self)
.sorted { lhs, rhs in
lhs.placement < rhs.placement
}
.compactMap { provider in
guard let modifier = provider.viewModifierInitialization else {
guard let modifier = provider.viewModifier else {
return nil
}
return (provider.id, modifier)
Expand Down
25 changes: 0 additions & 25 deletions Sources/Spezi/Capabilities/ViewModifier/WrappedViewModifier.swift

This file was deleted.

3 changes: 2 additions & 1 deletion Sources/Spezi/Dependencies/DependencyManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import XCTRuntimeAssertions


/// Gather information about modules with dependencies.
public class DependencyManager {
@MainActor
public class DependencyManager: Sendable {
/// Collection of already initialized modules.
private let existingModules: [any Module]

Expand Down
2 changes: 1 addition & 1 deletion Sources/Spezi/Dependencies/Module+DependencyRelation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


extension Module {
func dependencyRelation(to module: any Module) -> DependencyRelation {
func dependencyRelation(to module: DependencyReference) -> DependencyRelation {
let relations = dependencyDeclarations.map { $0.dependencyRelation(to: module) }

if relations.contains(.dependent) {
Expand Down
Loading
Loading