ReactiveSwift 1.1.1、MVVM + Flow Coordinatorパターン、Firebaseをバックエンドとして使用して、Swift 3のiOSアプリケーションを開発しています。私は最近、FRPに順応し始めましたが、私は既存のコードベースに新しい機能を統合する方法を見つけようとしています。ReactiveSwiftとFirebase非同期メソッド呼び出しを使用してSignalProducerを処理する方法は?
たとえば、私のモデルではFirebaseの非同期メソッドを使用してWebからサムネイルをダウンロードしています。サムネイルがダウンロードされている場合は、ViewModelクラスからサブスクライブしてUIを更新するようにしてください。SignalProducer<Content, NoError>
をご覧ください。
// field to be used from the view-models to observe
public let thumbnailContentSignalProducer = SignalProducer<Content, NoError> { (observer, disposable) in
// TODO: send next content via completion below
}
// thumbnail download method
public func findThumbnail(bucketId: String, contentId: String) {
guard let userId = userService.getCurrentUserId() else {
debugPring("Error id")
return
}
let ref = self.storageThumbnail.reference()
let contentRef = ref
.child(userId)
.child(bucketId)
.child(FirebaseConstants.pathImages)
.child("\(contentId).jpg")
contentRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
guard let data = data else {
debugPrint("Error download")
return
}
let content = Image(data: data)
content.id = contentId
content.userId = userId
content.bucketId = bucketId
// TODO: emit signal with content
// How to send the content via the SignalProducer above?
})
}
私は(observer, disposable)
タプルを受信するSignal<Content, NoError>.pipe()
メソッドを使用して、私はそれがFirebaseコールバックを形成アクセスするために、プライベート・グローバルフィールドとしてオブザーバーを保存したのに対し、私はまた、Signal<Content, NoError>
と同様のものを試してみました。
質問:
は、これは正しいアプローチですか私は何かが足りないのですか?
完了時にコンテンツオブジェクトを放出するにはどうすればよいですか?
UPDATE:痛みのいくつかの時間後
、私は信号を発するようにしてのviewmodelsから購読するSingalProducerを設計する方法を見つけました。
たぶん、次のコードスニペットはまた、他の人を助ける:
// model protocol
import ReactiveSwift
import enum Result.NoError
public protocol ContentService {
func findThumbnail(bucketId: String, contentId: String)
var thumbnailContentProducer: SignalProducer<Content, NoError> { get }
}
// model implementation using firebase
import Firebase
import FirebaseStorage
import ReactiveSwift
public class FirebaseContentService: ContentService {
// other fields, etc.
// ...
private var thumbnailContentObserver: Observer<Content, NoError>?
private var thumbnailContentSignalProducer: SignalProducer<Content, NoError>?
var thumbnailContentProducer: SignalProducer<Content, NoError> {
return thumbnailContentSignalProducer!
}
init() {
thumbnailContentSignalProducer = SignalProducer<Content, NoError> { (observer, disposable) in
self.thumbnailContentObserver = observer
}
}
func findThumbnail(bucketId: String, contentId: String) {
guard let userId = userService.getCurrentUserId() else {
// TODO handle error
return
}
let ref = self.storageThumbnail.reference()
let contentRef = ref
.child(userId)
.child(bucketId)
.child(FirebaseConstants.pathImages)
.child("\(contentId).jpg")
contentRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
guard let data = data else {
// TODO handle error
return
}
let content = Image(data: data)
content.id = contentId
content.userId = userId
content.bucketId = bucketId
// emit signal
self.thumbnailContentObserver?.send(value: content)
})
}
}
// usage from a ViewModel
contentService.thumbnailContentProducer
.startWithValues { content in
self.contents.append(content)
}
たぶん誰かが上記のコードを検証し、これはそれを行うための正しい方法であると言うことができます。
明確にするには、究極的には、 'findThumbnail'が呼び出されるたびに値を送信する1つのシグナルが必要ですか? – jjoelson
@jjoelsonはい、今まで私は独自の解決策を見つけ、上記の記事を編集しました。ソリューションは大丈夫ですか? –
あなたのソリューションは少し壊れるようです。 'init'のクロージャは、クライアントが' start'を呼び出すたびに呼び出されます。これは 'thumbnailContentObserver'がいつでも切り替えることができることを意味します。これは' Content'値がいつでも別の信号に迂回できることを意味します。私の解決策は、すべての 'Content'値が単一の' Signal'に来ることを保証します。必要に応じて複数回購読することができます。 – jjoelson