UIImagePickerController - how to save image to core data? (Swift) - ios

I want to save the image that I have picked from the ImagePicker and I want to save it in my CoreData. I create Attribute in my Entity with Binary Data type.

First of all, you need to convert UIImage to Data:
let data = UIImageJPEGRepresentation(image, 1.0)
After that set this data to property in the Entity in the perform method of NSManagedObjectContext:
context.perform {
entity.image = data
// Save context
}

I don't recommend you save UIImage to CoreData it's bad way. Just use https://github.com/Alamofire/AlamofireImage to cache an UIImage correct.

Related

Store sequence of images in CoreData

I tried to store images using CoreData. It is fine and I can store a single image. However how can I turn it to multiple images?
Currently, I have set the image field to Binary Data.
I save the image into the object by:
let imageData = UIImage(imageLiteralResourceName: "Dummy1").jpegData(compressionQuality: 1)
item.image = imageData
try? self.viewContext.save()
How can I turn it into an array of imageData that can store in CoreData?
I tried to do this but it fails:
let imageData1 = UIImage(imageLiteralResourceName: "Dummy1").jpegData(compressionQuality: 1)
let imageData2 = UIImage(imageLiteralResourceName: "Dummy2").jpegData(compressionQuality: 1)
item.image = [imageData1, imageData2]
try ? self.viewContext.save()
The compiler said that the attribute 'image' is Data? but not [Data]? type.
I have also tried to use the Type Transformable:
However, there is warning:
warning: Misconfigured Property: Items.imageArray is using a nil or insecure value transformer. Please switch to NSSecureUnarchiveFromDataTransformerName or a custom NSValueTransformer subclass of NSSecureUnarchiveFromDataTransformer
Any idea on the warning and how to resolve it?
On using the Transformable type, actually I can achieve this:
let imageData1 = UIImage(imageLiteralResourceName: "Dummy1").jpegData(compressionQuality: 1)
let imageData2 = UIImage(imageLiteralResourceName: "Dummy2").jpegData(compressionQuality: 1)
item.image = imageData1
item.imageArray = [imageData1!, imageData2!]
However, a few issues here:
It force to add ! to imageData, which indeed, should be optional in my case, I have no way to use ?? properly to give it a dummy imageData if that is found to be nil.
The same problem appear when I tried to display the array:
Image(uiImage: UIImage(data: (self.item.imageArray![0]))!)
.frame(width:300, height:300)
You can see that I have added ! to both imageArray! which can be nil and also the UIImage!
I would like to, instead provide default values for both cases, but I failed to use ?? to provide default value. Any idea?
You're trying to jam a square peg in a round hole. You can't assign an array of data into a data property or a transformable property because an array of data is neither data nor a transformable.
What you can do is to make your property a one-to-many relationship with another entity, let's call it ImageContainer, this other entity would have an imageData property. Now you could add as many images (within an image container) to your object.
Another alternative is to use a transformable value, which as I understand uses NSSecureCoding to transform your values into something CoreData can understand.
You should not save images directly in database, you will hit big performance issues.
Instead save the image name in the database and save the actual image with FileManager in the storage.
Then you can load the image/s from the file manager.

Updating a property in a struct inside an array

In my app I download a load of JSON.
I then store that as an array of structs and use that to populate a UITableView.
One of the properties of the struct is an NSURL for an image. Another property is an optional UIImage.
The struct has a mutating function downloadImage which uses the URL to download the image and store it in its property.
Like this...
struct SearchItem {
// other properties...
let iconURL: NSURL
var icon: UIImage?
mutating func downloadImage() -> Task<UIImage> {
let tsc = TaskCompletionSource<UIImage>()
NSURLSession.sharedSession().downloadTaskWithURL(iconURL) {
(location, response, error) in
if let location = location,
data = NSData(contentsOfURL: location),
image = UIImage(data: data) {
self.icon = image
tsc.setResult(image)
return
}
tsc.setError(NSError(domain: "", code: 1, userInfo: nil))
}.resume()
return tsc.task
}
}
The problem I'm having is this. (and I have been stumped by this in the past).
I have an array [SearchItem] that I use to populate the tableview.
In cellForRow I have the code... if let searchItem = items[indexPath.row]...
It then checks if the image is nil and downloads...
if let image = searchItem.icon {
cell.imageView.image = image
} else {
searchItem.downloadImage().continueOnSuccessWith(Executor.MainThread) {
_ in
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
}
}
But this never goes through to put the image into the cell. This is because the SearchItem is struct and so pass-by-value. So the search item that I am downloading the image for is not the same SearchItem as the one stored in the array.
How can I ensure that the image that is downloaded is then stored into the SearchItem inside the actual array?
Use classes.
You're getting a copy of searchItem in your cellForRow method. Whatever you do to this, will be done only to that copy. What you actually want is for the changes you make to that copy to be applied to the version in the array.
Therefore you want reference semantics, therefore use classes.
You could dance around re-inserting the updated copy into the original array if you liked, but what does that gain you besides a line of extra code and probably some other problems.
Structs are lightweight data objects that are not passed by reference, but instead copies itself as needed when you a) pass it to a new function, b) try and access it in a block. Arrays in Swift also work slightly differently than their Obj-C counterparts. When you have an Array of class objects the array will be a reference type, and you'll be able to achieve what you're trying to achieve here. But on the other hand if the Array is of Structs the array looses its reference semantics and uses copy-by-value instead.
This difference is really powerful when used appropriately, you can greatly optimise your code, make it run faster, have less errors produced by mutable object references having changes happen in multiple parts of your code, etc. But it's up to you as a developer to see where the gains of these optimisations are useful or where it makes sense to use objects instead.

