Does creating an INImage from a URL work? - ios

I would like to use INImage.init(url:) for displaying a user profile image in a notification.
This initializer is documented with:
A URL that specifies an image file on a remote server. The image file can be in any format supported by the system, but it is recommended that you use PNG images.
However, as far as I can tell, this simply does not work – the image in the URL is never displayed. I have seen many other posts claiming that it does not work for them either.
Am I missing something or is this simply a bug?
I am working around it right now with the following extension:
extension INImage {
convenience init?(contentsOf url: URL) {
if let data = try? Data(contentsOf: url) {
self.init(imageData: data)
} else {
return nil
}
}
}
This doesn't seem great, because the image isn't cached and has to be downloaded every time a notification comes in, which seems really bad.

Related

Save Camera Photo To File Location - Swift Xcode

In my app, I allow the user to take a photo with the camera. When didFinishPickingMedia executes, I want to save the photo to the FILE LOCATION (url... modelController.saveLocation) the user previously selected earlier in the app. The file location may be a folder in File Directory, Dropbox, etc. I can't seem to figure out how to do this, despite my reading efforts online. The other tutorials seem to be taking a photo that's in the assets folder and saving it somewhere, not a photo that was just taken.
I tried this code, but it doesn't work. The code in the catch statement is called due to some error.
let url = modelController.saveLocation[0].appendingPathComponent("\(saveLocationName.description).jpg")
if let data = image.jpegData(compressionQuality: 1.00) {
do {
try data.write(to: url)
} catch {
print("Unable to write image data to disk")
selectNewFileLocation()
}
}
Please explain your answers with code. It's probably a really simple solution, but I'm a rookie. Thanks for your help ahead of time!
It's been a while, I think you have came up with an answer. But this is for people encounter this problem. For some reasons, after I changed the format to png format instead of jpeg, it works, but after using the format jpeg again, it works too. I don't really know why, cause now I can't reproduce this problem.
let url = modelController.saveLocation[0].appendingPathComponent("\(saveLocationName.description).png")
if let data = image.pngData() {
do {
try data.write(to: url)
} catch {
print("Unable to write image data to disk")
selectNewFileLocation()
}
}

What is the fastest way to convert an imageURL from Firebase into a UIImage?

In my iOS app I need to take an imageURL string and convert it into a UIImage.
I wrote the below function to handle this:
func getImage(urlString: String) -> UIImage {
let url = URL(string: urlString)!
do {
let data = try Data(contentsOf: url)
let image = UIImage(data: data)!
return image
} catch {
print(error, " This was the error in p2")
}
return UIImage(named: "media")!
}
The issue is that this takes too long. I believe it's a solid second or longer for this to complete.
I need that time to be significantly shorter.
Question: Is there a faster way to get the UIImage based on an imageURL from Firebase? (maybe a cocoa-pod? or better way to write the code?)
Additional questions:
Would this be any faster if the image in Firebase were of lower quality?
Would it be a viable solution to lower the quality of the image right before being passed into this function?
A lot the prominent iOS apps (and web and mobile and general) that do a lot of downloading of images take advantage of progressive jpeg. This way your user will see at least something while the image loads and over time the image will get progressively better. As a lot of commenters have mentioned, you’re not in control of Firebase like you would be if you had your own backend server delivering the pictures that you could do performance optimizations. Therefore one of the best things you can do is implement progressive jpeg in your app.
The first link is a library that will allow you to use progressive jpeg in your iOS app. The second link is a detailed approach used at FaceBook on faster loading of images.
https://www.airpair.com/ios/posts/loading-images-ios-faster-with-progressive-jpegs
https://code.fb.com/ios/faster-photos-in-facebook-for-ios/

Override Local Data in Siesta?

I'm having trouble successfully setting local data for Siesta in Swift. My goal is to set a UIImage for a URL locally, so that this local image can be displayed with no download time.
To do this, I'm setting the image data for the URL as such:
let resource = CustomRemoteImageView.imageCache.resource(myPhoto.url.absoluteString)
let imageData = UIImagePNGRepresentation(image)! // I've also tried putting the UIImage directly in there, because the transformation chain doesn't apply to local data, right?
let entity: Entity<Any> = Entity(content: imageData, contentType: "*/*") // I've played around with the content type too!
resource.overrideLocalData(with: entity)
I'm then using a custom Service that always tries to parse content as an Image:
private let imageTransformer =
ResponseContentTransformer
{ Image(data: $0.content)}
convenience init() {
self.init(standardTransformers: [])
configure {
$0.pipeline[PipelineStageKey.parsing].add(self.imageTransformer, contentTypes: ["*/*"])
}
}
This system is working great for all remote images, but it always seems to fail to parse this overridden local image. It seems like it's trying to parse but just fails every time.
i.e. I'm getting a Siesta.ResourceEvent of
(Siesta.ResourceEvent) $R20 = newData {
newData = network
}
but the actual .typedContent is nil.
overrideLocalData and overrideLocalContent do not interact with the pipeline at all. Siesta won’t try to parse what you pass; what you override is what your resource gets.
Furthermore, overrideLocalData and overrideLocalContent don’t fail. They always update the resource’s content. If you call those methods, the resource content will match what you passed.
So … the problem isn’t parsing. What might it be?
Entity.typedContent is a shortcut for applying as? to a resource’s entity’s content. If you're getting nil, it means that either (1) the content of the entity you passed to the overrideLocalData was nil or (2) the contextual type in which you’re calling typedContent doesn’t match the content’s actual runtime type.
What do you see if you print resource.latestData.content? That will show you what’s actually there, and will rule out type conversion issues with typedContent.
If it’s not nil, compare its value from a network request and get the types to match.
If it is nil, then either something else cleared the content or you passed nil content in the first place. Try SiestaLog.Category.enabled = .common and see if you can spot where it is or isn’t getting set to the right thing.

Does Firebase Storage's getMetaData() call the backend (and thus introduce a small delay)?

I currently use the following (Swift 4) code to download an image stored on the Firebase Storage :
func getImage(completion: #escaping (UIImage?)->()) {
let ref = Storage.storage().reference().child("myImage.jpg")
ref.getMetadata() {
(metadata, error) in
guard let url = metadata?.downloadURLs?.first, error == nil else {
print(String(describing: error))
completion(nil)
return
}
//got url download image here and return it
//this function is not important but it does it asynchroniously
func downloadImageOrReturnACachedVersionOfItBy(url, completion)
}
I notice a considerable delay in downloading the image, even if downloadImageOrReturnACachedVersionOfItBy(...) actually takes care of image cashing
Questions:
Does the function getMetadata() contact the backend each time its called?
And thus can this introduce a round-trip web service calling delay?
If so...to avoid this, would it be a good idea store the returned url, when the image was uploaded, returned in the metadata from Firebase putData() locally in the app? Are these download url's fixed for life? Are the automatically being invalidated somehow?
1) Does the function getMetadata() contact the backend each time its
called? And thus can this introduce a round-trip web service calling
delay?
Yes, this is because the download URL may have changed (e.g. developer revoked it), so we need to fetch it again.
Note that we have a downloadURL() method that does the same thing but without you having to parse metadata manually.
2) If so...to avoid this, would it be a good idea store the returned
url, when the image was uploaded, returned in the metadata from
Firebase putData() locally in the app? Are these download url's fixed
for life? Are the automatically being invalidated somehow?
I'd recommend using the FirebaseUI integration with SDWebImage, which automatically caches and displays these images.
// Reference to an image file in Firebase Storage
let reference = storageRef.child("images/stars.jpg")
// UIImageView in your ViewController
let imageView: UIImageView = self.imageView
// Placeholder image
let placeholderImage = UIImage(named: "placeholder.jpg")
// Load the image using SDWebImage
imageView.sd_setImage(with: reference, placeholderImage: placeholderImage)

Refresh cached image in AlamofireImage?

Is it possible to automatically remove cached image and download newer if image was updated on server? I have tried AlamofireImage and STXImageCache but they both download an image only once and do not update it.
I try like that:
private let downloader = ImageDownloader()
func downloadImage(path: String?, completion: #escaping (UIImage?) -> ()) {
guard let path = path else { return }
let urlRequest = URLRequest(url: URL(string: path)!)
downloader.download(urlRequest) { response in
completion(response.result.value)
}
}
Manual update is not so good also, because if I don't know if an image was updated on server I have to forget about image caching at all.
Looks like none of available pods are able to refresh cached images, or any other cached content, when the content is changed on server. It is technically possible due to Content-Control technology, but apple does not seem to provide this possibility https://developer.apple.com/documentation/foundation/nsurlrequest.cachepolicy/1414422-reloadrevalidatingcachedata
reloadRevalidatingCacheData
Specifies that the existing cache data may
be used provided the origin source confirms its validity, otherwise
the URL is loaded from the origin source.
This constant is unimplemented and shouldn’t be used.
Your best move would be to ask server developer to generate unique URL addresses to any image that will change(avatar, backgrounds, icons etc), or if server developer is not available you should just get rid of the caching in places where there is such a problem and use casual download.
In any case you can use my gist for downloading images cached and not cached here https://gist.github.com/sam-moshenko/562ec61431c4a0ebeb68899b4d1b4d26 (Just don't forget to install pod 'AlamofireImage')

Resources