Swift Sync Data of Core Data an MySQL Database

swift sync data of core data an mysql database

There is no quickest way but I can definitely suggest something that works for me. Best or not - its up to you to decide.

Also your question is very generic - as it encompasses the entire backend infrastructure of a mobile app. I'll try to be as specific as I can -

1) Make an http post request to a php file, which select all data of the mysql database and give the result back to the app -

Once you receive the result, you can Parse it in Core Data like the example below -

    func writeDataToSqliteStore() throws {

// Configure the Network Call here with NSURL & NSData returning an array that you can write to the Core Data SQLITE backend.
do {
jsonResponse = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as? NSArray
} catch {
print(error)
}
let localEntity = self.insertNewEntity("EntityName") as? CoreDataEntityClass

for singleObject in jsonResponse {

localEntity.name = singleObject.valueForKey("name")
localEntity.address = singleObject.valueForKey("address")
}
}

2) Check which data exist in core data => nothing to do, and which data is new => save the new data in core data -

You can do good ol head call with a Time Stamp and check if the data has been updated.
If the data has been updated - it could get a little tricky updating individual Core Data attributes. What has always worked for me is deleting the SQLITE backend on the device and running the data write method again.
You can use the example below -

func removeSQLITEFiles() {
let fileManager = NSFileManager.defaultManager()
let URL1 = self.applicationDocumentsDirectory.URLByAppendingPathComponent("File.sqlite")
let URL2 = self.applicationDocumentsDirectory.URLByAppendingPathComponent("File.sqlite-wal")
let URL3 = self.applicationDocumentsDirectory.URLByAppendingPathComponent("File.sqlite-shm")

if fileManager.fileExistsAtPath(URL1.path!) {
do {
try fileManager.removeItemAtURL(URL1)
}
catch {error}
}

if fileManager.fileExistsAtPath(URL2.path!) {
do {
try fileManager.removeItemAtURL(URL2)
}
catch {error}
}

if fileManager.fileExistsAtPath(URL3.path!) {
do {
try fileManager.removeItemAtURL(URL3)
}
catch {error}
}

}

After that you can run the writeDataToSqlieStore() method again.

Hope that helps.

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)

How to synchronize a SQLite Core Data database with a remote MySQL database?

If you're looking at rails, try taking a peek at ActiveAdmin gem. It's what I used on my first iOS and rails project for a client. It gives you an administrative dashboard that'll handle a lot of what you'll be wanting if you can get it set up. It's very confusing at first, but a few weeks will give you a pretty good web solution. In addition, depending on your experience in creating servers, you might want to look at heroku for a low cost host that does all the work for you (if you start needing more processors though, Heroku gets pricy very quickly). From a github project, you can have heroku up and running your rails code in about 5 minutes flat.

As far as synchronizing your database from Server To Phone: You'll want to institute a type of last_updated_at timestamp for your models on the server DB. Now when anything is updated, you'll update the timestamp. Now the iOS app can pass a ?last_updated_at parameter to your server. This will allow your server to figure out everything that has changed since the last time they pinged the server. Then gnab it into your core data db on the phone.

For Syncing Phone to Server:

First make sure the phone is up to date before syncing (using last_updated_at param). If it's clear, then this is where it's hard. You'll need to translate the objects you want to sync from the CoreData db (since it adds it's own columns/tables automatically) and pass them up. Otherwise you can pass up your coreData db and do some kind of conversion on the server.

OR

Do a conversion in your iPhone app with an update to migrate off of CoreData. This will be a pain but it'll help in the long run if you deploy to other OS's. You'll need to create sqlite3 queries to convert the CoreData db into a new SQL DB (we were able to copy 2-3MB of data within 1.2 seconds on an iPhone 5 so it's pretty quick). Then the app will only use SQLite3 so that it can sync up the full DB to the server. Then this will make syncing with the phone easier, where it can just grab the full DB from the server and plug it in.

How to synchronise Core Data relationships?

Assuming you have an Entity for Word and Category you will need to make a relationship (naming may be a bit hazy). Also assuming a Category can have many words and

// Word Entity
Relationship Destination Inverse
category Categories words

// Category Entity
Relationship Destination Inverse
words Word category // To-Many relationship

You are correct you would not need the category_id field as all relationships are managed through the object graph that Core Data maintains. You will still need a primary key like server_id (or similar) in each entity or you will have trouble updating/finding already saved objects.

This is how I deal with syncing data from an external database (I use RESTful interfaces with JSON but that does not really matter)

  1. Grab the feed sorted by server_id
  2. Get the primary keys (server_id) of all the objects in the feed
  3. Perform a fetch using the a predicate like ... @"(serverId IN %@)", primaryKeys
    which is sorted by the primary key.
  4. Step through each array. If the fetch result has my record then I update it. If it does not then I insert a new one.
  5. You would need to do this for both Word and Category
  6. Next fetch all objects that form part of a relationship
  7. Use the appropriate methods generated by core data for adding objects. e.g. something like `[myArticle addWords:[NSSet setWithObjects:word1, word2, word3, nil];

It's hard for me to test but this should give you a starting point?
Good to see a fellow Shiny course attendee using stack overflow - it's not just me

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

swift do task after the preview task is finished

You should use closure blocks as callback when the operation ends

class func SyncMYSQL(onSuccess: ()->()){

// your custom events
// your custom events
// your custom events

if MYSQLisSynced == true {
// when MYSQL is synced you can call the success block
onSuccess()
}

}

In other file when you call the function SyncMYSQL() you have to specify the on success block

SyncMYSQL { () -> () in
//custom callback actions
}

Store and retrieve unique identifier from Core Data

There is no 'built in' solution for creating an auto-incrementing id in Core Data.
One important thing you should recognize is that Core Data is an object graph not a relational database. This is very important in understanding how you should approach design with Core Data.



Yes, Core Data does create unique identifiers for objects in the form of GUIDs - so it's not a number, but rather 32 hexadecimal digits (Globally unigue identifier).



You can write a method that will get the next number in a sequence for an entity, but Core Data will not do it for you.

You may find the information in this question useful: Set auto increment in Core data iOS. I would not attempt to use NSManagedObjectID. This value can change.

My suggestion is that you allow the MySQL database to assign the id's and simply store that id in a number property in the Core Data object. For items originating in the app leave the id property blank until it has been sent to the MySQL database for persistence. Then, retrieve the Id that the database assigned it and set it to the Core Data object's property.

Core Data Sync - Tracking Deleted Objects

How about you keep a delta history table with UUID and created/updated/deleted field, maybe with a revision number for each update? So you keep a small check list of changes since your last successful sync.

That way, if you delete an object you could add an entry in the delta history table with the deleted UUID and mark it deleted. Same with created and updated objects, you only need to check the delta table to see what items you the server needs to delete, update, create, etc. You could even store every revision on the server to support rolling back to a previous version in the future if you feel like it.

I think a revision number is better than relying on client's clock that could potentially be changed manually.

You could use NSManagedObjectContext's insertedObjects, updatedObjects, deletedObjects methods to create the delta objects before every save procedure :)

My 2 cents



Related Topics



Leave a reply



Submit