Ross Satchell
Ross Satchell

Reputation: 351

Core data doesn't seem to be saved

I am trying to create an app that requires the user to successfully enter a pin before being allowed onto the rest of the app. I did some searching around and found a basic existing coredata example app that works here.

I went into the xcdatamodel and deleted their attributes and replaced with "pin" which is a String. Here is a screenshot of the xcdatamodel.xcdatamodel screenshot

Then I modified the ViewController so that the createData UIbutton opens a alertController that prompts the user to enter a new pin twice, checks they are the same, and if they are it creates a coredata entry with that pin.

Here is the relevant code of the ViewController:

import UIKit
import CoreData

class ViewController: UIViewController {
var firstPinNumber:String = ""
var secondPinNumber:String = ""

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

}

@IBAction func createData(_ sender: Any) {
    let enterPinAlertController = UIAlertController(title: "Enter New PIN", message: "", preferredStyle: .alert)
    enterPinAlertController.addTextField{ (textField1:UITextField)->Void in
        textField1.placeholder = "Enter PIN"
        textField1.isSecureTextEntry = true
    }
    enterPinAlertController.addTextField{   (textField2:UITextField)->Void in
        textField2.placeholder = "Re-Enter PIN"
        textField2.isSecureTextEntry = true
    }
    let okAction = UIAlertAction(title: "OK", style: .cancel)   {(action) in
        if let textFields = enterPinAlertController.textFields  {
            let theTextFields = textFields as [UITextField]
            self.firstPinNumber = theTextFields[0].text!
            self.secondPinNumber = theTextFields[1].text!
            if self.firstPinNumber != self.secondPinNumber   {
                print ("PINs dont match!")
                let pinsDontMatchAlertController = UIAlertController(title: "PINs don't match!", message: "Try again", preferredStyle: .alert)
                let okAction = UIAlertAction(title: "OK", style: .cancel)   {(action) in
                }
                pinsDontMatchAlertController.addAction(okAction)
                self.present(pinsDontMatchAlertController, animated: true, completion: nil)
            }
        }
    }

    enterPinAlertController.addAction(okAction)
    self.present(enterPinAlertController, animated: true, completion: nil)
    createPIN(pinNum: secondPinNumber)


}
func createPIN(pinNum: String){

    //As we know that container is set up in the AppDelegates so we need to refer that container.
    guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }

    //We need to create a context from this container
    let managedContext = appDelegate.persistentContainer.viewContext

    //Now let’s create an entity and new user records.
    let userEntity = NSEntityDescription.entity(forEntityName: "User", in: managedContext)!

    let user = NSManagedObject(entity: userEntity, insertInto: managedContext)
    user.setValue(pinNum, forKeyPath: "pin")
    print(user.value(forKey: "pin") as Any)


    //Now we have set the pin. The next step is to save it inside the Core Data

    do {
        try managedContext.save()

    } catch let error as NSError {
        print("Could not save. \(error), \(error.userInfo)")
    }
}

@IBAction func retrieveData(_ sender: Any) {
    let storedPin = retrievePIN()
    print(storedPin)
}

func retrievePIN()->String {
    var storedPin:String = ""

    //As we know that container is set up in the AppDelegates so we need to refer that container.
    guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return "" }

    //We need to create a context from this container
    let managedContext = appDelegate.persistentContainer.viewContext

    //Prepare the request of type NSFetchRequest  for the entity
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "User")

    fetchRequest.fetchLimit = 1
//        fetchRequest.predicate = NSPredicate(format: "username = %@", "Ankur")
//        fetchRequest.sortDescriptors = [NSSortDescriptor.init(key: "email", ascending: false)]
//
    do {
        let result = try managedContext.fetch(fetchRequest)
        for data in result as! [NSManagedObject] {
            if data.value(forKey: "pin") != nil {
                storedPin = data.value(forKey: "pin") as! String
                print(storedPin)
            } else  {
                print ("Found nil")
            }
        }

    } catch {

        print("Failed")
    }
    return storedPin
}

Using breakpoints I have ascertained that it enters the createPin() function, but it seems to enter that function BEFORE it presents the enterPinAlertController to enter the new pin, even though createPin() is called AFTER the enterPinAlertController is presented.

Also if I use the retrieveData UIButton it prints out "Found nil"

So if what I'm thinking is correct, its creating a coredata entry with an empty string, or nothing at all? How can I fix this so that it creates a coredata entry with the string the user enters as the new pin, and also retrieves it later?

Upvotes: 0

Views: 41

Answers (1)

Paulw11
Paulw11

Reputation: 115041

Your call to createPin needs to be inside the action handler for okAction. As you have it now, secondPinNumber will be called before the alert has been shown, so it will be empty or nil, depending on how you initialise it.

IBAction func createData(_ sender: Any) {
    let enterPinAlertController = UIAlertController(title: "Enter New PIN", message: "", preferredStyle: .alert)
    enterPinAlertController.addTextField{ (textField1:UITextField)->Void in
        textField1.placeholder = "Enter PIN"
        textField1.isSecureTextEntry = true
    }
    enterPinAlertController.addTextField{   (textField2:UITextField)->Void in
        textField2.placeholder = "Re-Enter PIN"
        textField2.isSecureTextEntry = true
    }
    let okAction = UIAlertAction(title: "OK", style: .cancel)   {(action) in
        if let textFields = enterPinAlertController.textFields,
           let firstPinNumber = textFields[0].text,
           let secondPinNumber = textFields[1].text,
           firstPinNumber == secondPinNumber {
               createPIN(pinNum: secondPinNumber)
            } else {
               print ("PINs dont match!")
               let pinsDontMatchAlertController = UIAlertController(title: "PINs don't match!", message: "Try again", preferredStyle: .alert)
               let okAction = UIAlertAction(title: "OK", style: .cancel)  
               pinsDontMatchAlertController.addAction(okAction)
               self.present(pinsDontMatchAlertController, animated: true, completion: nil)
            } 
        }
    }

    enterPinAlertController.addAction(okAction)
    self.present(enterPinAlertController, animated: true, completion: nil)

}

Upvotes: 1

Related Questions