issue "unexpectedly found nil" with downloading image from firebase swift - ios

I have an issue when I'm downloading an image from firebase here is my code:
func getuser(){
FIRDatabase.database().reference().child("users").child(userID!).child("credentials").observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let name = value?["name"] as! String
let email = value?["email"] as! String
let profilePicLink = value?["profilePicLink"] as? String ?? ""
Variables.userName = name as String
Variables.userEmail = email as String
self.username.text = Variables.userName
print(profilePicLink)
// Create a storage reference from the URL
let storageRef = self.storage.reference(forURL: profilePicLink)
// Download the data, assuming a max size of 1MB (you can change this as necessary)
storageRef.data(withMaxSize: 1 * 1000 * 1000 ) { (data, error) -> Void in
let pic = UIImage(data: data!)
self.img.image = pic
}
})
}
I'm getting this error:
==> this the link as printed https://firebasestorage.googleapis.com/v0/b/eswitch-72b56.appspot.com/o/usersProfilePics%2FKYe6fIQReNM8Oog4ELOdRLsC99J3?alt=media&token=6f2392bc-d35b-4ebc-b2c1-2dc34bc4b95a
fatal error: unexpectedly found nil while unwrapping an Optional value
The error that I'm getting is in this line
let storageRef = self.storage.reference(forURL: profilePicLink)
below is my snapshot readings:
snap (credentials) {
email = "bilal#me.com";
mobile = 50955514;
name = Bilal;
profilePicLink = "https://firebasestorage.googleapis.com/v0/b/eswitch-72b56.appspot.com/o/usersProfilePics%2FKYe6fIQReNM8Oog4ELOdRLsC99J3?alt=media&token=6f2392bc-d35b-4ebc-b2c1-2dc34bc4b95a";
role = user;
}
Thanks
Here is the solution:
Added this line to func viewDidLoad()
storage = FIRStorage.storage()

You need to make sure that self.storage is initialized.
storage = FIRStorage.storage()

Related

How to retrieve the current user image and display in imageView?

I want to learn how to properly retrieve the image from Firebase for the current user.I am trying to get the user ImageUrl from the User table and use that url to display the image down below but it does not do it and crashes the app. I want to know if I am doing it properly or doing it wrong.
Thank you in advance
func retrieveTheImage() {
let userID = Auth.auth().currentUser?.uid
let retrieveTheUrl = Database.database().reference().child("User").child(userID!)
var capatureUrl :String = ""
retrieveTheUrl.observeSingleEvent(of: .value) { (snapShot) in
if let snapShotValue = snapShot.value as? Dictionary<String,String>{
let url = snapShotValue["ImageUrl"]! /
capatureUrl = url
print(capatureUrl)
}
}
let storage = Storage.storage()
var reference: StorageReference!
reference = storage.reference(forURL: capatureUrl)
reference.downloadURL { (url, error) in
let data = NSData(contentsOf: url!)
let image = UIImage(data: data! as Data )
self.imageUser.image = image
}
}
Your code actually works...almost.
The problem with the code is that Firebase is asynchronous so data only becomes valid within the closure following a firebase call.
So here's what's happening (condensed code)
func retrieveTheImage2() {
let userID = Auth.auth().currentUser?.uid
let retrieveTheUrl = Database.database().reference().child("User").child(userID!)
//code in closure//
}
//code after closure//
--> reference = storage.reference(forURL: capatureUrl) //not valid
reference.downloadURL { (url, error) in
}
}
The code after the closure will execute before the //code in closure//.
That means capatureUrl will be nil because it has not been populated yet. Code is faster than the internet.
To fix that, just move the code that accesses data from Firebase within the closure.
func retrieveTheImage2() {
let userID = Auth.auth().currentUser?.uid
let retrieveTheUrl = Database.database().reference().child("User").child(userID!)
var capatureUrl :String = ""
retrieveTheUrl.observeSingleEvent(of: .value) { (snapShot) in
if let snapShotValue = snapShot.value as? Dictionary<String,String>{
let url = snapShotValue["ImageUrl"]!
capatureUrl = url
print(capatureUrl)
let storage = Storage.storage()
var reference: StorageReference!
reference = storage.reference(forURL: capatureUrl) //will be valid here.
reference.downloadURL { (url, error) in
let data = NSData(contentsOf: url!)
let image = UIImage(data: data! as Data )
self.imageUser.image = image
}
}
}
}
In general, it takes time for data to return from the internet and that's the purpose of Firebase closures - that code executes when the data is valid. So if you want to work with Firebase data, only attempt to access it initially within those closures.

How to retrieve image from firebase storage Swift 4 ios

