In my following code when i am calling openDatabase() it is showing fatal error as pathToDatabase is nil.
Actually when i am calling findObjectsInBackground i am unable to enter the block. so that i am getting pathToDatabase as nil ? Please help me.
import UIKit
import Parse
class existDatabase: NSObject {
var databaseFileName = ""
var pathToDatabase: String!
var database: FMDatabase!
var userAccessCode = ""
override init()
{
super.init()
if let accesscode = USERDEFAULTS.string(forKey: "UserAccessCode"){
let query: PFQuery = PFQuery(className: "AccessCode")
query.whereKey("RegisterCode", equalTo: accesscode)
query.findObjectsInBackground { (objects, err) in
if err == nil{
if let users = objects{
if users.isEmpty {
}
else{
for post in users{
if let surgery = post["SurgeryType"] as? String{
if surgery == "Trigger Finger"{
self.databaseFileName = "thumbtriggerfinger"
self.pathToDatabase = Bundle.main.path(forResource: "thumbtriggerfinger", ofType: "db")
}
}
}
}
}
}
else{
print(err?.localizedDescription)
}
}
}
}
func openDatabase() -> Bool {
if database == nil {
if FileManager.default.fileExists(atPath: pathToDatabase) {
database = FMDatabase(path: pathToDatabase)
}
}
if database != nil {
if database.open() {
return true
}
}
return false
}
}
Presumably you have code something like
let x = existDatabase()
x.openDatabase()
The problem is that you have an asynchronous block in your initialiser, which means that the object won't be fully initialised when the initialiser returns; the Parse background fetch is not yet complete.
You get a crash because the implicitly unwrapped optional is nil
. The fact that you needed to use an implicitly unwrapped optional is your first warning sign. You should be able to use non-optionals for a property set in an initialiser, but you can't because the properties are set in the closure.
Having an asynchronous operation in an initialiser is rarely a good design.
I would suggest you restructure your code so that openDatabase
performs the asynchronous operation and accepts a completion hander.
Also, stylistically, class names should start with a capital letter, and are typically nouns, not verbs.
class DatabaseManager {
static database(for accessCode: String, completion: @escaping (FMDatabase?,Error?)) {
let query: PFQuery = PFQuery(className: "AccessCode")
query.whereKey("RegisterCode", equalTo: accessCode)
query.findObjectsInBackground { (objects, err) in
if err == nil{
if let users = objects, !users.isEmpty {
for post in users{
if let surgery = post["SurgeryType"] as? String{
if surgery == "Trigger Finger"{
let pathToDatabase = Bundle.main.path(forResource: "thumbtriggerfinger", ofType: "db")
if FileManager.default.fileExists(atPath: pathToDatabase) {
database = FMDatabase(path: pathToDatabase)
if database.open() {
completion(database,nil)
} else {
completion(nil, nil)
}
break
}
}
}
}
} else {
completion(nil,nil)
}
}
else{
completion(nil, err)
}
}
}
}
Call it using:
if let accesscode = USERDEFAULTS.string(forKey: "UserAccessCode"){
DatabaseManager.database(for: accesscode) { (database, error) in {
guard error == nil else {
print("error: (error!.localalizedDescription)")
return
}
guard let db = database else {
print("No database")
return
}
// Do something with db
}
}