Tarvo Mäesepp Tarvo Mäesepp - 1 month ago 7
Swift Question

Swift thinks that variable in Structure is optional

I made simple post making project based on Firebase. I save post into Firebase like this:

let data = UIImageJPEGRepresentation(newPostImageView.image!, 0.5)//Take photo from imageview
let metadata = FIRStorageMetadata()
metadata.contentType = "image/jpeg"//Define post image path

let postId = "\(currentUser.generalDetails.uid)\(NSUUID().uuidString)"//Generate postId
let imagePath = "postImages\(postId)/postPic.jpg"

storageRef.child(imagePath).put(data!, metadata: metadata) { (metadata, error) in
if error == nil {

let postRef = self.databaseRef.child("posts").childByAutoId()


let post = Post(postImageUrl: String(describing: metadata?.downloadURL()), profileImageUrl: self.currentUser.generalDetails.profileImageURL, postId: postId, content: self.newPostTextView.text, username: self.currentUser.generalDetails.userName)
postRef.setValue(post.toAnyObject())

}else {
print(error!.localizedDescription)
}
}
dismiss(animated: true, completion: nil)


currentUser.generalDetails. ...
is singleton I have.


  1. However, it sets
    "postImageUrl"
    into firebase like




"Optional(https://firebasestorage.googleapis.com/v0/b/"


I do not understand why since I do not have optionals. Is image from imagePicker optional?

Answer

I think swift works best when using if let to unwrap and test all needed preconditions for a block.

In this example you need an unwrapped image URL as a string that is not empty.

It's possible to set all of this up.

if let downloadUrl = metadata?.downloadURL() { // Unwrap the URL.
    if let imageUrl = downloadUrl.absoluteString { // URL as a string.
        if !imageUrl.isEmpty { // URL must not be empty.
        }
    }
}

Luckily, this can all be done on one line.

if let imageUrl = metadata?.downloadURL()?.absoluteString, !imageUrl.isEmpty {
}

Placed in context, this gives the following.

storageRef.child(imagePath).put(data!, metadata: metadata) { (metadata, error) in
    if let imageUrl = metadata?.downloadURL()?.absoluteString, !imageUrl.isEmpty {
        let postRef = self.databaseRef.child("posts").childByAutoId()

        let post = Post(postImageUrl: imageUrl,
                        profileImageUrl: self.currentUser.generalDetails.profileImageURL,
                        postId: postId,
                        content: self.newPostTextView.text,
                        username: self.currentUser.generalDetails.userName)

        postRef.setValue(post.toAnyObject())     
    } else if error != nil {
        print(error!.localizedDescription)
    } else {
        print("Unknown error")
    }
}

Finally, we can use a guard statement to exit early. This reduces the level of indent in the main line of code.

storageRef.child(imagePath).put(data!, metadata: metadata) { (metadata, error) in
    guard let imageUrl = metadata?.downloadURL()?.absoluteString, !imageUrl.isEmpty else {
        print(error?.localizedDescription ?? "Unknown error")
        return
    }

    let postRef = self.databaseRef.child("posts").childByAutoId()

    let post = Post(postImageUrl: imageUrl,
                    profileImageUrl: self.currentUser.generalDetails.profileImageURL,
                    postId: postId,
                    content: self.newPostTextView.text,
                    username: self.currentUser.generalDetails.userName)

    postRef.setValue(post.toAnyObject())     
}