2

I have a writing app which the user can create a post that being added to a tableview (and to Firebase) when I want to save a new post the app crashes. it is never accesses the if, its always goes to the else, so I think the if is not correct. how do I know what is my key name for the "forKey" in the userdefault in the if?

func readFromNSUserDefault()-> Memory?
{
    let d : UserDefaults = UserDefaults.standard

    if UserDefaults.standard.array(forKey: "") != nil
 {
        print("exists")
    let strTitle = d.object(forKey: "title") as? String
    let strBody = d.object(forKey: "body") as? String
    let strImageRef = d.object(forKey: "imageRef") as? String
    let uid = d.object(forKey: "uid") as? String
    let imageData = d.object(forKey: "imageData") as? Data
    let key = d.object(forKey: "key") as? String
    let date = d.object(forKey: "date") as? NSNumber

    let m = Memory(title: strTitle!, body: strBody!, key: key!, uid: uid!, imageRef: strImageRef!, date: date!) // A variable from type memory
    m.image = UIImage(data: imageData!)
    m.key = key!
    return m
    }
    print ("doesn't exist")
    let m : Memory?
    m = nil
    return m
}

save functions:

@IBAction func saveMemorey(_ sender: UIButton) {

    let d = UserDefaults.standard

    if( mode == "newMemory")
    {
        self.addNewMemory()
        d.set(true, forKey: "userAdded")
    }
    else if mode == "edit"
    {
        self.editNewMemory()
        d.set(true, forKey: "userEdited")
    }
    d.synchronize()
    self.navigationController?.popViewController(animated: true) // Returns to the memories page after clicking 'save'
}

func addNewMemory()
{
    let refposts = ref.child("MEmories").childByAutoId()
   dateToSet = Double(self.future.millisecondsSince1970)
    imgref = "\(txtTitle.text!).png"
    self.m = Memory(title: txtTitle.text!, body: tBody.text!, key: "", uid: (Auth.auth().currentUser!.uid), imageRef: imgref!, date: self.dateToSet as NSNumber)
    refposts.setValue(m.toAnyObject(), withCompletionBlock: {(error,data) in
        self.m.key = data.key!
        self.m.image = self.ivImage.image
        self.saveImage()
        self.saveToNSUserDefaualts(memory: self.m)
    })
}


func saveToNSUserDefaualts(memory:Memory)
 {
    let d : UserDefaults = UserDefaults.standard
    d.set(memory.title, forKey: "title")
    d.set(memory.body, forKey: "body")
    d.set(memory.key, forKey: "key")
    d.set(memory.imageRef, forKey: "imageRef")
    d.set(memory.uid, forKey: "uid")
    d.set(memory.key, forKey: "key")
    d.set(memory.date, forKey: "date")   
let imageData = NSData(data:ivImage.image!.pngData()!)
  d.set(imageData, forKey: "imageData")
    d.synchronize()
}

load function:

func loadMemories()

{
   let UID = Auth.auth().currentUser!.uid
   self.ref.child("MEmories").queryOrdered(byChild: "uid").queryEqual(toValue: UID).observeSingleEvent(of: .value, with: {
        snapShot in
        if let dict = snapShot.value as? NSDictionary
        {
            for d in (dict as? Dictionary<String,AnyObject>)!
            {
                let title = d.value["title"] as?String
                let body = d.value["body"] as? String
                let uid = d.value["uid"] as? String
                let imageRef = d.value["imageRef"] as? String
                let date = d.value["date"] as? NSNumber
                let m = Memory(title: title!, body: body!, uid: uid!,imageRef:imageRef!, date: date!)
                m.key = d.key
                let tempImageRef = self.sref.child(m.imageRef)
                tempImageRef.getData(maxSize: 500*1024*1024, completion: {(data,error) in
                    if error == nil
                    {
                        if let imageData = data
                        {
                            m.image = UIImage(data: imageData)
                            self.memories.append(m)
                            self.tbl.reloadData()
                        }
                    }
                }) 
            }                
        }//end of if
    })
}

memory class:

import Foundation
import Firebase

class Memory

{
    // Class Attributes

    var title  : String

    var uid  : String  // user id

    var body : String

    var key : String

    var imageRef : String!

    var image : UIImage?

    var date : NSNumber


    init( title:String , body:String , key:String = "", uid:String , imageRef:String , date:NSNumber ) // Build function
    {

        self.title = title

        self.key = key

        self.body = body

        self.uid = uid

        self.imageRef = imageRef

        self.key = ""

        self.date = date


    }

