16 Replies
      Latest reply on Jan 13, 2019 8:52 PM by OOPer
      Joel14 Level 1 Level 1 (0 points)

        I have a dictionary that I am trying to append information to but instead of appending to it just deletes the previous information. The dictionaries are in two different scopes. The dictionary name is personal info. They are on line 40 and 47. I tried doing personalInfo.append but it tells me that it has no member append. I am trying to save information to a database from to different views. This is the code that I have right now:

         

          @IBAction func sendPhoneAuthCode(_ sender: Any){
                if let phoneNum = phoneNumber.text {    // No more need to unwrap phoneNum later
                    
                    
                    PhoneAuthProvider.provider().verifyPhoneNumber(phoneNum, uiDelegate: nil) { (verificationID, error) in
                        
                        // Sign in using the verificationID and the code sent to the user
                        // …
                        //  if phoneNum == nil {
                        if error != nil {
                            print("verifyPhoneNumber went NOT OK")
                            // REPLACE BY print       self.present(alert, animated: true, completion: nil)
                            
                            
                            DispatchQueue.global(qos: .userInitiated).async {
                                let titre = " Missing Information "
                                let detailMsg = "Please enter your phone number"
                                let controller = UIAlertController(title: titre, message: detailMsg, preferredStyle: .alert)
                                
                                let okAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.cancel)
                                controller.addAction(okAction)
                                DispatchQueue.main.async {
                                    self.present(controller, animated: true, completion: nil)
                                }
                                
                            }
                            
                            
                            return
                        } else {
                            print("verifyPhoneNumber went OK")
                            // You have to test credential here, where do you do this ?
                        }
                    }
                } else {
                    print("phoneNumber.text is nil")
                    return
                }
                personalInfo = ["Phone Number" : phoneNumber.text]
                print("Setting userDefaults", verificationID)
                UserDefaults.standard.set(verificationID, forKey: "authVerificationID")
            }
        
        
         @IBAction func savePersonalInfo(_ sender: Any) {
                personalInfo = ["First Name" : firstName.text, "Last Name" : lastName.text, "Email" : email.text, "Address Line 1" : addressLine1.text, "Address Line 2" : addressLine2.text]
                let ref = Database.database().reference()
                ref.childByAutoId().setValue(personalInfo)
            }
        
        • Re: Dictionary Scope
          OOPer Level 7 Level 7 (4,615 points)

          When you write a question of Dictionary, you should better include how you declared the variable, to get better answer sooner.

           

          I assume:

          var personalInfo: [String: Any] = [:]

          If you declaration is something far from this, you may need to modify the following code.

           

          And, when you want to modify or add some limited entries of a Dictionary and keep all other entries as is, you just need to use the subscript notation:

                  personalInfo["Phone Number"] = phoneNumber.text ?? ""
          
          

          or:

                  personalInfo["Phone Number"] = phoneNumber.text ?? ""
                  personalInfo["First Name"] = firstName.text ?? ""
                  personalInfo["Last Name"] = lastName.text ?? ""
                  personalInfo["Email"] = email.text ?? ""
                  personalInfo["Address Line 1"] = addressLine1.text ?? ""
                  personalInfo["Address Line 2"] = addressLine2.text ?? ""
          
          

           

          Or else you can use `merge` method in your second case if you prefer:

                  personalInfo.merge([
                      "First Name" : firstName.text ?? "",
                      "Last Name" : lastName.text ?? "",
                      "Email" : email.text ?? "",
                      "Address Line 1" : addressLine1.text ?? "",
                      "Address Line 2" : addressLine2.text ?? ""] as [String: Any]
                      , uniquingKeysWith: {_, new in return new})
          
          

           

          You can omit `?? ""` in some cases.

           

          And this is not the main topic of your question, but the lines 39.-41. should be in the completion handler of `verifyPhoneNumber` method.

            • Re: Dictionary Scope
              Joel14 Level 1 Level 1 (0 points)

              It still is losing the phone number when it changes the view. How would I fix this?

                • Re: Dictionary Scope
                  OOPer Level 7 Level 7 (4,615 points)

                  You need to show more details, other parts of your code, what you have done and what you see.

                  • Re: Dictionary Scope
                    Claude31 Level 8 Level 8 (5,305 points)

                    What  do you mean « losing » ?

                     

                    What is the value you pass ro the view ?

                    what do you get ?

                    Did you find what this number is (another record ? The previous value ? ...)

                    • Re: Dictionary Scope
                      Polyphonic Apple Staff Apple Staff (60 points)

                      It's losing the phone number because that's what you told it to do. When you assign to a dictionary variable or property:

                       

                      personalInfo = ["Phone Number" : phoneNumber.text] 

                       

                      you're saying that you want to throw away the current dictionary (and all its contents), and create a new dictionary with only these new contents.

                       

                      You would fix this by not assigning new values to "personaInfo", but by replacing individual entries (keys/value pairs) as OOPer showed you.

                       

                      Another way looking at this is that you're confusing the container and the contents. You can make a new container, but the old contents are in the old container. The new container doesn't get the old contents unless you copy them there. (But in this case, you don't need a new container, you just want to put more stuff in the old container.)

                        • Re: Dictionary Scope
                          Joel14 Level 1 Level 1 (0 points)

                          This is how my code looks now:

                           

                          var personalInfo = [String : String?]()
                          
                          
                            @IBAction func sendPhoneAuthCode(_ sender: Any){
                                  if let phoneNum = phoneNumber.text {    // No more need to unwrap phoneNum later
                                      
                                      
                                      PhoneAuthProvider.provider().verifyPhoneNumber(phoneNum, uiDelegate: nil) { (verificationID, error) in
                                          
                                          // Sign in using the verificationID and the code sent to the user
                                          // …
                                          //  if phoneNum == nil {
                                          if error != nil {
                                              print("verifyPhoneNumber went NOT OK")
                                              // REPLACE BY print       self.present(alert, animated: true, completion: nil)
                                              
                                              
                                              DispatchQueue.global(qos: .userInitiated).async {
                                                  let titre = " Missing Information "
                                                  let detailMsg = "Please enter your phone number"
                                                  let controller = UIAlertController(title: titre, message: detailMsg, preferredStyle: .alert)
                                                  
                                                  let okAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.cancel)
                                                  controller.addAction(okAction)
                                                  DispatchQueue.main.async {
                                                      self.present(controller, animated: true, completion: nil)
                                                  }
                                                  
                                              }
                                              
                                              
                                              return
                                          } else {
                                              print("verifyPhoneNumber went OK")
                                              // You have to test credential here, where do you do this ?
                                          }
                                      }
                                  } else {
                                      print("phoneNumber.text is nil")
                                      return
                                  }
                                  personalInfo["Phone Number"] = phoneNumber.text
                                  print("Setting userDefaults", verificationID)
                                  UserDefaults.standard.set(verificationID, forKey: "authVerificationID")
                              }
                          
                          
                              @IBAction func savePersonalInfo(_ sender: Any) {
                                  personalInfo["First Name"] = firstName.text
                                  personalInfo["Last Name"] = lastName.text
                                  personalInfo["Email"] = email.text
                                  personalInfo["Address Line 1"] = addressLine1.text
                                  personalInfo["Address Line 2"] = addressLine2.text
                                  print(personalInfo)
                                  let ref = Database.database().reference()
                                  ref.childByAutoId().setValue(personalInfo)
                              }
                          

                           

                           

                          The issue is still happening where it deletes the phone number.

                            • Re: Dictionary Scope
                              Polyphonic Apple Staff Apple Staff (60 points)

                              You have a lot of "print" statements that will tell you what your code did. What's your evidence that the phone number was ever in the dictionary? When is the first time your code shows that it isn't there? What happens in between? (These are questions for you rather than the forum, since your code gives the answer.)

                                • Re: Dictionary Scope
                                  Joel14 Level 1 Level 1 (0 points)

                                  It saves it to the dictionary but when I go to the new view controller it deletes it and puts the new information in it. How do I save it to the dictionary even when I change view controllers?

                                    • Re: Dictionary Scope
                                      OOPer Level 7 Level 7 (4,615 points)

                                      You should better clarify that you go to the new view controller.

                                      Where are the codes of the old view controller and the new view controller and how you move between view controllers?

                                       

                                      Do you want to ask how do you store values into a Dictionary?

                                      Or do you want to ask how do you persist a Dictionary?

                                       

                                      How your Dictionary and database are related? And what is the database?

                                      When you cannot solve your issue with a few responses, that's because your question does not contain needed information to solve your issue.

                                       

                                      - Show all relevant codes. If your issue happens when moving to a new view controller, you may need to show both view contollers especially how you move.

                                      - Clarify what you do when checking, for example

                                          Enter phoneNumber in A-view controller

                                          Move to B-view controller by pushing B-button in A view controller

                                          Move back to A-view controller by pushing Back-button in the navigation bar of B-view controller

                                      - Clarify what you have observed. It does not work or The issue is still happening or something like that has no info to solve your issue.

                                         (In addition to the info when)

                                          You checked the display in some of your view controller?

                                          You used debugger?

                                          You observed the output in the debug console from your `print` statements?

                                       

                                          You need to show both expected result and actual result.

                                        • Re: Dictionary Scope
                                          Joel14 Level 1 Level 1 (0 points)

                                          The codes for the new and old view controller in the same file.

                                           

                                          The way that I get to a new view is with a show segue.

                                           

                                          The reason that I tried to use a dictionary is that I was having a problem that I would save the information into the database from one view and it would be in one block in the json tree than I would go to another view and it would save the information to another block in json tree.  I was trying to get all the information to be saved into one block. I am using the real-time database on Firebase. The code I have is what I showed to you.

                                            • Re: Dictionary Scope
                                              OOPer Level 7 Level 7 (4,615 points)

                                              If the two view controllers are the same, why do you move to new view controller?

                                               

                                              The code I have is what I showed to you.

                                               

                                              Your code never works without `class` header and some imports. All such things may affect. Please do not hide any lines of your code if you want better responses sooner.

                                                • Re: Dictionary Scope
                                                  Claude31 Level 8 Level 8 (5,305 points)

                                                  We must tell Joel to learn to better use the forum by providing the right and complete information at first time and not hiding half of the page (in many cases the error is in the hidden part!)

                                                  • Re: Dictionary Scope
                                                    Joel14 Level 1 Level 1 (0 points)

                                                    The reason I move to a new view controller is because of the first view controller doubles as phone auth for firebase. How would I make it so that I can get the information into one block in the json tree?

                                                      • Re: Dictionary Scope
                                                        OOPer Level 7 Level 7 (4,615 points)

                                                        because of the first view controller doubles as phone auth for firebase

                                                         

                                                        Please be more specific. What happens when you use only one view controller, and explain why it harms.

                                                         

                                                        I can get the information into one block in the json tree?

                                                         

                                                        It is written in the first answer of mine. You may need to ask how to keep the content of the json tree between view controllers.

                                                         

                                                        But the terminologie is not a big thing.

                                                        If you can describe what you have done and what you have seen, readers can guess what is happening even if some terms are ambiguous.

                                                         

                                                        Do you never push any of the buttons in the screen nor manipulate any other UI controls?

                                                        If you do, please write it or them in order.

                                                        Do you never check the displayed contents in the text field or any other thing of the screen?

                                                        If you do, please write them in addition to when.

                                                         

                                                        According to Claude31, you have some experience that the actual error is in the hidden part, not in the code you first have shown.

                                                        It is hard to say what would be the right and complete information when you do not know what is the cause of your issue,

                                                        But you could have shown whole code, I believe it is not too big to paste in an article.

                                                         

                                                        Where do you write the declaration  `var personalInfo = [String : String?]()`?

                                                        Is it inside the class definition? Or is a global variable which is placed outside of any classes?

                                                         

                                                        You should better count the question marks in my comments, and check if you have answered all of them.

                                                          • Re: Dictionary Scope
                                                            Joel14 Level 1 Level 1 (0 points)

                                                            Here is the code from the whole view controller:

                                                             

                                                            import UIKit
                                                            import FirebaseUI
                                                            import FirebaseAuth
                                                            import FirebaseDatabase
                                                            
                                                            class signInViewController: UIViewController{
                                                                //MARK: Properties
                                                                @IBOutlet weak var phoneNumber: UITextField!
                                                                @IBOutlet weak var firstName: UITextField!
                                                                @IBOutlet weak var lastName: UITextField!
                                                                @IBOutlet weak var email: UITextField!
                                                                @IBOutlet weak var addressLine1: UITextField!
                                                                @IBOutlet weak var addressLine2: UITextField!
                                                                @IBOutlet weak var verificationCode: UITextField!
                                                                @IBOutlet weak var phoneNumberContinueButton: CustomButton!
                                                                var personalInfo = [String : String?]()
                                                                let verificationID = UserDefaults.standard.string(forKey: "authVerificationID")
                                                                
                                                              
                                                                
                                                                //MARK: Sign In With Email
                                                                
                                                                func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
                                                                {
                                                                    if (textField == phoneNumber) {
                                                                        let newString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
                                                                        let components = newString.components(separatedBy: NSCharacterSet.decimalDigits.inverted)
                                                                        
                                                                        let decimalString = components.joined(separator: "") as NSString
                                                                        let length = decimalString.length
                                                                        let hasLeadingOne = length > 0 && decimalString.character(at: 0) == (1 as unichar)
                                                                        
                                                                        if length == 0 || (length > 10 && !hasLeadingOne) || length > 11 {
                                                                            let newLength = (textField.text! as NSString).length + (string as NSString).length - range.length as Int
                                                                            
                                                                            return (newLength > 10) ? false : true
                                                                        }
                                                                        var index = 0 as Int
                                                                        let formattedString = NSMutableString()
                                                                        
                                                                        if hasLeadingOne {
                                                                            formattedString.append("1 ")
                                                                            index += 1
                                                                        }
                                                                        if (length - index) > 3 {
                                                                            let areaCode = decimalString.substring(with: NSMakeRange(index, 3))
                                                                            formattedString.appendFormat("(%@)", areaCode)
                                                                            index += 3
                                                                        }
                                                                        if length - index > 3 {
                                                                            let prefix = decimalString.substring(with: NSMakeRange(index, 3))
                                                                            formattedString.appendFormat("%@-", prefix)
                                                                            index += 3
                                                                        }
                                                                        
                                                                        let remainder = decimalString.substring(from: index)
                                                                        formattedString.append(remainder)
                                                                        textField.text = formattedString as String
                                                                        return false
                                                                    }
                                                                    else {
                                                                        return true
                                                                    }
                                                                }
                                                                
                                                                override func viewDidLoad() {
                                                                    super.viewDidLoad()
                                                                }
                                                                
                                                                @IBAction func passwordAuth(_ sender: Any) {
                                                                    let authUI = FUIAuth.defaultAuthUI()
                                                                    
                                                                    guard authUI != nil else{
                                                                        //Log the error
                                                                        return
                                                                    }
                                                                    
                                                                    authUI?.delegate = self
                                                                    
                                                                    let authViewController = authUI!.authViewController()
                                                                    
                                                                    present(authViewController, animated: true, completion: nil)
                                                                }
                                                                
                                                                //MARK: Phone Auth
                                                                @IBAction func sendPhoneAuthCode(_ sender: Any){
                                                                    if let phoneNum = phoneNumber.text {    // No more need to unwrap phoneNum later
                                                                        
                                                                        
                                                                        PhoneAuthProvider.provider().verifyPhoneNumber(phoneNum, uiDelegate: nil) { (verificationID, error) in
                                                                            
                                                                            // Sign in using the verificationID and the code sent to the user
                                                                            // …
                                                                            //  if phoneNum == nil {
                                                                            if error != nil {
                                                                                print("verifyPhoneNumber went NOT OK")
                                                                                // REPLACE BY print       self.present(alert, animated: true, completion: nil)
                                                                                
                                                                                
                                                                                DispatchQueue.global(qos: .userInitiated).async {
                                                                                    let titre = " Missing Information "
                                                                                    let detailMsg = "Please enter your phone number"
                                                                                    let controller = UIAlertController(title: titre, message: detailMsg, preferredStyle: .alert)
                                                                                    
                                                                                    let okAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.cancel)
                                                                                    controller.addAction(okAction)
                                                                                    DispatchQueue.main.async {
                                                                                        self.present(controller, animated: true, completion: nil)
                                                                                    }
                                                                                    
                                                                                }
                                                                                
                                                                                
                                                                                return
                                                                            } else {
                                                                                print("verifyPhoneNumber went OK")
                                                                                // You have to test credential here, where do you do this ?
                                                                            }
                                                                        }
                                                                    } else {
                                                                        print("phoneNumber.text is nil")
                                                                        return
                                                                    }
                                                                    personalInfo["Phone Number"] = phoneNumber.text
                                                                    print(personalInfo)
                                                                    print("Setting userDefaults", verificationID)
                                                                    UserDefaults.standard.set(verificationID, forKey: "authVerificationID")
                                                                }
                                                            
                                                                
                                                                @IBAction func enterVerificationCode(_ sender: Any){
                                                                    let credential = PhoneAuthProvider.provider().credential(
                                                                        withVerificationID: verificationID!,
                                                                        verificationCode: verificationCode.text!)
                                                                    
                                                                    Auth.auth().signInAndRetrieveData(with: credential) { (authResult, error) in
                                                                        if let error = error {
                                                                            // ...
                                                                            return
                                                                        }
                                                                        // User is signed in
                                                                        // ...
                                                                    }
                                                                }
                                                                
                                                                @IBAction func savePersonalInfo(_ sender: Any) {
                                                                    personalInfo["First Name"] = firstName.text
                                                                    personalInfo["Last Name"] = lastName.text
                                                                    personalInfo["Email"] = email.text
                                                                    personalInfo["Address Line 1"] = addressLine1.text
                                                                    personalInfo["Address Line 2"] = addressLine2.text
                                                                    print(personalInfo)
                                                                    let ref = Database.database().reference()
                                                                    ref.childByAutoId().setValue(personalInfo)
                                                                }
                                                            }
                                                            
                                                            extension signInViewController: FUIAuthDelegate{
                                                                func authUI(_ authUI: FUIAuth, didSignInWith authDataResult: AuthDataResult?, error: Error?) {
                                                                    //Check if there was an error
                                                                    if error != nil{
                                                                        //Log the error
                                                                        return
                                                                    }
                                                                    
                                                                    authDataResult?.user.uid//Use for associating information in Firebase Database
                                                                    
                                                                    performSegue(withIdentifier: "passwordAuthLoggedIn", sender: self)
                                                                }
                                                            }
                                                            
                                                              • Re: Dictionary Scope
                                                                OOPer Level 7 Level 7 (4,615 points)

                                                                Getting better, but not showing enough info to solve your issue.

                                                                Have you ever read other thread in the forums? I mean other than yours. Some questions have only one answer, which is the right answer.

                                                                That happens because the question contained enough info to solve the issue.

                                                                 

                                                                You are still ignoring many of my question marks. But once you have shown your full (I hope you are not hiding any more) code, the most important thing might be these:

                                                                 

                                                                Do you never push any of the buttons in the screen nor manipulate any other UI controls?

                                                                If you do, please write it or them in order.

                                                                Do you never check the displayed contents in the text field or any other thing of the screen?

                                                                If you do, please write them in addition to when.

                                                                 

                                                                To check something is going wong or not, what do you do? Which button do you press? And what do you get? And what do you expect there?

                                                                 

                                                                And the segue "passwordAuthLoggedIn", where does it go?