我正在使用 ReactiveSwift 1.1.1、MVVM + Flow Coordinator 模式和 Firebase 作为后端开发一个带有 Swift 3 的 iOS 应用程序。我最近才开始适应 FRP,我仍在试图弄清楚如何将新功能集成到我现有的代码库中。
例如,我的模型使用 Firebase 的异步方法从 Web 下载缩略图,我想提供一个SignalProducer<Content, NoError>
订阅我的 ViewModel 类并观察是否已下载缩略图,然后更新 UI。
// 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?
})
}
我也尝试过类似的方法Signal<Content, NoError>
,而我使用该Signal<Content, NoError>.pipe()
方法接收一个(observer, disposable)
元组,并将观察者保存为一个私有全局字段,以便从 Firebase 回调中访问它。
问题:
这是正确的方法还是我错过了什么?
如何在完成时发出内容对象?
更新:
经过几个小时的痛苦,我发现了如何设计 SingalProducer 来发出信号并从 ViewModel 订阅。
也许以下代码片段对其他人也有帮助:
// 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)
}
也许有人可以验证上面的代码并说这是正确的方法。