iOS 实现 MVVM、网络和蓝牙的应用程序架构如何?

iOS App architecture implementing MVVM, Networking and Bluetooth, how?

问题:

我目前在 Swift 中开发 iOS 移动应用程序时遇到问题,该应用程序使用:

关于应用程序:

它以 蓝牙设置 视图开始,引导用户完成与外围设备配对的过程(与 TabBarController 不相交)。

与设备成功配对后,iOS 应用程序从设备请求所有配置,发送为 JSON

这个JSON包含App显示给用户进行操作的不同Model信息(编程),需要以某种方式存储在数组中Singleton庄园以其中 view-model 可以请求任何索引以显示给用户。

接收到所有数据后,Bluetooth View 关闭并显示 TabBarView

当前示例:

将此应用程序关联到的一个很好的例子是 Apple Watch 和允许您配置所有内容的关联 iOS 应用程序。我不得不做一些相同的概念。

来自此 blog post 的另一个很好的示例应用程序,他们正在做的事情与我正在努力实现的目标相似。不过,我 运行 的不同之处在于它们对 MVVM 的依赖注入设置(以及其他类似示例)。我使用了一个故事板,因为他们在 AppDelegate.

中以编程方式实例化了他们的视图控制器

还有我的问题...

如何在不使用 NSNotificationsPrepareForSegues 的情况下(有效地)将数据从 BluetoothView 传递到 TabBarView ?请记住,我打算将库 RxSwift 用于异步事件处理和 event/data 流。我试图让这个应用程序尽可能无状态。

这个 blog post 中的 Servers 是检索 view-models and/or 更新它们的好习惯吗?

我发现,当使用 RxSwift 时,"view-model" 最终成为一个单一的纯函数,它从输入 UI 参数和 returns 可观察值中获取可观察参数,然后绑定到输出 UI 个元素。

真正帮助我了解 Rx 的是 tutorial videos for cycle.js

至于你的具体难题...

你所做的不一定是"forward"运动。这样看... TabBarView 需要一些数据,它不关心这些数据来自哪里。因此,让 TabBarView 访问一个函数,该函数 returns 一个包含必要数据的可观察对象。该闭包将显示蓝牙视图、建立连接、获取必要的数据,然后关闭蓝牙视图并使用所需数据调用 onNext

查看 this gist 可能有助于理解我在说什么。授予 gist 使用 PromiseKit 而不是 RxSwift,但可以使用相同的原则(而不是 fulfill,你会想调用 onNext 然后 onCompletion。)在 gist 中,视图需要数据的控制器只需调用一个函数并订阅结果(在本例中,结果包含一个 UIImage。)函数的工作是确定可用的图像源,询问用户他们使用哪个源想要从中检索图像并呈现适当的视图控制器以获取图像。

要点的当前内容如下:

//
//  UIViewController+GetImage.swift
//
//  Created by Daniel Tartaglia on 4/25/16.
//  Copyright © 2016 MIT License
//

import UIKit
import PromiseKit


enum ImagePickerError: ErrorType {
    case UserCanceled
}

extension UIViewController {

    func getImage(focusView view: UIView) -> Promise<UIImage> {
        let proxy = ImagePickerProxy()
        let cameraAction: UIAlertAction? = !UIImagePickerController.isSourceTypeAvailable(.Camera) ? nil : UIAlertAction(title: "Camera", style: .Default) { _ in
            let controller = UIImagePickerController()
            controller.delegate = proxy
            controller.allowsEditing = true
            controller.sourceType = .Camera
            self.presentViewController(controller, animated: true, completion: nil)
        }
        let photobinAction: UIAlertAction? = !UIImagePickerController.isSourceTypeAvailable(.PhotoLibrary) ? nil : UIAlertAction(title: "Photos", style: .Default) { _ in
            let controller = UIImagePickerController()
            controller.delegate = proxy
            controller.allowsEditing = false
            controller.sourceType = .PhotoLibrary
            self.presentViewController(controller, animated: true, completion: nil)
        }
        let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)

        let alert = UIAlertController(title: nil, message: nil, preferredStyle: .ActionSheet)
        if let cameraAction = cameraAction {
            alert.addAction(cameraAction)
        }
        if let photobinAction = photobinAction {
            alert.addAction(photobinAction)
        }
        alert.addAction(cancelAction)
        let popoverPresentationController = alert.popoverPresentationController
        popoverPresentationController?.sourceView = view
        popoverPresentationController?.sourceRect = view.bounds
        presentViewController(alert, animated: true, completion: nil)
        let promise = proxy.promise
        return promise.always {
            self.dismissViewControllerAnimated(true, completion: nil)
            proxy.retainCycle = nil
        }
    }
}


private final class ImagePickerProxy: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    let (promise, fulfill, reject) = Promise<UIImage>.pendingPromise()
    var retainCycle: ImagePickerProxy?

    required override init() {
        super.init()
        retainCycle = self
    }

    @objc func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
        let image = (info[UIImagePickerControllerEditedImage] as? UIImage) ?? (info[UIImagePickerControllerOriginalImage] as! UIImage)
        fulfill(image)
    }

    @objc func imagePickerControllerDidCancel(picker: UIImagePickerController) {
        reject(ImagePickerError.UserCanceled)
    }
}