Firebase Storage Image Download [duplicate] - ios

This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 6 years ago.
I am trying to download image from Firebase storage.
func downloadThumbnail(thumbnail: String) -> URL {
var thumb: URL!
let _ = DataService.dataService.TAG_PHOTO_REF.child("\(thumbnail)").downloadURL { (thumbnailUrl, error) in
if error != nil {
print(error?.localizedDescription as Any)
} else {
thumb = thumbnailUrl
}
}
return thumb
}
cell.photo.kf.setImage(with: downloadThumbnail(thumbnail: selectedTag.thumbnail))
When I run this code I got
fatal error: unexpectedly found nil while unwrapping an Optional value
with return thumb line.
But if I run only print(thumbnailUrl) instead of return, it prints correct thumbnail url. Can anyone know why I got this error?
Thanks.

You can not guarantee that thumb will never be nil. Because of this, you should not be using !. Because you have no control over it and have not set it manually, you need to make it an optional.
var thumb: URL?
Secondly, you have an internet call. You are returning thumb before you get a response from that call, because of that, thumb is nil, but you told us with the ! that that is impossible, so you crash.
If you put in breakpoints, you should notice that you will hit return thumb on your method before you hit the if error != nil line. You can't use a return for this, because the method will always return before it gets a response from firebase, so your URL will always be nil. I would instead send a URL in a completion.
I haven't checked the firebase code, but if all is right with it, this is the order you want.
So:
func downloadThumbnail(thumbnail: String,withCompletion comp: #escaping (URL?, Error?) -> ()) {
let _ = DataService.dataService.TAG_PHOTO_REF.child("\(thumbnail)").downloadURL { (thumbnailUrl, error) in
if error != nil {
print(error?.localizedDescription as Any)
comp(nil, error)
} else {
comp(thumbnailUrl, nil)
}
}
}
So when you call it somewhere else:
func getMyImage(cell: UITableViewCell) {
downloadThumbnail(thumbnail: selectedTag.thumbnail) { (thumbnailUrl, error) in
if error != nil {
//show some sort of alert for the user here? or do something to handle the error?
} else {
//the url is an optional URL, so check to make sure it isn't nil
if let url = thumbnailUrl {
cell.photo.kf.setImage(with: url)
} else {
//you didn't get an error from your firebase response
//but the thumbnail url it gave you is broken for some reason
//so again, do something about your error here
}
}
}
If this doesn't match the design pattern of your app, let me know. I assumed you were using a tableview and that these methods might be in different classes.

Related

LAContext evaluatePolicy not showing TouchID prompt

func authenticateBiometry(completion: #escaping ErrorHandler) {
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: " ") { success, error in
guard let error = error else {
if success {
completion(nil)
}
return
}
completion(error)
}
}
But it prompts for touchId/faceId only the first time. What can I do to ask for it for example every time when I tap button? Let's say every 15 seconds.
Just tested locally and it works for me, this is the only way I found. I saw your comment above but I will put an answer here because probably someone will not find it ugly haha :).
I took some time to google some kind of reset method in LAContext class, but didn't find anything.
The solution was to reset the LAContext at the beginning of the method called on button tap:
func authenticateBiometry(completion: #escaping ErrorHandler) {
context = LAContext() //THIS, implying that context is declared as `var`
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: " ") { success, error in
guard let error = error else {
if success {
completion(nil)
}
return
}
completion(error)
}
}
You will be able to prompt face/touch ID on each button click, as soon as one ends.

Firebase runTransactionBlock() deletes node

I am using Firebase SDK (2.4.2) for iOS. The following code is part of the didSelectPost() function used in SLComposeServiceViewController object. It's very important to indicate that this behavior is not the same when used in a regular UIViewController (weird I know).
Assuming the following basic code:
override func presentationAnimationDidFinish() {
// Retrieving content and identifying type here
}
override func didSelectPost() {
self.myRootRef.runTransactionBlock({
(currentData:FMutableData!) in
currentData.value = "test"
// Finalizing the transaction
return FTransactionResult.successWithValue(currentData)
}
)
self.extensionContext!.completeRequestReturningItems([], completionHandler: nil)
}
When the transaction is complete node_value disappears completely from Firebase. The value is not set and the node is deleted. This is a very weird and unexpected behavior!
On the other hand, the following code works as expected.
override func didSelectPost() {
self.myRootRef.setValue("test", withCompletionBlock: {
(error:NSError?, ref:Firebase!) in
if (error != nil) {
print("Data could not be saved.")
} else {
print("Data saved successfully!")
}
})
self.extensionContext!.completeRequestReturningItems([], completionHandler: nil)
}
This problem did not exist a couple of days ago, last I ran the code above. Any insights as to what the problem is?
If you set nil to a path in a Firebase database, it will do the same as a .removeValue().
When running a transaction you should check for nil. Since transactions can be called with nil if no default value was written.
myRootRef.runTransactionBlock({
(currentData:FMutableData!) in
var value = currentData.value as? Int
if value == nil {
value = 0
}
currentData.value = value! + 1
return FTransactionResult.successWithValue(currentData)
})
Read the Firebase Docs for more information.

