Skip to content

Commit

Permalink
Merge pull request #6 from ra1028/1.1.0
Browse files Browse the repository at this point in the history
1.1.0
  • Loading branch information
ra1028 authored Feb 7, 2018
2 parents 5c2cde4 + d8a62f6 commit 161a4b3
Show file tree
Hide file tree
Showing 28 changed files with 289 additions and 143 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ matrix:
include:
- os: osx
language: objective-c
osx_image: xcode9
osx_image: xcode9.2
before_install:
- ./scripts/install_swiftlint.sh
script:
- swiftlint
- set -o pipefail
- xcodebuild build-for-testing test-without-building -scheme VueFlux -configuration Release ENABLE_TESTABILITY=YES | xcpretty -c
- xcodebuild build-for-testing test-without-building -scheme VueFlux -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 8' ENABLE_TESTABILITY=YES | xcpretty - c
- xcodebuild build-for-testing test-without-building -scheme VueFlux -configuration Release -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV 1080p' ENABLE_TESTABILITY=YES | xcpretty -c
- xcodebuild build-for-testing test-without-building -scheme VueFlux -configuration Release -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV' ENABLE_TESTABILITY=YES | xcpretty -c
- xcodebuild build -scheme VueFlux -configuration Release -sdk watchsimulator -destination 'platform=watchOS Simulator,name=Apple Watch - 38mm' ENABLE_TESTABILITY=YES | xcpretty -c