I am trying to retrieve an image from firebase storage but the image that I retrieve always is nil for some reason.
var ref: DatabaseReference!
var storageRef: StorageReference!
var hallData = [Hall]()
override func viewDidLoad() {
let refHandle = Database.database().reference().child("hallData").observe(DataEventType.value, with: { (snapshot) in
let postDict = snapshot.value as? [String : AnyObject] ?? [:]
 let values = Array(postDict.values)
//print(values)
let valueDict = values as! [[String:Any]]
for i in valueDict
{
var name = i["name"] as! String
var address = i["address"] as! String
var capacity = i["capacity"] as! String
var decorations = i["decorations"] as! String
var highPrice = i["highPrice"] as! String
var lowPrice = i["lowPrice"] as! String
var catering = i["catering"] as! String
var email = i["email"] as! String
self.storageRef = Storage.storage().reference().child("images").child(email)
var image: UIImage!
// Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
self.storageRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
if let error = error {
print("PLASESEE")
print(error.localizedDescription)
// Uh-oh, an error occurred!
} else {
// Data for "images/island.jpg" is returned
let image = UIImage(data: data!)
}
}
print(image)
self.hallData.append(Hall(name2: name, capacity2: capacity, lowPrice2: lowPrice, highPrice2: highPrice, catering2: catering,decorations2: decorations, address2:address, image2: image, email2: email))
}
})
}
I dont understand what I am doing wrong, I followed the api on firebase storage, checked out a lot of tutorials but I keep getting nil
The issue is that you're attempting to work with the image var outside the getData closure.
self.storageRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
if let error = error {
print("PLASESEE")
print(error.localizedDescription)
// Uh-oh, an error occurred!
} else {
// Data for "images/island.jpg" is returned
let image = UIImage(data: data!)
} <- closure ends here and image is only valid above this
}
print(image) <- image may not be populated at this point
self.hallData.append... image
}
That closure is asynchronous and the call to self.hallData.append... will occur way before the image var is populated within the closure. Code is much faster than the internet
Move that statement inside the closure, right after the let image = and it should work.
self.storageRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
if let error = error {
print("An error occurred in downloading the image")
print(error.localizedDescription)
} else {
let image = UIImage(data: data!)
self.hallData.append... image
//reload your tableView or UI as self.hallData is now valid
}
}
If you do it this way, the preceeding var image: UIImage! can be removed as it doesn't have a function. Otherwise, remove the let before let image = within the closure.

Convert observe .value to .childAdded in swift

Currently I'm having some problems with this bit of code that is loading data from firebase database into an array. Since this is inside of viewDidLoad I have to empty my array food = [] before loading the data into it, if I don't then it will duplicate all the objects and I will have double duplicates the second time it loads, triple the third time and etc... However this was not a good fix for multiple reasons so what I would like is that it would only add new objects from the database with .childAdded however if I just switch out .value with .childAdded it will crash, I get a Thread 1: signal SIGABRT on this line: let dict = user_snap.value as! [String: String?]. I am pretty new to swift and don't know how to fix this, would really appreciate some help.
let parentRef = Database.database().reference().child("Recipes")
let storage = Storage.storage()
parentRef.observe(.value, with: { snapshot in
if ( snapshot.value is NSNull ) {
// DATA WAS NOT FOUND
print("– – – Data was not found – – –")
} else {
//Clears array so that it does not load duplicates
food = []
// DATA WAS FOUND
for user_child in (snapshot.children) {
let user_snap = user_child as! DataSnapshot
let dict = user_snap.value as! [String: String?]
//Defines variables for labels
let recipeName = dict["Name"] as? String
let recipeDescription = dict["Description"] as? String
let downloadURL = dict["Image"] as? String
let storageRef = storage.reference(forURL: downloadURL!)
storageRef.getData(maxSize: 1 * 1024 * 1024) { (data, error) -> Void in
let recipeImage = UIImage(data: data!)
food.append(Element(name: recipeName!, description: recipeDescription!, image: recipeImage!))
self.tableView.reloadData()
}
}
}
})
let dict = user_snap.value as! [String: String?]
Instead of
let dict = snapshot.value as! Dictionary<String, String>
and maybe you can do null test :
let dict = snapshot.value as! Dictionary<String, String>
if let recipeName = dict["Name"] as String!, let recipeDescription = dict["Description"] as String!, let downloadURL = dict["Image"] as String! {
let storageRef = storage.reference(forURL: downloadURL)
storageRef.getData(maxSize: 1 * 1024 * 1024) { (data, error) -> Void in
let recipeImage = UIImage(data: data!)
food.append(Element(name: recipeName, description: recipeDescription, image: recipeImage!, downloadURL: downloadURL))
self.tableView.reloadData()
}
}else {
print("Error! Could not decode data")
}
try this. It should work
for child in snapshot.children.allObjects as! [FIRDataSnapshot] {
let dict = child.value as! Dictionary<String, Any>
//.....
}

