UA-17470720-3

Jump to content


Photo
- - - - -

looking for sample code for STV+CoreData+Parse - anyone have FTASync working with STV?

data-stores core-data web-services

  • Please log in to reply
9 replies to this topic

#1 markr

markr

    Sr. Member

  • STV 5.0 Pro
  • PipPipPip
  • 41 posts
Reputation: 5
Good

Posted 27 February 2014 - 10:55 AM

Hi All -

 

I'm attempting to use Parse.com as a backend for an app using STV+CoreData.  I've gotten FTASync built in the same app as STV+CoreData and in the simulator I'm able to use FTASync to push CoreData items to Parse as well as retrieve items from Parse and get them into CoreData.  I've tried this using separate managed object context definitions for STV and FTASync (i.e. FTASync uses MagicalRecord) with the same peristent data store (sqlite).  And, I've tried this using the same managed object context definitions (for context, model and store).  Neither approach seems to provide predictable results as for when objects will be seen or updated by both STV and FTASync.  Plus there's the issue of passing deletions which just doesn't seem to be handled by FTASync.

 

Is there anyone out there that's gotten FTASync to work with STV+CoreData?  And if so - are you able to post any sample code on how you did this?

 

Alternately - is there any sample code anywhere to mirror the STV+CoreData data store on Parse.com?

 

So you know where I am - it's going very very slowly but I am working on code based loosely on FTASync but stripped down to just what I need to integrate STV+CoreData with Parse.com.  So - if you're working on the same or something similar I'd appreciate a reply so we can share notes as we go.  If there's interest, I should be able to post some code soon (next day or two) to show what I have.  It will be buggy and limited in function...

 

Thanks,

Mark


  • creestiivo likes this

#2 Everett

Everett

    Ev

  • STV 5.0 Pro
  • PipPipPipPipPip
  • 191 posts
  • LocationMichigan
Reputation: 60
Outstanding

Posted 27 February 2014 - 05:57 PM

Hi Mark,

 

I'm not really sure how FTASync works - sounds like something to checkout - but if you have it syncing between core data and Parse, then you are in great shape.  STV works on the Core Data side and does it's thing, you probably don't need to worry about any integrating, but if you do, there there are a couple of places you can do so.

 

I'm guessing you either tell FTASync to sync manually or tell it to listen to the Core Data contextDidSave type notifications and sync automatically.  If it listens for the notifications, then you don't need to do anything special at all.  STV will save data and Core Data will make its notifications and FTASync will do its thing.

 

If it works manually, then you need to either listen for the notifications from Core Data or you can have STV call the appropriate FTASync methods when its values are changed.

 

Personally, I think I'd respond to Core Data, but if you do need to make the calls from STV, you would do so in this fashion.

 

You can respond when data changes at different points in the hierarchy.

 

When a value is changed at the cell level, there is a valueChanged action.

example:

 

 

    cell.cellActions.valueChanged = ^(SCTableViewCell *cell, NSIndexPath *indexPath) { 

       [self save];  //call FTASync here

    };

 

The next level up is the Section. Sections have several actions you can leverage including didUpdateItem,didCreateItem,didInsert ....

Example from SCSectionActions.h file:

sectionActions.didInsertItem = ^(SCArrayOfItemsSection *itemsSection, NSObject *item, NSIndexPath *indexPath)

    {

        NSLog(@"New item inserted at indexPath: %@", indexPath);

    };

 

models work just like sections generally, and for really fancy stuff you might want to subclass a SCDataStore and manage the integration at that level.

 

hope I'm not way off track here.

best of luck,

Ev



#3 markr

markr

    Sr. Member

  • STV 5.0 Pro
  • PipPipPip
  • 41 posts
Reputation: 5
Good

Posted 28 February 2014 - 01:27 PM

Hi Everett -

 

