我正在训练自己使用ReactiveSwift进行网络连接,并且一个好的用例似乎是从Google Places API for iOS获取某个位置的照片。
流程如下:
GMSPlacePhotoMetadata
列表
我编写的代码以我能想到的最佳ReactiveSwift方式执行此工作流程(参见下面的代码)但是当我调用我的服务时,虽然所有对Google Places API的API调用都已完成,但我没有进入观察部分。我觉得我在框架中遗漏了一些非常基本的东西,而且我在途中失去了一些观察者,但是我无法将手指放在问题所在。任何帮助都将受到欢迎。
import Foundation
import ReactiveSwift
import GooglePlaces
struct GooglePlacesPhotoService {
func findPlacePictures(googlePlaceID: String) -> SignalProducer<[UIImage], DataStoreError> {
return findPlacePicturesMetadata(googlePlaceID: googlePlaceID)
.map { (metadata) -> SignalProducer<UIImage, DataStoreError> in
debugPrint("Mapping metadata to SignalProducer for metadata: ", metadata)
return self.findPlacePicture(metadata: metadata)
} // After mapping, we have a SignalProducer of SignalProducer<UIImage>
.flatten(.merge) // After flatening, we get a single SignalProducer<UIImage>
.reduce([], { (imageArray: [UIImage], newImage: UIImage) -> [UIImage] in
debugPrint("Merging another picture")
return imageArray + [newImage]
}) // Now we have an array of UIImage
}
private func findPlacePicturesMetadata(googlePlaceID: String) -> SignalProducer<GMSPlacePhotoMetadata, DataStoreError> {
return SignalProducer<GMSPlacePhotoMetadata, DataStoreError> { observer, disposable in
GMSPlacesClient.shared().lookUpPhotos(forPlaceID: googlePlaceID) { photos, error in
guard error == nil else { return observer.send(error: .externalError(error!)) }
guard let photos = photos else { return }
photos.results.forEach { metadata in
debugPrint("Sending metadata value: ", metadata)
observer.send(value: metadata)
}
}
}
}
private func findPlacePicture(metadata: GMSPlacePhotoMetadata) -> SignalProducer<UIImage, DataStoreError> {
return SignalProducer<UIImage, DataStoreError> { observer, disposable in
let screenSize = UIScreen.main.bounds.size
let screenScale = UIScreen.main.scale
let myCallback: GMSPlacePhotoImageResultCallback = { image, error in
guard error == nil else {
print("ERROR: couln't load picture for metadata \(metadata)")
observer.send(error: .externalError(error!))
return
}
guard let image = image else {
print("ERROR: empty image returned")
observer.send(error: .unknownExternalError)
return
}
debugPrint("Got 1 picture from metadata: ", metadata)
observer.send(value: image)
}
GMSPlacesClient.shared().loadPlacePhoto(metadata,
constrainedTo: screenSize,
scale: screenScale,
callback: myCallback)
}
}
}
googlePlaceIDProperty.signal
.filter { $0.isPresent }
.flatMap(.latest) { googlePlaceID in
return GooglePlacesPhotoService().findPlacePictures(googlePlaceID: googlePlaceID!)
}.observe { event in
debugPrint("Signal event!") // I NEVER GET THERE
switch event {
case let .value(pictures):
// Do stuff
case let .failed(error):
// Do stuff
default:
break
}
}
"Sending metadata value: " <GMSPlacePhotoMetadata: 0x60000645f4d0>
"Mapping metadata to SignalProducer for metadata: " <GMSPlacePhotoMetadata: 0x60000645f4d0>
"Sending metadata value: " <GMSPlacePhotoMetadata: 0x60000645b1b0>
"Mapping metadata to SignalProducer for metadata: " <GMSPlacePhotoMetadata: 0x60000645b1b0>
"Sending metadata value: " <GMSPlacePhotoMetadata: 0x60000645b0f0>
"Mapping metadata to SignalProducer for metadata: " <GMSPlacePhotoMetadata: 0x60000645b0f0>
"Sending metadata value: " <GMSPlacePhotoMetadata: 0x600006459950>
"Mapping metadata to SignalProducer for metadata: " <GMSPlacePhotoMetadata: 0x600006459950>
"Sending metadata value: " <GMSPlacePhotoMetadata: 0x60000644e730>
"Mapping metadata to SignalProducer for metadata: " <GMSPlacePhotoMetadata: 0x60000644e730>
"Sending metadata value: " <GMSPlacePhotoMetadata: 0x60000645ef30>
"Mapping metadata to SignalProducer for metadata: " <GMSPlacePhotoMetadata: 0x60000645ef30>
"Sending metadata value: " <GMSPlacePhotoMetadata: 0x6000066420a0>
"Mapping metadata to SignalProducer for metadata: " <GMSPlacePhotoMetadata: 0x6000066420a0>
"Sending metadata value: " <GMSPlacePhotoMetadata: 0x600006448d60>
"Mapping metadata to SignalProducer for metadata: " <GMSPlacePhotoMetadata: 0x600006448d60>
"Sending metadata value: " <GMSPlacePhotoMetadata: 0x600006642130>
"Mapping metadata to SignalProducer for metadata: " <GMSPlacePhotoMetadata: 0x600006642130>
"Sending metadata value: " <GMSPlacePhotoMetadata: 0x6000066421f0>
"Mapping metadata to SignalProducer for metadata: " <GMSPlacePhotoMetadata: 0x6000066421f0>
"Got 1 picture from metadata: " <GMSPlacePhotoMetadata: 0x60000645f4d0>
"Merging another picture"
"Got 1 picture from metadata: " <GMSPlacePhotoMetadata: 0x60000645b1b0>
"Merging another picture"
"Got 1 picture from metadata: " <GMSPlacePhotoMetadata: 0x60000645b0f0>
"Merging another picture"
"Got 1 picture from metadata: " <GMSPlacePhotoMetadata: 0x600006459950>
"Merging another picture"
"Got 1 picture from metadata: " <GMSPlacePhotoMetadata: 0x60000644e730>
"Merging another picture"
"Got 1 picture from metadata: " <GMSPlacePhotoMetadata: 0x60000645ef30>
"Merging another picture"
"Got 1 picture from metadata: " <GMSPlacePhotoMetadata: 0x6000066420a0>
"Merging another picture"
"Got 1 picture from metadata: " <GMSPlacePhotoMetadata: 0x600006448d60>
"Merging another picture"
"Got 1 picture from metadata: " <GMSPlacePhotoMetadata: 0x600006642130>
"Merging another picture"
"Got 1 picture from metadata: " <GMSPlacePhotoMetadata: 0x6000066421f0>
"Merging another picture"
答案 0 :(得分:1)
The Event
contract表示它只会以失败,已完成或已中断的事件终止。因此,您需要确保在发送所有值后,在observer.sendCompleted()
关闭中致电SignalProducer
。
The documentation for reduce
表示它返回一个&#34;生产者,当self
完成&#34;时发送最终结果,这意味着结果将永远不会被发送,直到完成的事件发生。基本上,除非你SignalProducers
明确发送一个已完成的事件以表明他们没有更多的值要发送,否则它无法知道它已经收集了所有结果。它有很好的说明on this graph。
因此,在您的情况下,在findPlacePicture
中,您应该在得到预期结果后致电sendCompleted()
,即:
observer.send(value: image)
observer.sendCompleted() // <- That's the line to add.
和findPlacePicturesMetadata
:
photos.results.forEach { metadata in
debugPrint("Sending metadata value: ", metadata)
observer.send(value: metadata)
}
observer.sendCompleted() // <- That's the line to add.