Downloading Image from Database? Swift - swift

I have been recently trying to download an image from Firebase and it is simply not cooperating. Any help would be appreciated!
This is the function I used to download the image.
func downloadDefaultProfileImg() {
let imgRef = Storage.storage().reference(forURL: myurllol).child("defaultProfilePicture.jpg")
imgRef.getData(maxSize: 1 * 1024 * 1024) {
data, error in
if let error = error {
print("Nice try idiot" + "\(error.localizedDescription)")
} else {
let image = UIImage(data: data!)
DispatchQueue.main.async {
self.image = image
}
}
}
}
This is the image variable I have defined at the top of my class:
var image: UIImage? = nil
And lastly, this is where I call my function:
self.downloadDefaultProfileImg()
guard let defaultProfileImage = self.image else {
print("nope1")
return
}
guard let imageData = defaultProfileImage.jpegData(compressionQuality: 0.4) else {
print("nope2")
return
}
"nope1" appears in the console. Any help would be greatly appreciated, thank you so much!

Related

Swift / Firebase: Cancel downloading an image?

I have the following basic code implemented to download an image from Firebase. The function is called from within a UICollectionViewCell. There are cases when a user might scroll quickly past the cell and in those cases, I would like to cancel the .getData download task if it has not yet returned. Is there a way to cancel .getData?
private func downloadImage(path: StorageReference, handler: #escaping (_ image: UIImage?) -> ()) {
if let cachedImage = imageCache.object(forKey: path) {
handler(cachedImage)
} else {
path.getData(maxSize: 27 * 1024 * 1024) { (data, error) in
if let data = data, let image = UIImage(data: data) {
handler(image)
imageCache.setObject(image, forKey: path)
} else {
handler(nil)
guard let error = error else { return }
print(error)
}
}
}
}
If you capture your Firebase Storage operations as properties, you can call methods on them to pause, resume, or cancel them. For example:
let download = path.getData(maxSize: 27 * 1024 * 1024) { (data, error) in
if let data = data, let image = UIImage(data: data) {
handler(image)
imageCache.setObject(image, forKey: path)
} else {
handler(nil)
guard let error = error else { return }
print(error)
}
}
download.pause()
download.resume()
download.cancel()
https://firebase.google.com/docs/storage/ios/download-files?authuser=0#manage_downloads

Load image to imageView from Firabse Storage Swift

I am trying to load an image to an imageView from firebase storage as expected.
I have the below function that is called in viewDidLoad but not sure why it's not working.
I copied the urlName.com directly from the image within Firebase storage.
func setProfileImage() {
let currentUserID = Auth.auth().currentUser!.uid
Storage.storage().reference(forURL: "gs://urlName.com").child("Folder/\(currentUserID).jpg").getData(maxSize: 1048576, completion: { (data, error) in
guard let imageData = data, error == nil else {
return
}
self.profileImage.image = UIImage(data: imageData)
})
}
This code doesn't throw an error but also doesn't load anything.
Any help much appreciated
Thanks
1- Allow Access for development mode, In Storage > Rules:
service firebase.storage {
match /b/yourapp.appspot.com/o {
match /{allPaths=**} {
// Allow access by all users
allow read, write;
}
}
}
Get the data:
let reference = Storage.storage().reference(withPath: "your_folder_path)/image.jpg")
reference.getData(maxSize: (1 * 1024 * 1024)) { (data, error) in
if let _error = error{
print(_error)
} else {
if let _data = data {
let myImage:UIImage! = UIImage(data: _data)
self.profileImage.image = myImage
}
}
}

How do I update an MKPointAnnotation image after downloading the right image?