Thank you for the well thought out response.  I believe the first two approaches you've outlined would work in most situations as the notification (whether its contextDidSave or valueChanged) could be processed over the network when received.  In my case, the physical locations where the app will be used are unlikely to have network coverage (no cellular data and no WiFi).  And, I'm not sure about caching or deferring processing of the notifications to retain them for use when a connection becomes available.  I know it's old school, but what I'm doing for now is having the user press a button to start the sync.  In the future, I'm going to try to change that to automated on detection of an available connection when the user re-enters an area with coverage.

 

I'm using something along the lines of the third proposed approach.  I'm setting a dirty flag on new and updated items to tag them for retrieval from the context when the sync starts and resetting the flag after the item is saved to Parse.  There are other updates as well - set ParseID for new items to match updates from the server later, set updatedAt for sync'ing updates to the server, etc.  As in FTASync, I'm using an abstract parent entity to hold these details and the presence of the parent entity to determine what to sync from the model.  I'm still working through maintaining relationships among objects across the CoreData / Parse divide as well as deciding what if anything to do about grandchildren, etc. of the abstract parent entity.  I need relationships but may not need the grandchildren, etc. to sync. 

 

And in each SCTableViewController there's code to handle setting the flags correctly when STV does a new, update or delete.  I also needed to put in filtering to handle soft deletions.  There may be a way to handle all of that by sub-classing SCTableViewController - but, I haven't gotten that far yet...  The code includes defining an NSPredicate, attaching the NSPredicate to the SCArrayOfObjectsSection, code for the didUpdateItem (gets triggered for both new and update) and willDeleteItem sectionActions of the objectsSection and code for viewWillAppear.  viewWillAppear could definitely go in a sub-class. 

 

Any idea if it's possible to define an objectsSection in a subclass then override the entityDefinition for the objectsSection in the instance?

 

Here's the code that's in the view controller...

 

    NSPredicate *excludeDeletedObjects = [NSPredicate predicateWithFormat:@"deleted == NO"];
    SCArrayOfObjectsSection *objectsSection = [SCArrayOfObjectsSection sectionWithHeaderTitle:nil
                                                                             entityDefinition:entityDef
                                                                              filterPredicate:excludeDeletedObjects];
    objectsSection.addButtonItem = self.addButton;
    objectsSection.detailViewControllerOptions.presentationMode = SCPresentationModeModal;
    objectsSection.placeholderCell = [SCTableViewCell cellWithText:@"No entities added yet"
                                                     textAlignment:NSTextAlignmentCenter];

    objectsSection.sectionActions.didUpdateItem = ^(SCArrayOfItemsSection *itemsSection, NSObject *item, NSIndexPath *indexPath)
    {
        // set synced false to capture in next sync to service
        [item setValue:[NSNumber numberWithBool:NO] forKey:@"synced"];
    };
    
    objectsSection.sectionActions.willDeleteItem = ^BOOL(SCArrayOfItemsSection *itemsSection, NSObject *item, NSIndexPath *indexPath)
    {
        // change deleted to true to pass in next sync to server
        [item setValue:[NSNumber numberWithBool:YES] forKey:@"deleted"];
        [item setValue:[NSNumber numberWithBool:NO] forKey:@"synced"];
        
        // refresh the view
        [self.tableViewModel reloadBoundValues];
        [self.tableViewModel.tableView reloadData];
        
        // return false to prevent deletion
        return FALSE;

   };
    
    [self.tableViewModel addSection:objectsSection];
}

 

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    [self.tableViewModel reloadBoundValues];
    [self.tableViewModel.tableView reloadData];
}

 

Thanks,

Mark



#4 Everett

Everett

    Ev

  • STV 5.0 Pro
  • PipPipPipPipPip
  • 191 posts
  • LocationMichigan
Reputation: 60
Outstanding

Posted 01 March 2014 - 09:37 AM

Hi Mark,

 

I think you're on the right track.  Syncing data can very challenging, especially if your trying to sync both directions or if your syncing in both directions with more than 1 device or people and syncing edits - and/or offline.  Yeah, very challenging indeed.

 

I'm not 100% sure what you were asking here:

