Skip to content

Commit

Permalink
Merge branch 'main' into github_action
Browse files Browse the repository at this point in the history
  • Loading branch information
Mohammed Rokon Uddin authored Feb 9, 2024
2 parents db07b00 + 642c30c commit e051cdf
Show file tree
Hide file tree
Showing 17 changed files with 567 additions and 61 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ on:
jobs:
build:
name: Build project
runs-on: macos-12
runs-on: macos-latest
strategy:
matrix:
destination: ['platform=iOS Simulator,OS=16.1,name=iPhone 14']
destination: ['platform=iOS Simulator,OS=17.0,name=iPhone 15']

steps:
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
uses: maxim-lobanov/setup-xcode@v1.6.0
with:
xcode-version: latest
xcode-version: '15.0'
- name: Checkout
uses: actions/checkout@v3
- name: Build
Expand Down
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.2.0"
exact: "1.5.1"
),
],
targets: [
Expand Down
22 changes: 0 additions & 22 deletions {{cookiecutter.app_name}}/Common/Sources/Common/BaseAction.swift

This file was deleted.

123 changes: 123 additions & 0 deletions {{cookiecutter.app_name}}/Common/Sources/Common/FeatureReducer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//
// FeatureReducer.swift
// Common
//
// Created by {{ cookiecutter.creator }} on {% now 'utc', '%d/%m/%Y' %}.
// Copyright © {% now 'utc', '%Y' %} {{cookiecutter.company_name}}. All rights reserved.
//

import ComposableArchitecture
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

func reduce(into state: inout State, viewAction: ViewAction) -> Effect<Action>
func reduce(into state: inout State, internalAction: InternalAction) -> Effect<Action>
func reduce(into state: inout State, childAction: ChildAction) -> Effect<Action>
func reduce(into state: inout State, presentedAction: Destination.Action) -> Effect<Action>
func reduceDismissDestination(into state: inout State) -> Effect<Action>

associatedtype Destination: DestinationReducer = EmptyDestination
associatedtype ViewState: Equatable = Never
}

extension Reducer where Self: FeatureReducer {
public typealias Action = FeatureAction<Self>

public var body: some ReducerOf<Self> {
Reduce(core)
}

public func core(into state: inout State, action: Action) -> Effect<Action> {
switch action {
case .destination(.dismiss):
reduceDismissDestination(into: &state)
case let .destination(.presented(presentedAction)):
reduce(into: &state, presentedAction: presentedAction)
case let .view(viewAction):
reduce(into: &state, viewAction: viewAction)
case let .internal(internalAction):
reduce(into: &state, internalAction: internalAction)
case let .child(childAction):
reduce(into: &state, childAction: childAction)
case .delegate:
.none
}
}

public func reduce(into state: inout State, viewAction: ViewAction) -> Effect<Action> {
.none
}

public func reduce(into state: inout State, internalAction: InternalAction) -> Effect<Action> {
.none
}

public func reduce(into state: inout State, childAction: ChildAction) -> Effect<Action> {
.none
}

public func reduce(into state: inout State, presentedAction: Destination.Action) -> Effect<Action> {
.none
}

public func reduceDismissDestination(into state: inout State) -> Effect<Action> {
.none
}

}

public typealias PresentationStoreOf<R: Reducer> = Store<PresentationState<R.State>, PresentationAction<R.Action>>

// MARK: FeatureAction
@CasePathable
public enum FeatureAction<Feature: FeatureReducer>: Sendable, Equatable {
case destination(PresentationAction<Feature.Destination.Action>)
case view(Feature.ViewAction)
case `internal`(Feature.InternalAction)
case child(Feature.ChildAction)
case delegate(Feature.DelegateAction)
}

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

// MARK: EmptyDestination

public enum EmptyDestination: DestinationReducer {
public struct State: Sendable, Hashable {}
public typealias Action = Never
public func reduce(into state: inout State, action: Never) -> Effect<Action> {}
public func reduceDismissDestination(into state: inout State) -> Effect<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 {}

60 changes: 60 additions & 0 deletions {{cookiecutter.app_name}}/Common/Sources/Common/Heap.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// Heap.swift
// Common
//
// Created by {{ cookiecutter.creator }} on {% now 'utc', '%d/%m/%Y' %}.
// Copyright © {% now 'utc', '%Y' %} {{cookiecutter.company_name}}. All rights reserved.
//

private final class Reference<T: Equatable>: Equatable {
var value: T
init(_ value: T) {
self.value = value
}
static func == (lhs: Reference<T>, rhs: Reference<T>) -> Bool {
lhs.value == rhs.value
}
}

@propertyWrapper public struct Heap<T: Equatable>: Equatable {
private var reference: Reference<T>

public init(_ value: T) {
reference = .init(value)
}

public var wrappedValue: T {
get { reference.value }
set {
if !isKnownUniquelyReferenced(&reference) {
reference = .init(newValue)
return
}
reference.value = newValue
}
}
public var projectedValue: Heap<T> {
self
}
}

extension Heap: Hashable where T: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(wrappedValue)
}
}

extension Heap: Decodable where T: Decodable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let value = try container.decode(T.self)
self = Heap(value)
}
}

extension Heap: Encodable where T: Encodable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(wrappedValue)
}
}
2 changes: 1 addition & 1 deletion {{cookiecutter.app_name}}/Domain/Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 5.7
// swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand Down
35 changes: 26 additions & 9 deletions {{cookiecutter.app_name}}/Features/Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 5.7
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand All @@ -8,21 +8,38 @@ let package = Package(
platforms: [.macOS(.v12), .iOS(.v15)],
products: [
.library(
name: "Features",
targets: ["Features"]),
name: "App",
targets: ["App"]
),

.library(
name: "Counter",
targets: ["Counter"]
)
],
dependencies: [
.package(path: "../Common"),
.package(
url: "https://github.com/pointfreeco/swift-composable-architecture",
exact: "1.2.0"
exact: "1.5.1"
),
],
targets: [
.target(
name: "Features",
dependencies: []),
.testTarget(
name: "FeaturesTests",
dependencies: ["Features"]),
name: "App",
dependencies: [
"Counter",
.product(name: "Common", package: "Common"),
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
]
),

.target(
name: "Counter",
dependencies: [
.product(name: "Common", package: "Common"),
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
]
)
]
)
97 changes: 97 additions & 0 deletions {{cookiecutter.app_name}}/Features/Sources/App/AppFeature.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//
// AppFeature.swift
// Features
//
// Created by {{ cookiecutter.creator }} on {% now 'utc', '%d/%m/%Y' %}.
// Copyright © {% now 'utc', '%Y' %} {{cookiecutter.company_name}}. All rights reserved.
//

import Common
import Counter
import ComposableArchitecture

public struct AppFeature: FeatureReducer {
public init() { }

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

@PresentationState var destination: Destination.State?
}

public enum ViewAction: Equatable {
case showSheet
case showFullScreenCover
}

public enum InternalAction: Equatable {
case dismissDestination
}

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 .showSheet:
state.destination = .sheet(.init())
return .none

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

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

case .fullScreenCover(.delegate(.close)):
return .send(.internal(.dismissDestination))

default:
return .none
}
}

public func reduce(into state: inout State, internalAction: InternalAction) -> Effect<Action> {
switch internalAction {
case .dismissDestination:
state.destination = nil
return .none
}
}

public struct Destination: DestinationReducer {

public init() { }

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

@CasePathable
public enum Action: Equatable {
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 e051cdf

Please sign in to comment.