Core Data insert - swift

I try to learn Core Data and after many hours and a lot of tutorials, I am still confused. All the tutorials seem so complicated with so many elements involved. It would be very helpful if someone could give me an example of the most simple insert possible. (please without tables, navigation controllers... just insert a name and password)
I have created an entity of users with the attribute name and password
I have two textfields: name and password. I have the connection from the storyboard to the ViewController
I have a button
How can I save names and passwords when the user clicks to button?
#IBOutlet weak var name: UITextField!
#IBOutlet weak var password: UITextField!
#IBAction func button(_ sender: UIButton) {
}

First, do you have an NSManagedObjectContext available at this point? Assuming you do and it's called context
import UIKit
import CoreData
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if let viewController = window?.rootViewController as? ViewController {
viewController.context = persistentContainer.viewContext
}
return true
}
// ... other functions
}
import CoreData
class ViewController: UIViewController {
#IBOutlet weak var name: UITextField!
#IBOutlet weak var password: UITextField!
var context: NSManagedObjectContext!
override func viewDidLoad() {
super.viewDidLoad()
assert(context != nil)
}
#IBAction func button(_ sender: UIButton) {
guard name.hasText || password.hasText else { return }
let newUser = User(context: context)
if let nameString = name.text {
newUser.name = nameString
}
if let passwordString = password.text {
newUser.password = passwordString
}
context.insert(newUser)
saveContext()
}
func saveContext() {
if context.hasChanges {
do {
try context.save()
} catch {
print(error.localizedDescription)
}
}
}
}

Very simple example.
Instantiate the context lazily in the view controller
lazy var context : NSManagedObjectContext = {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}()
If it's going to be used only the in IBAction instantiate it temporarily in there (see commented out code)
Then in the IBAction write
#IBAction func button(_ sender: UIButton) {
guard name.hasText, !name.text!.first!.isWhitespace,
password.hasText, !password.text!.first!.isWhitespace else {
// show an alert that both text fields must not be empty and must not start with a whitespace characters.
return
}
// let appDelegate = UIApplication.shared.delegate as! AppDelegate
// let context = appDelegate.persistentContainer.viewContext
let user = User(context: context)
user.name = self.name.text!
user.password = self.password.text!
do {
try context.save()
} catch {
print(error)
}
}
Side note: To avoid ambiguity name the text fields more distinct for example
#IBOutlet weak var nameField: UITextField!
#IBOutlet weak var passwordField: UITextField!

Related

reloadData() from another viewController Swift

I have two viewControllers: the first one has a tableView in it and the second one has a textField with an action, if there is a specific text inserted in the textFiled, I want to call loadData1() function which has orderTable.reloadData() to reload the tableView from the logInviewController, but it returns nil when I call it.
tableViewController code :
import UIKit
import FirebaseFirestore
import Firebase
import FirebaseAuth
class orderTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
#IBOutlet var orderTable: UITableView!
var db: Firestore!
var firstName = [String]()
var lastName = [String]()
override func viewDidLoad() {
super.viewDidLoad()
orderTable.register(UINib(nibName: "Order1TableViewCell", bundle: nil) , forCellReuseIdentifier: "orderCell")
}
func loadData1() {
Firestore.firestore().collection("hola").getDocuments() { [self]
(querySnapshot, err) in
if let err = err
{
print("Error getting documents: \(err)");
}
else
{
for document in querySnapshot!.documents {
self.firstName.append(document.get("firstname") as? String ?? "")
self.lastName.append(document.get("lastname") as? String ?? "")
}
}
orderTable.reloadData() // from here i got Unexpectedly found nil while unwrapping an Optional value:
}
}
}
}
logInViewController code :
import UIKit
import Firebase
import FirebaseAuth
class logInViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var userNameField: UITextField!
#IBOutlet var passwordField: UITextField!
#IBOutlet var logInButton: UIButton!
var db: Firestore!
var order: orderTableViewController!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func textfieldDidChange(_ sender: Any) {
print(userNameField?.text ?? "")
if userNameField.text == "v#v.com" {
let i = orderTableViewController()
i.loadData1()
}
}
}
Where you have let i = orderTableViewController(), you are not referencing your existing table view controller, but rather are creating a new one, except this time it is not instantiated in conjunction with the storyboard scene, and thus all of your #IBOutlet references will be nil. Attempts to reference those #IBOutlet references will fail.
To fix this, you should pass a reference for the first view controller to the second one, using a protocol rather than an explicit class name, and then the second view controller can call a method in the first. Thus:
Create class protocol, e.g. LoginViewControllerDelegate:
protocol LoginViewControllerDelegate: class { }
Give that protocol one method requirement, loadData1:
protocol LoginViewControllerDelegate: class {
func loadData1()
}
Make your first view controller conform to that protocol:
extension OrderTableViewController: LoginViewControllerDelegate {
func loadData1() {
... your implementation here ...
}
}
Create a property in the second view controller, that LoginViewController, for this delegate-protocol reference, e.g.:
weak var delegate: LoginViewControllerDelegate?
When first view controller instantiates second, set this delegate property (e.g. if doing segues, it would be in prepareForSegue):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? LoginViewController {
destination.delegate = self
}
}
The second view controller then would call delegate?.loadData1() rather than i.loadData1().
If you do what I understand then you can do this. But you should use delegate or closure callback to do that.
#IBAction func textfieldDidChange(_ sender: Any) {
print(userNameField?.text ?? "")
if userNameField.text == "v#v.com" {
if let i = order {
i.loadData1()
}
}
}
}

