1

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

流程如下:

  1. GMSPlacePhotoMetadata从谷歌地点 ID获取列表
  2. 对于每个元数据,获取一张图片
  3. 将所有图片连接为一个数组

我以我能想到的最好的 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"
4

1 回答 1

1

Event合同说它只会在失败、完成或中断的事件中终止因此,您需要确保observer.sendCompleted()SignalProducer发送完所有值后调用闭包。

的文档reduce说它返回一个“将最终结果作为self完成发送的生产者”,这意味着在完成的事件发生之前,结果永远不会被发送。基本上,它无法知道它已收集所有结果,除非您SignalProducers明确发送完成的事件以表明它们没有更多值要发送。这张图很好地说明了这一点。

因此,在您的情况下,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.
于 2017-12-15T13:27:08.543 回答