Any idea if it's possible to define an objectsSection in a subclass then override the entityDefinition for the objectsSection in the instance?

 

 

Your approach seems reasonable though:

 - have a set of properties that define data that has been updated, deleted or new.  I'd probably make the didSync field to default to NO on new items and flip it on updates.

 

 - I don't know if you can modify the way STV deletes items without creating your own SCCoreDataStore, which is a lot more work for you.  You could fake out the interface by providing your own delete button then doing the soft delete (update isDeleted property) then reload your tableview.

 

Also - here is a link on syncing core data and parse - its does not cover syncing with STV but it may have some general concepts that could be helpful to you:  http://www.raywender...-service-part-1

 

 

good luck on this, its a very tricky challenge.


Edited by Everett, 01 March 2014 - 09:44 AM.


#5 Laeger

Laeger

    Experienced Member

  • STV 5.0 Pro
  • PipPipPipPip
  • 59 posts
Reputation: 6
Good

Posted 12 March 2014 - 02:03 PM

Hey Mark,

I also checked out FTASync but like you noticed it was very erratic.  Also, it is no longer actively developed (although I believe there are a couple of forks that have made progress since the author abandoned it).

 

I ended up abandoning FTASync and writing my own sync solution.  I took the approach of creating categories on NSManagedObject and NSManagedObjectContext and creating my own save and fetch methods.  This allowed me much more granular control over when sync would happen and what would happen.  I also was able to handle the special reserved Parse.com classes, such as _User, _PFInstallation and _PFRole this way (you have to modify the class name to include the _).  This does require that you be VERY careful with how you use STV, as STV will default to the standard save and fetch methods.  After much mucking about I was able to get it implemented perfectly but for a while there I seriously just considered subclassing SCCoreDataStore and over writing the save and fetch methods there to use my category methods.  For importing data from Parse.com, I utilize the MagicalImport portion of MagicalRecord.(MagicalImport tutorial by Saul Mora ) It is AWESOME!!!  You need to get the developer branch version of MR though, as there were a couple of data type import bugs in the master branch that were addressed in the developer branch.

 

I ALSO abandoned using the objectId assigned by the Parse.com server completely and instead have the client assign an UUID upon -awakeFromInsert.  The reason is that relating objects when saving to Parse.com is very very tricky for complex relationships, when they are new objects (must have a Parse.com assigned objectId before relating).  All of my entities have a property, guid, that is globally unique.  I also have a Cloud Code function that helps check that the guid is unique beforeSave (not really needed, though, and not perfect.  Will probably remove). (Parse.com thread).  My objects are "related" on Parse.com by the guid, not the objectId.

 

Finally, be very careful using a parent, abstract entity.  Depending on your model there may be a performance hit.  This is because all sub entities with the same abstract parent are part of the same sqlite table. 

 

Happy to answer additional questions.

-Aaron


Edited by Laeger, 16 March 2014 - 03:38 AM.

  • markr likes this

#6 markr

markr

    Sr. Member

  • STV 5.0 Pro
  • PipPipPip
  • 41 posts
Reputation: 5
Good

Posted 15 March 2014 - 04:59 PM

Hi Aaron -

 

Thank you!  I'll be looking into adding the attributes directly to the entities instead of using the abstract entity as a parent as well as using a GUID instead of Parse's objectId.  How did you get STV to use your category specific save and fetch methods?

 

Also - have you done anything with relationships?  I'm running into problems trying to get objectId's stuffed into a Parse relationship column from STV...

 

Thanks,
Mark



#7 Laeger

Laeger

    Experienced Member

  • STV 5.0 Pro
  • PipPipPipPip
  • 59 posts
Reputation: 6
Good

Posted 16 March 2014 - 03:50 AM

Hey Mark,

 

For the save methods, I just used the various actions: -didInsertItem, -didUpdateItem was all I needed.

 

For the fetch methods, after setting up STV for my view I have a call to my fetch method on the category.  That method has a completion block which executes after the update/import from the server has finished, and in that completion I reloadBoundValues and reloadTableView.

 

