Meteor 0.2.1

Meteor 0.2.1

测试已测试
Lang语言 Obj-CObjective C
许可 MIT
发布了最新发布2016年4月

Martijn Walraven维护。



 
依赖
PocketSocket>= 0
InflectorKit>= 0
SimpleKeychain>= 0
 

Meteor 0.2.1

  • Martijn Walraven

Meteor iOS

Meteor iOS 通过 DDP 将原生 iOS 应用与 Meteor 平台(https://www.meteor.js.cn)集成。它提供对延迟补偿的全支持,并支持 Core Data 编程模型。它用 Objective-C 编写,但也可以从 Swift 使用。

如果你是 Meteor 网络开发者... 现在原生 iOS 应用可以轻松参与 Meteor 的全栈反应式。

如果你是 iOS 开发者... Meteor 是一个节省你大量时间的 app 后端。

Meteor iOS 与其他 DDP 客户端之间的区别

Meteor iOS 不只是一个原始的 DDP 客户端。它不是简单地通知你单个数据更新就结束了,而是被设计用来将全栈反应性带给 iOS。目前,这最简单地通过集成 Core Data 来实现。仅通过编写几行代码,我们就能从数据库到 UI 获取反应性更新。

它包括对延迟补偿的完全支持,并支持编写自定义方法占位符。考虑了并发执行,并将所有处理保持在主线程之外,发布批量合并的变化通知,这些可以用于更新 UI。

它与原始 Meteor JavaScript 代码的语义保持尽可能接近。其行为由 200 多个单元测试覆盖,且还包含一些使用本地 Meteor 测试服务器运行的服务器集成测试。

入门

目前,包含的 Todos 示例(用 Swift 编写,适用于 iPhone 和 iPad)可能是了解 Meteor iOS 功能的最佳方式。如果你想试用,你应能够打开 Meteor 工作区并在 Todos 规划中运行。它连接到在 http://meteor-ios-todos.meteor.com 运行的 Meteor 示例应用。如果你想要快速了解它的功能,可以考虑查看这个简短的屏幕录制。

Meteor iOS — Todos example

使用

我仍在探索使用模式,并正在积极改进 API。我在自己的项目中使用 Meteor iOS 和 Core Data,所以这将是我在这里主要描述的。你也可以在较低级别使用 API 并直接处理文档。有关更高级 API 和不用 Core Data 使用 Meteor iOS 的更多信息,请参阅 这个维基页面

目前请勿期待任何稳定的功能,但请告诉我您对API的看法以及您希望看到哪些改进。

基本使用实际上非常简单

  • 使用WebSocket服务器URL初始化一个METCoreDataDDPClient,并调用它的connect方法。将其设置为单例通常很方便,您可以随时访问它。
  • 随时调用addSubscriptionWithName:parameters:来在服务器上调用发布函数并接收一组特定的文档。(如果您使用autopublish,Meteor服务器会自动发布所有集合,无需订阅。它有严重的缺点,但在开发初期可能很有用。)
  • 您需要在Xcode中设置一个托管对象模型,就像您通常使用Core Data时那样。实体对应于集合(通过类似于Rails的单复数自动映射),属性对应于字段。默认映射通常可以正常工作,但您可以在模型编辑器中将不同的collectionNamefieldName指定为userInfo。所有类型的关系——一对一、一对多和多对多——都受支持。默认情况下,引用存储在两边的文档中(双向引用)。但您可以在模型编辑器中指定关系端为userInfostorage = false
  • 现在您可以使用正常的Core Data方法访问和修改文档。客户端保留一个mainQueueManagedObjectContext,并自动合并更改,这通常是您所需要的,但更复杂的设置(例如后台上下文、子上下文)也是可能的。
  • 如果您使用NSFetchedResultsController,所有影响指定检索请求的更改都将自动传播,无论它们是从Core Data、直接到文档中,还是从另一个客户端发送并由服务器发送。您可以使用此功能自动更新UITableViewUICollectionView等(这为您提供了一些巧妙的动画)。当然,您也可以自行观察NSManagedObjectContextDidSaveNotification通知,并根据需要进行更改。
  • 您自己代码中做出的更改(无论是通过Core Data还是直接对文档),将立即反映在本地缓存中,并且将发布更改通知(以便更新UI)。这被称为延迟补偿,因为我们不必等待服务器响应。如果服务器响应稍后返回并且与更改一致,则不会发生任何事情。但如果存在差异,本地缓存将添加由服务器发送的更改,并发布另一个更改通知。
  • 如果您调用服务器上的自定义方法,通常您必须等待可能的服务器更改回来。但您可以定义自己的方法存根(defineStubForMethodWithName:usingBlock:),这可以使本地更改并因此参与延迟补偿。
  • 如果您用户登录(例如使用loginWithEmail:password:completionHandler),依赖于userID的服务器发布将自动运行,并且服务器将发送更改到文档集(如果有)。(例如,待办事项示例使用自动发布属于当前登录用户的列表的privateLists发布函数。)

示例代码

在Swift中设置METDDPClientMETCoreDataDDPClient最便利的方式是作为全局变量(Swift在底层使用dispatch_once进行懒加载和线程安全的初始化)。

let Meteor = METCoreDataDDPClient(serverURL: NSURL(string: "wss://meteor-ios-todos.meteor.com/websocket"))

@UIApplicationMain
class AppDelegate: UIApplicationDelegate {
  func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {  
    Meteor.connect()
  }
}

等待订阅

“待办事项”示例中包含一个可以被用于等待订阅准备就绪的SubscriptionLoader类(类似于Iron Router中的waitOn选项)。在viewWillAppear中使用它,可以轻松避免显示部分数据集(并且可能显示加载提示符,如在待办事项示例中)。由于订阅是共享和重复使用的, Therefore, calling viewWillAppear again will not incur any additional cost, and if all subscriptions are loaded when called, whenReady will be synchronous.

subscriptionLoader.addSubscriptionWithName("publicLists")
subscriptionLoader.addSubscriptionWithName("privateLists")
// Parameter 'list' is an NSManagedObject that will be automatically converted to a documentID
subscriptionLoader.addSubscriptionWithName("todos", parameters: list)

subscriptionLoader.whenReady {
  self.fetchedResultsController.performFetch()
}

修改内容

// The managedObjectContext is preferably set as a property on a UIViewController and passed on to the next one to support child contexts
let managedObjectContext = Meteor.mainQueueManagedObjectContext

let list = NSEntityDescription.insertNewObjectForEntityForName("List", inManagedObjectContext:managedObjectContext) as List
list.name = "Favorite Scientists"
let lovelace = NSEntityDescription.insertNewObjectForEntityForName("Todo", inManagedObjectContext:managedObjectContext) as Todo
lovelace.text = "Ada Lovelace"
lovelace.list = list
list.incompleteCount++

var error: NSError?
if !managedObjectContext.save(&error) {
  println("Encountered error saving objects: \(error)")
}

功能

  • 完全支持延迟补偿,忠实地(希望)重现原始JavaScript代码的语义。对文档的修改将立即反映在本地缓存中,并且只有当服务器完成对该方法的(多个并发修改相同文档的方法)数据更新发送后,才会由服务器更改进行修正。
  • 适时发布批量合并的变更通知,而不是依赖于细致的更新。这有助于将UI工作尽可能保持在主线程上,同时不牺牲响应性。
  • 使用NSIncrementalStore子类集成了Core Data。文档与NSManagedObject之间的映射是自动进行的(但可以进行自定义)。还支持不同类型的关联,无论是读取还是保存(可配置存储)。对文档的修改(可能来自其他客户端)将导致发布一个对象变更通知,用于将更改合并到NSManagedObjectContext中。
  • 当可能时,订阅是共享和重复使用的。如果订阅不再使用,我们不会实际取消订阅,直到配置的超时时间过后。这意味着如果稍后再次需要,数据无需再次添加和删除。
  • 正确处理重新连接。新连接上的数据更新将被缓冲,直到之前已准备就绪的所有订阅再次就绪。仅在此之后才应用更新,并发布更改通知(如果实际上有变化)。

Swift

尽管该框架是用Objective-C编写的,但它与Swift配合得很好。事实上,我自己的待办事项示例以及我正在工作的一个更大的项目完全使用Swift。我计划在将来更新API以更好地利用Swift语言功能。我还计划包括(并记录!)从待办事项示例和我的项目代码中提取的某些Swift编写的实用代码。

在Swift发布的时候,我已经开始这项工作了。虽然我对Swift印象深刻,但我决定还不到重写的时机。这个语言当时还在发展中,一些可能有用的语言特性仍然缺失(特别是在泛型和协议方面)。性能可能不可预测(特别是处理数组和字典时),而且工具支持(Xcode)比Objective-C不稳定。一旦Swift语言和实现稳定下来,并且语言习惯确立,完整的或部分的重写可能变成一个可行的选择。

一些实现细节

  • 来自服务器的数据更新被缓冲并批量应用。缓冲区使用GCD dispatch source来聚合事件,这意味着在缓冲区有足够时间刷新之前到达的数据更新将一起应用。
  • 除非明确指定,否则文档ID将在客户端随机生成,并且`method` DDP消息中将包含一个共享的`randomSeed`,以保持客户端和服务器生成的ID在复杂场景下(例如方法存根递归调用其他存根)同步。
  • 数据访问和修改应该是线程安全的。本地缓存支持并发读和阻塞写。(注意,使用Core Data不会让你利用并发读,因为`NSPersistentStoreCoordinator`会序列化访问。)
  • 登录方法的调用充当一个障碍,因此其他方法无法并发执行。重新连接时,如果之前已经登录,则在其他正在进行的操作之前发送带有恢复令牌的登录方法。将来,账户也应该在应用程序重新启动后继续存在,并将存储在密钥链中。像Facebook和Twitter这样的服务也应得到支持,并且最好与iOS ACAccount集成,这样用户就不需要提供登录凭证,而只需给予允许链接账户的权限。
  • 当与服务器的连接丢失(无论通过连接关闭、网络错误还是DDP心跳失败检测到)时,客户端会自动尝试重新连接。如果重新连接失败,它将使用指数退避(带有随机化因子)反复尝试。客户端还会监听网络可达性更改,并在网络不可达时不会反复重新连接。当网络再次看起来可达时,它会立即尝试重新连接。
  • 当应用程序转到后台时,不会立即关闭连接,而是尽可能长时间地保持连接(这最终由操作系统决定,但目前意味着180秒)。如果在后台期间连接丢失,则不会尝试重新连接。不过,将应用程序转到前台始终会立即尝试重新连接。
  • 目前,预计所有的Core Data关系都在两侧定义,并且在两侧的文档中应包含关系细节并保持同步。使用Core Data保存对象会自动处理这一点。例如,如果你更改多对多关系中的一侧,关系中将参与的所有其他文档也将发生变化。这可能在根据实际使用场景进行回顾时有必要,但现在似乎作为默认设置工作得很好。
  • Core Data获取的实现方式相当简单,尽管可以使用但效率非常低。在应用`predicate`和`sortDescriptors`之前,给定集合中的所有文档都实例化为`NSManagedObject`。至少应该将这部分工作卸载到本地缓存,这样我们只需要处理对象子集。如果想支持引用模型级别的关系的谓词,这并不容易。

作者

许可证

Meteor iOS在MIT许可证下可用。有关更多信息,请参阅LICENSE文件。

“待办事项”示例包含由Icons8提供的图标,这些图标是在Creative Commons Attribution-NoDerivs 3.0 Unported许可证下提供的。