Writing to DB failed?

I'm trying to write 2 data to db.
#IBAction func show(button: UIButton) {
let userRef = self.dataBaseRef.child("users/\(FIRAuth.auth()!.currentUser!.uid)")
userRef.observe(.value, with: { (snapshot) in
let user = User(snapshot: snapshot)
let currentUser = user.email
let request = Requests(requestBy: user.email!,
requestTo: self.email!)
let contactRef = self.ref.child(currentUser! + "--" + self.email!)
contactRef.setValue(request.toAnyObject())
self.items.append(request)
print(request)
}) { (error ) in
print(error.localizedDescription)
}
self.dismiss(animated: true, completion: nil);
}
items is an array
var items: [Requests] = []
user.email and self.email! isn't empty I printed it.
request file looks
struct Requests {
let key: String
let requestBy: String
let requestTo: String
let ref: FIRDatabaseReference?
init(requestBy: String, requestTo: String, key: String = "") {
self.key = key
self.requestBy = requestBy
self.requestTo = requestTo
self.ref = nil
}
init(snapshot: FIRDataSnapshot) {
key = snapshot.key
let snapshotValue = snapshot.value as! [String: AnyObject]
requestBy = snapshotValue["requestBy"] as! String
requestTo = snapshotValue["requestTo"] as! String
ref = snapshot.ref
}
func toAnyObject() -> Any {
return [
"requestBy": requestBy,
"requestTo": requestTo
]
}
}
and I'm getting error - unexpectedly found nil while unwrapping an Optional value
According to what I'm seeing in your posted code, you create a request but never assign a value to the ref property. However, it is not clear since we cannot see where it crashes or when is it really used.
Also, assuming self.ref is an optional (as you've been handled that way in your visible code) then this could be the problem:
let contactRef = self.ref.child
you should first check if you can unwrap that optional, like:
if let realRef = self.ref {
realRef.child(currentUser! + "--" + self.email!)
}
let currentUser = user.email //this is not an optional
let request = Requests(requestBy: user.email!,
requestTo: self.email!)
contactRef = self.ref.child(currentUser! + "--" + self.email!)
currentUser! doesn't need to be unwrapped.

Convert JSON to dictionary in swift 3 for uplaod image from facebook to firebase

I want to upload my profile picture from facebook to firebase and then want to retrieve...
my code was working well in swift 2.0 i-e
let urlPic = (data?.objectForKey("url"))! as! String
when I convert it to swift 3.0 i-e
let urlPic = ((data as AnyObject).object("url"))! as! String
There is an error
Cannot call Value of non-function type 'Any?!'
There is my complete code please help...
if let user = FIRAuth.auth()?.currentUser {
// User is signed in.
let name = user.displayName
let photoUrl = user.photoURL
let uid = user.uid
self.username.text = name
let data = try? Data(contentsOf: photoUrl!)
self.profilepic.image = UIImage(data: data!)
//------Saving in DB-----
let storage = FIRStorage.storage()
let storageRef = storage.reference(forURL: "my storage_url..")
let profilePicRef = storageRef.child(user.uid+"/userPic.jpg")
profilePicRef.data(withMaxSize: 1 * 1024 * 1024) { (data, error) -> Void in
if (error != nil) {
print("Unable to download the image")
} else {
if (data != nil)
{
self.profilepic.image = UIImage(data:data!)
}
}
}
if (profilepic.image == nil)
{ var profilepic = FBSDKGraphRequest(graphPath: "/me/picture", parameters: ["height":300 ,"width":300, "redirect": false], httpMethod: "GET")
profilepic?.start(completionHandler: {(connection, result, error) -> Void in
if error == nil {
let dictionary = result as? NSDictionary
let data = dictionary?.object(forKey: "data")
let urlPic = ((data as AnyObject).object("url"))! as! String
if let imageData = NSData(contentsOfURL: NSURL(string:urlPic)!) {
let profilePicRef = storageRef.child(user.uid+"/userPic.jpg")
let uploadTask = profilePicRef.putData(imageData, metadata:nil){
metadata, error in
if( error == nil){
let downloadUrl = metadata!.downloadURL
}
else { print("Error in downloading image") }
}
self.profilepic.image = UIImage(data: imageData)
}
}
})
}
} else {
// No user is signed in.
}
Thanks in advance!
In Swift use native Dictionary instead of NSDictionary and instead of object(forKey:) use subscript with Dictionary, so instead of casting result to NSDictionary cast it to [String:Any].
if error == nil {
if let dictionary = result as? [String:Any],
let dataDic = dictionary["data"] as? [String:Any],
let urlPic = dataDic["url"] as? String {
//access urlPic here
}
}
Note: In swift 3 use native Data and URL instead of NSData and NSURL.

Resources