diff --git a/.github/workflows/swift-package.yml b/.github/workflows/swift-package.yml index da685a2..265a7dc 100644 --- a/.github/workflows/swift-package.yml +++ b/.github/workflows/swift-package.yml @@ -4,8 +4,7 @@ on: [push] jobs: build: - - runs-on: macos-13 + runs-on: macos-latest steps: - uses: actions/checkout@v2 diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/AsyncReactor.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/AsyncReactor.xcscheme new file mode 100644 index 0000000..c5e1627 --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/AsyncReactor.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Reactor.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Reactor.xcscheme new file mode 100644 index 0000000..04313aa --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/Reactor.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Package.swift b/Package.swift index 07c446d..015dc91 100644 --- a/Package.swift +++ b/Package.swift @@ -1,5 +1,4 @@ -// swift-tools-version: 5.7 -// The swift-tools-version declares the minimum version of Swift required to build this package. +// swift-tools-version: 6.2 import PackageDescription @@ -12,7 +11,6 @@ let package = Package( .macOS(.v12) ], products: [ - // Products define the executables and libraries a package produces, and make them visible to other packages. .library( name: "AsyncReactor", targets: ["AsyncReactor"] @@ -22,13 +20,8 @@ let package = Package( targets: ["Reactor"] ) ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), - ], + dependencies: [], targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "ReactorBase", dependencies: [] diff --git a/Package@swift-5.swift b/Package@swift-5.swift new file mode 100644 index 0000000..32d64c2 --- /dev/null +++ b/Package@swift-5.swift @@ -0,0 +1,43 @@ +// swift-tools-version: 5.9 + +import PackageDescription + +let package = Package( + name: "AsyncReactor", + platforms: [ + .iOS(.v15), + .tvOS(.v15), + .watchOS(.v8), + .macOS(.v12) + ], + products: [ + .library( + name: "AsyncReactor", + targets: ["AsyncReactor"] + ), + .library( + name: "Reactor", + targets: ["Reactor"] + ) + ], + dependencies: [], + targets: [ + .target( + name: "ReactorBase", + dependencies: [] + ), + .target( + name: "Reactor", + dependencies: ["ReactorBase"] + ), + .target( + name: "AsyncReactor", + dependencies: ["ReactorBase"] + ), + .testTarget( + name: "AsyncReactorTests", + dependencies: ["AsyncReactor"], + path: "Tests" + ), + ] +) diff --git a/Sources/ReactorBase/ReactorBase.swift b/Sources/ReactorBase/ReactorBase.swift index 48f38f7..34d6dcd 100644 --- a/Sources/ReactorBase/ReactorBase.swift +++ b/Sources/ReactorBase/ReactorBase.swift @@ -53,33 +53,40 @@ extension ReactorBase { // MARK: - Cancellation Support -public struct CancelId: Hashable { - let id: AnyHashable +public struct CancelId: Hashable, Sendable { + let id: UUID let mode: Mode - public init(id: AnyHashable, mode: Mode) { + public init(id: UUID, mode: Mode) { self.id = id self.mode = mode } - public struct Mode: OptionSet, Hashable { + public struct Mode: OptionSet, Hashable, Sendable { public let rawValue: Int public init(rawValue: Int) { self.rawValue = rawValue } - public static let lifecycle = Mode(rawValue: 1 << 0) - public static let inFlight = Mode(rawValue: 1 << 1) + public static var lifecycle: Mode { + Mode(rawValue: 1 << 0) + } + + public static var inFlight: Mode { + Mode(rawValue: 1 << 1) + } } } -struct TasksHolder { - @MainActor - static var tasks = [TaskKey: Task]() +@MainActor +class TasksHolder { + var tasks = [TaskKey: Task]() + + static var shared: TasksHolder = TasksHolder() } -struct TaskKey: Hashable { +struct TaskKey: Hashable, Sendable { let reactorId: ObjectIdentifier let id: CancelId @@ -95,19 +102,19 @@ extension ReactorBase { let key = TaskKey(reactor: self, id: id) if id.mode.contains(.inFlight) { - TasksHolder.tasks[key]?.cancel() + TasksHolder.shared.tasks[key]?.cancel() } let task = Task { await self.action(action) } - TasksHolder.tasks[key] = task + TasksHolder.shared.tasks[key] = task await task.value if !task.isCancelled { - TasksHolder.tasks.removeValue(forKey: key) + TasksHolder.shared.tasks.removeValue(forKey: key) } } @@ -115,26 +122,26 @@ extension ReactorBase { Task { await self.action(action, id: id) } } - public func lifecycleTask(_ action: @escaping @Sendable () async -> Void) { + public func lifecycleTask(_ action: @escaping @Sendable () async -> ()) { Task { @MainActor in let key = TaskKey(reactor: self, id: .init(id: UUID(), mode: .lifecycle)) let task = Task.detached { await action() - await MainActor.run { _ = TasksHolder.tasks.removeValue(forKey: key) } + await MainActor.run { _ = TasksHolder.shared.tasks.removeValue(forKey: key) } } - TasksHolder.tasks[key] = task + TasksHolder.shared.tasks[key] = task } } public func cancelLifecycleTasks() { Task { @MainActor in - let keys = TasksHolder.tasks.keys.filter { $0.id.mode.contains(.lifecycle) && $0.reactorId == ObjectIdentifier(self) } + let keys = TasksHolder.shared.tasks.keys.filter { $0.id.mode.contains(.lifecycle) && $0.reactorId == ObjectIdentifier(self) } for key in keys { - TasksHolder.tasks[key]?.cancel() - TasksHolder.tasks.removeValue(forKey: key) + TasksHolder.shared.tasks[key]?.cancel() + TasksHolder.shared.tasks.removeValue(forKey: key) } } }