Meteor iOS integrates native iOS apps with the Meteor platform (http://www.meteor.com) through DDP. It offers full support for latency compensation and supports a Core Data programming model. It has been written in Objective-C, but is also perfectly usable from Swift.
If you're a Meteor web developer… it's now easy for a native iOS app to participate in Meteor's full stack reactivity.
If you're an iOS developer… Meteor is an amazing backend for your app that will save you tons of time.
Meteor iOS is more than a barebones DDP client. Rather than notifying you of individual data updates and leaving it at that, it has been designed to bring full stack reativity to iOS. Currently, this is most easily done by integrating with Core Data. By only writing a few lines of code, we get reactive updates from the database to the UI.
Among other things, it includes full support for latency compensation and supports writing your own method stubs. It has been implemented with concurrent execution in mind and keeps all processing off the main thread, posting batched and consolidated change notifications that can be observed to update the UI.
It keeps as close as possible to the semantics of the original Meteor JavaScript code. Its behavior is covered by over 200 unit tests and it also has some server integration tests that run using a local Meteor test server.
For now, the included Todos example (written in Swift, for both iPhone and iPad) is probably the best way to get an understanding of what Meteor iOS is capable of. If you want to try it out, you should be able to open the Meteor workspace and run the Todos scheme. It connects to a Meteor example app running at http://meteor-ios-todos.meteor.com. If you want to get a quick idea of what it's capable of you may want to have a look at this short screen recording:
The easiest way to use Meteor iOS in your own project is through CocoaPods. Add pod 'Meteor'
to your Podfile and run pod install
to install the library.
To use CocoaPods with Swift, you'll have to install CocoaPods 0.36 or later and enable framework support in your Podfile:
platform :ios, '8.0'
use_frameworks!
pod 'Meteor'
With this Podfile, Meteor iOS will be built as a framework and can easily be imported without further configuration (you may need to build the project first before the module is recognized however):
In Objective-C:
@import Meteor;
In Swift:
import Meteor
As an alternative, you should also be able to install the framework through Carthage. Add github "martijnwalraven/meteor-ios"
to your Cartfile and run carthage update
to build the framework. Then, drag the built .framework file to your application's Xcode project to use it.
I'm still figuring out usage patterns and I'm actively improving the API. I'm using Meteor iOS with Swift and Core Data in my own projects, so that's what I'll mostly be describing here. You can also use the API at a lower level and deal with documents directly though. See this wiki page for more information about the lower-level API and about using Meteor iOS without Core Data.
Don't expect anything to be stable yet, but please do let me know what you think of the API and what improvements you would like to see.
Basic usage is actually pretty simple:
METCoreDataDDPClient
with a WebSocket server URL and call its connect
method. It is often convenient to set the client up as a singleton so you can access it from anywhere.addSubscriptionWithName:parameters:
at any time to invoke a publish function on the server and receive a specific set of documents. (If you use autopublish
, the Meteor server will publish all of its collections automatically, without the need to subscribe. It has serious downsides, but can be great to get started during development.)collectionName
or fieldName
as userInfo
in the model editor. All types of relationships – one-to-one, one-to-many and many-to-many – are supported. By default, references are stored in documents on both sides (two-way referencing). But you can specify storage = false
as userInfo
for a relationship end in the model editor.mainQueueManagedObjectContext
and automatically merges changes, which is often what you need, but more complex setups (e.g. background contexts, child contexts) are also possible.NSFetchedResultsController
, all changes that affect the specified fetch request will be propagated automatically, whether they were made from Core Data, directly to documents, or come from a different client and were sent by the server. You can use this to automatically update a UITableView
or UICollectionView
for instance (which gives you some nifty animations for free). You can of course also observe NSManagedObjectContextDidSaveNotification
notifications yourself and decide what to do with changes as they happen.defineStubForMethodWithName:usingBlock:
) that can make local changes and thus participate in latency compensation.loginWithEmail:password:completionHandler
for example), publications on the server that depend on the userID
will automatically run again and the server will send changes to the document set (if any). (The Todos example uses a privateLists
publish function for instance, that automatically publishes lists owned by the currently logged in user.)The most convenient way to set up a METDDPClient
or METCoreDataDDPClient
in Swift is as a global variable (Swift uses dispatch_once
under the hood for lazy and thread safe initialization):
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()
}
}
The Todos example includes a SubscriptionLoader
class that can be used used to wait for subscriptions to become ready (similar to the waitOn
option in Iron Router). Using this in viewWillAppear
makes it easy to avoid displaying partial data sets (and perhaps show a loading indicator, as in the Todos example). (Subscriptions are shared and reused, so there won't be any extra cost to calling viewWillAppear
again, and if all subscriptions are loaded when it is 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)")
}
NSIncrementalStore
subclass. Mapping between documents and NSManagedObject
s is performed automatically (but can be customized). Different types of relationships are also supported, both for fetching and saving (with configurable storage). Modifications made to documents (possibly from other clients) will lead to the posting of an object change notification that is used to merge changes into NSManagedObjectContext
s.Although the framework has been written in Objective-C, it works well with Swift. In fact, both the Todos example and a larger project I work on myself exclusively use Swift. In the future, I plan on updating the API to take better advantage of Swift language features. I'm also planning on including (and documenting!) some utility code written in Swift extracted from the Todos example and my own project code.
I had already started work on this project when Swift was announced. Although I'm impressed by Swift, I decided it was too soon to consider a rewrite. The language was and is evolving, and some potentially useful language features are still missing (in particular around generics and protocols). Performance can be unpredictable (especially when dealing with arrays and dictionaries) and tool support (Xcode) is not as stable as it is for Objective-C. Once the Swift language and implementation stabilize and language idioms become established, a complete or partial rewrite might be a viable option.
randomSeed
is included with the method
DDP message to keep generated IDs synchronized between client and server even in complex scenarios (such as method stubs recursively calling other stubs).NSPersistentStoreCoordinator
serializes access.)ACAccount
s so users don't have to supply login credentials but only have to give permission to link the account.NSManagedObject
s before a predicate
and sortDescriptors
are applied. At least some of this work should be offloaded to the local cache, so we only have to perform further processing on a subset of objects. This isn't easy however, if we want to support predicates referring to relationships at the model level.Meteor iOS is available under the MIT license. See the LICENSE file for more info.
The Todos example contains icons provided by Icons8 under the Creative Commons Attribution-NoDerivs 3.0 Unported license.