From c201b6b940ca856ac96b3f7226746b7a7a6f2fdd Mon Sep 17 00:00:00 2001 From: Jineun Jeong Date: Fri, 5 Apr 2024 18:48:55 +0900 Subject: [PATCH 1/4] feat(): changed `mutate(_:)` to perform only on main thread --- Sources/Reducer/Reducer.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Sources/Reducer/Reducer.swift b/Sources/Reducer/Reducer.swift index a86b442..5c088d2 100644 --- a/Sources/Reducer/Reducer.swift +++ b/Sources/Reducer/Reducer.swift @@ -65,6 +65,7 @@ final class TaskBag { } } +@MainActor open class Reducer: ObservableObject, Mutable { public typealias Action = R.Action public typealias Mutation = R.Mutation @@ -96,8 +97,10 @@ open class Reducer: ObservableObject, Mutable { // MARK: - Lifecycle open func mutate(_ mutation: Mutation) { - // Reduce state from mutation. - state = reduce(state: state, mutation: mutation) + Task { @MainActor in + // Reduce state from mutation. + state = reduce(state: state, mutation: mutation) + } } // MARK: - Public From 69be65ef6fa55d16dd20fcfa0310117d5e4ba010 Mon Sep 17 00:00:00 2001 From: Jineun Jeong Date: Fri, 5 Apr 2024 18:51:15 +0900 Subject: [PATCH 2/4] fix(): change `TaskBag` doesn't use unsafe pointer for resolving sendable warning --- Sources/Reducer/Reducer.swift | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Sources/Reducer/Reducer.swift b/Sources/Reducer/Reducer.swift index 5c088d2..de8f767 100644 --- a/Sources/Reducer/Reducer.swift +++ b/Sources/Reducer/Reducer.swift @@ -45,12 +45,9 @@ final class TaskBag { func store(_ item: TaskItem) { items.insert(item) - let ref = UnsafeMutablePointer?>.allocate(capacity: 1) - ref.pointee = items - - Task { + Task { @MainActor [weak self] in await item.task.value - ref.pointee?.remove(item) + self?.items.remove(item) } } From 1ffc3c789bd6f6a38e525b89c3ffc5670aa6e9b6 Mon Sep 17 00:00:00 2001 From: Jineun Jeong Date: Fri, 5 Apr 2024 18:52:21 +0900 Subject: [PATCH 3/4] fix(): fixed actor isolated warning of `XCTestCase` inheritance --- Sources/Reducer/Reducer.swift | 2 +- Tests/MacroTests/ReduceMacroTests.swift | 1 - Tests/ReducerTests/Model/CountIncreaseReduce.swift | 1 + Tests/ReducerTests/ReducerTests.swift | 14 ++++++++++++-- Tests/ReducerTests/TaskBagTests.swift | 1 - 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Sources/Reducer/Reducer.swift b/Sources/Reducer/Reducer.swift index de8f767..ed0c85a 100644 --- a/Sources/Reducer/Reducer.swift +++ b/Sources/Reducer/Reducer.swift @@ -39,7 +39,7 @@ final class TaskBag { } // MARK: - Property - private var items = Set() + private(set) var items = Set() // MARK: - Public func store(_ item: TaskItem) { diff --git a/Tests/MacroTests/ReduceMacroTests.swift b/Tests/MacroTests/ReduceMacroTests.swift index 8fd5791..c5856fb 100644 --- a/Tests/MacroTests/ReduceMacroTests.swift +++ b/Tests/MacroTests/ReduceMacroTests.swift @@ -14,7 +14,6 @@ let testMacros: [String: Macro.Type] = [ "Reduce": ReduceMacro.self, ] -@MainActor final class ReduceMacroTests: XCTestCase { // MARK: - Property diff --git a/Tests/ReducerTests/Model/CountIncreaseReduce.swift b/Tests/ReducerTests/Model/CountIncreaseReduce.swift index 6c96e4e..34b2828 100644 --- a/Tests/ReducerTests/Model/CountIncreaseReduce.swift +++ b/Tests/ReducerTests/Model/CountIncreaseReduce.swift @@ -5,6 +5,7 @@ // Created by JSilver on 2023/03/07. // +import Foundation import Reducer @Reduce diff --git a/Tests/ReducerTests/ReducerTests.swift b/Tests/ReducerTests/ReducerTests.swift index 199e424..e1393ce 100644 --- a/Tests/ReducerTests/ReducerTests.swift +++ b/Tests/ReducerTests/ReducerTests.swift @@ -9,7 +9,6 @@ import XCTest @testable import Reducer import Combine -@MainActor final class ReducerTests: XCTestCase { // MARK: - Property @@ -23,6 +22,7 @@ final class ReducerTests: XCTestCase { } // MARK: - Test + @MainActor func test_that_count_increases_when_receiving_increase_action() async throws { // Given let reducer = Reducer( @@ -46,6 +46,7 @@ final class ReducerTests: XCTestCase { XCTAssertEqual(result, [0, 1, 2]) } + @MainActor func test_that_count_does_not_mutate_when_receiving_same_action_twice() async throws { // Given let reducer = Reducer( @@ -69,6 +70,7 @@ final class ReducerTests: XCTestCase { XCTAssertEqual(result, [0, 1]) } + @MainActor func test_that_all_action_cancelled_when_reducer_deinit() async throws { // Given var reducer: Reducer? = Reducer( @@ -94,6 +96,7 @@ final class ReducerTests: XCTestCase { XCTAssertEqual(result, [0]) } + @MainActor func test_that_count_increases_when_mutate_in_start() async throws { // Given let reducer = Reducer(TimerReduce( @@ -110,6 +113,7 @@ final class ReducerTests: XCTestCase { XCTAssertGreaterThan(result.last ?? 0, 0) } + @MainActor func test_that_count_increases_when_await_mutate_in_start() async throws { // Given let reducer = Reducer(AwaitStartReduce( @@ -126,6 +130,7 @@ final class ReducerTests: XCTestCase { XCTAssertEqual(result, [0, 1]) } + @MainActor func test_that_reducer_should_be_able_to_assign_proxy_reduce() async throws { // Given var reducer = Reducer(CountIncreaseReduce( @@ -165,7 +170,7 @@ final class ReducerTests: XCTestCase { XCTAssertEqual(result, [0, 10]) } - + @MainActor func test_that_initial_count_is_100_when_proxy_set_initial_count() async throws { // Given let reducer = Reducer(proxy: .init( @@ -184,6 +189,7 @@ final class ReducerTests: XCTestCase { XCTAssertEqual(result, [100]) } + @MainActor func test_that_count_increases_when_proxy_receiving_increase_action() async throws { // Given let reducer = Reducer(proxy: .init( @@ -220,6 +226,7 @@ final class ReducerTests: XCTestCase { XCTAssertEqual(result, [0, 1]) } + @MainActor func test_that_count_increases_10_when_proxy_receiving_increase_action() async throws { // Given let reducer = Reducer(proxy: .init( @@ -257,6 +264,7 @@ final class ReducerTests: XCTestCase { XCTAssertEqual(result, [0, 10, 20]) } + @MainActor func test_that_count_does_not_mutate_when_proxy_same_action_twice() async throws { // Given let reducer = Reducer(proxy: .init( @@ -296,6 +304,7 @@ final class ReducerTests: XCTestCase { XCTAssertEqual(result, [0, 1]) } + @MainActor func test_that_count_increases_when_proxy_mutate_in_start() async throws { // Given var cancellable: AnyCancellable? = nil @@ -327,6 +336,7 @@ final class ReducerTests: XCTestCase { XCTAssertGreaterThan(result.last ?? 0, 0) } + @MainActor func test_that_reducer_can_assign_proxy_inherited_reduce() async throws { // Given let reducer = Reducer( diff --git a/Tests/ReducerTests/TaskBagTests.swift b/Tests/ReducerTests/TaskBagTests.swift index 7563983..f0265d2 100644 --- a/Tests/ReducerTests/TaskBagTests.swift +++ b/Tests/ReducerTests/TaskBagTests.swift @@ -9,7 +9,6 @@ import XCTest @testable import Reducer import Combine -@MainActor final class TaskBagTests: XCTestCase { // MARK: - Property From bc61263ff8aa2af6240acf7039dac96d80028ff3 Mon Sep 17 00:00:00 2001 From: Jineun Jeong Date: Fri, 5 Apr 2024 18:56:24 +0900 Subject: [PATCH 4/4] feat(): add `TaskBag` test case that removed from the bag when the task is complete --- Tests/ReducerTests/TaskBagTests.swift | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Tests/ReducerTests/TaskBagTests.swift b/Tests/ReducerTests/TaskBagTests.swift index f0265d2..5d4ac34 100644 --- a/Tests/ReducerTests/TaskBagTests.swift +++ b/Tests/ReducerTests/TaskBagTests.swift @@ -22,6 +22,33 @@ final class TaskBagTests: XCTestCase { } // MARK: - Test + func test_that_task_bag_remove_complete_task() async throws { + // Given + let taskBag = TaskBag() + + taskBag.store(.init( + 1, + with: Task { + try? await Task.sleep(nanoseconds: 1_000_000_000) + } + )) + taskBag.store(.init( + 2, + with: Task { + try? await Task.sleep(nanoseconds: 100_000_000) + } + )) + + let count = taskBag.items.count + + // When + try await Task.sleep(nanoseconds: 500_000_000) + + // Then + XCTAssertEqual(count, 2) + XCTAssertEqual(taskBag.items.count, 1) + } + func test_that_task_bag_cancel_all_task_when_deinit() async throws { // Given let expectation = expectation(description: "")