I have a bunch of annotations on a map, which all have a custom photos. Some of the photos may not be downloaded to the application yet from Firebase, so if they do not the image available, it defaults to a white circle image and initiates a download. When the download completes, it does not set the new image. How can I go about this?
Heres some code:
func generateAnnotations() {
for photo in photos {
let annotation = DetailPhotoPointAnnotation()
let latitude = photo.latitude
let longitude = photo.longitude
annotation.coordinate.latitude = latitude
annotation.coordinate.longitude = longitude
if photo.image != nil {
annotation.image = photo.image
} else {
annotation.image = #imageLiteral(resourceName: "whiteCircle")
let path = getDocumentsDirectory().appendingPathComponent(photo.uid)
if let image = UIImage(contentsOfFile: path) {
annotation.image = image
} else {
let ref = FIRStorage.storage().reference(forURL: photo.imageUrl)
ref.data(withMaxSize: 5*1024*1024, completion: { (data, error) in
if error != nil {
print(error!)
} else {
if let imageData = data {
if let image = UIImage(data: imageData) {
photo.assignImage(image: image)
annotation.image = image
}
}
}
})
}
}
self.coordinates.append(CLLocationCoordinate2DMake(latitude, longitude))
self.mapView.addAnnotation(annotation)
}
generateOverlay()
}
As you can see, it first looks to if the photo object contains an image. If it doesn't, it looks in the documents directory for that image. If its not there, it will finally download it. Any suggestions?
You need to do something like this. Go to main thread. And then update
DispatchQueue.main.async(execute: {
imageView.image = image
})
In my solution it works.
Hope this helps.

Setting Marker Icon to UIImage and Properly Resizing it with a background to Make it appear as an icon

the code I currently have gets a profile picture from firebase and displays it on the map as the marker icon.
self.ref.child("users").child(location.key as! String).child("userPhoto").observeSingleEventOfType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
if(snapshot.exists()) {
let profileUrl = snapshot.value as? String
let url = NSURL(string: profileUrl!)
NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, response, error) in
if error != nil {
print(error)
return
}
dispatch_async(dispatch_get_main_queue(), {
if let downloadedImage = UIImage(data: data!) {
marker.icon = downloadedImage
}
})
}).resume()
}
})
Unfortunately, this code just plops a really big image on the map which i dont want. i want a circular image with a border/background that makes it look like a pin/marker. Like this:
Ideal Deign of The Marker Icon
Would anyone know how to do this?
Any help would be appreciated.
Thanks!
P.S: I am new to swift and would really appreciate if you could show me some code with whatever solution you provide. Thanks!
func changeImageDimensions(image: UIImage, newWidth: CGFloat, newHeight: CGFloat) -> UIImage {
let widthRat = newWidth/image!.size.width
let heightRat = newHeight/image!.size.height
var newSize: CGSize = CGSize()
if(widthRat > heightRat) {
newSize = CGSizeMake(image!.size.width * heightRat, image!.size.height * heightRat)
} else {
newSize = CGSizeMake(image!.size.width * widthRat, image!.size.height * widthRat)
}
UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight))
let frameRect = CGRectMake(0, 0, newSize.width, newSize.height)
image.drawInRect(frameRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
self.ref.child("users").child(location.key as! String).child("userPhoto").observeSingleEventOfType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
if(snapshot.exists()) {
let profileUrl = snapshot.value as? String
let url = NSURL(string: profileUrl!)
NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, response, error) in
if error != nil {
print(error)
return
}
dispatch_async(dispatch_get_main_queue(), {
if let downloadedImage = UIImage(data: data!) {
marker.icon = changeImageDimensions (downloadedImage!,newWidth:CGFloat(150),newHeight:CGFloat(150))
}
})
}).resume()
}
})

Load image from URL on WatchKit

