In my app, I have a local SQLite database and use a parse server (Back4App.com). The reason for the two is to allow sync between user devices and if a user deletes and later reinstalls app, can download data.
I am using the parse sign function. After they sign in, I check to see if the local database has data, if not, I go to Parse and download from there then to insert data into local database.
The strange thing is the sequence of events is not happening as I would expect and this is where my issue is.
SigninVC is below:
@IBAction func signinButton(sender: UIButton) {
activityIndicator.startAnimating()
// hides keyboard
self.view.endEditing(true)
// if username or password is empty
if usernameTextField.text!.isEmpty || passwordTextField.text!.isEmpty {
SCLAlertView().showError("Opps".localized, subTitle: "missingDetail1".localized)
}
// login functions
PFUser.logInWithUsername(inBackground: self.usernameTextField.text!, password: self.passwordTextField.text!) {
(user: PFUser?, error: Error?) -> Void in
if let error = error {
if let errorString = (error as NSError).userInfo["error"] as? String {
self.activityIndicator.stopAnimating()
NSLog(errorString);
SCLAlertView().showError("Error".localized, subTitle: "loginError".localized)
}
} else {
self.userDefault.set(PFUser.current()!.username, forKey: "username")
self.userDefault.synchronize()
self.activityIndicator.stopAnimating()
self.signInCompleted ()
}
self.rlDatabase.checkForEmtpySpeciesDetail ()
let datbaseCheckResult = self.userDefault.bool (forKey: "databaseEmpty")
if datbaseCheckResult == true {
print("No data in RLDatabase, now checking if Parse has Data")
print("1 Calling downloadFromParse function")
self.downloadFromParse { [weak self] in
print("6 Calling insertParseToSQLite function")
self?.rlDatabase.insertParseToSQLite()
}
} else {
// print("Database Not empty, no update needed")
}
}
}
func downloadFromParse (_ completion: ()->()) {
print("2 Starting downloadFromParse function")
userDefault.bool (forKey: "parseHasData")
var userCommonNameArray = parseArrayModel.userCommonNameArray
var userCommonNameESArray = parseArrayModel.userCommonNameESArray
var userCommonNameFRArray = parseArrayModel.userCommonNameFRArray
var userCommonNameDEArray = parseArrayModel.userCommonNameDEArray
var speciesNameArray = parseArrayModel.speciesNameArray
var userNotesArray = parseArrayModel.userNotesArray
let downloadQuery = PFQuery(className:"ReefLifeApps")
downloadQuery.whereKey("username", equalTo: PFUser.current()!.username!)
downloadQuery.findObjectsInBackground {(downloadedData, error) -> Void in
if error == nil {
print("3 begining downloadQuery")
for downloadData in downloadedData! {
userCommonNameArray.append(downloadData.object(forKey: "userCommonName") as! String)
userCommonNameESArray.append(downloadData.object(forKey: "userCommonNameES") as! String)
userCommonNameFRArray.append(downloadData.object(forKey: "userCommonNameFR") as! String)
userCommonNameDEArray.append(downloadData.object(forKey: "userCommonNameDE") as! String)
speciesNameArray.append(downloadData.object(forKey: "speciesName") as! String)
userNotesArray.append(downloadData.object(forKey: "userNotes") as! String)
print (speciesNameArray)
print("4 finished downloadQuery")
}
} else {
self.userDefault.set(false, forKey: "parseHasData")
// print("No username registered in parse")
}
}
if speciesNameArray.isEmpty == true {
self.userDefault.set(false, forKey: "parseHasData")
} else {
self.userDefault.set(true, forKey: "parseHasData")
}
completion()
print("5 finished downloadFromParse function")
}
RLDatabase.swift has the insert to SQLite
func insertParseToSQLite () {
print("7 begining insertParseToSQLite function")
let parseCheckResult = self.userDefault.bool (forKey: "parseHasData")
if parseCheckResult == true {
let userCommonNameArray = parseArrayModel.userCommonNameArray
let userCommonNameESArray = parseArrayModel.userCommonNameESArray
let userCommonNameFRArray = parseArrayModel.userCommonNameFRArray
let userCommonNameDEArray = parseArrayModel.userCommonNameDEArray
let speciesNameArray = parseArrayModel.speciesNameArray
let userNotesArray = parseArrayModel.userNotesArray
var statement: OpaquePointer? = nil
let update = String(format:"UPDATE RL_Species SET user_common_name = '\(userCommonNameArray)', user_common_name_fr = '\(userCommonNameFRArray)', user_common_name_es = '\(userCommonNameESArray)', user_common_name_de = '\(userCommonNameDEArray)', user_notes = '\(userNotesArray)' WHERE RL_Species.specie = '\(speciesNameArray)';")
print("8 starting SQLite3 insert")
if sqlite3_prepare_v2(database, update, -1, &statement, nil) == SQLITE_OK {
if sqlite3_step(statement) == SQLITE_DONE {
// print("Successfully updated database with Parse data.")
} else {
// print("Could NOT Update database with Parse data.")
}
} else {
// forces SQLite to send error messagesddd
let errorMessage = String.init(validatingUTF8: sqlite3_errmsg(database))!
print("update failed! \(errorMessage)")
}
sqlite3_finalize(statement)
} else {
}
print("9 finishing insertParseToSQLite function")
}
ParseArrayModel.swift
struct ParseArrayModel
{
var userCommonNameArray = [String]()
var userCommonNameESArray = [String]()
var userCommonNameFRArray = [String]()
var userCommonNameDEArray = [String]()
var speciesNameArray = [String]()
var userNotesArray = [String]()
}
As you can see, the first call is to download from Parse, then call the insert function. However, what is happening is it goes to the downloadFromParse, but after the
downloadQuery.findObjectsInBackground {(downloadedData, error) -> Void in
it skips OVER the the remainder of the function, then goes to the insertToSQLite function and as there is no data in the array, nothing gets inserted. Then it goes back to the DownloadFromParse and then grabs the data. So, in the end, all the data is there, but in the wrong sequence of events.
Completely stumped. Any help is greatly appreciated.
EDIT: Added // print lines to show order after update.
Here is what the console displayed:
No data in RLDatabase, now checking if Parse has Data
1 Calling downloadFromParse function
2 Starting downloadFromParse function
6 Calling insertParseToSQLite function
7 begining insertParseToSQLite function
9 finishing insertParseToSQLite function
5 finished downloadFromParse function
3 begining downloadQuery
["Acanthaster planci"]
4 finished downloadQuery
I am posting the results to help anyone whom may come across this issue of wanting to transfer data from a parse server to a local database, in my case SQLite.
Instead of trying to complete the download before calling the insertToSQLite func, I needed only to add the insertToSQLite func inside the query call.
Plus some other programmer errors on my side. Below are the results of what worked after testing: