To apply local changes on the server, use one of the “sync up” methods. These methods update the server with data from the given SmartStore soup. They look for created, updated, or deleted records in the soup, and then replicate those changes on the server. The options argument specifies a list of fields to be updated. In Mobile SDK5.1 and later, you can override this field list by initializing the sync manager object with separate field lists for create and update operations. See Handling Field Lists in Create and Update Operations .
Locally created objects must include an “attributes” field that contains a “type” field that specifies the sObject type. For example, for an account named Acme, use: {Id:”local_x”, Name: Acme, attributes: {type:”Account”}}.
Android: Set the options parameter to SSyncOptions.optionsForSyncUp(fieldlist, SyncState.MergeMode.OVERWRITE)
Hybrid: Set the syncOptions parameter to {mergeMode:"OVERWRITE"}
If any server record has changed since it was synced down to that client, leave it in its current state. The corresponding client record also remains in its current state. When you call the syncUp() method:
Android: Set the options parameter to SyncOptions.optionsForSyncUp(fieldlist, SyncState.MergeMode.LEAVE_IF_CHANGED)
Hybrid: Set the syncOptions parameter to {mergeMode:"LEAVE_IF_CHANGED"}
If your local record includes the target’s modification date field, Mobile SDK detects changes by comparing it to the server record’s matching field. The default modification date field is lastModifiedDate. If your local records do not include the modification date field, the LEAVE_IF_CHANGED sync up operation reverts to an overwrite sync up.
The LEAVE_IF_CHANGED merge requires extra round trips to the server. More importantly, the status check and the record save operations happen in two successive calls. In rare cases, a record that is updated between these calls can be prematurely modified on the server.
Important
iOS Example
The MobileSyncExplorerSwift sample app demonstrates how to use named syncs and sync configuration files with the Salesforce Contact object. In iOS, this sample defines a ContactSObjectData class that represents a contact record as a Swift object. The sample also defines several support classes:
ContactSObjectDataSpec
SObjectData
SObjectDataSpec
SObjectDataFieldSpec
SObjectDataManager
To sync Contact data with the SmartStore soup, this app defines the following named sync operations in the Resources/usersyncs.json file:
For the first argument of updateRemoteData, which represents success, syncUpDown passes a block that calls the refreshList() method of RootViewController. This method filters the local contacts according to customer input and refreshes the view.
updateRemoteData calls reSync using the syncUpContacts model—aliased here as kSyncUpName—. Syncing up ensures that allowed soup changes are merged into the Salesforce org.
1func updateRemoteData(_ onSuccess: @escaping([SObjectData])-> Void,2 onFailure:@escaping(NSError?, SyncState)-> Void)-> Void {3 do{4 try self.syncMgr.reSync(named: kSyncUpName){[weak self](syncState) in5 guard let strongSelf = self else{6 return7}8 switch(syncState.status){9 case .done:10 do{11 let objects = try strongSelf.queryLocalData()12 strongSelf.populateDataRows(objects)13 try strongSelf.refreshRemoteData({(sobjs) in14 onSuccess(sobjs)15}, onFailure:{(error,syncState) in16 onFailure(error,syncState)17})18}catch let error as NSError {19 MobileSyncLogger.e(SObjectDataManager.self,20 message: "Error with Resync \(error)")21 onFailure(error,syncState)22}23 break24 case .failed:25 MobileSyncLogger.e(SObjectDataManager.self,26 message: "Resync \(syncState.syncName) failed")27 onFailure(nil,syncState)28 break29 default:30 break31}32}33}catch{34 onFailure(error as NSError, nil)35}36}
If sync up succeeds—that is, if the SyncState status indicates “done”—several things happen:
queryLocalData retrieves all raw data from the freshly updated soup.
1let objects = try strongSelf.queryLocalData()
populateDataRows transforms the soup’s data to ContactSObjectData objects and stores these objects in an internal array.
1strongSelf.populateDataRows(objects)
Control passes to refreshRemoteData(_:onFailure:). The refreshRemoteData method looks similar to updateRemoteData with two exceptions:
It performs a sync down instead of sync up.
If sync down succeeds, it “closes the circle” by executing the block that’s been passed to it from syncUpDown via updateRemoteData.
To summarize everything that happens in the syncUpDown call stack:
Sync up: It syncs soup changes up to the server by calling updateRemoteData on SObjectsDataManager. This step ensures that all allowable local and offline changes are merged into Salesforce.
Sync down: After the soup records are merged with server data, it syncs server data down to the soup through a call to refreshRemoteData. This step ensures that the soup reflects changes originating on the server and also changes merged from sync up. Remember: The sync up merge mode determines which soup edits are allowed on the server.
Finally, it updates its UI with the updated contact records from the soup.
When you’re syncing records, always apply a sync up-sync down pair in the sequence demonstrated by the MobileSyncExplorerSwift sample app.
If the update block provided here determines that the sync operation has finished, it calls the completion block that’s passed into updateRemoteData. A user initiates a syncing operation by tapping a button. Therefore, to see the definition of the completion block, look at the syncUpDown button handler in ContactListViewController.m. The handler calls updateRemoteData with the following block.
1[self.dataMgr updateRemoteData:^(SFSyncState *syncProgressDetails)2{3 dispatch_async(dispatch_get_main_queue(), ^{4 __strong typeof(weakSelf) strongSelf = weakSelf;5 strongSelf.navigationItem.rightBarButtonItem.enabled = YES;67 // When the sync failed it means not everything could be synced up8 // it doesn't necessarily mean nothing could be synced up9 // Therefore we refresh regardless of success status10[strongSelf.dataMgr refreshLocalData:completionBlock];11[strongSelf.dataMgr refreshRemoteData:completionBlock];1213 // We’ll again call refreshLocalData when completing1415 // Letting the user know whether the sync succeeded16 if([syncProgressDetails isDone]){17[strongSelf showToast:@"Sync complete!"];18}else if([syncProgressDetails hasFailed]){19[strongSelf showToast:@"Sync failed."];20}21});22}];
If the sync up operation succeeded, this block first refreshes the display on the device, along with a “Sync complete!” confirmation toast. Regardless of the status of the sync operation, this method refreshes local and remote data. This step covers partial successes and completions.
Android Example
To sync up to the server, you call syncUp() with the same arguments as syncDown(): list of fields, name of source SmartStore soup, and an update callback. The only coding difference is that you can format the list of affected fields as an instance of SyncOptions instead of SyncTarget. Here’s the way it’s handled in the MobileSyncExplorer sample:
1public synchronized void syncUp(){2 final SyncUpTarget target = new SyncUpTarget();3 final SyncOptions options =4 SyncOptions.optionsForSyncUp(Arrays.asList(ContactObject.CONTACT_FIELDS_SYNC_UP),5 MergeMode.LEAVE_IF_CHANGED);6 try{7 syncMgr.syncUp(target, options, ContactListLoader.CONTACT_SOUP,8 new SyncUpdateCallback(){9 @Override10 public void onUpdate(SyncState sync){11 if(Status.DONE.equals(sync.getStatus())){12 syncDown();13}14}15});16}catch(JSONException e){17 Log.e(TAG, "JSONException occurred while parsing", e);18}catch(MobileSyncException e){19 Log.e(TAG, "MobileSyncException occurred while attempting to sync up", e);20}21}
In the internal SyncUpdateCallback implementation, this example takes the extra step of calling syncDown() when sync up is done. This step guarantees that the SmartStore soup remains up-to-date with any recent changes made to Contacts on the server.
We've Moved
Welcome to the new home of the Mobile SDK Developer Guide! For now, the Japanese guide can be found in PDF form.