Search code examples
swiftnsdocumentdirectoryuserdefaults

saving document path in custom struct array in Userdefaults error swift


I have a custom struct Information. for the property image(string) I want to insert the path of the document directory where the image is saved. When i try to use the UserDefaults to save the struct array, it is saved successfully and also retrieved. But when i use the path to retrieve the image from the document directory it shows the following error: fatal error: unexpectedly found nil while unwrapping an Optional value. And when I use the if-else block to catch the exception, No image is displayed on the tableview. Below is my code:

struct Information{

var image : String
var content : String?
var url : String?

init(image:String,content:String?,url:String?){
    self.image = image
    self.content = content
    self.url = url

}
init(dictionary : [String:String]) {
    self.image = dictionary["image"]!
    self.content = dictionary["content"]!
    self.url = dictionary["url"]!

}

var dictionaryRepresentation : [String:String] {
    return ["image" : image, "content" : content!, "url" : url!]
}

} And my View Controller:

 override func viewDidAppear(_ animated: Bool) {
    savePath()
    loadDefaults()
    tableView.reloadData()
}

func saveDefaults()
{
    let cfcpArray = information.map{ $0.dictionaryRepresentation }
    UserDefaults.standard.set(cfcpArray, forKey: "cfcpArray")
}

func loadDefaults()
{
    information = (UserDefaults.standard.object(forKey: "cfcpArray") as! [[String:String]]).map{ Information(dictionary:$0) }
    for info in information{
        print(info)
    }
}

func savePath(){
    let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
    // Get the Document directory path
    let documentDirectorPath:String = paths[0]
    // Create a new path for the new images folder
    imagesDirectoryPath = documentDirectorPath + "/ImagePicker"
    var objcBool:ObjCBool = true
    let isExist = FileManager.default.fileExists(atPath: imagesDirectoryPath, isDirectory: &objcBool)
    // If the folder with the given path doesn't exist already, create it
    if isExist == false{
        do{
            try FileManager.default.createDirectory(atPath: imagesDirectoryPath, withIntermediateDirectories: true, attributes: nil)
        }catch{
            print("Something went wrong while creating a new folder")
        }
    }
    tableView.reloadData()
}
 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    // The info dictionary may contain multiple representations of the image. You want to use the edited.
            guard  let selectedImage = info[UIImagePickerControllerEditedImage] as? UIImage
                       else {
                    fatalError("Expected a dictionary containing an image, but was provided the following: \(info)")
                    }
      image = selectedImage
      dismiss(animated: true, completion: nil)
        saveImage()
}

func saveImage(){
    // Save image to Document directory
    var imagePath = Date().description
    imagePath = imagePath.replacingOccurrences(of: " ", with: "")
    imagePath = imagesDirectoryPath + "/\(imagePath).png"
    path = imagePath
 //   print(path!)
    let data = UIImagePNGRepresentation(image)
    let success = FileManager.default.createFile(atPath:path!, contents: data, attributes: nil)
    information.append(Information(image:path!, content:" ", url: " "))
    saveDefaults()
    tableView.reloadData()

}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cellIdentifier = "TableViewCell"
    /*Because you created a custom cell class that you want to use, downcast the type of the cell to your custom cell subclass, MealTableViewCell.*/
    guard  let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier , for: indexPath) as? TableViewCell
        else{
            fatalError("The dequeued cell is not an instance of MealTableViewCell.")
    }

    let info = information[indexPath.row]

   if let data = FileManager.default.contents(atPath: info.image)
   {
    let decodeimage = UIImage(data: data)
    cell.photos.image = decodeimage

    }
   else{
    print("Not displaying image")
    }
  //  cell.photos.image = UIImage(data: data!)

   return cell
}

Any suggestions is really appreciated. Thank you.


Solution

  • Don't do that. The URL to the documents folder changes periodically for security reasons.

    Save a relative path – or just the file name – and after reading the data get the current documents URL and append the path.

    Note: NSSearchPathForDirectoriesInDomains is outdated. Use url(for:in:appropriateFor:create:) and the URL related API of FileManager

    PS: Why are content and url optionals although the dictionary initializer passes always non-optional values?