Getting image from Core Data - ios

I'm working on a Cocktailapp and want to save my image data to Core Data.
I watched some tutorials and did the same but it's still not working an I don't know why.
I have an Array with all titles for the images:
let imagesAperitif: [String] = ["americano.jpg", "daiquiri.jpg",
"frozen daiquiri.jpg",
"banana frozen daiquiri.jpg", "bronx.jpg", "kir.jpg",
"hugo.jpg", "Manhattann.jpg", "manhattan dry.jpg", "manhattan medium.jpg", "margarita.jpg",
"martini dry.jpg",...
Thats where I call my method for saving the images to Core Data:
setCocktails(nameInsert, zutaten: zutatenInsert, zubereitung: zubereitungInsert, dekoration: dekorationInsert, stil: stilInsert, bild: UIImage(named: imagesAperitif[index])!)
That's a part from the code of saveCocktails method:
let imageData = NSData(data: UIImageJPEGRepresentation(bild, 1.0)!)
eintrag.setValue(imageData, forKey: "bild")
do {
try managedContext.save()
That's part of the fetching method:
let fetchRequest = NSFetchRequest(entityName: "Cocktail")
do {
let results =
try managedContext.executeFetchRequest(fetchRequest)
cocktails = results as! [NSManagedObject]
And here I want to get my image back from NSData:
imagesAperitif.append(UIImage(data: eintrag.valueForKey("bild") as! NSData)!)
But the App crashes with this line and I get a "fatal error: unexpectedly found nil while unwrapping an Optional value" error.
Is anybody able to explain this to me because I don't know what to change. Everything I tried went also wrong.

You may want to check nil for eintrag.valueForKey("bild") before case it to NSData
As
func valueForKey(_ key: String) -> AnyObject?
And it's always save to check nil before you append UIImage(data: eintrag.valueForKey("bild") as! NSData)

(I like using guard so here goes :) )
So what I would do here since it looks like the inserting of the NSData object fails:
// We are simultaniously unwrapping objects and checking if objects are nil.
guard let imgAsNSData: NSData = UIImageJPEGRepresentation(bild, 1.0),
let entity: Cocktail = eintrag as? Cocktail else {
// Stop executing the method here, there is no point in going further. Handle any errors here! Either imgAsNSData is nil, or could not cast to your class. A guard statement handles it's errors here.
return
}
// At this point we know we have an NSData object. Assign it to the entity.
entity.bild = imgAsNSData
do {
// Save our entity.
try managedContext.save()
} catch {
// Handle error
}
Fetching:
let fetchRequest = NSFetchRequest(entityName: "Cocktail")
do {
let results: [NSManagedObject] = try managedContext.executeFetchRequest(fetchRequest)
guard let cocktails = results as? [Cocktail] else {
// Could not cast to an array of Cocktail objects.
return
}
// Do stuff with the cocktails object.
Add to your array:
// 1: Check if entity not is nil
// 2: Check if entity's bild property not is nil.
// 3: Check if we can create an image using the NSData
guard let cocktail: Cocktail = eintrag as? Cocktail,
let imgAsNSData: NSData = cocktail.bild,
let image: UIImage = UIImage(data: imgAsNSData) else {
// Required values are nil. Cannot proceed.
return
}
imagesAperitif.append(image)
Untested code so be careful :)

Related

how to load an image from the url getting from server

I am having and imageView and I am getting the userData from the server when loggedIn in the userData I have a parameter for "profilePic":"penguins.jpg". now I am adding the domain like "http://.....(self.userData.value(forKey: "profilePic")!)" and saving this value into a string variable to store and when I am accessing the value and trying to convert the url into data and adding to imageView.image it is throwing : unexpectedly found nil while unwrapping an optional value..
All my other userData like name,address,phoneNumber are showing fine except for Image.
My Code:
here are the some of the many ways I tried:
way:1
let ad : AppDelegate = UIApplication.shared.delegate as! AppDelegate
let imageUrlString = ad.userImagePath
let imageUrl:URL = URL(string: imageUrlString)! // it is throwing error here(: unexpectedly found nil while unwrapping an optional value)
DispatchQueue.global(qos: .userInitiated).async {
let imageData:NSData = NSData(contentsOf: imageUrl)!
DispatchQueue.main.async {
let image = UIImage(data: imageData as Data)
self.profileImage.image = image
}
}
way:2
if let url = NSURL(string: ad.userImagePath) {
if let data = NSData(contentsOf: url as URL){
if let imageUrl = UIImage(data: data as Data) {
profileImage.image = imageUrl
}
}
}
I have tried different ways to solve this, can't figure out what is my mistake.. finally I am here.. Please someone help he...

How can I read Plist in Swift?

I want read a NSArray form plist , and the code :
func loadPlistArray() -> [Any] {
var path: String? = Bundle.main.path(forResource:"MyCenter", ofType: "plist")
if let arry = NSArray(contentsOfFile: path!) {
return arry as! NSArray
}else{
return nil;
}
}
but always got errors below:
And After I got the data from plist, I fount that I can't see the Details of Dictionary :
And here is my plist:
should I add a generic in the array by var plistArray : [[String:Any]]?
The errors messages you are getting tell you what is wrong with your method, this is how I would write the function:
func loadPlistArray() -> [Any] { // 1
guard
let url = Bundle.main.url(forResource: "MyCenter", withExtension: "plist"), // 2
let list = NSArray(contentsOf: url) as? [Any] // 3
else { return [] } // 4
return list
}
And some commentary:
You are declaring the method to return an Array of Any items, but your method tries to return an NSArray.
It is recommended to use the URL based methods for accessing files, rather then the string based paths.
You have to use the Array methods to read the plist, but you can cast it to [Any]. However, if you know the type of items you have in the plist, I recommend that you return a properly type array from this method e.g. [String], [Int] etc.
You don't need to return an optional if the file can't be read. Depending on how you want to handle the error you could either return an empty array (as I've shown here) or convert your function into a throwing one so that if you can't read the file an error is thrown and can be handled by the calling code.
Your method signature clearly states that it returns an [Any] (i.e., Swift native Array containing elements of any type whatsoever), while you try to cast the return value into NSArray (even though it already is by virtue of intialization: NSArray(contentsOfFile:)).
Change it to:
return arry as? [Any]
// (will return nil if the cast fails - not a problem if you
// also apply the fix mentioned below...)
The other path tries to return nil; for that to be acceptable, your signature needs to be defined as returning an optional:
func loadPlistArray() -> [Any] // WRONG
func loadPlistArray() -> [Any]? // RIGHT
EDIT: If your app is structured in such a way that you can't afford to return nil from your method, you can instead return an empty array on failure:
else {
return [] // Empty array
}
(use [:] for empty dictionary)
Also, try to avoid using ! whenever possible, and switch to ? instead, unless you are 100% sure that whatever it is you are forcing will not fail and cause a runtime error (crash).
return (arry ) as! [Any]
You cannot return NSArray on type Any
I am using something like this in my project.
if let fileUrl = Bundle.main.url(forResource: "new", withExtension: "plist"),
let myDict = NSDictionary(contentsOf: fileUrl) as? [String:Any] {
print(myDict)
}
I have another plist for color which have array as a root.
if let fileUrl = Bundle.main.url(forResource: "color", withExtension: "plist"),
let data = try? Data(contentsOf: fileUrl) {
if let result = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [Any] {
print(result)
}
}

Firebase reference is 'variable not available' when downloading picture in Swift

Title says everything. I'm just unable to download an image from Firebase Storage dir. Here is the snippet of the code which calls the function for setting data and it also calls the function which tries to download the picture:
for element in Dict {
if let itemDict = element.value as? [String:AnyObject]{
let name = itemDict["name"] as! String
let price = itemDict["price"] as! Float
let imageObject = itemDict["image"] as! NSDictionary
let hash = imageObject["hash"] as! String
let storageDir = imageObject["storageDir"] as! String
let image:UIImage = self.downloadImageProductFromFirebase(append: hash)!
let product = Product(name: name, image: image, imageName:hash, price: price, storageDir : storageDir)
self.productList.append(product)
}
}
print(Dict)
self.myTable.reloadData()
And here is the code which tries to download the image:
func downloadImageProductFromFirebase(append:String) -> UIImage?{
let gsReference = Storage.storage().reference(forURL: "gs://fridgeapp-3e2c6.appspot.com/productImages/productImages/" + append)
var image : UIImage?
gsReference.downloadURL(completion: { (url, error) in
if error != nil {
print(error.debugDescription)
return
}
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print(error.debugDescription)
return
}
guard let imageData = UIImage(data: data!) else { return }
DispatchQueue.main.async {
image = imageData
}
}).resume()
})
return image
}
But, for some reason, it crashes just when calling this last function, saying that "fatal error: unexpectedly found nil while unwrapping an Optional value". I tried to use the debugger, and I found out that Firebase reference to Storage variable says "variable not available".
Could someone of you guys help me with this? I think I read the Firebase doc about a hundred times, and still can't get the point.
Thank you!
Downloading an image from a remote server is an asynchronous task, that means that the result is not immediately available. This is the reason that gsReference.downloadURL accepts a completion callback as an argument, and has no return value.
Since your function (downloadImageProductFromFirebase) is simply a wrapper to gsReference.downloadURL, it should also accept a completion callback as an argument, and should not have a return value (i.e. remove the -> UIImage?).
When you call self.downloadImageProductFromFirebase pass in a closure that receives the image, finds the index of the corresponding product in productList, and sets itself as the cell's image (assuming you're showing the image in the cell).
See this answer for how to asynchronously set cell images.

