Error when trying to save image in NSUserDefaults using Swift - swift

When i try to save an image in NSUserDefaults, the app crashed with this error.
Why? Is it possible to save an image with NSUserDefaults? If not, then how do I save the image?
Image...
Code...
var image1:UIImage = image1
var save1: NSUserDefaults = NSUserDefaults.standardUserDefaults()
save1.setObject(Info.Image1, forKey: "Image1")
save1.synchronize()
Log error...
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)

NSUserDefaults isn't just a big truck you can throw anything you want onto. It's a series of tubes which only specific types.
What you can save to NSUserDefaults:
NSData
NSString
NSNumber
NSDate
NSArray
NSDictionary
If you're trying to save anything else to NSUserDefaults, you typically need to archive it to an NSData object and store it (keeping in mind you'll have to unarchive it later when you need it back).
There are two ways to turn a UIImage object into data. There are functions for creating a PNG representation of the image or a JPEG representation of the image.
For the PNG:
let imageData = UIImagePNGRepresentation(yourImage)
For the JPEG:
let imageData = UIImageJPEGRepresentation(yourImage, 1.0)
where the second argument is a CGFloat representing the compression quality, with 0.0 being the lowest quality and 1.0 being the highest quality. Keep in mind that if you use JPEG, each time you compress and uncompress, if you're using anything but 1.0, you're going to degrade the quality over time. PNG is lossless so you won't degrade the image.
To get the image back out of the data object, there's an init method for UIImage to do this:
let yourImage = UIImage(data:imageData)
This method will work no matter how you converted the UIImage object to data.
In newer versions of Swift, the functions have been renamed, and reorganized, and are now invoked as:
For the PNG:
let imageData = yourImage.pngData()
For the JPEG:
let imageData = yourImage.jpegData(compressionQuality: 1.0)
Although Xcode will autocorrect the old versions for you

In order to save UIImage in NSUserDefaults, you need to convert UIImage into NSData using UIImageJPEGRepresentation(image, 1) then save it in NSUserDefaults. And on the other side, while retrieving it on another ViewController (within that same application) you need to get NSData and then convert it into UIImage.
Here i am writing small code snippet in swift to demonstrate this. I tried this in XCODE 6.4
/*Code to save UIImage in NSUserDefaults on viewWillDisappear() event*/
override func viewWillDisappear(animated: Bool)
{
super.viewWillDisappear(animated)
//Get some image in image variable of type UIImage.
var image = ....
let defaults = NSUserDefaults.standardUserDefaults()
var imgData = UIImageJPEGRepresentation(image, 1)
defaults.setObject(imgData, forKey: "image")
}
/*Code to get Image and show it on imageView at viewWillAppear() event*/
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
let defaults = NSUserDefaults.standardUserDefaults()
if let imgData = defaults.objectForKey("image") as? NSData
{
if let image = UIImage(data: imgData)
{
//set image in UIImageView imgSignature
self.imgSignature.image = image
//remove cache after fetching image data
defaults.removeObjectForKey("image")
}
}
}

Updated for Swift 3:
If you want to save image in UserDefault used below lines of code save and retrieve the image;
To Save image in UserDefault:
if let image = response.result.value {
UserDefaults.standard.register(defaults: ["key":UIImageJPEGRepresentation(image, 100)!])
UserDefaults.standard.set(UIImageJPEGRepresentation(image, 100), forKey: "key")
}
To Retrieve the image from UserDefault and set it to ImageView:
var mLogoImageView = UIImageView()
if let imageData = UserDefaults.standard.value(forKey: "key") as? Data{
let imageFromData = UIImage(data: imageData)
mLogoImageView.image = imageFromData!
}
Enjoy..!

In Swift 4 - 5
Set:
setImage(image: UIImage(named: "12")!)
func setImage(image : UIImage) {
UserDefaults.standard.set(image.jpegData(compressionQuality: 100), forKey: "key")
}
Get
func getImage() -> UIImage? {
if let imageData = UserDefaults.standard.value(forKey: "key") as? Data{
if let imageFromData = UIImage(data: imageData){
return imageFromData
}
}
return nil
}

Related

How to store an NSImage as Data in an app group on macOS

