Reputation: 351
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.
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
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