    func toAnyObject()-> [String:AnyObject]
            {
                return ["title" : self.title as AnyObject,"body":self.body as AnyObject,"uid":self.uid as AnyObject,"imageRef":self.imageRef as AnyObject,"date":self.date as AnyObject
 ]

    }

}
SwiftCode
  • 39
  • 6
  • You would have to choose the key yourself the first time you write to it, then use it again to read from user defaults. – Chris Jun 09 '19 at 22:04
  • Also, you mention it goes to the `else` block but you don’t have an `else block. The code below the `if` will run regardless of the result of the `if` condition. – Chris Jun 09 '19 at 22:06
  • Also, user defaults is not nested - an array might be stored under one key, but each of those other keys you have is not associated with the array you are checking for - they are all separate. Each key is a unique item. – Chris Jun 09 '19 at 22:23
  • @Chris By `else` I meant not the `if`. it never accesses the `if` and I think something in the `if` is incorrect, do you know what? – SwiftCode Jun 10 '19 at 08:18
  • [This answer](https://stackoverflow.com/a/37357869) shows how to store and retrieve an array from user defaults. Another problem is that you cannot use an empty string (`""`) as a key. Chose something unique and use it each time. – Chris Jun 10 '19 at 08:31
  • @Chris If it accesses the `if` and goes to the first `return`, will it go anyway to the second `return`? I thought that the first time a `return` is being activated the function stop there, isn't it? – SwiftCode Jun 10 '19 at 08:32
  • @Chris thank you. I know I need to write a key there, but I don't know which key... how do I know? sorry for misunderstanding – SwiftCode Jun 10 '19 at 08:34
  • No problem. You’re right that the first return should stop the function at that point. – Chris Jun 10 '19 at 08:36
  • The key is entirely up to you. You can chose any string at all, as long as it’s the same when you save and load. User defaults is basically a fancy dictionary. – Chris Jun 10 '19 at 08:38
  • @Chris I just added the `save` functions... do you know what is the `key` to the `if` in the `read` function? – SwiftCode Jun 10 '19 at 10:33
  • Only you would know the key - you use it in the save function and the same one in the load function. For example, you use a key "title" which you decided on. Choose one for the array. Remember that the array is a totally different object from the other keys - there is no relation between different user default keys, so there is no key that will fetch all of the other keys, for example. – Chris Jun 10 '19 at 10:38
  • @Chris Just added the load function too now. I just don't understand what is the key used for both of them... sorry for bugging – SwiftCode Jun 10 '19 at 10:44
  • I think the problem is that you are approaching this the wrong way I will think of another way to do this. You are wanting to check to see if a user default entry exists and then load it? – Chris Jun 10 '19 at 10:44
  • @Chris Yeah, I think so – SwiftCode Jun 10 '19 at 10:45
  • Can I just check - you are saving data to Firebase as well, so why exactly are you using User Defaults? It is not a relational database so for local storage, NSCoding, Codable protocol or (more advanced) Core Data May be the best bet – Chris Jun 10 '19 at 12:20
  • @Chris This is what I've been taught... I really need to solve that `if` problem by tomorrow so I don't really have time to change it all. do u have an idea? – SwiftCode Jun 10 '19 at 12:24
  • Can you share where you declare the class `Memory`? – Chris Jun 10 '19 at 12:31
  • @Chris Just added `Memory` class... thank you for trying to help – SwiftCode Jun 10 '19 at 13:09
  • Thanks for sharing that code. So User Defaults cannot store an instance of a class. Every time you set a value for key “title”, the value would get overwritten - essentially only ever saving the most recent one. You have a method on the class that converts to a dictionary, you could save them as an array of these dictionaries but that would require a lot of type casting then you open it again. – Chris Jun 10 '19 at 13:20
  • @Chris So... isn't there a more simple way? :) – SwiftCode Jun 10 '19 at 14:11
  • 1
    @Chris I will chime in here as this question has been asked 3-4 times previously by the same poster. That user account was deleted, as well as the prior one, and this is the same question. For the OP, generally speaking It's not a good idea to store that kind of data in user defaults - that's not what user defaults are for. [User Defaults](https://developer.apple.com/documentation/foundation/userdefaults) should store data so the app behaves consistently between restarts - storing large data such as pictures should be avoided. – Jay Jun 10 '19 at 18:46
  • Thanks @Jay - agreed. User Defaults is not the way to do this. – Chris Jun 10 '19 at 18:50
  • @SwiftCode Sorry I cant be of more help. Unfortunately this is not the optimal way to achieve what you need. – Chris Jun 10 '19 at 18:50
  • Possible duplicate of [Thread 1: Fatal error: Unexpectedly found nil](https://stackoverflow.com/questions/56516215/thread-1-fatal-error-unexpectedly-found-nil) – Jay Jun 10 '19 at 18:53

0 Answers0