how to bind local image to the image view using ReactiveKit? - ios

For fetching image from server I have done this
// fetch image from server
func fetchImage(url: NSURL) -> Operation<UIImage, NSError> {
return Operation { observer in
// use almofire to deal with server request
let request = Alamofire.request(.GET, url).response { request, response, data, error in
// if error occurs then abort the operation
if let error = error {
observer.failure(error)
} else {
// if doesnt occurs error then convert imageData back to image
if(data != nil)
{
observer.next(UIImage(data : data!)!)
observer.success()
}
}
}
// if response is nil then execute this block
return BlockDisposable {
request.cancel()
}
}
}
& then bind it to the image view
if (url != nil)
{
let image : ObservableBuffer<UIImage>? = self.fetchImage(url!).shareNext()
if ((image) != nil)
{
image!.bindTo(customCellObj.mContentCellImageView)
}
}
Here I am fetching image from url using almofire. Is there any way by which I can bind local DB image with an image view directly using reactive kit?

Here is how you connect an imageView to your stream:
Stream.just(NSURL(string: "https://exoticcars.enterprise.com/etc/designs/exotics/clientlibs/dist/img/homepage/Homepage-Hero-Car.png")!)
.flatMapLatest(fetchImage)
.flatMapError { _ in return Stream.just(UIImage()) }
.observeNext { [weak self] image in self?.imageView.image = image }
.disposeIn(bag)
However, at the risk of sounding way too obvious... That's what ReactiveUIKit is for.
Stream.just(NSURL(string: "https://exoticcars.enterprise.com/etc/designs/exotics/clientlibs/dist/img/homepage/Homepage-Hero-Car.png")!)
.flatMapLatest(fetchImage)
.flatMapError { _ in return Stream.just(UIImage()) }
.bindTo(imageView.rImage)
.disposeIn(bag)
Also there is an Alamofire extension to ReactiveKit (AlamofireReactive) that you might find useful.

Related

Swift: Downloading Images from DropBox

Here's the code I've tried so far:
client?.files.download(path: "/AlloyTest/\(imageName)").response { response, error in
if let response = response {
let responseMetadata = response.0
print(responseMetadata)
let fileContents = response.1
print(fileContents)
} else if let error = error {
print(error)
}
}
.progress { progressData in
print(progressData)
}
This is the error I'm getting when trying the function below:
API route error - {
".tag" = path;
path = {
".tag" = "not_found";
};
}
NEW CODE
func getImage(imageName: String, completion: #escaping (UIImage, NetworkingError) -> ()) {
// Get Image from dropbox
// Download to Data
client?.files.listFolder(path: "/AlloyTest").response { response, error in
if let response = response {
let entries = response.entries
print("ENTRIES:", entries)
} else if let error = error {
print(error)
}
}
}
A path/not_found error indicates that there was nothing at the specified path, in this case "/AlloyTest/\(imageName)", in the connected Dropbox account. Make sure you provide the correct path.
For example, you can list the contents of any particular folder to get the correct path values of its contents using listFolder/listFolderContinue. The path for any particular returned item is Metadata.pathLower.

Firebase Storage Warning: downloadURL()' is deprecated: Use `StorageReference.downloadURLWithCompletion()