Force unwrapping nil optional for UIImageView when transitioning to view controller

I'm running into an error when transitioning to view controllers by overriding the built-in prepare() function in Swift. I have a UIImageView for backgrounds on my screens. Here is the code for two of the view controllers in question.
import UIKit
import FirebaseAuth
class HomeVC: UIViewController {
#IBOutlet weak var signOutButton: UIButton!
#IBOutlet weak var backgroundImageView: UIImageView!
#IBOutlet weak var friendsNavButton: UIButton!
#IBOutlet weak var homeNavButton: UIButton!
#IBOutlet weak var profileNavButton: UIButton!
#IBOutlet weak var bumpButton: UIButton!
#IBOutlet weak var welcomeLabel: UILabel!
#IBOutlet weak var doNotDisturbLabel: UILabel!
#IBOutlet weak var doNotDisturbButton: UIButton!
var userName = ""
var dndIsOn: Bool = false
#IBAction func dndToggled(_ sender: Any) {
dndIsOn = !dndIsOn
User.current.available = !dndIsOn
FirestoreService.db.collection(Constants.Firestore.Collections.users).document(User.current.uid).updateData([Constants.Firestore.Keys.available : !dndIsOn])
if dndIsOn {
print("DND is on!")
setupDNDUI()
} else if !dndIsOn {
print("DND is off!")
setupActiveUI()
}
}
#IBAction func signOutTapped(_ sender: Any) {
let firAuth = Auth.auth()
do {
try firAuth.signOut()
} catch let signOutError as NSError {
print ("Error signing out: %#", signOutError)
}
print("Successfully signed out")
}
#IBAction func bumpTapped(_ sender: Any) {
self.performSegue(withIdentifier: Constants.Segues.toCall, sender: self)
}
#IBAction func friendsNavTapped(_ sender: Any) {
self.performSegue(withIdentifier: Constants.Segues.toFriends, sender: self)
}
#IBAction func profileNavTapped(_ sender: Any) {
let nav = self.navigationController //grab an instance of the current navigationController
DispatchQueue.main.async { //make sure all UI updates are on the main thread.
nav?.view.layer.add(CATransition().segueFromLeft(), forKey: nil)
nav?.pushViewController(ProfileVC(), animated: false)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(true, animated: true)
self.backgroundImageView.contentMode = UIView.ContentMode.scaleAspectFill
doNotDisturbLabel.isHidden = true
if !userName.isEmpty {
welcomeLabel.text = "Welcome Back, " + userName + "!"
} else {
welcomeLabel.text = ""
}
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .darkContent
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let friendsVC = segue.destination as? FriendsVC else {
return
}
FirestoreService.db.collection(Constants.Firestore.Collections.users).document(User.current.uid).getDocument { (snapshot, err) in
if let err = err {
print(err.localizedDescription)
} else {
let data = snapshot!.data()!
let requests = data[Constants.Firestore.Keys.requests] as? [String]
if let requests = requests {
friendsVC.requests = requests
}
}
}
}
class FriendsVC: UIViewController {
//var friends: [Friend] = User.current.friends
var friends: [User] = []
var requests: [String]?
#IBOutlet weak var requestsNumberLabel: UILabel!
#IBOutlet weak var backgroundImageView: UIImageView!
#IBOutlet weak var friendRequestsButton: UIButton!
#IBOutlet weak var homeNavButton: UIButton!
#IBOutlet weak var friendsTitle: UILabel!
#IBOutlet weak var friendTableView: UITableView!
#IBOutlet weak var addFriendButton: UIButton!
#IBOutlet weak var tableViewTopConstraint: NSLayoutConstraint!
#IBAction func friendRequestsTapped(_ sender: Any) {
self.performSegue(withIdentifier: Constants.Segues.toRequests, sender: self)
}
#IBAction func homeNavTapped(_ sender: Any) {
let nav = self.navigationController //grab an instance of the current navigationController
DispatchQueue.main.async { //make sure all UI updates are on the main thread.
nav?.view.layer.add(CATransition().segueFromLeft(), forKey: nil)
nav?.pushViewController(HomeVC(), animated: false)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(true, animated: true)
backgroundImageView.contentMode = UIView.ContentMode.scaleAspectFill
friendTableView.backgroundView?.backgroundColor = .white
friendsTitle.isHidden = false
UserService.getUserArray(uids: User.current.friendUids, completion: { (users) in
guard let users = users else {
print("User has no friends")
return
}
self.friends = users
self.friendTableView.reloadData()
})
guard let requests = self.requests else {
friendRequestsButton.isHidden = true
requestsNumberLabel.isHidden = true
self.tableViewTopConstraint.constant = 0
return
}
requestsNumberLabel.text = requests.count.description
// Do any additional setup after loading the view.
friendTableView.delegate = self
friendTableView.dataSource = self
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let homeVC = segue.destination as? HomeVC {
homeVC.userName = User.current.firstName
} else if let requestsVC = segue.destination as? RequestsVC {
UserService.getUserArray(uids: self.requests!) { (requesters) in
if let requesters = requesters {
requestsVC.requesters = requesters
}
}
}
}
}
When my app loads into the home screen, there is no problem, and when a button is tapped to transition to FriendsVC, there is no problem. However, when I try to initiate the transition from HomeVC to ProfileVC or from FriendVC to HomeVC, I get the error: "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value" at the self.backgroundImageView.contentMode = UIView.ContentMode.scaleAspectFill lines in my viewDidLoad methods. These segues have something in common in that these are the ones where I override the prepare() function, but I'm not sure what I'm doing wrong

