Skip to content

Commit

Permalink
Add docs (#307)
Browse files Browse the repository at this point in the history
Patch
  • Loading branch information
muukii authored Oct 5, 2022
1 parent cf0c689 commit c1bad70
Show file tree
Hide file tree
Showing 10 changed files with 1,048 additions and 3 deletions.
86 changes: 86 additions & 0 deletions Sources/Verge/Documentation.docc/Activity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Activity

## What Activity brings to us

Activity enables Event-driven partially.

Verge supports to send any events that won’t be stored persistently. Even if an application runs with State-Driven, it might have some issues that not easy to something with State-Driven.

For example, something that would happen with the timer’s trigger. It’s probably not easy to expressing that as a state.
In this case, Activity helps that can do easily.

This means Verge can use Event-Driven from Data-Driven partially.
We think it’s not so special concept. SwiftUI supports these use cases as well that using Combine’s Publisher.

```swift
func onReceive<P>(_ publisher: P, perform action: @escaping (P.Output) -> Void) -> some View where P : Publisher, P.Failure == Never
```

[Apple’s SwiftUI Ref](https://developer.apple.com/documentation/swiftui/view/3365935-onreceive)

## Add Activity to the Store

In sample code following this:

```swift
final class MyStore: StoreComponentType {

struct State {
...
}

}
```

To enable using Activity, we add new decralation just like this:

```swift
final class MyStore: StoreComponentType {

struct State {
...
}

/// 👇
enum Activity {
case didSendMessage
}

}
```

## Send an Activity

And finally, that Store now can emit an activity that we created.

```swift
extension MyStore {
func sendMessage() {
send(.didSendMessage)
}
}
```

---

## Subscribe the Activity

**Normal**

```swift
store.sinkActivity { activity in
...
}
.store(in: &subscriptions)
```

**Using Combine**

```swift
store
.activityPublisher
.sink { event in
// do something
}
.store(in: &subscriptions)
```
115 changes: 115 additions & 0 deletions Sources/Verge/Documentation.docc/Changes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# ``Verge/Changes``

`Changes<State>` wraps primitive value inside and previous one.
This data structure enables to get differences between now one and previous one.
It helps us to prevent duplicated operation with the same value from the state.

![Changes structure](changes)

## How we update UI with the state uniquely in UIKit

In subscribing the state and binding UI, it’s most important to reduce the meaningless time to update UI.
What things are the meaningless? that is the update UI operations which contains no updates.
Basically, we can prevent this with like followings:

```swift
func updateUI(newState: State) {
if self.label.text != newState.name {
self.label.text = newState.name
}
}
```

Although, this approach make the code a little bit complicated by increasing the code that updates UI.
Especially, same words come up a lot.

## Changes simplify the code

Actually, state property of Store returns `Changes<State>` implicitly.
From the power of `dynamicMemberLookup`, we can use the property of the State with we don’t know that is actually Changes object.

How to know there is differences is using `ifChanged`.

```swift
let store: MyStore
let state: Changes<MyState> = store.state

state.ifChanged(\.name) { name in
// called when `name` changed only
}
```

## Patterns of `ifChanged`

`ifChanged` has a bunch of overloads.
Let’s take a look of patterns.

```swift
state.ifChanged(\.aaa, \.bbb) { aaa, bbb in
// Executes every two properties change.
}
```

```swift
state.ifChanged({ "Mr." + $0.name }) { composedName in

}
```

```swift
state.ifChanged({ "Mr." + $0.name }, { $0 == $1 }) { aaa, bbb in

}
```

```swift
state.ifChanged({ ($0.aaa, $0.bbb, "Mr" + $0.name)}, ==) { composedName in
// Returning tuple that contains composed value enables to do anything with the value you want.
// It releases us from complicated streaming mixing.
// Current Swift version does not support tuple conforming with Equatable,
// You need passing `==` function in the second argument.
}
```

## Subscribing the state

```swift
class ViewController: UIViewController {

var subscriptions = Set<UntilDeinitCancellable>()

let store: MyStore

override func viewDidLoad() {

super.viewDidLoad()

store.sinkChanges { [weak self] (changes) in
// it will be called on the thread which committed
self?.update(changes: changes)
}
.store(in: &subscriptions)
}

private func update(changes: Changes<MyState> {
changes.ifChanged(\.name) { name in
// called only name changed
}
...
}

}
```

## Make Changes object the first-time value

If you have a `Changes` from anywhere, it might have previous value,
Using `ifChanged` might return false because compared with the previous one.
You can create the Changed object that always returns true from `ifChanged` with followings:

```swift
let changes: Changes<State>

let firstTimeChanges: Changes<State> = changes.droppedPrevious()
```# <#Title#>

Loading

0 comments on commit c1bad70

Please sign in to comment.