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
4 changes: 2 additions & 2 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 1 addition & 21 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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", .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.
Expand All @@ -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
95 changes: 55 additions & 40 deletions Sources/AppLifecycleMiddleware/AppLifecycleMiddleware.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import UIKit

// sourcery: Prism
public enum AppLifecycleAction {
case start
case didEnterBackground
case willEnterForeground
case didBecomeActive
Expand All @@ -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
}
}
}
Expand All @@ -59,16 +78,16 @@ extension Reducer where ActionType == AppLifecycleAction, StateType == AppLifecy

// sourcery: AutoMockable, imports = ["Combine", "SwiftRex"]
public protocol NotificationPublisher {
func receiveContext(
getState: @escaping GetState<AppLifecycleMiddleware.StateType>,
func start(
state: @escaping GetState<AppLifecycleMiddleware.StateType>,
output: AnyActionHandler<AppLifecycleMiddleware.OutputActionType>
) -> 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

Expand All @@ -80,23 +99,19 @@ public final class AppLifecycleMiddleware: Middleware {
notificationPublisher = publisher
}

public func receiveContext(
getState: @escaping GetState<StateType>,
output: AnyActionHandler<OutputActionType>
) {
cancellable = notificationPublisher.receiveContext(getState: getState, output: output)
}
public func handle(action: AppLifecycleAction, from dispatcher: ActionSource, state: @escaping GetState<Void>) -> IO<AppLifecycleAction> {
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<AppLifecycleMiddleware.StateType>,
public func start(
state _: @escaping GetState<AppLifecycleMiddleware.StateType>,
output: AnyActionHandler<AppLifecycleMiddleware.OutputActionType>
) -> AnyCancellable {
let notificationCenter = NotificationCenter.default
Expand Down
24 changes: 12 additions & 12 deletions Sources/AppLifecycleMiddleware/AutoMockable.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<AppLifecycleMiddleware.StateType>, output: AnyActionHandler<AppLifecycleMiddleware.OutputActionType>)?
open var receiveContextGetStateOutputReturnValue: AnyCancellable!
open var receiveContextGetStateOutputClosure: ((@escaping GetState<AppLifecycleMiddleware.StateType>, AnyActionHandler<AppLifecycleMiddleware.OutputActionType>) -> AnyCancellable)?

open func receiveContext( getState: @escaping GetState<AppLifecycleMiddleware.StateType>, output: AnyActionHandler<AppLifecycleMiddleware.OutputActionType> ) -> AnyCancellable {
receiveContextGetStateOutputCallsCount += 1
receiveContextGetStateOutputReceivedArguments = (getState: getState, output: output)
return receiveContextGetStateOutputClosure.map({ $0(getState, output) }) ?? receiveContextGetStateOutputReturnValue
open var startStateOutputReceivedArguments: (state: GetState<AppLifecycleMiddleware.StateType>, output: AnyActionHandler<AppLifecycleMiddleware.OutputActionType>)?
open var startStateOutputReturnValue: AnyCancellable!
open var startStateOutputClosure: ((@escaping GetState<AppLifecycleMiddleware.StateType>, AnyActionHandler<AppLifecycleMiddleware.OutputActionType>) -> AnyCancellable)?

open func start(state: @escaping GetState<AppLifecycleMiddleware.StateType>, output: AnyActionHandler<AppLifecycleMiddleware.OutputActionType> ) -> AnyCancellable {
startStateOutputCallsCount += 1
startStateOutputReceivedArguments = (state: state, output: output)
return startStateOutputClosure.map({ $0(state, output) }) ?? startStateOutputReturnValue
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down