I just updated my project to the latest version of Firebase Storage and I am now getting a warning:
downloadURL() is deprecated: Use StorageReference.downloadURLWithCompletion() to obtain a current download URL.
I looked at the Firebase image upload documentation but it still references using downloadURL() which is now depreciated. In the code below I am getting the download URL of the image as a String. The code works but now to be updated since downloadURL() is depreciated
uploadProfilePicTask.observe(.success) { snapshot in
guard let profilePicStringURL = snapshot.metadata?.downloadURL()?.absoluteString else { return }
...
Here is my attempted updated. I tried the code below with the new downloadURLWithCompletion() but something in snapshot.metadata?.storageReference? is returning nil so I am not able to retrieve the url String. Does anyone know how to use the new downloadURLWithCompletion() appropriately below?
uploadProfilePicTask.observe(.success) { snapshot in
snapshot.metadata?.storageReference?.downloadURL { URL, error in
if let urlString = URL?.absoluteString {
// Do something
} else {
return
}
}
Basically not using the metadata but instead just getting the url after the success of your observe event. Since it's successful and you know it's there, you can download the URL. It's there in their docs to 'Generate a download URL'. Below, I'm assuming your StorageReference is uploadProfilePicTask.
uploadProfilePicTask.downloadURL(completion: { (url, error) in
if (error == nil) {
if let downloadUrl = url {
// Make you download string
let downloadString = downloadUrl.absoluteString
}
} else {
// Do something if error
}
})
I had the same problem, but I fixed it with this code:
uploadTask.observe(.success) { snapshot in
guard let imageURL = snapshot.metadata?.storageReference?.downloadURL(completion: { (url, error) in if error != nil {
print(error as Any)
} else { //add all you want
}
}) else { return }
let imageStr = String(describing: imageURL)
DBService.manager.updatePhoto(profileImageUrl: imageStr)
AuthService.manager.updatePhoto(urlString: imageStr)
}
}

Downloading UIImage via AlamofireImage? [duplicate]

This question already has answers here:
How can I get the Data from NSURLSession.sharedSession().dataTaskWithRequest
(2 answers)
Closed 5 years ago.
I have a URL and want to download the image via a return function, however I cant get it to cooperate properly, here is my func:
func getTabImage(url: URL) -> UIImage {
Alamofire.request(url)
.responseImage { response in
if let image = response.result.value {
return image
} else {
print("Failed to get image")
}
}
}
I pass in the URL, and want a UIImage returned from the alamofire response.
But i get
Unexpected non-void return value in void function
for the return statement.
How can i achieve this correctly?
You can use the below function for downloading the image:
func getImage(_ url:String,handler: #escaping (UIImage?)->Void) {
print(url)
Alamofire.request(url, method: .get).responseImage { response in
if let data = response.result.value {
handler(data)
} else {
handler(nil)
}
}
}
Uses
getImage("http://") { (image) in
if image != nil {
print(image)
}
}
Or
If you want to set the image on UIImageView use extension of AlamofireImage.
if let imageURL = URL(string: "http://"), let placeholder = UIImage(named: "default") {
imageView.af_setImage(withURL: imageURL, placeholderImage: placeholder) //set image automatically when download compelete.
}

Downloaded image doesn't always show up immediately

For some reason my photoimg will not update consistantly , sometimes it does sometimes it doesn't.
I'm pretty sure it has something to do with async calls but I've been stuck trying to figure out the root reason why its not updating. So this is in my mainVC and for a user to upload/update image they go to the settingsVC and when they segue back sometimes it shows to the updated image, other times still shows the old image , other times showing nothing . But oddly if I click on my settings and dismiss it then the image will show updated.
So I think my issue lies where I'm calling my method and my async queue.
func fetchProfileImage() {
Dataservice.dataService.USERS_REF_CURRENT_PROFILE_IMAGE.downloadURL { (url, error) in
if error != nil {
}
else {
let url = url?.downloadURL
URLSession.shared.dataTask(with: url!, completionHandler: { (data, resonse, error) in
if error != nil {
print("Fetching did not download \(error.debugDescription)")
}
if let data = data {
print("Fetching Image did download data")
DispatchQueue.main.async {
self.profilePhoto.image = UIImage(data: data)
}
}
}).resume()
}
}
}
Why not just use the built in download mechanism, which always presents callbacks on the main thread*:
let image: UIImage!
let ref = FIRStorage.storage().reference(forURL: Dataservice.dataService.USERS_REF_CURRENT_PROFILE_IMAGE)
ref.data(withMaxSize: 1 * 1024 * 1024) { data, error in
if let error = error {
// Uh-oh, an error occurred!
} else {
// Data for your profile image is returned
image = UIImage(data: data!)
}
}
*unless you explicitly change the thread by providing your own queue ;)
after you perform async, you need to back to main thread to set the download image
DispatchQueue.main.async {
DispatchQueue.main.sync {
self.profilePhoto.image = UIImage(data: data)
}
}

Swift (iOS), waiting for all images to finish downloading before returning

I am writing a Swift iOS app (my first, so please bear with me) where I use Swifter HTTP server to process various requests. One such request is an HTTP POST with a JSON array specifying images to download from the web (and do some other stuff, not pertinent to the issue at hand).
I use Alamofire to download the images (this works fine), but I am looking for good (preferably simple) way to wait for all the images to finish downloading before returning a response to the POST request above (since the response has to contain JSON indicating the result, including any failed downloads).
What is a good way to accomplish this (preferably w/o blocking the main thread)?
Here are some snippets to illustrate:
public func webServer(publicDir: String?) -> HttpServer {
let server = HttpServer()
server.POST["/images/update"] = { r in
let images = ...(from JSON array in body)
let updateResult = ImageUtil.updateImages(images)
let resultJson: String = Mapper().toJSONString(updateResult, prettyPrint: true)!
if updateResult.success {
return .OK(.Text(resultJson))
}
return HttpResponse.RAW(500, "Error", nil, { $0.write([UInt8](updateResult.errorMessage.utf8)) })
}
}
static func updateImages(images: [ImageInfo]) -> UpdateResult {
let updateResult = UpdateResult()
for image in images {
Alamofire.download(.GET, serverFile.imageUrl) { temporaryURL, response in return destinationPath }
.validate()
.response{_, _, _, error in
if let error = error {
Log.error?.message("Error downloading file \(image.imageUrl) to \(image.fileName): \(error)")
} else {
updateResult.filesDownloaded++
Log.info?.message("Downloaded file \(image.imageUrl) to \(image.fileName)")
}}
}
return updateResult // It obviously returns before any images finish downloading. I need to wait until all images have downloaded before I can return an accurate result.
}
Update 1/23/2016, using dispatcher per bbum
This is an attempt to use the dispatcher mechanism, but the call to updateImages still return right away (even when using dispatch_sync).
How can I await the completion of all downloads before returning my HTTP response to the caller?
public func webServer(publicDir: String?) -> HttpServer {
let server = HttpServer()
server.POST["/images/update"] = { r in
let imageDownloader = ImageDownloader()
imageDownloader.updateimageFiles(adFilesOnServer)
let resultJson: String = Mapper().toJSONString(imageDownloader.updateResult, prettyPrint: true)!
if imageDownloader.updateResult.success {
return .OK(.Text(resultJson))
}
return HttpResponse.RAW(500, "Error", nil, { $0.write([UInt8](imageDownloader.updateResult.errorMessage.utf8)) })
}
}
class ImageDownloader {
var updateResult = AdUpdateResult()
private var imageFilesOnServer = [ImageFile]()
private let fileManager = NSFileManager.defaultManager()
private let imageDirectoryURL = NSURL(fileURLWithPath: Settings.imageDirectory, isDirectory: true)
private let semaphore = dispatch_semaphore_create(4)
private let downloadQueue = dispatch_queue_create("com.acme.downloader", DISPATCH_QUEUE_SERIAL)
func updateimageFiles(imageFilesOnServer: [ImageFile]) {
self.imageFilesOnServer = imageFilesOnServer
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
for serverFile in imageFilesOnServer {
downloadImageFileFromServer(serverFile)
}
dispatch_sync(downloadQueue) {
dispatch_sync(dispatch_get_main_queue()) {
print("done") // It gets here before images have downloaded.
}
}
}
private func downloadImageFileFromServer(serverFile: ImageFile) {
let destinationPath = imageDirectoryURL.URLByAppendingPathComponent(serverFile.fileName)
Alamofire.download(.GET, serverFile.imageUrl) { temporaryURL, response in return destinationPath }
.validate()
.response { _, _, _, error in
if let error = error {
Log.error?.message("Error downloading file \(serverFile.imageUrl) to \(serverFile.fileName): \(error)")
} else {
self.updateResult.filesDownloaded++
Log.info?.message("Downloaded file \(serverFile.imageUrl) to \(serverFile.fileName)")
}
dispatch_semaphore_signal(self.semaphore)
}
}
}
First, you really don't want to be firing off a request-per-image without some kind of a throttle. Semaphores work well for that sort of thing.
Secondly, you need to basically count the number of operations outstanding and then fire a completion handler when they are all done. Or, if new operations can be started at any time, you'll probably want to group operations.
So, pseudo code:
sema = dispatch_semaphore_create(4) // 4 being # of concurrent operations allowed
serialQ = dispatch_queue_create(.., SERIAL)
dispatch_async(serialQ) {
dispatch_semaphore_wait(sema, FOREVER) // will block if there are 4 in flight already
for image in images {
downloader.downloadAsync(image, ...) { // completion
dispatch_semaphore_signal(sema) // signal that we are done with one
... handle downloaded image or error ...
... add downloaded images to downloadedImages ...
}
}
}
dispatch_async(serialQ) {
// since serialQ is serial, this will be executed after the downloads are done
dispatch_async(main_queue()) {
yo_main_queue_here_be_yer_images(... downloadedImages ...)
}
}

Resources