Swift Core Data Sync with Web Server

Swift Core Data Sync With Web Server

  1. Add new BOOL attribute to your CoreData Entity and name it as synched. This will basically store the synched status for each object.

  2. Now you will have to check for internet connectivity. Refer to this link

  3. Whenever you have internet connectivity, just fetch the objects from CoreData using the following NSPredicate in your fetch request:

    let isSynchedPredicate = NSPredicate(format: "synched = %@", false)
  4. Now you can just use your webservice to sync and update all the fetched objects to server. On successfull upload, DONOT forget to change the synched property to true

sync core data with web server

Following might be helpful

http://publications.csail.mit.edu/tmp/MIT-CSAIL-TR-2005-014.pdf

http://iphone2009.crowdvine.com/talk/presentation_file/5104/Grover_Syncing.pdf

https://stackoverflow.com/a/5052208/1294448

How to sync data from web service with Core Data?

You are running into the classic insert/update/delete paradigm.

The answer is, it depends. If you get a chunk of json data then you can use KVC to extract the unique ids from that chunk and do a fetch against your context to find out what exists already. From there it is a simple loop over the chunk of data, inserting and updating as appropriate.

If you do not get the data in a nice chunk like that then you will probably need to do a fetch for each record to determine if it is an insert or update. That is far more expensive and should be avoided. Batch fetching before hand is recommended.

Deleting is just about as expensive as fetching/updating since you need to fetch the objects to delete them anyway so you might as well handle updating properly instead.

Update

Yes there is an efficient way of building the dictionary out of the Core Data objects. Once you get your array of existing objects back from Core Data, you can turn it into a dictionary with:

NSArray *array = ...; //Results from Core Data fetch
NSDictionary *objectMap = [NSDictionary dictionaryWithObjects:array forKeys:[array valueForKey:@"identifier"]];

This assumes that you have an attribute called identifier in your Core Data entity. Change the name as appropriate.

With that one line of code you now have all of your existing objects in a NSDictionary that you can then look up against as you walk the JSON.

Saving CoreData to a Web Server with Swift 3.0

The question that you have linked is not trying to explain how to communicate with a web server. It is explaining how to store data in core data and tag/mark it in a way that you know which records have been sent to the web server or not.

So the Predicate will fetch all records that have not been sent to the web server and allow you to send them when you have an internet connection available.

Communicating with a web server can be a broad topic and will depend on your web server and API setup, so it is too much to explain here fully. I refer you to some free online resources that will help you understand networking in Swift.

  • Udacity - Networking with Swift
  • Ray Wenderlich - Alamofire Tutorial
  • Stack Overflow - Swift POST Request

Here is an example of a POST Request from the StackOverflow answer above

var request = URLRequest(url: URL(string: "http://test.tranzporthub.com/street45/customer_login.php")!)
request.httpMethod = "POST"
let postString = "user_id=chaitanya3191@gmail.com&password=123"
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(error)")
return
}

if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")

}

let responseString = String(data: data, encoding: .utf8)
print("responseString = \(responseString)")
}
task.resume()

Using code similar to this, you should be able to send data to your web server, then your web server can do whatever it likes with it.

UPDATE:

To encode your parameters to JSON you can use the following code as a guide

var dictionary = [
"username": "Test User",
"password": "Password"
]

if let jsonData = try? JSONSerialization.data(withJSONObject: dictionary, options: []) {
// jsonData is a byte sequence, to view it you would need to convert to string
print(String(bytes: jsonData, encoding: String.Encoding.utf8))
}

Which would output:

Optional("{\"username\":\"Test User\",\"password\":\"Password\"}")

Note: you would send it as data, not the string version. so your code might look like this:

request.httpBody = jsonData

How to Sync iPhone Core Data with web server, and then push to other devices?

I suggest carefully reading and implementing the sync strategy discussed by Dan Grover at iPhone 2009 conference, available here as a pdf document.

This is a viable solution and is not that difficult to implement (Dan implemented this in several of its applications), overlapping the solution described by Chris. For an in-depth, theoretical discussion of syncing, see the paper from Russ Cox (MIT) and William Josephson (Princeton):