swift read write to firestore documents and collections

from the IOS app i'm trying to update with new data 5 different "collection" in Firestore. I'm able to do that but to 1 collection only. I can't figure it out how to push the updates to all 5 collections at once. Any help is greatly appreciated.
import UIKit
import Firebase
class itemDataViewController: UIViewController {
#IBOutlet var itemLabels: [UILabel]!
#IBOutlet weak var testLabel: UILabel!
#IBOutlet weak var firstItemField: UITextField!
#IBOutlet weak var secondItemField: UITextField!
#IBOutlet weak var thirdItemField: UITextField!
#IBOutlet weak var fourthItemField: UITextField!
#IBOutlet weak var fithItemField: UITextView!
var docRef: DocumentReference!
var infoListener: ListenerRegistration!
#IBAction func updateData(_ sender: Any) {
guard let firstItemText = firstItemField.text, !firstItemText.isEmpty else { return }
guard let secondItemText = secondItemField.text, !secondItemText.isEmpty else { return }
guard let thirdItemText = thirdItemField.text, !thirdItemText.isEmpty else { return }
guard let fourthItemText = fourthItemField.text, !fourthItemText.isEmpty else { return }
guard let fithItemText = fithItemField.text, !fithItemText.isEmpty else { return }
let dataToSave: [String: Any] = ["firstItem": firstItemText]
docRef.setData(dataToSave) { (error) in
if let error = error {
print("Oh no! Could not save: \(error.localizedDescription)")
} else {
print("Data Saved")
}
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
infoListener = docRef.addSnapshotListener { (docSnapshot, error) in
guard let docSnapshot = docSnapshot, docSnapshot.exists else { return }
let myData = docSnapshot.data()
let firstItemUpdate = myData?["firstItem"] as? String ?? ""
self.firstItemField.text = "\(firstItemUpdate)"
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillAppear(animated)
infoListener.remove()
}
override func viewDidLoad() {
super.viewDidLoad()
docRef = Firestore.firestore().document("2/firstItem")
(itemLabels as NSArray).setValue(10, forKey: "cornerRadius")
// Do any additional setup after loading the view.
}
}

Taking Facebook values (email, name, etc) from one view controller and transferring them to another one

So I'm currently designing an application and am working on the account registration. I've implemented Facebook login into it but I am having trouble taking a user's Facebook information and transferring it to the next page, where, ideally, the fields for name and email would be filled with those values received from Facebook.
The initial page with the Facebook login looks like this:
class RegisterVC: UIViewController, FBSDKLoginButtonDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let loginButton = FBSDKLoginButton()
view.addSubview(loginButton)
loginButton.frame = CGRect(x: 82, y: 325, width: view.frame.width - 210, height: 59)
loginButton.delegate = self
}
func loginButtonDidLogOut(_ loginButton: FBSDKLoginButton!) {
print("Did log out of facebook")
}
func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!) {
if error != nil {
print(error)
return
}
print("Successfully logged in")
FBSDKGraphRequest(graphPath: "/me", parameters: ["fields": "id, name, email"]).start {(connection, result, err) in
if err != nil {
print("Failed to start graph request", err)
return
} else {
guard let data = result as? [String:Any] else {return}
let fbEmail = data["email"]
let fbName = data["name"]
}
print(result)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//get destination view an set the fullname
let vc = segue.destination as? CreateAccountVC
vc?.email = self.fbEmail
vc.fullname = self.fbName
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
And the next view controller, a typical registration page, has these text fields:
#IBOutlet weak var fullname: UITextField!
#IBOutlet weak var username: UITextField!
#IBOutlet weak var age: UITextField!
#IBOutlet weak var email: UITextField!
#IBOutlet weak var verifyEmail: UITextField!
#IBOutlet weak var password: UITextField!
#IBOutlet weak var verifyPassword: UITextField!
I have no idea how to take the values from Facebook and put them into these text entry boxes. I'm very new to programming so any help would be much appreciated, thanks!
First, store the FB values in a var(s) you can access later.
Next, You can set the values for your next view before you transition. For example if you are using a segue, you can do it in the prepareForSegue like this:
class RegisterVC: UIViewController, FBSDKLoginButtonDelegate {
var fbName:String?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//get destination view an set the fullname
let vc=segue.destination as? MyCustomVC
vc.fullName=self.fbName
}

user Log In, let the users in without errors (SWIFT) (Parse)

i'm making an app that required Logging In. the problem is when i run my app to try it and type wrong user info it proceed to the next view controller without giving the error !. Heres my code i don't whats the problem !
{
#IBOutlet weak var ActivityIndicator: UIActivityIndicatorView!
#IBOutlet weak var Message: UILabel!
#IBOutlet weak var UsernameTextField: UITextField!
#IBOutlet weak var PasswordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
ActivityIndicator.hidden = true
ActivityIndicator.hidesWhenStopped = true
// Do any additional setup after loading the view.
}
#IBAction func LogInButtonTapped(sender: AnyObject) {
LogIn()
}
func LogIn() {
// Start activity indicator
ActivityIndicator.hidden = false
ActivityIndicator.startAnimating()
// if there is a user
var user = PFUser()
user.username = UsernameTextField.text
user.password = PasswordTextField.text
PFUser.logInWithUsernameInBackground(UsernameTextField.text, password:PasswordTextField.text) {
(user: PFUser?, error: NSError?) -> Void in
if user != nil {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("LogInToHomeVC", sender: self)}
println("Logged In")
} else {
self.ActivityIndicator.stopAnimating()
if let Message: AnyObject = error!.userInfo!["error"] {
self.Message.text = "\(Message)"}
println("Could Not Find User")
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
so the question is how to let the user try again and not let him enter the Home Page?