为什么我不能在启用ReactiveSwift的网络工作流程中观察事件?

时间:2017-12-15 10:28:24

标签: ios swift google-places-api reactive-swift

我正在训练自己使用ReactiveSwift进行网络连接,并且一个好的用例似乎是从Google Places API for iOS获取某个位置的照片。

流程如下:

  1. 从Google地方ID
  2. 获取GMSPlacePhotoMetadata列表
  3. 对于每个元数据,获取图片
  4. 将所有图片连接为数组
  5. 我编写的代码以我能想到的最佳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"
    

1 个答案:

答案 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.