【iOS】FluxのDispatcherのデータフローを単一方向に保つ案③
以前に【iOS】RxSwiftでFluxを実装する際のちょっと痒いところの改善案①と【iOS】FluxのDispatcherを1つでデータフローを単一方向に保つ案②でFluxのデータフローを単一方向に保つ改善案を書いてみました。
そちらでは、内部でPublishSubject<Element>のTypeParameterがenumであるものをDispatcherの中で1つ実装されている状態でした。
しかし、上記の場合はStoreでsubscribeをする際に、enumをswitch-case文で分岐をしてbindする必要がありました。
今回は、Dispatcherの中で複数のPublishSubjectを持ち、Storeでswitch-case文を使わずともbindできつつ、データフローを単一方向に保つ方法を解説していきたいと思います。
AnyObserverDispatcherとAnyObservableDispatcher
データフローを単一方向に保つために
- onNextだけができるDispathcherのAnyObserverDispatcher
- subscribeだけができるDispatcherのAnyObservableDispatcher
を実装します。
DispatcherType
というprotocolを定義します。
DispatcherTypeではclass自身のシングルトンの実装を強制します。
AnyObserverDispatcherとAnyObservableDispatcher、それぞれのclassのTypeParameterのDispatcherがDispatcherTypeを採用している状態にします。
それぞれのclassでDispatcherを引数とし、そのインスタンスをpropertyとして保持します。
protocol DispatcherType {
static var shared: Self { get }
}
final class AnyObserverDispatcher<Dispatcher: DispatcherType> {
let dispatcher: Dispatcher
init(_ dispatcher: Dispatcher = .shared) {
self.dispatcher = dispatcher
}
}
final class AnyObservableDispatcher<Dispatcher: DispatcherType> {
let dispatcher: Dispatcher
init(_ dispatcher: Dispatcher = .shared) {
self.dispatcher = dispatcher
}
}
Dispatcherの実装
まずDispatcherTypeを採用しstatic propertyを定義しつつ、必要な要素をfileprivateなPublishSubjectのPropertyとして定義していきます。
ここでfileprivateにする理由としては、SearchDispatcher自体ではonNextもsubscribeも行わないので、外からpropertyを見えなくするためです。
AnyObservableDispatcherのextensionをSearchDispatcherと同一ファイル内で定義します。
AnyObservableDispatcherのDispatcherがSearchDispatcherである場合は、それぞれのPropertyをObservableとして返すPropertyを定義します。
同じく、AnyObserverDispatcherのextensionをSearchDispatcherと同一ファイル内で定義します。
AnyObserverDispatcherのDispatcherがSearchDispatcherである場合は、それぞれのPropertyをObserverとして返すPropertyを定義します。
final class SearchDispatcher: DispatcherType {
static let shared = SearchDispatcher()
fileprivate let items = PublishSubject<[Item]>()
fileprivate let error = PublishSubject<Error>()
fileprivate let lastItemsRequest = PublishSubject<ItemsRequest>()
private init() {}
}
extension AnyObservableDispatcher where Dispatcher: SearchDispatcher {
var items: Observable<[Item]> {
return dispatcher.items
}
var error: Observable<Error> {
return dispatcher.error
}
var lastItemsRequest: Observable<ItemsRequest> {
return dispatcher.lastItemsRequest
}
}
extension AnyObserverDispatcher where Dispatcher: SearchDispatcher {
var items: AnyObserver<[Item]> {
return dispatcher.items.asObserver()
}
var error: AnyObserver<Error> {
return dispatcher.error.asObserver()
}
var lastItemsRequest: AnyObserver<ItemsRequest> {
return dispatcher.lastItemsRequest.asObserver()
}
}
利用例
実際にSearchActionでAnyObserverDispatcherのProeprtyを持ち、初期化時にSearchDispatcher.sharedを引数として渡すことで、subscribeのみを行うことができるDispatcherとして利用できるようになります。
final class SearchAction {
static let shared = SearchAction()
private let searchDispatcher: AnyObserverDispatcher<SearchDispatcher>
private let searchStore: SearchStore
private let session: QiitaSession
private let disposeBag = DisposeBag()
init(
searchDispatcher: AnyObserverDispatcher<SearchDispatcher> = .init(.shared),
searchStore: SearchStore = .shared,
session: QiitaSession = .shared
) {
self.searchDispatcher = searchDispatcher
self.searchStore = searchStore
self.session = session
}
func search(query: String? = nil) {
//〜〜
let request = ItemsRequest(page: nextPage, perPage: perPage, query: nextQuery)
//AnyObserverDispatcher<SearchDispatcher>なので、onNextしか見えない
searchDispatcher.lastItemsRequest.onNext(request)
session.send(request)
.subscribe(onNext: {
//〜〜
})
.addDisposableTo(disposeBag)
}
}
実際にSearchStoreでAnyObservableDispatcherをinitializerの引数とし、初期化時にSearchDispatcher.sharedを渡すことで、onNextのみを行うことができるDispatcherとして利用できるようになります。
final class SearchStore {
static let shared = SearchStore()
let items = Variable<[Item]>([])
let error = Variable<Error?>(nil)
let lastItemsRequest = Variable<ItemsRequest?>(nil)
private let disposeBag = DisposeBag()
init(searchDispatcher: AnyObservableDispatcher<SearchDispatcher> = .init(.shared)) {
//AnyObservableDispatcher<SearchDispatcher>なので、subscribeしか見えない
searchDispatcher.subscribe(onNext: { [unowned self] element in
//〜〜
})
.addDisposableTo(disposeBag)
}
}
最後に
Fluxのデータフローを単一方向に保つ改善案として今までに3つほど提案してみましたが、それぞれに良さがあるのでプロジェクトによってどのタイプを導入するかの参考になればと思います。
Author And Source
この問題について(【iOS】FluxのDispatcherのデータフローを単一方向に保つ案③), 我々は、より多くの情報をここで見つけました https://qiita.com/marty-suzuki/items/4b70dc28786340ae15a5著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .