iOS 实现 MVVM、网络和蓝牙的应用程序架构如何?
iOS App architecture implementing MVVM, Networking and Bluetooth, how?
问题:
我目前在 Swift
中开发 iOS 移动应用程序时遇到问题,该应用程序使用:
BTLE
: 连接到外围设备并sending/receiving数据to/from它。
Networking
:如果外围设备连接到网络(无线 and/or 以太网),则通过 BTLE
"could" 进行的通信将改为通过网络进行。
Model-View-ViewModel
架构
- RxSwift
关于应用程序:
它以 蓝牙设置 视图开始,引导用户完成与外围设备配对的过程(与 TabBarController
不相交)。
与设备成功配对后,iOS 应用程序从设备请求所有配置,发送为 JSON
。
这个JSON
包含App显示给用户进行操作的不同Model
信息(编程),需要以某种方式存储在数组中Singleton
庄园以其中 view-model
可以请求任何索引以显示给用户。
接收到所有数据后,Bluetooth View 关闭并显示 TabBarView。
当前示例:
将此应用程序关联到的一个很好的例子是 Apple Watch
和允许您配置所有内容的关联 iOS 应用程序。我不得不做一些相同的概念。
来自此 blog post 的另一个很好的示例应用程序,他们正在做的事情与我正在努力实现的目标相似。不过,我 运行 的不同之处在于它们对 MVVM 的依赖注入设置(以及其他类似示例)。我使用了一个故事板,因为他们在 AppDelegate
.
中以编程方式实例化了他们的视图控制器
还有我的问题...
如何在不使用 NSNotifications
或 PrepareForSegues
的情况下(有效地)将数据从 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)
}
}
问题:
我目前在 Swift
中开发 iOS 移动应用程序时遇到问题,该应用程序使用:
BTLE
: 连接到外围设备并sending/receiving数据to/from它。Networking
:如果外围设备连接到网络(无线 and/or 以太网),则通过BTLE
"could" 进行的通信将改为通过网络进行。Model-View-ViewModel
架构- RxSwift
关于应用程序:
它以 蓝牙设置 视图开始,引导用户完成与外围设备配对的过程(与 TabBarController
不相交)。
与设备成功配对后,iOS 应用程序从设备请求所有配置,发送为 JSON
。
这个JSON
包含App显示给用户进行操作的不同Model
信息(编程),需要以某种方式存储在数组中Singleton
庄园以其中 view-model
可以请求任何索引以显示给用户。
接收到所有数据后,Bluetooth View 关闭并显示 TabBarView。
当前示例:
将此应用程序关联到的一个很好的例子是 Apple Watch
和允许您配置所有内容的关联 iOS 应用程序。我不得不做一些相同的概念。
来自此 blog post 的另一个很好的示例应用程序,他们正在做的事情与我正在努力实现的目标相似。不过,我 运行 的不同之处在于它们对 MVVM 的依赖注入设置(以及其他类似示例)。我使用了一个故事板,因为他们在 AppDelegate
.
还有我的问题...
如何在不使用 NSNotifications
或 PrepareForSegues
的情况下(有效地)将数据从 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)
}
}