How to parse json data that provides another url with more data

I'm a little confused how would I parse a json API that gives me 20 objects but then gives me a key of "next" having a url that gives me another 20 objects. I'm using this Pokemon API. It gives me 4 keys: count, previous, results and next. I'm trying to display them all in a collection view but not all at the same time. I would like to load more when the collection view is scrolling down.
I'm just trying to get the name at the moment. This is how my code looks like.
I get it to load the first 20 Pokemon in the collection view. However I don't know how to load the next 20 Pokemon or the 20 after. This is how the json file looks like if the link didn't work.
I would appreciate any help given. :)
You can try using a recursive function reusing the loadPokemonsData function something like this:
func loadPokemonsData(url: String, quantity: Int?) {
let request = URLRequest(url: URL(string: url)!)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if error != nil {
print(error!)
}
do {
let jsonResults = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! NSDictionary
let pokemonArray = jsonResults.value(forKey: "results") as! [[String: Any]]
var isPokemonsEqualsToQuantity: Bool = false
for pokemonData in pokemonArray {
if let quantity = quantity {
guard self.pokemons.count < quantity else {
isPokemonsEqualsToQuantity = true
break
}
}
guard let name = pokemonData["name"] as? String else {
return
}
self.pokemon = Pokemon(name: name)
self.pokemons.append(self.pokemon)
}
guard let nextURL = jsonResults.value(forKey: "next") as? String, !isPokemonsEqualsToQuantity else {
for pokemon in self.pokemons {
print(pokemon.name)
}
print(self.pokemons.count)
return
}
self.loadPokemonsData(url: nextURL, quantity: quantity)
} catch let err as NSError {
print(err.localizedDescription)
}
}
task.resume()
}
Attach a screen of algorithm function running... it prints 791 pokemons.
Hope it helps you!
EDITED
Next time you ask put your code please... it will be easier help you!.
I've updated the code to set the quantity you want (nil if you want to get all pokemons), Therefore it will only get the pokemons in the order API returns it, if you want a specific pokemons from ALL pokemons you may do a sort after obtaining all pokemons.