Is there a more elegant solution to load an external image on the watch than the following ?
let image_url:String = "http://placehold.it/350x150"
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let url:NSURL = NSURL(string:image_url)!
var data:NSData = NSData(contentsOfURL: url)!
var placeholder = UIImage(data: data)!
// update ui
dispatch_async(dispatch_get_main_queue()) {
self.imageView.setImage(placeholder)
}
}
NSURL is meant to be used for local files. Instead use NSURLSession. It's also useful to set the scale for the remote image.
import WatchKit
public extension WKInterfaceImage {
public func setImageWithUrl(url:String, scale: CGFloat = 1.0) -> WKInterfaceImage? {
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) { data, response, error in
if (data != nil && error == nil) {
let image = UIImage(data: data!, scale: scale)
dispatch_async(dispatch_get_main_queue()) {
self.setImage(image)
}
}
}.resume()
return self
}
}
Use it like this
self.imageView.setImageWithUrl(image_url, scale: 2.0)
Here is the category
import WatchKit
public extension WKInterfaceImage {
public func setImageWithUrl(url:String) -> WKInterfaceImage? {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let url:NSURL = NSURL(string:url)!
var data:NSData = NSData(contentsOfURL: url)!
var placeholder = UIImage(data: data)!
dispatch_async(dispatch_get_main_queue()) {
self.setImage(placeholder)
}
}
return self
}
}
Use it like this
self.imageView.setImageWithUrl(image_url)
I thinks that solution is good because it can help your application out of lagging when you're trying to load some Images from web.
you can make a new function like this:
func loadImage(url:String, forImageView: WKInterfaceImage) {
// load image
let image_url:String = url
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let url:NSURL = NSURL(string:image_url)!
var data:NSData = NSData(contentsOfURL: url)!
var placeholder = UIImage(data: data)!
// update ui
dispatch_async(dispatch_get_main_queue()) {
forImageView.setImage(placeholder)
}
}
}
after that any where you want to load image from urlString you can use like this:
loadImage("http://...", forImageView: self.myImageView)
Hope this help.
I think by this solution you can store image in cache and display image from cache also.so you can call this function and use it.
func loadImage(url:String, forImageView: WKInterfaceImage) {
forImageView.setImageNamed("placeholder")
let image_url:String = url
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let url:NSURL = NSURL(string:image_url)!
print(url)
//if image is already stored in cache
if WKInterfaceDevice.currentDevice().cachedImages[image_url] != nil{
dispatch_async(dispatch_get_main_queue()) {
forImageView.setImageNamed(image_url)
}
}else{
if let data = NSData(contentsOfURL: url){
//load image
let image = UIImage(data: data)!
//Store image in cache
WKInterfaceDevice.currentDevice().addCachedImage(image, name: image_url)
dispatch_async(dispatch_get_main_queue()) {
forImageView.setImage(placeholder)
}
}
}
}
}
Just had the same task, the answers here helped me, but I needed to do some modifications. So I wanted to share the updated version (without any forced unwraps) of the common answers here (should work with Swift 4.2):
public extension WKInterfaceImage {
public func setBackgroundImage(url: String) {
let asyncQueue = DispatchQueue(label: "backgroundImage")
asyncQueue.async {
do {
if let url = URL(string: url) {
let data = try Data(contentsOf: url)
if let placeholder = UIImage(data: data) {
self.setImage(placeholder)
}
}
} catch let error {
print("Could not set backgroundImage for WKInterfaceImage: \(error.localizedDescription)")
}
}
}
}
public extension WKInterfaceImage {
public func setImageWithUrl(url:String, scale: CGFloat = 1.0) -> WKInterfaceImage? {
URLSession.shared.dataTask(with: NSURL(string: url)! as URL) { data, response, error in
if (data != nil && error == nil) {
let image = UIImage(data: data!, scale: scale)
DispatchQueue.main.async() {
self.setImage(image)
}
}
}.resume()
return self
}
}
if let url = NSURL(string: "http://google.net/img/upload/photo2.png") {
if let data = NSData(contentsOfURL: url){
imageWK.setImage(UIImage(data: data))
}
}
Try this code.
Dont forget to add NSTransportSecurity in your Plist.
Swift 4.2
Using URLSession, proper GCD and #discardableResult to silence the Result of call to '...' is unused warning.
plist
App Transport Security Settings
Allow Arbitrary Loads - YES
You can set a fixed image size of 100x100 in the storyboard if you like.
let url = "https://i.imgur.com/UZbLC0Q.jpg"
public extension WKInterfaceImage {
#discardableResult public func setImageWithUrl(url:String, scale: CGFloat = 1.0) -> WKInterfaceImage? {
URLSession.shared.dataTask(with: NSURL(string: url)! as URL) { data, response, error in
if (data != nil && error == nil) {
let image = UIImage(data: data!, scale: scale)
DispatchQueue.main.async {
self.setImage(image)
}
}
}.resume()
return self
}
}
call
row.image.setImageWithUrl(url: url, scale: 1.0)