File Synchronization with Vector Time Pairs

which applies equally well to core data with some obvious modifications. This provides an overall much more robust and reliable sync strategy, but requires more effort to be implemented correctly.

EDIT:

It seems that the Grover's pdf file is no longer available (broken link, March 2015). UPDATE: the link is available through the Way Back Machine here

The Objective-C framework called ZSync and developed by Marcus Zarra has been deprecated, given that iCloud finally seems to support correct core data synchronization.

sync core data with web server

Following might be helpful

http://publications.csail.mit.edu/tmp/MIT-CSAIL-TR-2005-014.pdf

http://iphone2009.crowdvine.com/talk/presentation_file/5104/Grover_Syncing.pdf

https://stackoverflow.com/a/5052208/1294448

Core data and server data sync

Your server would probably require a CRUD (create, read, update, delete) type API. Requests should be queued when offline and sent repeatedly until a valid response is received, server should handle duplicate requests gracefully.

To handle add, you could call a method like 'createJob' with some parameters and receive an id from the server which you could then use to populate the job_id field.

To handle changes made server-side, you essentially need a 'last update' timestamp which you store locally, you would sync with the server and provide the newest timestamp you have previously received (and stored) or zero (never received one). The server would then return everything that has changed after this point in time along with the new timestamp (last change) that the app will need to send next time. The response should include enough information in order to update the current state and resolve any conflicts.

To tackle the deleted state, your record could have a 'hidden' flag to indicate whether it has been deleted (this is known as a 'soft delete'), this helps with referential integrity, but if you are sure you can purge the record without breaking any relationships you could do this once you have received a valid 'deleteJob' response from the server.

Without architecting your solution there is not a great deal more help I can provide. However, there are various tools out there to help less experienced developers tackle this process, such as RESTKit. Server-side you could look at Node.js or ruby on rails or anything else that handles REST for you.

You can use different HTTP methods in order to achieve this without having multiple endpoints, as defined in RFC2616.

iOS - Core Data and Server Database Synchronization Best Practices

I store a last modified timestamp in the database on both the core data records on the phone, and the mysql tables on the server.

The phone searches for everything that has changed since the last sync and sends it up to the server along with a timestamp of the last sync, and the server responds with everything that has changed on it's end since the provided sync timestamp.

Performance is an issue when a lot of records have changed. I do the sync on a background NSOpeartion which has it's own managed object context. When the background thread has finished making changes to it's managed object context, there is an API for merging all of the changes into the main thread's managed object context - which can be configured to simply throw away all the changes if there are any conflicts caused by the user changing data while the sync is going on. In that case, I just wait a few seconds and then try doing a sync again.

On older hardware even after many optimisations it was necessary to abort the sync entirely if the user starts doing stuff in the app. It was simply using too many system resources. I think more modern iOS devices are probably fast enough you don't need to do that anymore.

(by the way, when I said "a lot of records have changed" I meant 30,000 or so rows being updated or inserted on the phone)

Sync offline core data with the server when app have internet connection

I would suggest the following flow:

  1. Implement a "request manager" that has a "private queue" context inside it
  2. When some module need to issue a request, it does so using the manager
  3. when a request is needed, the manager ALWAYS write it to the store (using its context) with a timestamp of creation date
  4. The manager will also listen to online/offline status changes
    1. When online status is detected, the managed query the store for pending requests and issue them one by one to the server
    2. when a new request is needed, the manager will act as described in (4.1) to prevent request starvation
    3. You can use a flag indicating if the manager is currently in operation (processing requests) so that a new request inserted will not imediatly trigger a fetch from the store
    4. reqests issued to the server might have their own context to write to the store so that they won't intrfere with the manager work
    5. When "offline" status is detected, the manager may cancel all active requests (they will be executed the next time online status is detected
    6. When a request if finished (commited to the server and local store), it is deleted from the store

Before activating the manager, you could query the store for pending requests and cancel/delete the ones that are no longer relevant.



Related Topics



Leave a reply



Submit