Skip to content
This repository has been archived by the owner on May 20, 2021. It is now read-only.

Usage without Core Data

Martijn Walraven edited this page Feb 5, 2015 · 3 revisions

Connecting to a Meteor server is done through a WebSocket URL that needs to be specified when creating an METDDPClient:

METDDPClient *client = [[METDDPClient alloc] initWithServerURL:[NSURL URLWithString:@"ws://localhost:3000/websocket"]];
[client connect];

A client can then be used to subscribe to sets of documents:

METSubscription *subscription = [client addSubscriptionWithName:@"playersWithMinimumScore" parameters:@[@20] completionHandler:^(NSError *error) {
}];

The client keeps a local cache of documents sent by the server. These can be accessed through the METDatabase and METCollection classes. Documents are represented by METDocuments and identified by a METDocumentKey. (Keys encapsulate a collectionName and documentID).

A document stored in the local cache can be accessed using METDatabase#documentWithKey:. Alternatively, documents can be accessed by documentID through the collection:

METCollection *players = [database collectionWithName:@"players"];
METDocument *lovelace = [players documentWithID:@"lovelace"];

All documents in a collection can be accessed as an NSArray:

METCollection *players = [database collectionWithName:@"players"];
NSArray *allPlayers = [players allDocuments];

(More complex fetches will be supported by passing a METFetchRequest to METDatabase#executeFetchRequest:, but this has not been implemented yet.)

METDocuments are immutable and data can only be modified through method invocations. Collections are used to encapsulate modification method invocations:

METCollection *players = [database collectionWithName:@"players"];
id documentID = [players insertDocumentWithFields:@{@"name": @"Ada Lovelace", @"score": @25}];
[players updateDocumentWithID:documentID changedFields:@{@"score": @30, @"color": [NSNull null]}];
[players removeDocumentWithID:@"gauss"];

Under the hood, the above code calls three methods on the server, modifying the local cache through a predefined stub in the process.

Unless specified explicitly, document IDs are randomly generated on the client and a shared 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).

Data access and modification should be thread safe. The local cache supports concurrent reads and blocking writes. (Note that using Core Data will not allow you to take advantage of concurrent reads however, because NSPersistentStoreCoordinator serializes access.)

Change Notifications

- (void)databaseDidChange:(NSNotification *)notification {
  dispatch_async(dispatch_get_main_queue(), ^{
    // If updating the UI, make sure this happens on the main thread
    METDatabaseChanges *databaseChanges = notification.userInfo[METDatabaseChangesKey];
    [databaseChanges enumerateDocumentChangeDetailsUsingBlock:^(METDocumentChangeDetails *documentChangeDetails, BOOL *stop) {
      ...
    }];
  });
}

A METDatabaseDidChangeNotification contains a METDatabaseChanges object that can be asked for information about the changes that occurred. Information about changes to an individual document is encapsulated in a METDocumentChangeDetails. (This mechanism is modeled somewhat after the PHChange and PHObjectChangeDetails classes used by the iOS 8 Photos framework.)

A METDocumentChangeDetails knows its fieldsBeforeChanges and fieldsAfterChanges, and can also be asked for the changedFields. It reflects the changes to a document since the last notification. Changes are consolidated and a METDocumentChangeDetails is only included if the fields before and after are actually different. If a document is first added and then removed, or if a field is changed back to its original value, before the notification is posted, no METDocumentChangeDetails is created and a METDatabaseDidChangeNotification may not be posted.

Data updates from the server are buffered and applied in batches. Buffering uses a GCD dispatch source to coalesce events, meaning data updates that arrive before the buffer has had a chance to be flushed will be applied together.

Method invocations that change documents will also post a METDatabaseDidChangeNotification. If a stub has been defined, changes to the local cache are posted first and another notification will only be posted when server updates have been flushed (and if the effects are different from that of the stub).

METDatabase#performUpdates: can be used to group batches of updates to only post a single METDatabaseDidChangeNotification (or none if no net changes have occured, like below):

[database performUpdates:^{
  id documentID = [collection insertDocumentWithFields:@{@"name": @"Ada Lovelace", @"score": @25}];
  [collection updateDocumentWithID:documentID changedFields:@{@"score": @30, @"color": [NSNull null]}];
  [collection removeDocumentWithID:documentID];
}];

Custom methods

Methods are called on METDDPClient and an optional completionHandler can be specified to receive a result:

[client callMethodWithName:@"doSomething" parameters:@[@"someParameter"] completionHandler:^(id result, NSError *error) {
  ...
}];

If a stub has been defined, it will be executed first and could call more methods in the process (and so on). If at any point a data modification method is called, it will make changes to the local cache and participate in latency compensation:

[client defineStubForMethodWithName:@"doSomething" usingBlock:^id(NSArray *parameters) {
  [[client.database collectionWithName:@"players"] updateDocumentWithID:@"lovelace" changedFields:@{@"score": @20}];
  return nil;
}];
Clone this wiki locally