In iOS I can store UIImage to an app group as Data. I can convert the png image with pngData() to a Data object:
let imageData = scaledImage.pngData()!
I store that object in an app group, retrieve it and convert ik back to an UIImage:
let image = UIImage(data: imageData)
It's works great, but it doesn't work on macOS. MacOS doesn't have a UIImage, but a NSImage. How can I convert an NSImage tot Data and back?
Update: I use this code for macOS
let image = NSImage(named: "axl")!
let imageData = image.tiffRepresentation!
I store the imageData in a array.
In another part of the code I get the imageData from the array and convert it back:
let imageData = entry.images[0]
let image = NSImage(data: imageData)
Somehow the tiffRepresentation converting back with NSImage(data: ...) doesn't work.
Update: it does work!!
I use this code for macOS
let image = NSImage(named: "axl")!
let imageData = image.tiffRepresentation!
I store the imageData in a array.
In another part of the code I get the imageData from the array and convert it back:
let imageData = entry.images[0]
let image = NSImage(data: imageData)
It does work!!

Store image in Core Data in swift

i'm trying to save a local image in my app to the core data model. I have created the entity and created an attribute for the image. But when i run the app it crashes with this error, Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for attribute: property = "image"; desired type = NSData; given type = UIImage; value = <UIImage: My code to save image is,
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Saloon", in: context)
let newUser = NSManagedObject(entity: entity!, insertInto: context)
newUser.setValue(nameLbl.text, forKey: "name")
newUser.setValue(addressLbl.text, forKey: "address")
newUser.setValue(distanceLbl.text, forKey: "distance")
newUser.setValue(rating.text, forKey: "rating")
newUser.setValue(imageView.image, forKey: "image") as? UIImage
do {
try context.save()
print("saved")
} catch {
print("Failed saving")
}
This is how i'm fetching the image,
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Saloon")
//request.predicate = NSPredicate(format: "age = %#", "12")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
print(data.value(forKey: "name") as! String)
let image: Data = data.value(forKey: "image")! as! Data
let decodedimage = UIImage(data: image)
imageView2.image = decodedimage
name2Lbl.text = data.value(forKey: "name") as? String
address2Lbl.text = data.value(forKey: "address") as? String
distance2Lbl.text = data.value(forKey: "distance") as? String
rating2Lbl.text = data.value(forKey: "rating") as? String
}
} catch {
print("Failed")
}
You cannot save a UIImage directly in Core Data. Instead, you’ll have to convert the image to NSData (as in “desired type = NSData” in your error message). Here’s a UIImage extension for that:
extension UIImage {
func jpegData(withCompressionQuality quality: CGFloat) -> Data? {
return autoreleasepool(invoking: {() -> Data? in
return UIImageJPEGRepresentation(self, quality)
})
}
}
The quality is a value from 0.0 through 1.0 with the latter representing the maximum quality of the JPEG compressed image. The use of autoreleasepool helps avoid occasional memory leakage problems observed when using UIImageJPEGRepresentation.
You can then save the image using this code:
if let imageData = imageView.image .jpegData(withCompressionQuality: 1.0) {
newUser.setValue(imageData, forKey: "image")
}
BTW, you can also convert to PNG instead of JPEG which is lossless if this is what you need. Use UIImagePNGRepresentation instead of UIImageJPEGRepresentation.
Deferring from the previous answer, it is possible to store images in Core Data as UIImage. Although this is not considered to be good practice as Databases are not meant for storing files. We don't use core data to store the images directly instead we use the file path to where the image data is stored on your phone.
Anyways, the best method I found out while developing a side-project is demonstrated below
Set the entity codeGen to Manual/None
Select the attribute you want to be of Image type and choose transformable as it's type
Enter Custom class identifier as UIImage
Go to editor and select Create NSManagedObject Subclass
The Process will create 2 swift files depending on the number of your entities(I only had 1 entity). Now, select the <EntityName> + CoreDataProperties.swift file and import UIKit
If, on clicking Jump to Definition for the #NSManaged public var <attributeName: UIImage? you are able to land on the UIImage definition your work is done.
Perform actions on the entities just like you would and you will be able to fetch, save, edit the image attribute by down-casting <EntityName>?.value(forKey: "<attributeName>") as? UIImage.
I had to use the entity as NSManagedObject type and was for some reason not able to access the image directly from the created subclass.

