Posted at

SwiftでReduxを実装してみた

More than 3 years have passed since last update.


ReduxをSwiftで使ってみたい

もしもSwiftの静的型付けの世界でReduxのコンセプトを実装したら、どうなるか見てみたくなって、作ってみました。本家の方は非常に充実した実装をしていますが、こちらはコアとなる部分だけを実装した超簡易的なものになってます。


なんでRabbitっていう名称なの?

小説「帰ってきたウサギ(原題:Rabbit Redux)」から取ってきました。ウサギって素早いし「Swfit」という単語のイメージに合うなと思いまして。


使い方

Rabbit.swiftを自分のプロジェクトに含めるだけでOKです。

基本的にはReduxで必要となる、State, Action, Reducerをstructで定義して、RBStoreをsubscribeしてStateの変更を補足するという流れです。これによって、「Stateの変更」→「ビューの変更」という1方向に限定した構造を実現できます。詳細はgithubにあげたREADMEを御覧ください。

//

// Stateの定義
//
struct AppState: RBState {
var todos: [Todo]
var filter: TodoFilter

...
}

struct Todo {
var id: Int
var title: String
var completed: Bool

...
}

//
// Actionの定義
//
enum TodoFilter: String {
case All = "ALL"
case Completed = "COMPLETED"
case NotCompleted = "NOT_COMPLETED"
}

struct AddTodo: RBAction {
let title: String
}

struct ToggleTodo: RBAction {
let id: Int
}

struct SetFilter: RBAction {
let filter: TodoFilter
}

//
// Reducerの定義
//
func filter(filter: TodoFilter, action: RBAction) -> TodoFilter {
switch action {
case let action as SetFilter:
return action.filter

default:
break
}
return filter
}

func todos(var todos: [Todo], action: RBAction) -> [Todo] {
switch action {
case let action as AddTodo:
todos.append(Todo(title: action.title))

case let action as ToggleTodo:
todos[action.id].completed = !todos[action.id].completed

default:
break
}
return todos
}

func todoApp(state: AppState, action: RBAction) -> AppState {
let appState = state
return AppState(
todos: todos(appState.todos, action: action),
filter: filter(appState.filter, action: action)
)
}

//
// RBStoreをsubscribe
//
let state = AppState(todos: [], filter: .All))
store = RBStore<AppState>(reducer: todoApp, initState: state)

store.subscribe({action in
let currentState = store.getState()
//update view
})


ReduxにはあるけどRabbitで実装していないもの


  • combineReducers

  • middleware

  • redux-react


Swiftらしさ


静的型付け

Javascriptと違って型が利用できるので、Actionの区別は as? 演算子を使ってます。


値渡し

ReduxではImmutable stateという概念で、アプリの状態を表すオブジェクトをReducerで変更せずに新たに作成するという設計方針があります。これを実現するため、Javascriptでは毎回オブジェクトの再生成をしていますが、Swiftでは構造体、配列、辞書は値渡しで取り扱われるという特性を利用しました。

値渡しについては、AppleのWWDCのビデオが参考になりますね。