From 64718f9bf0969ac010760b2ca7244466cc673047 Mon Sep 17 00:00:00 2001 From: Luiz Rodrigo Martins Barbosa Date: Sun, 14 Nov 2021 21:17:40 +0100 Subject: [PATCH 1/2] Adopt Middleware Protocol --- Package.resolved | 6 +- Package.swift | 22 +---- .../AppLifecycleMiddleware.swift | 95 +++++++++++-------- .../AutoMockable.generated.swift | 24 ++--- .../AppLifecycleMiddlewareTests.swift | 12 --- 5 files changed, 71 insertions(+), 88 deletions(-) diff --git a/Package.resolved b/Package.resolved index 429c0af..26c2be2 100644 --- a/Package.resolved +++ b/Package.resolved @@ -23,9 +23,9 @@ "package": "SwiftRex", "repositoryURL": "https://github.com/SwiftRex/SwiftRex.git", "state": { - "branch": null, - "revision": "660a3c805e7752e44674305174e637bd398c36ed", - "version": "0.8.0" + "branch": "develop", + "revision": "4d7b494a7c59fe39ceff61df25a6442777b353be", + "version": null } } ] diff --git a/Package.swift b/Package.swift index b9d24c9..dc601f2 100644 --- a/Package.swift +++ b/Package.swift @@ -24,10 +24,7 @@ let package = Package( dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), - .package(url: "https://github.com/SwiftRex/SwiftRex.git", from: "0.8.0"), -// .package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.46.3"), -// .package(url: "https://github.com/Realm/SwiftLint", from: "0.40.3"), -// .package(url: "https://github.com/orta/Komondor", from: "1.0.0"), + .package(url: "https://github.com/SwiftRex/SwiftRex.git", .branch("develop")), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -42,20 +39,3 @@ let package = Package( ), ] ) - -// The settings for the git hooks for our repo -//#if canImport(PackageConfig) -// import PackageConfig -// -// PackageConfiguration([ -// "komondor": [ -// // When someone has run `git commit`, first run -// // SwiftFormat and the auto-correcter for SwiftLint -// "pre-commit": [ -// "swift run swiftformat .", -// "swift run swiftlint autocorrect", -// ], -// ], -// ]) -// .write() -//#endif diff --git a/Sources/AppLifecycleMiddleware/AppLifecycleMiddleware.swift b/Sources/AppLifecycleMiddleware/AppLifecycleMiddleware.swift index 2726e9d..6142bd5 100644 --- a/Sources/AppLifecycleMiddleware/AppLifecycleMiddleware.swift +++ b/Sources/AppLifecycleMiddleware/AppLifecycleMiddleware.swift @@ -7,6 +7,7 @@ import UIKit // sourcery: Prism public enum AppLifecycleAction { + case start case didEnterBackground case willEnterForeground case didBecomeActive @@ -27,30 +28,48 @@ public enum AppLifecycle: Equatable { // MARK: - REDUCER extension Reducer where ActionType == AppLifecycleAction, StateType == AppLifecycle { - public static let lifecycle = Reducer { action, state in + public static let lifecycle = Reducer.reduce { action, state in switch (state, action) { - case (.backgroundActive, .didEnterBackground): return state - case (.backgroundInactive, .didEnterBackground): return state - case (.foregroundActive, .didEnterBackground): return .backgroundActive - case (.foregroundInactive, .didEnterBackground): return .backgroundInactive - - case (.backgroundActive, .willEnterForeground): return .foregroundActive - case (.backgroundInactive, .willEnterForeground): return .foregroundInactive - case (.foregroundActive, .willEnterForeground): return state - case (.foregroundInactive, .willEnterForeground): return state - - case (.backgroundActive, .didBecomeActive): return state - case (.backgroundInactive, .didBecomeActive): return .backgroundActive - case (.foregroundActive, .didBecomeActive): return state - case (.foregroundInactive, .didBecomeActive): return .foregroundActive - - case (.backgroundActive, .willBecomeInactive): return .backgroundInactive - case (.backgroundInactive, .willBecomeInactive): return state - case (.foregroundActive, .willBecomeInactive): return .foregroundInactive - case (.foregroundInactive, .willBecomeInactive): return state - - case (_, .didFinishLaunchingWithOptions): return state - case (_, .willFinishLaunchingWithOptions): return state + case (_, .start): + return + + case (.backgroundActive, .didEnterBackground), + (.backgroundInactive, .didEnterBackground): + return + case (.foregroundActive, .didEnterBackground): + state = .backgroundActive + case (.foregroundInactive, .didEnterBackground): + state = .backgroundInactive + + case (.backgroundActive, .willEnterForeground): + state = .foregroundActive + case (.backgroundInactive, .willEnterForeground): + state = .foregroundInactive + case (.foregroundActive, .willEnterForeground), + (.foregroundInactive, .willEnterForeground): + return + + case (.backgroundActive, .didBecomeActive): + return + case (.backgroundInactive, .didBecomeActive): + state = .backgroundActive + case (.foregroundActive, .didBecomeActive): + return + case (.foregroundInactive, .didBecomeActive): + state = .foregroundActive + + case (.backgroundActive, .willBecomeInactive): + state = .backgroundInactive + case (.backgroundInactive, .willBecomeInactive): + return + case (.foregroundActive, .willBecomeInactive): + state = .foregroundInactive + case (.foregroundInactive, .willBecomeInactive): + return + + case (_, .didFinishLaunchingWithOptions), + (_, .willFinishLaunchingWithOptions): + return } } } @@ -59,16 +78,16 @@ extension Reducer where ActionType == AppLifecycleAction, StateType == AppLifecy // sourcery: AutoMockable, imports = ["Combine", "SwiftRex"] public protocol NotificationPublisher { - func receiveContext( - getState: @escaping GetState, + func start( + state: @escaping GetState, output: AnyActionHandler ) -> AnyCancellable } // MARK: - MIDDLEWARE -public final class AppLifecycleMiddleware: Middleware { - public typealias InputActionType = Never +public final class AppLifecycleMiddleware: MiddlewareProtocol { + public typealias InputActionType = AppLifecycleAction public typealias OutputActionType = AppLifecycleAction public typealias StateType = Void @@ -80,23 +99,19 @@ public final class AppLifecycleMiddleware: Middleware { notificationPublisher = publisher } - public func receiveContext( - getState: @escaping GetState, - output: AnyActionHandler - ) { - cancellable = notificationPublisher.receiveContext(getState: getState, output: output) - } + public func handle(action: AppLifecycleAction, from dispatcher: ActionSource, state: @escaping GetState) -> IO { + guard case .start = action else { return .identity } - public func handle( - action _: InputActionType, - from _: ActionSource, - afterReducer _: inout AfterReducer - ) {} + return IO { [weak self] output in + guard let self = self else { return } + self.cancellable = self.notificationPublisher.start(state: state, output: output) + } + } } extension NotificationCenter: NotificationPublisher { - public func receiveContext( - getState _: @escaping GetState, + public func start( + state _: @escaping GetState, output: AnyActionHandler ) -> AnyCancellable { let notificationCenter = NotificationCenter.default diff --git a/Sources/AppLifecycleMiddleware/AutoMockable.generated.swift b/Sources/AppLifecycleMiddleware/AutoMockable.generated.swift index dd50955..729aae3 100644 --- a/Sources/AppLifecycleMiddleware/AutoMockable.generated.swift +++ b/Sources/AppLifecycleMiddleware/AutoMockable.generated.swift @@ -16,20 +16,20 @@ import Combine import SwiftRex open class NotificationPublisherMock: NotificationPublisher { - //MARK: - receiveContext + //MARK: - start - open var receiveContextGetStateOutputCallsCount = 0 - open var receiveContextGetStateOutputCalled: Bool { - return receiveContextGetStateOutputCallsCount > 0 + open var startStateOutputCallsCount = 0 + open var startStateOutputCalled: Bool { + return startStateOutputCallsCount > 0 } - open var receiveContextGetStateOutputReceivedArguments: (getState: GetState, output: AnyActionHandler)? - open var receiveContextGetStateOutputReturnValue: AnyCancellable! - open var receiveContextGetStateOutputClosure: ((@escaping GetState, AnyActionHandler) -> AnyCancellable)? - - open func receiveContext( getState: @escaping GetState, output: AnyActionHandler ) -> AnyCancellable { - receiveContextGetStateOutputCallsCount += 1 - receiveContextGetStateOutputReceivedArguments = (getState: getState, output: output) - return receiveContextGetStateOutputClosure.map({ $0(getState, output) }) ?? receiveContextGetStateOutputReturnValue + open var startStateOutputReceivedArguments: (state: GetState, output: AnyActionHandler)? + open var startStateOutputReturnValue: AnyCancellable! + open var startStateOutputClosure: ((@escaping GetState, AnyActionHandler) -> AnyCancellable)? + + open func start(state: @escaping GetState, output: AnyActionHandler ) -> AnyCancellable { + startStateOutputCallsCount += 1 + startStateOutputReceivedArguments = (state: state, output: output) + return startStateOutputClosure.map({ $0(state, output) }) ?? startStateOutputReturnValue } } diff --git a/Tests/AppLifecycleMiddlewareTests/AppLifecycleMiddlewareTests.swift b/Tests/AppLifecycleMiddlewareTests/AppLifecycleMiddlewareTests.swift index fdac493..af819de 100644 --- a/Tests/AppLifecycleMiddlewareTests/AppLifecycleMiddlewareTests.swift +++ b/Tests/AppLifecycleMiddlewareTests/AppLifecycleMiddlewareTests.swift @@ -3,18 +3,6 @@ import XCTest final class AppLifecycleMiddlewareTests: XCTestCase { func testAppLifecycleActions() { - let sut = AppLifecycleMiddleware() - var getStateCount = 0 - var dispatchActionCount = 0 - - sut.receiveContext( - getState: { getStateCount += 1 }, - output: .init { _, _ in - dispatchActionCount += 1 - } - ) - - // XCTAssertEqual(AppLifecycleMiddleware().text, "Hello, World!") } static var allTests = [ From 2535ec89e7140526462951d57a4ee4f2e232ca1a Mon Sep 17 00:00:00 2001 From: Luiz Rodrigo Martins Barbosa Date: Tue, 23 Nov 2021 14:25:08 +0100 Subject: [PATCH 2/2] Update SwiftRex to use Middleware IO --- Package.resolved | 6 +++--- Package.swift | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Package.resolved b/Package.resolved index 26c2be2..3e7f54e 100644 --- a/Package.resolved +++ b/Package.resolved @@ -23,9 +23,9 @@ "package": "SwiftRex", "repositoryURL": "https://github.com/SwiftRex/SwiftRex.git", "state": { - "branch": "develop", - "revision": "4d7b494a7c59fe39ceff61df25a6442777b353be", - "version": null + "branch": null, + "revision": "c51328031f943d8f055dbcb738bc66915825b8cc", + "version": "0.8.8" } } ] diff --git a/Package.swift b/Package.swift index dc601f2..a91d4b9 100644 --- a/Package.swift +++ b/Package.swift @@ -24,7 +24,7 @@ let package = Package( dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), - .package(url: "https://github.com/SwiftRex/SwiftRex.git", .branch("develop")), + .package(url: "https://github.com/SwiftRex/SwiftRex.git", .upToNextMajor(from: "0.8.8")), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite.