Based on other question, a based64 image Data is about +37% extra size of the actual String.
Therefore consider this case.
An API Client responses with an Array of Object,
The object contains a lot of properties ..etc, but the one matters is a property i found with a Key of ImageString64 that returns an image of maxWidth & maxHeight by 300x300.
Desired solution:
I want the fastest, yet memory friendly way to decode those images
Notes to consider:
I do know how to encode them, the question is where and why, does it
even matter ?
Options i have: //You can add yours if you have better one
1- inside the Request response callback
2- pass those Objects with their ImageString64 as they are plain String, and then Decode them inside the UIViewController.
More general information about the response:
1- maximum array of object as response is limited between 6 - 9 .
2- each response object, has 17 Keys.
If you need to work with the base64 decoded image, I will transform it neither in the request call back or the view controller.
You should have a type of internal repository, from where your view controllers get the Object instances. The repository contains the raw data, with the base64 string.
If you access one of the object instances from the repository, you should transform the base64 string to an UIImage instance. The view controller should only see the UIImage.
To get good performance for transform and showing the data, you should do this transform before trying to display it, but as mentioned, only if its needed.
If you want to display the data in an UITableView or UICollectionView you should use the prefetching protocols UICollectionViewDataSourcePrefetching (https://developer.apple.com/documentation/uikit/uicollectionviewdatasourceprefetching/prefetching_collection_view_data) or UITableViewDataSourcePrefetching (https://developer.apple.com/documentation/uikit/uitableviewdatasourceprefetching).
What's your use case for displaying the images?
Related
I have a problem with deleting images from my app. I have an array of string, that are images converted to base64 string. So I get back array from API back to my app, and I'm stuck when I want to delete one picture that user has select.
I tried to delete with filter and map method but didn't solve the problem. Here is my "try" "
func deleteImage(image : UIImageView) {
for img in newAdedImages {
newAdedImages = newAdedImages.filter({$0 !== image})
newAdedImages.append(img)
}
}
Bad code
As #vadian already pointed out code you've posted doesn't make sense because you are trying to filter array of strings with instance of UIImageView. Also you are adding string to array which already contains that string that means you will have a lot of duplicates.
Possible solution
You could check how base64 string is used to create UIImage that is used in UIImageView then you can try to reverse process and extract base64 string from UIImage. Then you can filter array of newAddedImages by comparing string values.
Check this answer on SO: https://stackoverflow.com/a/47610733/4949050
try === operator
or add some property to identify your image, also all UIView subclasses have .tag property which can be used as identifier
upd: if you are trying to compare base64 string with UIImageView then seems like u doing something wrong, its better to store UIImageView instead of base64 strings. Imagine your app in abstractions, all the "UI/visual" is view abstraction and the "data" (e.g. base64 strings coming from server) is **data abstraction so u shouldnt mix them up. I dont know the context of your task or so, but there is some pointers I can give:
1) get base64 strings from service/API/etc. (this is data abstraction)
2) use some helper (e.g. some swift class with function) to make UIImage (there goes view abstraction)
3) operate your uiviews as u wish
But this is very simple explanation, I hardly recommend to read more about architecture patterns such as mvvm for example
I have a custom class with which i made an (swift-y) struct NSCodable. Now I want to convert that into a CKRecord in order to use CloudKit. Even though I set 'key value'-pairs when encoding my struct, it is in my understanding that the struct is converted into NSData and that you can't convert it to a Dictionary (or another key-value object). So I get the feeling that this is not the way to go.
Is there a way to make this conversion directly? Or with a step in-between (for instance converting the Data into a [String: String]- dictionary)?
NSCoding is only for going from/to the same class. Instead, write a toServerDictionary method on your custom class, including only the properties you want to send to CloudKit, and then use the result to call setValuesForKeys on a CKRecord.
You'll likely find there are properties that need to be specific types, in which case its better to make your method toServerRecord and create a CKRecord and return it. You can also have a updateWithServerRecord to set anything you receive back.
My app allows users to customize (add UILabels and UIImages to a UIView. I want the user to be able to retrieve the same view (everything remains at the same location where it was defined). I'm thinking CoreData. I guess I can store everything necessary in a Dictionary<String, AnyObject> including a subview's frame and whatever the user puts inside that view (can be an image or text depending on whether it is a UILabel or UIImage). The storage structure I have in my head is the following:
Dictionary<String, AnyObject>
"frame" : CGRect
"image" : UIImage OR "label" : UILabel
Once I have this ready I can just convert the dictionary into NSData and stuff it into core data.
But is this the correct way of storing views? Any alternative/suggestion on this? Thanks!
=================================UPDATE=================================
The structure I ended up using looks something like this:
NSDictionary
"frame" : CGRect
"content" : UIImage's path on disk / text (for labels)
"type" : "Img" / "Txt"
Since I'm using CoreData, I only needed to make my "view" object extend NSManagedObject and have a dictionary property. This way, the encoding process was no longer needed.
Your approach to creating a dictionary is fair. In a number of ways it would be better to create a custom class (or set of) as these are more specific and provide more compiler checks.
In either case, archiving the store to data is the correct approach (using NSCoding).
Once you have the data you should save it directly to disk rather than using Core Data. Core Data doesn't provide any advantages when storing a single large binary data blob.
Using Core Data properly will only help you if you have many views that you want to store and you don't need to reload them all at the same time. If doing this you wouldn't use a dictionary or data, you would define entities with appropriate attributes.
I'm using Rest Kit with Core Data, one of the Core Data entities has an attribute 'image' that has a binary type.
I'm still in mockup stage so the image is populated with this code:
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://lorempixel.com/60/60/people"]]];
entry.image = UIImagePNGRepresentation(image);
Another tab has a collection view that uses fetchedResultsController.
After creating a new entity, if I only save the context the image works fine.
But if I push the entity to the web server using 'postObject:' the image is corrupted when it comes back from the server. I've confirmed the server receives the same string representation of the image "<2f396a2f 34414151 536b5a4a 52674142 ... 6a6e502f 32513d3d>" and stores it directly into a MySQL column of type long blob and at all points the string representation is the same.
But when the collection view is populated using a server call via RestKit the entities image is invalid. I'm think the issue is the data is being converted into the data representation of the description of the data.
Does anyone have a working example with images. The only thing I can think of is that I need to add a custom transformation, but the documentation and examples are lacking as far as how to actually implement one.
RestKit is storing the plain NSData for the image in Core Data - it has no idea what else you might want to do with it. Generally you don't want to manage images directly in Core Data or using RestKit.
Generally, store the path of the image in Core Data and the file on disk. Download them asynchronously (from the URL's which would also be in Core Data).
For uploading, you could make RestKit upload the data, but you probably actually want to file upload or convert to base64. You will need to write some code for this (which you could have RestKit pick up by using the key of the method name that returns the appropriate data). A similar process will work for mapping the data in.
RestKit data transformers are hard to make work in this situation as you are converting between data and strings and they are too general to be able to intercept accurately.
In my app I will have an array of up to 50 images that people can maintain. They can choose to create new or delete existing images. Each image will have a few things associated with them, like a rating for example.
My question is how I should go about storing them. Should I create a CoreData entity called "Image" and store them that way? Should I set up a UIView subclass that conforms to NSCoding and just encode and decode the array and store it on the device? Is there another way I should consider? Thanks for any suggestions.
You can create an entity that represents the image with its information, and use core data's external storage property for entity's attribute. This way, you get the convenience of core data without actually storing the images on the persistent store.
I had to make a similar decision recently and I decided to store the image in CoreData. I have a managed object called photo with a Binary Data field called image:
photo.image = UIImagePNGRepresentation(imageFile); // <- imageFile is a UIImage
And I recreate the image using:
[UIImage imageWithData:self.image];
One immediate advantage is that the images are deleted automatically with the object and there's no extra overhead in retrieving the image if you've already queried for the record.
Core Data is probably overkill for what you want to do. Choose some key value pairs for descriptive information about the image. One key will be "path" and the value will be the path to the image file (or just its name). You can serialize array (or set) of dictionaries.
When your app starts up, read in the serialized array of dictionaries. Every time something changes, serialize and save the information. Write a short audit routine to insure that there is a one - to - one correspondence between dictionaries and images on file, and if one or the other is missing delete the other (will handle situations were you crash before getting to update something or the other).
Later on you can add more attributes to the dictionaries if you want, or even remove some.
[PS: I did this in a shipping app for one version, when the information needed to become relational I switched to Core Data.]