Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 1 addition & 2 deletions .github/workflows/swift-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ on: [push]

jobs:
build:

runs-on: macos-13
runs-on: macos-latest

steps:
- uses: actions/checkout@v2
Expand Down
67 changes: 67 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/AsyncReactor.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AsyncReactor"
BuildableName = "AsyncReactor"
BlueprintName = "AsyncReactor"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AsyncReactor"
BuildableName = "AsyncReactor"
BlueprintName = "AsyncReactor"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
67 changes: 67 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/Reactor.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Reactor"
BuildableName = "Reactor"
BlueprintName = "Reactor"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Reactor"
BuildableName = "Reactor"
BlueprintName = "Reactor"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
11 changes: 2 additions & 9 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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"]
Expand All @@ -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: []
Expand Down
43 changes: 43 additions & 0 deletions Package@swift-5.swift
Original file line number Diff line number Diff line change
@@ -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"
),
]
)
45 changes: 26 additions & 19 deletions Sources/ReactorBase/ReactorBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Void, Never>]()
@MainActor
class TasksHolder {
var tasks = [TaskKey: Task<Void, Never>]()

static var shared: TasksHolder = TasksHolder()
}

struct TaskKey: Hashable {
struct TaskKey: Hashable, Sendable {
let reactorId: ObjectIdentifier
let id: CancelId

Expand All @@ -95,46 +102,46 @@ 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)
}
}

public func send(_ action: Action, id: CancelId) {
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)
}
}
}
Expand Down