Parse Cloud Code: Retrieve Image Data For iOS App - ios

I'm writing an iOS app with a Parse backend, and amongst other things it will list blog entries.
Each blog entry has an associated image, but I don't want to fetch the image object for each entry and then have to fetch the image file for each of those. So I wrote a cloud function that would combine the two steps and return the raw image data with a single request.
Here's the cloud code:
var imageFile = image.get("photoFile");
Parse.Cloud.httpRequest({ url: imageFile.url()
}).then(function(dataResponse){
response.success(dataResponse.buffer);
});
When I do this, I get back an array of integers, and I don't know where to go from here to get a UIImage. Here's the iOS code I have so far:
PFCloud.callFunctionInBackground(latestImageCloudFunctionName, withParameters: parameters) { (imageData, error) -> Void in
if let data = imageData {
//imageData is of the type [Int]
}
}
Is the image base64 encoded here? How do I get an NSData object from this that will create a UIImage?

Related

How can I convert AFDataResponse<Data> into Data in order to render an Image using bytes?

I've created a Service file in order to handle all of my networking within the Weather Application that I am fine tuning. Within this service file, I use protocols, in order to return the retrieved data from GET requests to the appropriate View Controller.
During my code refactoring, and for the sake of learning, rather than using URLSessions, I decided I wanted to learn how to use Alamofire.
One of my GET requests retrieves an image of either a (sun, cloud, rain cloud, etc.), depending on the weather of a certain city (this is an example of the URL I am submitting my GET request to: http://openweathermap.org/img/wn/03n#2x.png.
Before I imported Alomofire, I would GET the bytes of this image, and render the bytes within UIImage like so:
self.weatherIcon.image = UIImage(data: result)
This worked just fine. But now, when using Alamofire for my request, the challenge I am having is that I'm unable to convert AFDataResponse to type Data, in order to then be rendered to UIImage.
Below you may see my GET Request.
AF.request(myUrl).responseData{response in
debugPrint(reponse)
self.delegate3?.iconServiceDelegateDidFinishWithData(result: response)
}
The response is of type AFDataResponse.
Therefore, when trying to write:
self.weatherIcon.image = UIImage(data: result)
I get an error saying,
Cannot convert value of type 'AFDataResponse (aka 'DataResponse<Data, AFError') to expected argument type 'Data'.
Any help would be much appreciated from the community.
Thanks.
Simple answer:
Create a variable of type Data and assign response.data to this variable.
AF.request(myUrl).responseData{ response in
debugPrint(response)
var imgData : Data //create variable of type data
imgData = Data(response.data!) // access the data through response.data
self.delegate3?.iconServiceDelegateDidFinishWithData(result: imgData)
}
There are many ways to do this. You can access the data directly, as you originally suggested (though I would treat it as an Optional rather than force unwrapping, as it'll crash otherwise).
.responseData { response in
let image = response.data.map(UIImage.init(data:)) // Creates UIImage?
}
You can transform the Result value to maintain any Error you receive.
.responseData { response in
let image = response.result.map(UIImage.init(data:)) // Creates Result<UIImage, AFError>
}
Or you can map the entire DataResponse.
.responseData { response in
let imageResponse = response.map(UIImage.init(data:)) // Creates DataResponse<UIImage, AFError>
}

Extensive memory usage when uploading assets (images, videos) to firebase in Swift?

Suppose I have an array of UIImage called photos, they are to be uploaded to Firebase storage. I wish to do the following things:
Upload them to Firebase storage
Get paths of the uploaded photos and store in an array called uploadedAssets (paths, not download url, it looks like this: "photos/folder_name/photo_id"), where "folder_name" is randomly generated and "photo_id" is an integer, representing the order of photos
Call Cloud Function and pass uploadedAssets to it. The server then uses the paths to find all pictures and generates a thumbnail for each one.
Finally, store the original photos' download urls and thumbnails' download urls in database.
I have something that's working, but uses too much memory (300+MB when uploading only 4 pictures):
// Swift
let dispatchGroup = DispatchGroup()
let dispatchQueue = DispatchQueue.init(label: "AssetQueue")
var uploadedAssets = [String]()
let folderName: String = UUID().uuidString
dispatchQueue.async {
for i in 0..<photos.count {
dispatchGroup.enter()
let photo: UIImage = photos[i]
let fileName: String = "\(folderName)/\(i)"
let assetRef = Storage.storage().reference().child("photos/\(fileName)")
let metaData = StorageMetaData()
metaData.contentType = "image/jpg"
if let dataToUpload = UIImageJPEGRepresentation(photo, 0.75) {
assetRef.putData(
dataToUpload,
metaData: metaData,
completion: { (_, error) in
uploadedAssets.append("photos/\(fileName)")
dispatchGroup.leave()
}
)
}
}
}
dispatchGroup.notify(queue: dispatchQueue) {
Alamofire.request(
"https://<some_url>",
method: .post,
parameters: [
"uploadedAssets": uploadedAssets
]
)
}
And the code that generates thumbnails runs on server side, therefore, in my opinion, is irrelevant, I won't post it here. So, the above code snippet consumes 300+MB of memory when there are 4 photos to upload. After successfully uploaded those photos, the memory usage stays at 300+MB and never drops. When I try to upload more, say another 4 photos, it could even go up to 450+MB. I know that's not normal, but can't seem to figure out why this would happen?

Retrieve images from a DJI Drone with EXIF data

I'm using the DJISDK in iOS to download pictures from the aircraft.
I'm using the downloadSelectedFiles method from PlaybackManager class.
Here is my process callback:
process: { (data, error) in
if data != nil{
if self.downloadedImageData != nil{
self.downloadedImageData!.append(data!)
}else{
self.downloadedImageData = data!
}
}
}
And this is filecompletition callback:
fileCompletion: {
self.downloadedFilesCount += 1
let image = UIImage(data: self.downloadedImageData!)
if let img = image {
self.downloadedImagesArray?.append(img)
}
self.downloadedImageData = nil
}
I'm correctly retrieving the image but without the EXIF data. How can I get that info and add it to the image?
I already downloaded and tried the iOS-MediaManagerDemo and it's the same thing, downloads the image but without the exif data but the official DJI Go app retrieves all the info so thereĀ“s must be some way to do it.
There's also a similar issue in their forums regarding empty metadata and downloadSelectedFilesWithPreparation. The user that created the post
also found a solution:
I solved the problem by not converting the NSData into any format instead saved the NSData directly. Using PHAssets and temporary file to store the NSData as PHAssets only accepts data from URL.
Try using fetchFileDataWithOffset:updateQueue:updateBlock (it will be called fetchFileData(with:updateQueue:updateBlock) in Swift)
[...] fetching the media data will return all data for a video or image
Sample code (objc): here

Downloading image from Amazon S3 (using AWS mobile hub helper)

I'm trying to download an image called "aaa.jpeg" in my s3 bucket using AWSMobileHubHelper. I found this function on their documentation site.
func downloadContent(content: AWSContent, pinOnCompletion: Bool) {
content.downloadWithDownloadType( .Always, pinOnCompletion: pinOnCompletion, progressBlock: {(content: AWSContent?, progress: NSProgress?) -> Void in
// Handle progress feedback
}, completionHandler: {(content: AWSContent?, data: NSData?, error: NSError?) -> Void in
if let error = error {
print("Failed to download a content from a server.)")
// Handle error here
return
}
// Handle successful download here
if let image = UIImage(data: data!){
self.imageView = image
}
})
}
Once the download is successful (it Did, There is no error message), I'm trying to assign the image to an imageView.
I can tell that the data has been downloaded successfully. I can print the data and see a familiar binary structure of an image. But for some reasons I can't assign the UIImage to the imageView. Because I can't convert the data to a UIImage.
I just want to know if this is the proper way to download image from s3 or am I missing something. Does "data" in the completion block carry the downloaded image? I can't seem to find any documentations on this.
Is this the correct function to use to download from S3?
Yes, the data contains the actual image data. You can put the downloaded data in an UIImageViewController and it should open up fine. Also, this is demonstrated in a Sample App which can be downloaded from the Mobile Hub Console.

Downloading Images Asynchronously in Sequence

I am trying to download images but it is crucial that the images I download are in a specific order. I am using the following code to download the image:
func downloadImage(url: NSURL, completionHandler: (response: UIImage) -> ()){
print("Started downloading \"\(url.URLByDeletingPathExtension!.lastPathComponent!)\".")
manager.getDataFromUrl(url) { (data, response, error) in
dispatch_async(dispatch_get_main_queue()) { () -> Void in
guard let data = data where error == nil else { return }
print("Finished downloading \"\(url.URLByDeletingPathExtension!.lastPathComponent!)\".")
completionHandler(response: UIImage(data: data)!)
}
}
}
and I am using this code to call downloadImage
self.downloadImage(NSURL(string: self.url)!, completionHandler: { response in
dispatch_async(dispatch_get_main_queue()) {
self.images.append(response)
}
})
The problem is that the images start downloading in the correct order however the response isn't (this is obviously because the size of the images are different and one comes in faster). So what is happening is that all images start downloading, and whichever comes first appends to the images : [UIImage] array. How can I make it so that the images in the images array is in order?
I've also tried to remove the main block when calling downloadImage function
self.downloadImage(NSURL(string: self.url)!, completionHandler: { response in
self.images.append(response)
})
You cannot control the download order in the sense that all the requests to the same server will be pipelined no matter what the order you create the URL objects in. Also, some URLs may be cached while others may need to go to the remote server. What you need to do is maintain a mutable array or dictionary that contains the url to actual data mapping, then wait until all the urls have been completely downloaded and then iterate in a known order.
The simplest method is that you can save every image in Dictionary with their url after downloading. Like var imageData = [String: NSData](). Later you can sort it or use it by keys(url).

Resources