When you say "Parse relationship", do you mean a PFRelation?  If so, then no I have not utilized those yet but will probably have to soon.  The only reason to use a PFRelation is if you know that the to-many relationship will have a large number of related objects, say over 100.  Otherwise, just use an array of GUID's to represent the relationship.  If you DO need to use it though, the only difference is that you will need to add a test to your relationships during the importing and sync processes, similar to what you would do if you want Parse to save your BOOL primitives as actual primitives instead of numbers.  You may need an additional API call to refresh the PFRelation object, depending on your implementation.

 

Hopefully that helps!  If not maybe I can elaborate.

 

-Aaron


Edited by Laeger, 16 March 2014 - 03:57 AM.


#8 Laeger

Laeger

    Experienced Member

  • STV 5.0 Pro
  • PipPipPipPip
  • 59 posts
Reputation: 6
Good

Posted 16 March 2014 - 03:56 AM

One additional thought:  If you wanted to have STV use specific save and fetch methods "naturally" without having to place the calls into actions, etc., you will need to subclass SCCoreDataStore, and override the appropriate methods.  I think -commitData, -updateObject, -deleteObject, -fetchObjectsWithOptions, and their async cousins are the methods to look at.



#9 markr

markr

    Sr. Member

  • STV 5.0 Pro
  • PipPipPip
  • 41 posts
Reputation: 5
Good

Posted 16 March 2014 - 07:54 AM

Hi Aaron -

 

Yes - I meant PFRelation.  See this thread http://sensiblecocoa...tionattributes/ for more details on my work in progress.

 

As a step 1 I'm using an active connection to Parse instead of the sync process.  At the moment, I have two view controllers - one for the "parent"  (the 1 side of the to-many) and one for selection of the children (the to-many end).  Using STV to get the parent to add/edit/delete to the Parse service was a snap.  Using SCPropertyTypeArrayOfObjects with cellActions.detailViewController isn't working the way I expect.

 

Before that, I tried using SCPropertyTypeObjectSelection which displays user interface to select children from the rows already entered for class TaskStep using Parse's Data Browser.  However, if children are selected, the parent is not created or updated upon clicking Done after completing editing.  This produces the following error message:   **STV** Serialization error: Error Domain=TODO_DOMAIN Code=-1 "Cannot serialize data of type '__NSSetM'" UserInfo=0x8be3f40 {NSLocalizedDescription=Cannot serialize data of type '__NSSetM'}.  Also, for SCPropertyTypeObjectSelection with SCObjectSelectionAttributes:attributesWithObjectsWebServiceDefinition I'm still looking for a way to set allowAddingItems, allowDeletingItems and/or allowMovingItems to true.

 

Once I can get this working with an active connection I'll also be adding use of relations to the sync code so the parent & children are saved in Core Data offline then sync'ed to Parse when the user presses a button.

 

--- Mark



#10 Laeger

Laeger

    Experienced Member

  • STV 5.0 Pro
  • PipPipPipPip
  • 59 posts
Reputation: 6
Good

Posted 17 March 2014 - 05:05 AM

Mark,

In your Parse.com backend, is the "taskSteps" relationship defined as a PFRelation also?  If so, have you tried deleting the column and re-defining it as an array?

 

I have not played with the STV Parse.com definition but I don't recall it supporting PFRelation attributes.  PFRelations are tricky as they are essentially objects themselves (functionally similar to a join table I believe), which will need to be fetched, updated and saved independently of the parent and child(ren).  Their primary use is in cases where the "to-many" relationship will have more than 100 child objects (great guide here from parse.com).  Less than that and it probably makes more sense to use arrays.  Another benefit of using arrays is that you can use -includeKey: method on the PFQuery to fetch the child objects in the array when you fetch the parent object.  Otherwise you'll need to call -fetchAll.







Also tagged with one or more of these keywords: data-stores, core-data, web-services

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users