Reputation: 9072
I was thinking of using coreData to store my username and password for persistence from the users. While testing I noticed that when I quickly close the program after it saves the data and relaunch it to check if the data was persisted that it would sometimes say nothing was there, and then when I relaunch the app again then it would be there. The longer I waited the more likely when I relaunched the app that the persisted data would appear.
I was adding coreData to an existing project so I created a controller called DataController.swift and copied the suggested code from apple. That code is below.
import UIKit
import CoreData
class DataController: NSObject {
var managedObjectContext: NSManagedObjectContext
override init() {
// This resource is the same name as your xcdatamodeld contained in your project.
guard let modelURL = NSBundle.mainBundle().URLForResource("AppSettings", withExtension:"momd") else {
fatalError("Error loading model from bundle")
}
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
guard let mom = NSManagedObjectModel(contentsOfURL: modelURL) else {
fatalError("Error initializing mom from: \(modelURL)")
}
let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)
self.managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
self.managedObjectContext.persistentStoreCoordinator = psc
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
let docURL = urls[urls.endIndex-1]
/* The directory the application uses to store the Core Data store file.
This code uses a file named "DataModel.sqlite" in the application's documents directory.
*/
let storeURL = docURL.URLByAppendingPathComponent("AppSettings.sqlite")
do {
try psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil)
} catch {
fatalError("Error migrating store: \(error)")
}
}
}
}
My LoginViewController.swift is below:
import UIKit
import CoreData
class LoginViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var usernameField: UITextField!
@IBOutlet weak var passwordField: UITextField!
let moc = DataController().managedObjectContext
@IBAction func SignUpButtonPressed(sender: UIButton) {
print("sign up")
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
override func viewDidLoad() {
super.viewDidLoad()
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
view.addGestureRecognizer(tap)
print("view loaded, check if already signed in here")
let loggedIn = checkIfLoggedInAlready() //checks database to see
if(loggedIn){
print("was logged in!")
}
}
func checkIfLoggedInAlready() -> Bool{
let fetchRequest = NSFetchRequest(entityName: "AppSettings")
//let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) //Deletes ALL appsettings entities
do {
let fetchRequest = try moc.executeFetchRequest(fetchRequest) as! [AppSettings]
guard let appSettingsArrayItem = fetchRequest.first where fetchRequest.count>0 else {
print ("no entities found...")
return false
}
guard let username = (appSettingsArrayItem as AppSettings).username else{
print ("username not found")
return false
}
print("number Of AppSetting Entities =\(fetchRequest.count)")
print(username)
//The following code deletes ALL the entities!
//try moc.persistentStoreCoordinator!.executeRequest(deleteRequest, withContext: moc)
//To delete just '1' entry use the code below.
//moc.deleteObject(appSettingsArrayItem)
// try moc.save()//save deletion change.
print("deleted particular entity item")
return true
} catch{
fatalError("bad things happened \(error)")
}
}
func dismissKeyboard() {
//Causes the view (or one of its embedded text fields) to resign the first responder status.
view.endEditing(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
print("prepar seque")
}
func displayErrorMessage(errorMessage: String){
print("show error console with Error:"+errorMessage)
let alert = UIAlertController(title: "Error", message: errorMessage, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
switch(identifier){
case "loginSegue":
print("loginSegue Check")
guard let password = passwordField.text!.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet()) where !password.isEmpty else {
displayErrorMessage("Password can not be empty!")
return false
}
guard let username = usernameField.text!.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet()) where !username.isEmpty else{
displayErrorMessage("Username can not be empty!")
return false
}
let url = "http://mywebsite.com/restapi/v1/userlogin?email="+username+"&password="+password
print(url)
let json = JSON(url:url)
print(json)
if(json["status"].asInt==1){
let entity = NSEntityDescription.insertNewObjectForEntityForName("AppSettings", inManagedObjectContext: moc) as! AppSettings
entity.setValue(username, forKey: "username")
entity.setValue(password, forKey: "password")
entity.setValue(json["tokenid"].asString, forKey: "token")
entity.setValue(json["roleid"].asInt, forKey: "roleid")
entity.setValue(json["role"].asString, forKey: "role")
entity.setValue(json["companyid"].asInt , forKey: "companyid")
entity.setValue(json["isdev"].asInt, forKey: "isdev")
//save token and other details to database.
do {
try moc.save()
print("saved to entity")
}catch{
fatalError("Failure to save context: \(error)")
}
//token
//roleid int
//role
//companyid int
return true //login succesfull
}else{
displayErrorMessage("Incorrect Username or Email")
return false//failed
}
default:
displayErrorMessage("Unknown Error Related To Segue Not Found")
}
return false //if it gets to this point assume false
}
}
To test my code you would just uncomment the part that deletes the entity (//moc.deleteObject(appSettingsArrayItem)
) in the function checkIfLoggedInAlready().
In short though the answer to this question is a simple yes or no, I have a hunch its threaded and that is why the delay matters. I think its threaded because of this line in the DataController.swift
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0))
but not sure if that is what is affecting it. The code that creates the entity is done in shouldPerformSegueWithIdentifier of LoginViewController.swift
Upvotes: 0
Views: 107
Reputation: 4513
Not by default. All of the NSManagedObjects are bound to NSManagedContext which has it's own queue. If you want concurrency, you need to create another NSManagedContext with private queue. You can read about concurrency in Core Data over here:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Concurrency.html
Also, never use dispatch_async
for Core Data. Each context has it's own peformBlock
which run on it's queue.
Upvotes: 3