Skip to content

Commit

Permalink
76
Browse files Browse the repository at this point in the history
  • Loading branch information
stephencelis committed Oct 14, 2019
1 parent 031f0e4 commit 12b64b4
Show file tree
Hide file tree
Showing 42 changed files with 3,017 additions and 0 deletions.

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// ComposableArchitecture.h
// ComposableArchitecture
//
// Created by Stephen Celis on 9/8/19.
// Copyright © 2019 Point-Free. All rights reserved.
//

#import <Foundation/Foundation.h>

//! Project version number for ComposableArchitecture.
FOUNDATION_EXPORT double ComposableArchitectureVersionNumber;

//! Project version string for ComposableArchitecture.
FOUNDATION_EXPORT const unsigned char ComposableArchitectureVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <ComposableArchitecture/PublicHeader.h>


Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import Combine
import SwiftUI

public typealias Effect = () -> Void

public typealias Reducer<Value, Action> = (inout Value, Action) -> Effect

//Button.init("Save", action: <#() -> Void#>)

public final class Store<Value, Action>: ObservableObject {
private let reducer: Reducer<Value, Action>
@Published public private(set) var value: Value
private var cancellable: Cancellable?

public init(initialValue: Value, reducer: @escaping Reducer<Value, Action>) {
self.reducer = reducer
self.value = initialValue
}

public func send(_ action: Action) {
let effect = self.reducer(&self.value, action)
effect()
}

public func view<LocalValue, LocalAction>(
value toLocalValue: @escaping (Value) -> LocalValue,
action toGlobalAction: @escaping (LocalAction) -> Action
) -> Store<LocalValue, LocalAction> {
let localStore = Store<LocalValue, LocalAction>(
initialValue: toLocalValue(self.value),
reducer: { localValue, localAction in
self.send(toGlobalAction(localAction))
localValue = toLocalValue(self.value)
return {}
}
)
localStore.cancellable = self.$value.sink { [weak localStore] newValue in
localStore?.value = toLocalValue(newValue)
}
return localStore
}
}

public func combine<Value, Action>(
_ reducers: Reducer<Value, Action>...
) -> Reducer<Value, Action> {
return { value, action in
let effects = reducers.map { $0(&value, action) }
return {
for effect in effects {
effect()
}
}
}
}

public func pullback<LocalValue, GlobalValue, LocalAction, GlobalAction>(
_ reducer: @escaping Reducer<LocalValue, LocalAction>,
value: WritableKeyPath<GlobalValue, LocalValue>,
action: WritableKeyPath<GlobalAction, LocalAction?>
) -> Reducer<GlobalValue, GlobalAction> {
return { globalValue, globalAction in
guard let localAction = globalAction[keyPath: action] else { return {} }
let effect = reducer(&globalValue[keyPath: value], localAction)
return effect
}
}

public func logging<Value, Action>(
_ reducer: @escaping Reducer<Value, Action>
) -> Reducer<Value, Action> {
return { value, action in
let effect = reducer(&value, action)
let newValue = value
return {
print("Action: \(action)")
print("Value:")
dump(newValue)
print("---")
effect()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// ComposableArchitectureTests.swift
// ComposableArchitectureTests
//
// Created by Stephen Celis on 9/8/19.
// Copyright © 2019 Point-Free. All rights reserved.
//

import XCTest
@testable import ComposableArchitecture

class ComposableArchitectureTests: XCTestCase {

override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
}

override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}

func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}

func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// Counter.h
// Counter
//
// Created by Stephen Celis on 9/8/19.
// Copyright © 2019 Point-Free. All rights reserved.
//

#import <Foundation/Foundation.h>

//! Project version number for Counter.
FOUNDATION_EXPORT double CounterVersionNumber;

//! Project version string for Counter.
FOUNDATION_EXPORT const unsigned char CounterVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <Counter/PublicHeader.h>


Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import ComposableArchitecture
import PrimeModal
import SwiftUI

public enum CounterAction {
case decrTapped
case incrTapped
}

public func counterReducer(state: inout Int, action: CounterAction) {
switch action {
case .decrTapped:
state -= 1

case .incrTapped:
state += 1
}
}

public let counterViewReducer = combine(
pullback(counterReducer, value: \CounterViewState.count, action: \CounterViewAction.counter),
pullback(primeModalReducer, value: \.self, action: \.primeModal)
)

struct PrimeAlert: Identifiable {
let prime: Int
var id: Int { self.prime }
}

public typealias CounterViewState = (count: Int, favoritePrimes: [Int])

public enum CounterViewAction {
case counter(CounterAction)
case primeModal(PrimeModalAction)

var counter: CounterAction? {
get {
guard case let .counter(value) = self else { return nil }
return value
}
set {
guard case .counter = self, let newValue = newValue else { return }
self = .counter(newValue)
}
}

var primeModal: PrimeModalAction? {
get {
guard case let .primeModal(value) = self else { return nil }
return value
}
set {
guard case .primeModal = self, let newValue = newValue else { return }
self = .primeModal(newValue)
}
}
}

public struct CounterView: View {
@ObservedObject var store: Store<CounterViewState, CounterViewAction>
@State var isPrimeModalShown = false
@State var alertNthPrime: PrimeAlert?
@State var isNthPrimeButtonDisabled = false

public init(store: Store<CounterViewState, CounterViewAction>) {
self.store = store
}

public var body: some View {
VStack {
HStack {
Button("-") { self.store.send(.counter(.decrTapped)) }
Text("\(self.store.value.count)")
Button("+") { self.store.send(.counter(.incrTapped)) }
}
Button("Is this prime?") { self.isPrimeModalShown = true }
Button(
"What is the \(ordinal(self.store.value.count)) prime?",
action: self.nthPrimeButtonAction
)
.disabled(self.isNthPrimeButtonDisabled)
}
.font(.title)
.navigationBarTitle("Counter demo")
.sheet(isPresented: self.$isPrimeModalShown) {
IsPrimeModalView(
store: self.store
.view(
value: { ($0.count, $0.favoritePrimes) },
action: { .primeModal($0) }
)
)
}
.alert(item: self.$alertNthPrime) { alert in
Alert(
title: Text("The \(ordinal(self.store.value.count)) prime is \(alert.prime)"),
dismissButton: .default(Text("Ok"))
)
}
}

func nthPrimeButtonAction() {
self.isNthPrimeButtonDisabled = true
nthPrime(self.store.value.count) { prime in
self.alertNthPrime = prime.map(PrimeAlert.init(prime:))
self.isNthPrimeButtonDisabled = false
}
}
}

func ordinal(_ n: Int) -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .ordinal
return formatter.string(for: n) ?? ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>
Loading

0 comments on commit 12b64b4

Please sign in to comment.