从解析服务器到本地 sqlite (Swift 3)
From parse server to local sqlite (Swift 3)
在我的应用程序中,我有一个本地 SQLite 数据库并使用解析服务器 (Back4App.com)。两者的原因是允许用户设备之间同步,如果用户删除并稍后重新安装应用程序,可以下载数据。
我正在使用解析符号功能。他们登录后,我检查本地数据库是否有数据,如果没有,我去解析并从那里下载,然后将数据插入本地数据库。
奇怪的是事件的顺序并没有像我预期的那样发生,这就是我的问题所在。
SigninVC 如下:
@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 插入到 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]()
}
可以看到,首先调用的是从Parse下载,然后调用insert函数。然而,发生的事情是它转到 downloadFromParse,但在
之后
downloadQuery.findObjectsInBackground {(downloadedData, error) -> Void in
它跳过函数的其余部分,然后转到 insertToSQLite 函数,由于数组中没有数据,因此不会插入任何内容。然后它返回到 DownloadFromParse 然后抓取数据。所以,最后,所有数据都在那里,但事件顺序错误。
完全被难住了。非常感谢任何帮助。
编辑:
添加 // 打印行以显示更新后的顺序。
这是控制台显示的内容:
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
这是因为您在后台异步下载,因此您的代码将在该过程进行时继续执行。您将需要创建一个回调,让您知道下载完成时,然后您可以调用下一个函数。
func downloadFromParse (_ completion: ()->()) {
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 {
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)
}
} 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()
}
那么你可以这样做:
downloadFromParse { [weak self] in
// call next function knowing the parse download is complete.
}
我发布结果是为了帮助任何可能遇到将数据从解析服务器传输到本地数据库(在我的例子中是 SQLite)的人。
我不需要在调用 insertToSQLite 函数之前尝试完成下载,我只需要在查询调用中添加 insertToSQLite 函数。
加上我这边的一些其他程序员错误。以下是测试后的工作结果:
var userCommonNameArray = String()
var userCommonNameESArray = String()
var userCommonNameFRArray = String()
var userCommonNameDEArray = String()
var speciesNameArray = String()
var userNotesArray = String()
var speciesIDArray = Int()
func txFromParseToSQLite () {
let downloadQuery = PFQuery(className:"ReefLifeApps")
downloadQuery.whereKey("username", equalTo: PFUser.current()!.username!)
downloadQuery.findObjectsInBackground {(downloadedData, error) -> Void in
if error == nil {
if (downloadedData?.count == 0) {
print("DownloadData.count == 0")
return
} else {
if let downloadedData = downloadedData {
for downloadData in downloadedData {
self.userCommonNameArray = downloadData["userCommonName"] as! String
self.userCommonNameFRArray = downloadData["userCommonNameFR"] as! String
self.userCommonNameESArray = downloadData["userCommonNameES"] as! String
self.userCommonNameDEArray = downloadData["userCommonNameDE"] as! String
self.userNotesArray = downloadData["userNotes"] as! String
self.speciesNameArray = downloadData["speciesName"] as! String
self.speciesIDArray = downloadData["speciesID"] as! Int
print (downloadData)
self.insertParseToSQLite ()
}
}
}
}
}
}
func insertParseToSQLite () {
var statement: OpaquePointer? = nil
let update = String(format:"UPDATE RL_User SET user_common_name = '\(userCommonNameArray)', user_common_name_fr = '\(userCommonNameFRArray)', user_common_name_es = '\(userCommonNameESArray)', user_common_name_de = '\(userCommonNameDEArray)', user_notes = '\(userNotesArray)', speciesName = '\(speciesNameArray)' WHERE id = \(speciesIDArray);")
if sqlite3_prepare_v2(databaseUser, update, -1, &statement, nil) == SQLITE_OK {
if sqlite3_step(statement) == SQLITE_DONE {
} else {
}
} else {
let errorMessage = String.init(validatingUTF8: sqlite3_errmsg(databaseUser))!
print("update failed! \(errorMessage)")
}
sqlite3_finalize(statement)
}
在我的应用程序中,我有一个本地 SQLite 数据库并使用解析服务器 (Back4App.com)。两者的原因是允许用户设备之间同步,如果用户删除并稍后重新安装应用程序,可以下载数据。
我正在使用解析符号功能。他们登录后,我检查本地数据库是否有数据,如果没有,我去解析并从那里下载,然后将数据插入本地数据库。
奇怪的是事件的顺序并没有像我预期的那样发生,这就是我的问题所在。
SigninVC 如下:
@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 插入到 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]()
}
可以看到,首先调用的是从Parse下载,然后调用insert函数。然而,发生的事情是它转到 downloadFromParse,但在
之后downloadQuery.findObjectsInBackground {(downloadedData, error) -> Void in
它跳过函数的其余部分,然后转到 insertToSQLite 函数,由于数组中没有数据,因此不会插入任何内容。然后它返回到 DownloadFromParse 然后抓取数据。所以,最后,所有数据都在那里,但事件顺序错误。
完全被难住了。非常感谢任何帮助。
编辑: 添加 // 打印行以显示更新后的顺序。
这是控制台显示的内容:
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
这是因为您在后台异步下载,因此您的代码将在该过程进行时继续执行。您将需要创建一个回调,让您知道下载完成时,然后您可以调用下一个函数。
func downloadFromParse (_ completion: ()->()) {
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 {
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)
}
} 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()
}
那么你可以这样做:
downloadFromParse { [weak self] in
// call next function knowing the parse download is complete.
}
我发布结果是为了帮助任何可能遇到将数据从解析服务器传输到本地数据库(在我的例子中是 SQLite)的人。
我不需要在调用 insertToSQLite 函数之前尝试完成下载,我只需要在查询调用中添加 insertToSQLite 函数。
加上我这边的一些其他程序员错误。以下是测试后的工作结果:
var userCommonNameArray = String()
var userCommonNameESArray = String()
var userCommonNameFRArray = String()
var userCommonNameDEArray = String()
var speciesNameArray = String()
var userNotesArray = String()
var speciesIDArray = Int()
func txFromParseToSQLite () {
let downloadQuery = PFQuery(className:"ReefLifeApps")
downloadQuery.whereKey("username", equalTo: PFUser.current()!.username!)
downloadQuery.findObjectsInBackground {(downloadedData, error) -> Void in
if error == nil {
if (downloadedData?.count == 0) {
print("DownloadData.count == 0")
return
} else {
if let downloadedData = downloadedData {
for downloadData in downloadedData {
self.userCommonNameArray = downloadData["userCommonName"] as! String
self.userCommonNameFRArray = downloadData["userCommonNameFR"] as! String
self.userCommonNameESArray = downloadData["userCommonNameES"] as! String
self.userCommonNameDEArray = downloadData["userCommonNameDE"] as! String
self.userNotesArray = downloadData["userNotes"] as! String
self.speciesNameArray = downloadData["speciesName"] as! String
self.speciesIDArray = downloadData["speciesID"] as! Int
print (downloadData)
self.insertParseToSQLite ()
}
}
}
}
}
}
func insertParseToSQLite () {
var statement: OpaquePointer? = nil
let update = String(format:"UPDATE RL_User SET user_common_name = '\(userCommonNameArray)', user_common_name_fr = '\(userCommonNameFRArray)', user_common_name_es = '\(userCommonNameESArray)', user_common_name_de = '\(userCommonNameDEArray)', user_notes = '\(userNotesArray)', speciesName = '\(speciesNameArray)' WHERE id = \(speciesIDArray);")
if sqlite3_prepare_v2(databaseUser, update, -1, &statement, nil) == SQLITE_OK {
if sqlite3_step(statement) == SQLITE_DONE {
} else {
}
} else {
let errorMessage = String.init(validatingUTF8: sqlite3_errmsg(databaseUser))!
print("update failed! \(errorMessage)")
}
sqlite3_finalize(statement)
}