2

I am using Azure Mobile Service as a backend for an iOS application. I have set up everything to work with offline sync which allows me to view, add, or modify data even when there is no network connection. I am running into a problem when I add a new object into a table. The add works well locally but when I synchronize data it creates a duplicate item on the local database with a slightly different objectId. The created item is not duplicated on the server side.

Here's how I am setup. By the way, thanks to @TheBasicMind for posting this model. enter image description here

Here's a link to his explanation of the model: enter link description here

Here's what I do to setup the sync context and sync table:

    // Initialize the Mobile Service client with your URL and key
    MSClient *client = self.hpc.client;

    NSManagedObjectContext *context = self.hpc.syncContext;
    MSCoreDataStore *store = [[MSCoreDataStore alloc] initWithManagedObjectContext:context];

    client.syncContext = [[MSSyncContext alloc] initWithDelegate:syncDelegate dataSource:store callback:nil];

    // Add a Mobile Service filter to enable the busy indicator
    self.client = [client clientWithFilter:self];

    // Create an MSSyncTable instance to allow us to work with the Athlete table
    self.syncAthleteTable = [self.client syncTableWithName:@"Athlete"];

Here's how I add a record for the moment:

NSDictionary *newItem = @{@"firstname": firstname, @"lastname": lastname, @"laterality" : laterality};

[self.athletesService addItem:newItem completion:^{

    NSLog(@"New athlete added");
}];

-(void)addItem:(NSDictionary *)item completion:(CompletionBlock)completion
{
    // Insert the item into the Athlete table
    [self.syncAthleteTable insert:item completion:^(NSDictionary *result, NSError *error)
     {
         [self logErrorIfNotNil:error];

         // Let the caller know that we finished
         dispatch_async(dispatch_get_main_queue(), ^{
             completion();
         });
     }];
}

The add works as expected and it is added in a UITableView as I have an NSFetchedResultsController listening on my Main Context.

Here's where the problem occurs. When I synchronize data with the server using this function:

-(void)syncData:(CompletionBlock)completion
{

    // push all changes in the sync context, then pull new data
    [self.client.syncContext pushWithCompletion:^(NSError *error) {
        [self logErrorIfNotNil:error];
        [self pullData:completion];
    }];
}

-(void)pullData:(CompletionBlock)completion
{
     MSQuery *query = [self.syncAthleteTable query];

    // Pulls data from the remote server into the local table.
    // We're pulling all items and filtering in the view
    // query ID is used for incremental sync
    [self.syncAthleteTable pullWithQuery:query queryId:@"allAthletes" completion:^(NSError *error) {
        [self logErrorIfNotNil:error];
        [self refreshDataOnSuccess:completion];

    }];
 }

- (void) refreshDataOnSuccess:(CompletionBlock)completion
{
     MSQuery *query = [self.syncAthleteTable query];

     [query readWithCompletion:^(MSQueryResult *results, NSError *error) {
         [self logErrorIfNotNil:error];

         NSLog(@"Data that pulled from local store: ");
         for ( NSDictionary *dict in results.items ) {
            NSLog(@"%@ %@", [dict objectForKey:@"firstname"], [dict objectForKey:@"lastname"] );
        }

        // Let the caller know that we finished
        dispatch_async(dispatch_get_main_queue(), ^{
            completion();
        });
    }];
}

After the synchronization the NSFetchedResultsChangeInsert is called a second time for the same record with a slightly different objectID. Here's an example of the first and second objectIDs: tD7ADE77E-0ED0-4055-BAF6-B6CF8A6960AE9
tD7ADE77E-0ED0-4055-BAF6-B6CF8A6960AE11
I am stuck here.

Any help is highly appreciated. Thank you!

Community
  • 1
  • 1
CharleyXIV
  • 1,332
  • 3
  • 11
  • 23

1 Answers1

2

In the past, when I've seen this happen, its because the "id" field the client is sending was being changed or ignored by the server logic.

Locally the store finds the object in core data using that field, so a change to it could result in the client SDK thinking it needs to insert a new object and not update an existing one.

One easy way to confirm this, is by using the tableOperation:complete: method on the data delegate and comparing the "id" column between the item originally and that being returned by operation execute.

phillipv
  • 1,707
  • 9
  • 9