Thread 1: EXC_BAD_ACCESS Trying to upload images to Firebase - firebase

I am trying to upload images to Firebase like this:
let storageRef = Storage().reference()
if let uploadData = self.profileImageView.image!.pngData() {
storageRef.putData(uploadData, metadata: nil, completion: { (metadata, error) in
if error != nil {
print(error as Any)
return
}
print(metadata as Any)
})
}
and it's redirecting me to this code here:
- (void)dispatchAsync:(void (^)(void))block {
dispatch_async(self.dispatchQueue, block);
}
It does not get past the if let statement. (if let uploadData = self.profileImageView.image!.pngData())
I have no idea why. It does not give me any additional error messages in the console.

The answer by king_T did not work for me. The issue is related to this line
Storage().reference()
As noted in this post you should use
Storage.storage().reference()
It's very unintuitive.

I just had similar issues, and I solved it by compressing my image.
let scaledimage = self.profileImageView.image!.jpegData(compressionQuality: 0.5)
let storageRef = Storage().reference()
if let uploadData = scaledimage {
storageRef.putData(uploadData, metadata: nil, completion: { (metadata, error) in
if error != nil {
print(error as Any)
return
}
print(metadata as Any)
})
}
that solved it for me.

Related

SwiftUI: upload multiple images to Firebase