NSUserDefaults fail to retrieve image Array

I try to store array of UIImage, it used to work but somehow all of sudden it refused to work. I wrote this test example to check if it's stored properly and I guess the problem is somewhere here
let images = NSKeyedArchiver.archivedDataWithRootObject(self.globalImageArray)
NSUserDefaults.standardUserDefaults().setObject(images, forKey: "morningImages")
NSUserDefaults.standardUserDefaults().synchronize()
println("Images saved")
let images2 = NSUserDefaults.standardUserDefaults().objectForKey("morningImages") as? NSData
let imagesArray = NSKeyedUnarchiver.unarchiveObjectWithData(images2!) as! NSArray
var testArray = imagesArray as! [UIImage]
println("Check if images are loaded " + "\(testArray.count)")
The testArray.count is equal to zero which means it either fails to save them properly or fails to retrieve them.
I tried printing images2 and it does contain data, but printing the next value which is imagesArray leads to the result equals to "()" Guess the problem is with this line:
let imagesArray = NSKeyedUnarchiver.unarchiveObjectWithData(images2!) as! NSArray
Thanks for help in advance.
As others have pointed out, this really isn't the best use case for NSUserDefaults. This should be very simple to do by writing to and reading from files on disk.
Here's how I'd do it, and it's less code than saving to NSUserDefaults!
NSKeyedArchiver.archiveRootObject(self.globalImageArray, toFile: "/path/to/archive")
println("Images saved")
if let testArray = NSKeyedUnarchiver.unarchiveObjectWithFile("/path/to/archive") as? [UIImage] {
println("Check if images are loaded " + "\(testArray.count)")
} else {
println("Failed to load images.")
}
EDIT As it turns out this doesn't work on cached images (e.g. any UIImage loaded with either of the UIImage(named:) variants because the cached images don't seem to get serialized to disk. So, while the above works for not cached UIImages, the following works regardless of the image's cached status.
// Save the raw data of each image
if NSKeyedArchiver.archiveRootObject(imageArray.map { UIImagePNGRepresentation($0) }, toFile: archivePath) {
println("\(imageArray.count) Images saved")
} else {
println("failed to save images")
}
// Load the raw data
if let dataArray = NSKeyedUnarchiver.unarchiveObjectWithFile(archivePath) as? [NSData] {
// Transform the data items to UIImage items
let testArray = dataArray.map { UIImage(data: $0)! }
println("\(testArray.count) images loaded.")
} else {
println("Failed to load images.")
}

Resources