deleteInBackgroundWithBlock always return true

I've been delving into swift and using the Parse SDK and was wondering if anyone could shed some light on the following:
I am trying to delete an object in the Parse DB, and have set the method up to fail - but it fails to fail.
func destroy(onComplete: Bool -> Void) {
let object = PFObject(className: "ClassName")
object.deleteInBackgroundWithBlock({
(success: Bool, error: NSError?) -> Void in
NSLog("Error: \(error)")
if let error = error {
onComplete(false)
NSLog("Error: \(error)")
} else {
onComplete(success)
}
})
}
When I set the objectId property the object is destroyed fine, but here I am omitting it so, obviously, nothing is destroyed, but when the closure runs, success is always equal to true, and error is always nil.
Does anyone know if this is intended behaviour, because if no object is destroyed, surely, either success should equal false, or error should be non-nil?
Thanks
Paul

Firebase runTransactionBlock() in iOS share extension

My share extension has the following code as part of the didSelectPost() segment:
override func didSelectPost() {
if self.sharedURL != nil {
// Send data to Firebase
self.myRootRef.runTransactionBlock({
(currentData:FMutableData!) in
var value = currentData.value as? String
// Getting the current value
// and checking whether it's null
if value == nil {
value = ""
}
// Setting the new value to the clipboard
// content
currentData.value = self.sharedURL?.absoluteString
// Finalizing the transaction
return FTransactionResult.successWithValue(currentData)
}, andCompletionBlock: {
// Completion Check
(error:NSError!, success:Bool, data:FDataSnapshot!) in
print("DEBUG- We're done:\(success) and \(error)")
}
)
}
// This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
self.extensionContext!.completeRequestReturningItems([], completionHandler: nil)
}
I'm getting the following error at runtime:
host connection <NSXPCConnection: 0x7fb84af2e8c0> connection from pid 16743 invalidated
I believe this error is due to the andCompletionBlock and related to the following issue: Debug info when run today extension
How can I cleanly and successfully deal with the completion status of the above transaction?
Like the answer you linked to stated, the NSXPCConnection error doesn't matter here.
The issue is that .runTransactionBlock() is asynchronous and .completeRequestReturningItems() will get called and exit the extension before you ever get a value from your Firebase database.
Try running .completeRequestReturningItems() in the andCompletionBlock.
override func didSelectPost() {
if self.sharedURL != nil {
// Send data to Firebase
self.myRootRef.runTransactionBlock({
(currentData:FMutableData!) in
var value = currentData.value as? String
// Getting the current value
// and checking whether it's null
if value == nil {
value = ""
}
// Setting the new value to the clipboard
// content
currentData.value = self.sharedURL?.absoluteString
// Finalizing the transaction
return FTransactionResult.successWithValue(currentData)
}, andCompletionBlock: {
// Completion Check
(error:NSError!, success:Bool, data:FDataSnapshot!) in
self.extensionContext!.completeRequestReturningItems([], completionHandler: nil)
}
)
}
}

Parse Swift Update Errors

After the recent Swift update, I have been trying to debut a few lines of code and don't seem to be understanding what's wrong..
The lines are
PFGeoPoint.geoPointForCurrentLocationInBackground {
with the error message "Cannot invoke 'geoPointForCurrentLocationInBackground' with an argument list of type '((PFGeoPoint", NSError!) -> Void)'"
The second line is
PFUser.logInWithUsernameInBackground(username:usernameTextField.text, password:passwordTextField.text, target: self) {
With the error "Extra argument 'target' in call"
I've tried looking online and debugging these, but I honestly have no idea what's going on. It seems to be an error in the parse code and I'm not sure why that is...
Edit: I fixed second error I was having. code:
PFUser.logInWithUsernameInBackground(usernameTextField.text, password:passwordTextField.text) {
Start from Swift 1.2, the Failable Casts is introduced. you can use the PFGeoPoint.geoPointForCurrentLocationInBackground method like the following:
If you're quite sure that the downcasting will succeed, you can use as! to force the cast:
PFGeoPoint.geoPointForCurrentLocationInBackground {
(point:PFGeoPoint!, error:NSError!) -> Void in
if (error == nil) {
println(point)
} else {
println(error)
}
}
If you're not sure if the casting will succeed, just use the as? operator. By using as?, it returns an optional value, but in case the downcasting fails, the value will be nil.
PFGeoPoint.geoPointForCurrentLocationInBackground {
(point:PFGeoPoint?, error:NSError!) -> Void in
if (error == nil) {
if let myPoint = point {
println(myPoint)
}
} else {
println(error)
}
}

Resources