notifications:
Expand Down
2 changes: 2 additions & 0 deletions Example/Example.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.ryo.Example;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -409,6 +410,7 @@
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.ryo.Example;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down
11 changes: 3 additions & 8 deletions Example/Sources/CounterViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import VueFluxReactive
final class CounterViewController: UIViewController {
@IBOutlet private weak var counterView: CounterView!

private let store = Store<CounterState>(state: .init(max: 1000), mutations: .init(), executor: .immediate)
private let store = Store<CounterState>(state: .init(max: 1000), mutations: .init(), executor: .queue(.global()))

override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
Expand All @@ -26,13 +26,8 @@ private extension CounterViewController {
counterView.openGitHubButton.addTarget(self, action: #selector(openGithub), for: .touchUpInside)
counterView.intervalSlider.addTarget(self, action: #selector(updateInterval(_:)), for: .valueChanged)

store.computed.countText
.observe(on: .mainThread)
.bind(to: counterView.counterLabel, \.text)

store.computed.intervalText
.observe(on: .mainThread)
.bind(to: counterView.intervalLabel, \.text)
store.computed.countText.bind(to: counterView.counterLabel, \.text)
store.computed.intervalText.bind(to: counterView.intervalLabel, \.text)

store.computed.command
.observe(on: .mainThread)
Expand Down
38 changes: 16 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ VueFlux makes a unidirectional and predictable flow by explicitly dividing the r
It's constituted of following core concepts.
State changes are observed by the ViewController using the reactive system.
Sample code uses VueFluxReactive which will be described later.
You can see example implementation [here](./Examples/Example).
You can see example implementation [here](./Example).

- [State](#state)
- [Actions](#actions)
Expand All @@ -53,7 +53,7 @@ You can see example implementation [here](./Examples/Example).
### State
This is the protocol that only just for constraining the type of Action and Mutations, represents the state managed by the Store.
Implement some properties of the state, and keeps them readonly by fileprivate access control, like below.
Will be mutated only by Mutations, and the properties will be published only by Computed.
Will be mutated only by Mutations, and the properties will be published only by Computed.

```swift
final class CounterState: State {
Expand Down Expand Up @@ -91,7 +91,7 @@ extension Actions where State == CounterState {
This is the protocol that represents `commit` function that mutate the state.
Be able to change the fileprivate properties of the state by implementing it in the same file.
The only way to actually change State in a Store is committing an Action via Mutations.
Changes of State must be done `synchronously`.
Changes of State must be done **synchronously**.

```swift
struct CounterMutations: Mutations {
Expand All @@ -114,8 +114,8 @@ Properties of State in the Store can only be accessed via this.

```swift
extension Computed where State == CounterState {
var count: Constant<Int> {
return state.count.constant
var countTextValues: Signal<String> {
return state.count.signal.map { String($0) }
}
}
```
Expand All @@ -136,10 +136,7 @@ final class CounterViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()

store.computed.count.signal
.map { String($0) }
.observe(on: .mainThread)
.bind(to: counterLabel, \.text)
store.computed.countTextValues.bind(to: counterLabel, \.text)
}

@IBAction func incrementButtonTapped(sender: UIButton) {
Expand Down Expand Up @@ -252,14 +249,14 @@ constant.signal.observe { print($0) }
## Advanced Usage
### Executor
Executor determines the execution context of function such as execute on main-thread, on a global queue and so on.
Executor determines the execution context of function such as execute on main thread, on a global queue and so on.
Some contexts are built in default.
- immediate
Executes function immediately and synchronously.
- mainThread
Executes immediately and synchronously if execution thread is main-thread. Otherwise enqueue to main-queue.
Executes immediately and synchronously if execution thread is main thread. Otherwise enqueue to main-queue.
- queue(_ dispatchQueue: DispatchQueue)
All functions are enqueued to given dispatch queue.
Expand Down Expand Up @@ -344,7 +341,7 @@ DispatchQueue.global().async {
```
### Disposable
Disposable represents something that can be disposed, usually unregister a observe that registered to Signal.
Disposable represents something that can be disposed, usually unregister a observe that registered to Signal.
```swift
let disposable = signal.observe { value in
Expand Down Expand Up @@ -382,19 +379,18 @@ signal.observe(duringScopeOf: self) { value in
### Bind
Binding makes target object's value be updated to the latest value received via Signal.
The binding is no longer valid after the target object is deinitialized.
Bindings work on **main thread** by default.
Closure binding.
```swift
text.signal
.observe(on: .mainThread)
.bind(to: label) { label, text in label.text = text }
text.signal.bind(to: label) { label, text in
label.text = text
}
```
Smart KeyPath binding.
```swift
text.signal
.observe(on: .mainThread)
.bind(to: label, \.text)
text.signal.bind(to: label, \.text)
```
Binder
Expand All @@ -412,9 +408,7 @@ extension UIView {
}
}
isViewHidden.signal
.observe(on: .mainThread)
.bind(to: view.setHiddenBinder(duration: 0.3))
isViewHidden.signal.bind(to: view.setHiddenBinder(duration: 0.3))
```
### Shared Store
Expand Down Expand Up @@ -506,4 +500,4 @@ If your pull request including new function, please write test cases for it.
## License
VueFlux and VueFluxReactive is released under the MIT License.
---
---
48 changes: 24 additions & 24 deletions Tests/ThreadSafeTests.swift → Tests/AtomicReferenceTests.swift
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
import XCTest
@testable import VueFlux

final class ThreadSafeTests: XCTestCase {
final class AtomicReferenceTests: XCTestCase {
func testSynchronized() {
let threadSafe = ThreadSafe(0)
let atomicReference = AtomicReference(0)

var targetValue = 1

threadSafe.synchronized { value in
atomicReference.synchronized { value in
targetValue = value
}

XCTAssertEqual(targetValue, 0)
}

func testSynchronizedResult() {
let threadSafe = ThreadSafe(0)
let atomicReference = AtomicReference(0)

let result = threadSafe.synchronized { value in
let result = atomicReference.synchronized { value in
"\(value)"
}

XCTAssertEqual(result, "0")
}

func testModify() {
let threadSafe = ThreadSafe(0)
let atomicReference = AtomicReference(0)

let expectedValue = 1

threadSafe.modify { value in
atomicReference.modify { value in
value = expectedValue
}

XCTAssertEqual(threadSafe.synchronized { $0 }, expectedValue)
XCTAssertEqual(atomicReference.synchronized { $0 }, expectedValue)
}

func testModifyResult() {
let threadSafe = ThreadSafe(0)
let atomicReference = AtomicReference(0)

let result: String = threadSafe.modify { value in
let result: String = atomicReference.modify { value in
value = 1
return "\(value)"
}
Expand All @@ -48,12 +48,12 @@ final class ThreadSafeTests: XCTestCase {
}

func testValueGetterAndSetter() {
let threadSafe = ThreadSafe(0)
let atomicReference = AtomicReference(0)

let expectedValue = 1

threadSafe.value = expectedValue
let resultValue = threadSafe.value
atomicReference.value = expectedValue
let resultValue = atomicReference.value

XCTAssertEqual(resultValue, expectedValue)
}
Expand All @@ -62,43 +62,43 @@ final class ThreadSafeTests: XCTestCase {
let initialValue = 0
let expectedValue = 1

let threadSafe = ThreadSafe(initialValue)
let atomicReference = AtomicReference(initialValue)

let oldValue = threadSafe.swap(expectedValue)
let newValue = threadSafe.value
let oldValue = atomicReference.swap(expectedValue)
let newValue = atomicReference.value

XCTAssertEqual(oldValue, initialValue)
XCTAssertEqual(newValue, expectedValue)
}

func testAsync() {
let threadSafe = ThreadSafe(0)
let atomicReference = AtomicReference(0)

let expectation = self.expectation(description: "async modify")
let expectation = self.expectation(description: "testAsync")

threadSafe.modify { value in
atomicReference.modify { value in
value = 1
sleep(1)
}

DispatchQueue.globalDefault().asyncAfter(deadline: .now() + 0.5) {
XCTAssertEqual(threadSafe.synchronized { $0 }, 1)
XCTAssertEqual(atomicReference.synchronized { $0 }, 1)

threadSafe.modify { value in
atomicReference.modify { value in
value += 1
}

XCTAssertEqual(threadSafe.synchronized { $0 }, 2)
XCTAssertEqual(atomicReference.synchronized { $0 }, 2)

threadSafe.modify { value in
atomicReference.modify { value in
value += 1
}

expectation.fulfill()
}

waitForExpectations(timeout: 2) { _ in
XCTAssertEqual(threadSafe.synchronized { $0 }, 3)
XCTAssertEqual(atomicReference.synchronized { $0 }, 3)
}
}
}
Loading

0 comments on commit 161a4b3

Please sign in to comment.