Skip to content

Commit

Permalink
Power Effect with SignalProducer from ReactiveSwift (#38)
Browse files Browse the repository at this point in the history
* Rename project & add ReactiveSwift

* Make project run with ReactiveSwift

* Use fireAndForget in logging

* Fix issues in pull request
  • Loading branch information
haaakon authored and mbrandonw committed Dec 4, 2019
1 parent 2e935d3 commit e1c1baa
Show file tree
Hide file tree
Showing 42 changed files with 3,244 additions and 0 deletions.
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,123 @@
import Combine
import SwiftUI
import ReactiveSwift

//public struct Effect<A> {
// public let run: (@escaping (A) -> Void) -> Void
//
// public init(run: @escaping (@escaping (A) -> Void) -> Void) {
// self.run = run
// }
//
// public func map<B>(_ f: @escaping (A) -> B) -> Effect<B> {
// return Effect<B> { callback in self.run { a in callback(f(a)) } }
// }
//}
public typealias Effect<A> = SignalProducer<A, Never>

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

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

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

public func send(_ action: Action) {
let effects = self.reducer(&self.value, action)
effects.forEach { effect in
effectDisposable += effect.startWithValues({ (action) in
self.send(action)
})
}
}

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.viewCancellable = 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.flatMap { $0(&value, action) }
return effects
}
}

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 localEffects = reducer(&globalValue[keyPath: value], localAction)

return localEffects.map { localEffect in
localEffect.map { localAction -> GlobalAction in
var globalAction = globalAction
globalAction[keyPath: action] = localAction
return globalAction
}
// Effect { callback in
// localEffect.sink { localAction in
// var globalAction = globalAction
// globalAction[keyPath: action] = localAction
// callback(globalAction)
// }
// }
}
}
}

public func logging<Value, Action>(
_ reducer: @escaping Reducer<Value, Action>
) -> Reducer<Value, Action> {
return { value, action in
let effects = reducer(&value, action)
let newValue = value
return [.fireAndForget {
print("Action: \(action)")
print("Value:")
dump(newValue)
print("---")
}] + effects
}
}

extension Effect {
public static func fireAndForget(work: @escaping () -> Void) -> Effect<Value> {
return Effect<Value> { observer , _ -> () in
work()
observer.sendCompleted()
}
}

public static func sync(work: @escaping () -> Value) -> Effect<Value> {
return Effect { observer, lifetime in
observer.send(value: work())
observer.sendCompleted()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//extension Effect where A == (Data?, URLResponse?, Error?) {
// public func decode<M: Decodable>(as type: M.Type) -> Effect<M?> {
// return self.map { data, _, _ in
// data
// .flatMap { try? JSONDecoder().decode(M.self, from: $0) }
// }
// }
//}
//
//extension Effect {
// public func receive(on queue: DispatchQueue) -> Effect {
// return Effect { callback in
// self.run { a in
// queue.async {
// callback(a)
// }
// }
// }
// }
//}
//
//public func dataTask(with url: URL) -> Effect<(Data?, URLResponse?, Error?)> {
// return Effect { callback in
// URLSession.shared.dataTask(with: url) { data, response, error in
// callback((data, response, error))
// }
// .resume()
// }
//}
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,33 @@
//
// ComposableArchitectureTests.swift
// ComposableArchitectureTests
//
// 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>


Loading

0 comments on commit e1c1baa

Please sign in to comment.