Swift keep pointer/reference to object or another a pattern?

I am converting a project from another tool and language:
Suppose I have a
singleimagecache: UIImage;
I now pass this to a structure which does
var myimage: UIImage = singleimagecache;
Now, this struct is passed to a function that does some work.
This function determines another image should be cached. In my original code, it would simply use myimage and assign its content some other image-bitmap data. Since the object-reference itself was not changed (only its content) singleimagecache would still point to valid fresh new cache data.
However, this is not possible in Swift since UIImage requires to be reconstructed like this:
myimage = UIImage(...)
But doing that would leave singleimagecache with wrong data
So that leaves me the following options:
Any support in Swift for keeping references in sync?
Any support in Swift for keeping pointers to objects (that themselves possibly can be nillable)
Wrap UIImage inside another object that is persistant and use that.
There is no built-in Swift support for what you wish to do. I would just make a wrapper class with 2 UIImage properties. One would be myimage and the other would be singleimagecache. You could then use the didSet property observer on myimage to achieve the desired synchronization so that singleimagecache will always be up to date with myimage. didSet will be called everytime a new value is stored/set in the property.
class imageCache
{
var myimage:
= UIImage() {
didSet {
singleimagecache = myimage
}
}
var singleimagecache = UIImage()
}

Saving and retrieving images with correct order

I'm trying save some images to an array, and their name to another array as string with below code;
self.countryNamesArray.append(object["CountryName"] as! String)
let image = UIImage(data:imageData!)
self.countryImages.append(image!)
But it doesn't retrieve images with same order I saved while retrieve CountryNamesArray with correct order.
rowString = countryNamesArray[row]
myImageView.image = self.countryImages[row]
Should I use custom class to save images with string or are there any way to fix?

How to pass UI image from one view controller to another?

I need to pass a image from one view controller to another I had user NSuserdefault to declare the image variable as global but it shows this error app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object
func imagePickerController(picker: UIImagePickerController!, didFinishPickingImage image: UIImage!, editingInfo: NSDictionary!) {
self.dismissViewControllerAnimated(true, completion: { () -> Void in
})
imageview.image = image
NSUserDefaults.standardUserDefaults().setObject(image, forKey:"image")
NSUserDefaults.standardUserDefaults().synchronize()
}
You can't save image directly into the UserDefaults, You can compress image to png/jpeg data,and you can save the data into the userDefault. using method
NSData *UIImagePNGRepresentation(UIImage *image);
and
if imageData {
NSUserDefaults.standardUserDefaults().setObject(imageData, forKey:"image")
NSUserDefaults.standardUserDefaults().synchronize()
}
when you read the userDefaults, you can read data firstly, then convert data to image using method
UIImage-imageWithData: or swift style UIImage(data:)
generally, we don't do like above, we save image to file, and save the file's path into the userDefault
What do you means? Pass a UIImage's variables to another viewController? The information you provide is too little.
Maybe you want to look this link Using Delegation to Communicate With Other View Controllers.
Simply create a property of NSString *imageName and setting it before pushing/presenting a viewcontroller ,and in your other viewcontroller you could read this property to create a UIImage.
To store the image with NSUSerdefaults:
var testimage = UIImage(named: "testimage.png");
NSUserDefaults.standardUserDefaults().setObject(UIImagePNGRepresentation(testimage), forKey:"image")
}
To retrieve it in another class:
var imagedata: NSData = NSUserDefaults.standardUserDefaults().dataForKey("image")!
imageview.image = UIImage (data: imagedata)

Resources