Goal: upload 3 images from SwiftUI app to Firebase, with different URL for each.
Problem: I only managed to upload 1.
What I have tried (but didn't work)....
:
storagePostRef.putData(image1, image2, image3, metadata: metadata) { (storageMetadata, error) in
Full function bellow:
static func savePostPhoto(
//id
userId: String,
image1: Data,
image2: Data,
image3: Data,
// imagesArray : [Data],
metadata: StorageMetadata,
storagePostRef: StorageReference,
onSuccess: #escaping() -> Void,
onError: #escaping(_ errorMessage: String) -> Void)
{
let arrayOfImages : [Data] = [image1, image2, image3]
//image storage
storagePostRef.putData(image1, metadata: metadata) { (storageMetadata, error) in
if error != nil {
onError(error!.localizedDescription)
return
}
//image URL
storagePostRef.downloadURL { (url, error) in
let image1 = url?.absoluteString
let image2 = url?.absoluteString
let image3 = url?.absoluteString
}
}
}
Each call to putData stores a single image, in the location that you call putData on.
So if you want to store three separate images, you'll have to call putData on three difference StorageReference objects. To then get the three download URLs, you call downloadURL on each of the three StorageReference objects too.
storagePostRef1.putData(image1, metadata: metadata) { (storageMetadata, error) in
storagePostRef1.downloadURL { (url1, error) in
let image1 = url?.absoluteString storagePostRef2.putData(image2, metadata: metadata) { (storageMetadata, error) in
storagePostRef2.downloadURL { (url2, error) in
storagePostRef3.putData(image3, metadata: metadata) { (storageMetadata, error) in
storagePostRef3.downloadURL { (url3, error) in
You can probably clean this up a bit, by creating your own helper function that handles the calls to putData and downloadUrl with a single closure/callback.
This works pretty well.
var photoArrayModel = PhotoArrayModel(photoArray: [])
let userPhotosFirstoreRef = self.ref.document(uid)
imagesData.enumerated().forEach { index, imageData in
let userPhotosStorageRef = self.storageRoot.child("user_photos").child(uid).child("image_\(index)")
userPhotosStorageRef.putData(imageData, metadata: nil) { metaData, error in
if error != nil {
promise(.failure(.uploadingPhoto))
}
userPhotosStorageRef.downloadURL { url, error in
if error != nil {
promise(.failure(.uploadingPhoto))
}
guard let urlString = url?.absoluteString else { return promise(.failure(.uploadingPhoto))}
let photo = PhotoModel(ownerID: uid, imageURL: urlString, timeStamp: Date().millisecondsSince1970)
photoArrayModel.photoArray.append(photo)
if photoArrayModel.photoArray.count == imagesData.count {
do {
try userPhotosFirstoreRef.setData(from: photoArrayModel.self)
promise(.success(()))
} catch {
promise(.failure(.uploadingPhoto))
}
}
}
}
}

SwiftUI Firebase - How to query a document then update?

Trying to query a document and then update it in a function in my ViewModel. Trying something like the below, but it doesn't work. Any advice would be appreciated!
func addToFruits(name: String) {
db.collection("fruits").whereField("name", isEqualTo: name)
.getDocument()
.limit(to: 1)
.updateData(["fruits": FieldValue.arrayUnion([name])])
}
func addToRoutine(routine: String, habit: String) {
db.collection("routines").whereField("name", isEqualTo: routine).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
document.updateData(["habits": FieldValue.arrayUnion([habit])])
}
}
}
}
In the first one, error I get is "Value of type 'Query' has no member 'getDocument'" and not sure how to resolve this. Second one, error I get is "Value of type 'QueryDocumentSnapshot' has no member 'updateData'"
It's not exactly clear what you're attempting to update but here's some quick example code that will read in a user named 'Steve' and update his age to 50. Keep in mind this will read in the FIRST user named 'Steve' and update their age - then when it's run again, will read the NEXT Steve etc etc - that may be what your attempting to do.
func readUsersAndUpdateTheirAgeTo50() {
let users = self.db.collection("users") //self.db points to *my* firestore
users.whereField("name", isEqualTo: "Steve").limit(to: 1).getDocuments(completion: { querySnapshot, error in
if let err = error {
print(err.localizedDescription)
return
}
guard let docs = querySnapshot?.documents else { return }
for doc in docs {
let docId = doc.documentID
let name = doc.get("name")
print(docId, name)
let ref = doc.reference
ref.updateData(["age": 20])
}
})
}
If I just wanted to update all Steve's age to 50, remove the limit
.limit(to: 1)
Note this code is kind of sloppy as since there is a limit of 1, we wouldn't need the loop. Also note that not every Steve is 50 so there should be additional parameters to narrow down which Steve it is - like a uid for example.

Firebase SwiftUI and Firebase Auth - not reading user ID?

Below is the code for my signup page. I want to make it so that when someone creates an account on the sign up page, I create a document in the users collection and include uuid in the document. However, session.session?.uid ends up being nil. Does anyone know why this is?
struct SignUpView: View {
#State var email = ""
#State var password = ""
#State var name = ""
#State var error = ""
#EnvironmentObject var session: SessionStore
func signUp() {
let db = Firestore.firestore()
let user = db.collection("users").document()
let test = db.collection("users").document(user.documentID).collection("routines").document()
session.signUp(email: email, password: password) { (result, error) in
if let error = error {
self.error = error.localizedDescription
print("This is the error \(error)")
return
} else {
self.email = ""
self.password = ""
}
}
user.setData(["id": user.documentID, "email": email]) { (err) in
if err != nil {
print((err?.localizedDescription)!)
return
}
}
print(session.session?.uid)
test.setData(["id:": test.documentID, "msg": "samwell Tarly", "uuid": session.session?.uid]) { (err) in
print("ummmmm test data?")
if err != nil {
print((err?.localizedDescription)!)
return
}
}
}
The Firebase APIs are asynchronous, simply because they access a remote system, across the internet, which takes a little time. The same applies for accessing the local disk, by the way. This blog post explains this in more detail.
Consequently, session.signUp is an asynchronous process. I.e. the call to print(session.session?.uid) is executed before session.signUp returns. Thus, session.session?.uid is still nil.
To work around this, you can nest your calls like this:
session.signUp(email: email, password: password) { (result, error) in
if let error = error {
self.error = error.localizedDescription
print("This is the error \(error)")
return
}
else {
self.email = ""
self.password = ""
user.setData(["id": user.documentID, "email": email]) { (err) in
if err != nil {
print((err?.localizedDescription)!)
return
}
}
}
}
Generally speaking, I would strongly recommend to not perform so much logic in your views, but instead keep your views as anaemic as possible - meaning: put all your logic into view models, and bind the view to the view models by using Combine. This will make your code much cleaner, easier to test, and maintainable.
See https://peterfriese.dev/replicating-reminder-swiftui-firebase-part2/ for how to do this.

Human Face Recognition Variable (google-cloud-vision)

I am working on implementing human face recognition into an iOS application. I receive back many tags like 'glasses' or 'smiling' but don't see an actual variable that tells me it is a human face (and with what degree of confidence).
What variable am I missing and how can we use that functionality?
I think that you may not using the correct feature type as it is seems that you are getting labels instead of facial attributes.
I recommend you to check the Detecting Faces and Face Detection Tutorial documentation where you can find detailed information and some useful examples that you can use as a reference to know more about the process of detecting faces with Vision API.
You can follow few steps to detect faces from an image.
Create your URLRequest
func createRequest() -> URLRequest? {
// Create your request URL
if let url = URL(string: "YOUR_API_KEY") {
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue(Bundle.main.bundleIdentifier ?? "", forHTTPHeaderField: "X- Ios-Bundle-Identifier")
let jsonRequest = [
"requests": [
"features": [
[
"type": "FACE_DETECTION",
"maxResults": 10 //change as per your requirement
]
]
]
]
let jsonData = try? JSONSerialization.data(withJSONObject: jsonRequest)
request.httpBody = jsonData
return request
}
return nil
}
Run the request in background thread
let task: URLSessionDataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "")
return
}
print(data)// Analyze with this data
}
task.resume()
Analyze data (on main thread if you want to update any UI component)
DispatchQueue.main.async(execute: {
do {
guard let json =
try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { return }
guard let responses = json["responses"] as? NSArray else { return }
if responses.count > 0 {
guard let response = responses.firstObject as? [String: Any] else { return }
guard let faceAnnotations = response["faceAnnotations"] as? NSArray else {
print(false, "No face detected, please try another photo.")
return
}
if faceAnnotations.count > 0 {
print("Face successfully detected: \(faceAnnotations.count)")
} else {
print("No face detected, please try another photo.")
}
} else {
print("Error while face detection process, please try again.")
}
} catch {
print("Error while face detection process, please try again.")
}
})

How to by pass Firebase Swift 3 Ambiguous reference to member 'observe(_:with)' error?

I know this question has been asked in the past but, I am unable to find a solution that works for swift 3. Could someone please point me in the right direction. Here is my code:
ref.child(uid).child("flights").observe(.value, with:{ (snapshot: FIRDataSnapshot!) -> Void in
self.messages.append(snapshot)
let row = [IndexPath(row: self.messages.count-1, section: 0) ]
print(snapshot)
self.flightTableView.insertRows(at: row, with: .automatic)
DispatchQueue.main.async {
self.flightTableView.reloadData()
}
})
}
I broke the code up into two statements and it started to work.
let child:FIRDatabaseReference = ref.child(uid).child("flights")
child.observe(.childAdded) { (snapshot:FIRDataSnapshot) in
print(snapshot)
}
Swift 3 & Firebase 3.17.0
This will do the trick, try this code
let ref = FIRDatabase.database().reference().child("uid").child("flights")
ref.observe(.value, with: { (snapshot) in
print("Success get the snapshot \(snapshot)")
// do something with snapshot than
DispatchQueue.main.async {
self.yourTableView.reloadData()
}
}) { (error) in
print("Failed get the snapshot \(error.localizedDescription)")
// do something to handle error
}

Resources