How to create a NSImage from a NSData with Swift

Sorry to ask such a simple but this is my first program in Swift... I try to create a NSImage from a NSData that contains a JPEG image I loaded from disk (URLs are in an array name choseFiles[]).
The compiler issues an error on the second and I'm stuck:'NSData' is not implicitly convertible to 'Data'; did you mean to use 'as' to explicitly convert?
Thank you
let imageAsNSData = NSData(contentsOf: chosenFiles[0]) // UIKit/UIImage for iOS not MacOS !
let imageAsNSImage = NSImage(data: imageAsNSData)
if (imageAsNSImage) {
// image could be created from NSData
//
} else {
// image could NOT be created from NSData
//
}
----- EDIT -----
I tried
let imageAsNSImage = NSImage(data: imageAsNSData! as Data)
if (imageAsNSImage != nil) {
which seems to work (at least for the compiler). Am I correct?
You can use swift Data to get your image:
do {
let imageData = try Data(contentsOf: chosenFiles[0])
NSImage(data: imageData)
} catch {
print("Unable to load data: \(error)")
}
Now in swift3 NSData is replaced by Data. When you are downloading image from url store it as Data not NSData.
imageView.image = NSImage.init(data: data! as Data)

Saving UIImage Into Core Data Always Returns Nil when using ImagePickerView - Swift 3

I've been trying to save a single picture to an entity containing a single property titled "prof" and configured as a Binary Data type.
I go through the hoops to select a picture from UIImagePickerViewController, then I call up my method that handles saving the picture in Core Data in the desired NSData format.
My issue stems from loading the picture, in my loadImage method the entity for the image is not nil, meaning it does exist. However, I get nil when I try to parse the fetched NSData to a UIImage format to recreate the picture and then be able to use it.
Now i am using Swift 3 and Xcode 8, so far all the troubleshooting questions on here have the solution of casting the NSData to UImage like so:
let image : UIImage = UIImage(data: imageData)
however, xcode gives me a compiler error when I do this, and instead forces me to cast it as:
let image : UIImage = UIImage(data: (imageData as Data?)!)
which is where i get the nil that's throwing up my flow in the air... i've tried saving the data in many different ways, but still nothing.
if anyone could go through my following methods, see if i might be doing something wrong in the saving part, or the formating of NSData on the fetch method... anything would help.
My configuration:
-the prof property has "Allow external storage" set to true
-my persistent store is seeded blank at the app installation, meaning all the needed properties are already set up when the app is launched for the first time, but obviously set to nil until changed or modified by my various data flows.
-There is no other picture entity in my data model, this is the only one.
func saveProfilePicture(_ pic: UIImage){
let picData = UIImagePNGRepresentation(pic)
let request: NSFetchRequest<UsePics> = UsePics.fetchRequest()
do {
let records = try coreDataManager.managedObjectContext.fetch(request) as [UsePics]
let first = (records.first)
first?.setValue(picData, forKey: "prof")
try context.save()
} catch let err {
print(err)
}
}
func getProfilePicture() -> UIImage? {
let request: NSFetchRequest<UsePics> = UsePics.fetchRequest()
var image : UIImage?
do {
let records = try coreDataManager.managedObjectContext.fetch(request) as [UsePics]
let first = (records.first?.prof) as NSData
if let parsedImage : UIImage = UIImage(data: (first as Data?)!) as? UIImage {
image = parsedImage
}
} catch let err {
print(err)
}
return image
}
EDIT
The solution was found by noticing that in Swift 3, the UIImage class adheres to the Transformable protocol. Swapping my property type for the image from Binary Data to Transformable actually made it possible to save the UIImage as UIImage directly into Core Data without parsing it to another data type.
func saveProfilePicture(_ image: UIImage){
let request: NSFetchRequest<UsePics> = UsePics.fetchRequest()
do {
let records = try coreDataManager.managedObjectContext.fetch(request) as [UsePics]
let first = (records.first)
first?.prof = image
print(first)
coreDataManager.saveData()
} catch let err {
print(err)
}
}
func loadProfilePicture() -> UIImage? {
var image : UIImage?
let request: NSFetchRequest<UsePics> = UsePics.fetchRequest()
do {
let records = try coreDataManager.managedObjectContext.fetch(request) as [UsePics]
let first = records.first
if let img = first?.prof {
image = img as? UIImage
} else {
print("no image")
}
} catch let err {
print(err)
}
return image
}

NSData to UIImage

I'm trying to save a UIIMage and then retrieve it and display it. I've been successful by saving an image in the bundle, retrieving it from there and displaying. My problem comes from when I try to save an image to disk (converting it to NSData), then retrieving it. I convert the image to NSData like so...
NSData* topImageData = UIImageJPEGRepresentation(topImage, 1.0);
then I write it to disk like so...
[topImageData writeToFile:topPathToFile atomically:NO];
then I tried to retrieve it like so...
topSubView.image = [[UIImage alloc] initWithContentsOfFile:topPathToFile];
which returns no image (the size is zero). so then I tried...
NSData *data = [[NSData alloc] initWithContentsOfFile:topPathToFile];
topSubView.image = [[UIImage alloc] initWithData:data];
to no avail. When I step through the debugger I do see that data contains the correct number of bytes but I'm confused as to why my image is not being created. Am I missing a step? Do I need to do something with NSData before converting to an image?
Try this code. This worked for me.
Saving the data.
create path to save the image.
let libraryDirectory = NSSearchPathForDirectoriesInDomains(.libraryDirectory,
.userDomainMask,
true)[0]
let libraryURL = URL(fileURLWithPath: libraryDirectory, isDirectory: true)
let fileURL = libraryURL.appendingPathComponent("image.data")
convert the image to a Data object and save it to the file.
let data = UIImageJPEGRepresentation(myImage, 1.0)
try? data?.write(to: fileURL)
retrieving the saved image
let newImage = UIImage(contentsOfFile: fileURL.relativePath)
Create an imageview and load it with the retrieved image.
let imageView = UIImageView(image: newImage)
self.view.addSubview(imageView)
You should be able to use class methods of UIImage
[UIImage imageWithContentsOfFile:topPathToFile];
OR
[UIImage imageWithData:data];
Did that not work?
Hope this helps!
Just in case this helps someone, from iOS6 we have the imageWithData:scale method. To get an UIImage with the right scale from an NSData object, use that method, declaring the scale you use to store the original image. For example:
CGFloat screenScale = [[UIScreen mainScreen] scale];
UIImage *image = [UIImage imageWithData:myimage scale:screenScale];
Here, myimage is the NSData object where you stored the original image. In the example, I used the scale of the screen. If you use another scale for the original image, use that value instead.
Check this out: http://www.nixwire.com/getting-uiimage-to-work-with-nscoding-encodewithcoder/.
It has exactly what I think the solution to your problem is. You can't just make an image out of NSData, even though that's what common sense suggests. You have to use UIImagePNGRepresentation. Let me know if this helps.
Use if-let block with Data to prevent app crash & safe execution of code, as function UIImagePNGRepresentation returns an optional value.
if let img = UIImage(named: "TestImage.png") {
if let data:Data = UIImagePNGRepresentation(img) {
// Handle operations with data here...
}
}
Note: Data is Swift 3+ class. Use Data instead of NSData with
Swift 3+
Generic image operations (like png & jpg both):
if let img = UIImage(named: "TestImage.png") { //UIImage(named: "TestImage.jpg")
if let data:Data = UIImagePNGRepresentation(img) {
handleOperationWithData(data: data)
} else if let data:Data = UIImageJPEGRepresentation(img, 1.0) {
handleOperationWithData(data: data)
}
}
*******
func handleOperationWithData(data: Data) {
// Handle operations with data here...
if let image = UIImage(data: data) {
// Use image...
}
}
By using extension:
extension UIImage {
var pngRepresentationData: Data? {
return UIImagePNGRepresentation(img)
}
var jpegRepresentationData: Data? {
return UIImageJPEGRepresentation(self, 1.0)
}
}
*******
if let img = UIImage(named: "TestImage.png") { //UIImage(named: "TestImage.jpg")
if let data = img.pngRepresentationData {
handleOperationWithData(data: data)
} else if let data = img.jpegRepresentationData {
handleOperationWithData(data: data)
}
}
*******
func handleOperationWithData(data: Data) {
// Handle operations with data here...
if let image = UIImage(data: data) {
// Use image...
}
}