Skip to content

Commit

Permalink
Merge pull request #19 from Arman-Morshed/feature/observation
Browse files Browse the repository at this point in the history
feat: introduce observation
  • Loading branch information
Mohammed Rokon Uddin authored Feb 14, 2024
2 parents 5c54c9e + e8f6a47 commit 6d1bdd3
Show file tree
Hide file tree
Showing 12 changed files with 280 additions and 294 deletions.
2 changes: 1 addition & 1 deletion {{cookiecutter.app_name}}/Common/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ let package = Package(
dependencies: [
.package(
url: "https://github.com/pointfreeco/swift-composable-architecture",
exact: "1.5.1"
exact: "1.8.0"
)
],
targets: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import SwiftUI
// MARK: FeatureReducer
public protocol FeatureReducer: Reducer
where State: Sendable & Hashable, Action == FeatureAction<Self> {
associatedtype ViewAction: Sendable & Equatable = Never
associatedtype InternalAction: Sendable & Equatable = Never
associatedtype ChildAction: Sendable & Equatable = Never
associatedtype DelegateAction: Sendable & Equatable = Never
associatedtype ViewAction: Sendable = Never
associatedtype InternalAction: Sendable = Never
associatedtype ChildAction: Sendable = Never
associatedtype DelegateAction: Sendable = Never

func reduce(into state: inout State, viewAction: ViewAction) -> Effect<Action>
func reduce(into state: inout State, internalAction: InternalAction)
Expand Down Expand Up @@ -93,7 +93,7 @@ public typealias PresentationStoreOf<R: Reducer> = Store<

// MARK: FeatureAction
@CasePathable
public enum FeatureAction<Feature: FeatureReducer>: Sendable, Equatable {
public enum FeatureAction<Feature: FeatureReducer>: Sendable {
case destination(PresentationAction<Feature.Destination.Action>)
case view(Feature.ViewAction)
case `internal`(Feature.InternalAction)
Expand All @@ -103,7 +103,7 @@ public enum FeatureAction<Feature: FeatureReducer>: Sendable, Equatable {

// MARK: DestinationReducer
public protocol DestinationReducer: Reducer
where State: Sendable & Hashable, Action: Sendable & Equatable & CasePathable {}
where State: Sendable & Hashable, Action: Sendable & CasePathable {}

// MARK: EmptyDestination

Expand All @@ -116,31 +116,3 @@ public enum EmptyDestination: DestinationReducer {
Action
> { .none }
}

//MARK: FeatureAction + Hashable
extension FeatureAction: Hashable
where
Feature.Destination.Action: Hashable,
Feature.ViewAction: Hashable,
Feature.ChildAction: Hashable,
Feature.InternalAction: Hashable,
Feature.DelegateAction: Hashable
{
public func hash(into hasher: inout Hasher) {
switch self {
case let .destination(action):
hasher.combine(action)
case let .view(action):
hasher.combine(action)
case let .internal(action):
hasher.combine(action)
case let .child(action):
hasher.combine(action)
case let .delegate(action):
hasher.combine(action)
}
}
}

/// For scoping to an actionless childstore
public func actionless<T>(never: Never) -> T {}
2 changes: 1 addition & 1 deletion {{cookiecutter.app_name}}/Features/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ let package = Package(
.package(path: "../PersistentPlatform"),
.package(
url: "https://github.com/pointfreeco/swift-composable-architecture",
exact: "1.5.1"
exact: "1.8.0"
),
],
targets: [
Expand Down
204 changes: 103 additions & 101 deletions {{cookiecutter.app_name}}/Features/Sources/App/AppFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,123 +13,125 @@ import Domain

public struct AppFeature: FeatureReducer {

@Dependency(\.appClient) var appClient
@Dependency(\.appClient) var appClient

public init() {}

public struct State: Equatable, Hashable {
public init() {}

@PresentationState var destination: Destination.State?
var product: Product?
}

public enum ViewAction: Equatable {
case onAppear
case showSheet
case showFullScreenCover
case save
}

public enum InternalAction: Equatable {
case dismissDestination
case productResponse(TaskResult<Product?>)
}

public var body: some ReducerOf<Self> {
Reduce(core)
.ifLet(\.$destination, action: \.destination) {
Destination()
}
}

public func reduce(into state: inout State, viewAction: ViewAction) -> Effect<
Action
> {
switch viewAction {
case .onAppear:
return .run { send in
await appClient.prepare(Void())
await send(
.internal(
.productResponse(
TaskResult {
try await appClient.product(1)
})))
}
case .showSheet:
state.destination = .sheet(.init())
return .none

case .showFullScreenCover:
state.destination = .fullScreenCover(.init())
return .none

case .save:
return .run { [product = state.product] send in
do {
try await appClient.save(product!)
} catch {
@ObservableState
public struct State: Equatable, Hashable {
public init() {}

}
}
@Presents var destination: Destination.State?
var product: Product?
}
}

public func reduce(
into state: inout State, presentedAction: Destination.Action
) -> Effect<Action> {
switch presentedAction {
case .sheet(.delegate(.close)):
return .send(.internal(.dismissDestination))
public enum ViewAction {
case onAppear
case showSheet
case showFullScreenCover
case save
}

case .fullScreenCover(.delegate(.close)):
return .send(.internal(.dismissDestination))
public enum InternalAction {
case dismissDestination
case productResponse(Result<Product?, Error>)
}

default:
return .none
public var body: some ReducerOf<Self> {
Reduce(core)
.ifLet(\.$destination, action: \.destination) {
Destination()
}
}
}

public func reduce(into state: inout State, internalAction: InternalAction)
-> Effect<Action>
{
switch internalAction {
case let .productResponse(.success(product)):
state.product = product
return .none
case let .productResponse(.failure(error)):
print(error)
return .none
case .dismissDestination:
state.destination = nil
return .none

public func reduce(into state: inout State, viewAction: ViewAction) -> Effect<
Action
> {
switch viewAction {
case .onAppear:
return .run { send in
await appClient.prepare(Void())
await send(
.internal(
.productResponse(
Result {
try await appClient.product(1)
})))
}
case .showSheet:
state.destination = .sheet(.init())
return .none

case .showFullScreenCover:
state.destination = .fullScreenCover(.init())
return .none

case .save:
return .run { [product = state.product] send in
do {
try await appClient.save(product!)
} catch {

}
}
}
}
}

public struct Destination: DestinationReducer {
public func reduce(
into state: inout State, presentedAction: Destination.Action
) -> Effect<Action> {
switch presentedAction {
case .sheet(.delegate(.close)):
return .send(.internal(.dismissDestination))

public init() {}
case .fullScreenCover(.delegate(.close)):
return .send(.internal(.dismissDestination))

@CasePathable
public enum State: Hashable {
case sheet(Counter.State)
case fullScreenCover(Counter.State)
default:
return .none
}
}

@CasePathable
public enum Action: Equatable {
case sheet(Counter.Action)
case fullScreenCover(Counter.Action)
public func reduce(into state: inout State, internalAction: InternalAction)
-> Effect<Action>
{
switch internalAction {
case let .productResponse(.success(product)):
state.product = product
return .none
case let .productResponse(.failure(error)):
print(error)
return .none
case .dismissDestination:
state.destination = nil
return .none
}
}

public var body: some ReducerOf<Self> {
Scope(state: \.sheet, action: \.sheet) {
Counter()
}
Scope(state: \.fullScreenCover, action: \.fullScreenCover) {
Counter()
}
public struct Destination: DestinationReducer {

public init() {}

@dynamicMemberLookup
@CasePathable
public enum State: Hashable {
case sheet(Counter.State)
case fullScreenCover(Counter.State)
}

@CasePathable
public enum Action {
case sheet(Counter.Action)
case fullScreenCover(Counter.Action)
}

public var body: some ReducerOf<Self> {
Scope(state: \.sheet, action: \.sheet) {
Counter()
}
Scope(state: \.fullScreenCover, action: \.fullScreenCover) {
Counter()
}
}
}
}
}
Loading

0 comments on commit 6d1bdd3

Please sign in to comment.