Updating to Xcode 10 made value wrapped with "Optional()" - ios

So recently I switch from Xcode 9 to Xcode 10 for my iOS app development. the first thing I noticed that when I tried to print out a variable, the value is wrapped with optional, while in Xcode 9 it never happened. for example here is the code that I test.
let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
let build = Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as! String
let parameters = ["branch_id": loginData.profile.branchId,
"version": "\(version).\(build)",
"os_name" : "ios",
"user_id" : loginData.uid]
print(parameters)
and the output was like :
["os_name": "ios",
"branch_id": Optional("2"), "version": "1.5.8.3",
"user_id": Optional("1141")]
I've tried to force unwrap the code with exclamation mark
"branch_id": loginData.profile.branchId!,
or even better with coalescing operator
"branch_id": loginData.profile.branchId ?? "0"
It works, but I have like, 30+ lines of code with the same problem, do I need to do it one by one? Or is there a way to change this behaviour?
FYI I'm using Swift 4 for my project.
Edit : FYI this was tested on iOS 12, while before in Xcode 9 was tested in iOS 11
Edit:
To answer matt comment asking info about where loginData.profile.branchId come from, here it is.
So, the data is fetched from data model, and I use this code to fetch it:
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "User")
let fetchResults = try context.fetch(request) as? [NSManagedObject]
let loginData = fetchResults![0]
let profile = loginData.value(forKey: "profile") as! NSManagedObject
self.profile = Profile()
self.profile.branchId = profile.value(forKey: "branchId") as? String

Use Optional Unwrapping with if-let statement
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "User")
if let fetchResults = try context.fetch(request) as? [NSManagedObject]{
let loginData = fetchResults[0]
let profile = loginData.value(forKey: "profile") as! NSManagedObject
self.profile = Profile()
if let branchId = profile.value(forKey: "branchId") as? String{
self.profile.branchId = branchId
}
}
if let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String, let build = Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as? String{
let branchId = loginData.profile.branchId ?? ""
let branchId = loginData.uid ?? ""
let parameters = ["branch_id": branchId,
"version": "\(version).\(build)",
"os_name" : "ios",
"user_id" : login_tty.uid]
print(parameters)
}
Never use force unwrapping, i mean ! directly, it may result in crash,
instead safely unwrap using if let and guard let

if you are printing an optional value Xcode prints the value warapped with the word optional("value").
if you want to avoid this you must upwarapped the value.
you have 3 ways to do that:
the careful way, use guard let or if let:
if let branchId = profile.value(forKey: "branchId") as? String {
//here branchId != nil
}
guard let branchId = profile.value(forKey: "branchId") as? String else { return }
the force unwarapped way:
let branchId = profile.value(forKey: "branchId") as! String
on that way, if the value is nil the app will crash so be careful
use default value:
let branchId = profile.value(forKey: "branchId") as? String ?? "default value"

I ended up using coalescing operator which is ?? "" and implemented it my code like:
var parameters = [String: Any]()
if loginData.profile != nil {
if loginData.profile.branchId != "" {
let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
let build = Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as! String
parameters = ["branch_id": loginData.profile.branchId ?? "",
"version" : "\(version).\(build)",
"os_name" : "ios",
"user_id" : loginData.uid ?? ""
]
}
}

Related

How to check nil for a property already forcefully downcasts [duplicate]

This question already has answers here:
Initializer for conditional binding must have Optional type, not '[String : Any]'
(2 answers)
Closed 2 years ago.
I have a simple problem but couldn't find a proper solution. I have a swift code that looks like this.
let id = dic["id"] as! String
I need to check if dic["id"] is nil. I can check for nil like this
if let safeId = dic["id"]{
let id = safeId as! String
}
But the problem is I have a number of values to be unwrapped and doing the above step for every property seems impractical. I would like to have something like below but it doesn't work because the downcasting always returns a value so it is not optional hence cannot be unwrapped.
if let snap = child as! DataSnapshot,
let dic = snap.value as! [String : Any],
let firstName = dic["first_name"] as! String,
let lastName = dic["last_name"] as! String,
let image = dic["user_image"] as! String,
let id = dic["id"] as! String{
/* My code */
}
This method gives an error called Initializer for conditional binding must have Optional type, not 'String'
I'm not an advanced developer, please help me to figure out this problem.
Replace all ! with ?, if let unwraps optionals
if let snap = child as? DataSnapshot,
let dic = snap.value as? [String : Any],
let firstName = dic["first_name"] as? String,
let lastName = dic["last_name"] as? String,
let image = dic["user_image"] as? String,
let id = dic["id"] as? String{
/* My code */
}
Your I can check for nil example is bad practice, too. It's supposed to be
if let safeId = dic["id"] as? String {
let id = safeId
}
Please read (the section Optionals in) the Language Guide
You should be doing optional casting here, if you are force casting there is no point in putting it inside an if let block.
if let snap = child as? DataSnapshot,
let dic = snap.value as? [String : Any],
let firstName = dic["first_name"] as? String,
let lastName = dic["last_name"] as? String,
let image = dic["user_image"] as? String,
let id = dic["id"] as? String{
/* My code */
}

getting array of child values from firebase database

database structure
I am trying to get an array of users(strings) from a comment, but am having trouble since it is kinda deep in my database, here is the implementation I've tried, it is returning 0 for the count.
REF_FEEDMESSAGES.child(messageKey).child("comments").observeSingleEvent(of: .value) { (commentSnapshot) in
guard let commentSnapshot = commentSnapshot.children.allObjects as? [DataSnapshot] else {return}
for comment in commentSnapshot {
let theComment = comment.value as? [String: Any]
let theContent = theComment?["content"] as? String ?? ""
let theIcon = theComment?["icon"] as? String ?? ""
let theColor = theComment?["color"] as? String ?? ""
let theDate = theComment?["date"] as? String ?? "0"
let theName = theComment?["userName"] as? String ?? ""
let theVerified = theComment?["isVerified"] as? String ?? "no"
let profileImageURL = theComment?["profileImageURL"] as? String ?? ""
let postedBy = theComment?["postedBy"] as? String ?? ""
let likes = theComment?["likes"] as? String ?? ""
let key = comment.key
let likers = comment.childSnapshot(forPath: "likedBy").value as? [String] ?? []
print(likers.count)
guard let likers = comment.childSnapshot(forPath: "likedBy").children.allObjects as? [DataSnapshot] else {return}
for like in likers {
let theLike = like.value as? [String:Any]
print(theLike!["user"] as? String ?? "")
commentLiked.append(theLike!["user"] as? String ?? "")
}

Unwrapping a optional nil when getting value out of a firebase snapshot dictionary in swift 4.0

I am having trouble unwrapping a optional nil when getting value out of a firebase snapshot dictionary in swift 4.0
Here's my code
Database.database().reference().child("questionPosts").queryOrderedByKey().observe(.childAdded) { (snapshot) in
if let dict = snapshot.value as? NSDictionary {
//var questionName = dict["name"] as! String
//var created_by = dict["email"] as! String
let questionTitle = dict["name"] as? String
let created_by = dict["email"] as? String
let question = Question(questionName: questionTitle!, created_by: created_by!)
self.questions.append(question)
print(self.questions.count)
}
}
And when I run this it gives me an error saying:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an
Optional value
I am also writing this code in swift 4.0 in Xcode 9.0
could anyone please help I have been banging my head on this for weeks
so help would be really appreciated
Just replace below code with your code.
let questionTitle = dict["name"] as? String ?? ""
let created_by = dict["email"] as? String ?? ""
It will work for you.
If you need those 2 values to continue executing, I would suggest using guard then you'll be able keep them in a line and avoid indents for ease of readability.
defer { print(self.questions.count) }
guard let dict = snapshot.value as? NSDictionary else { return }
guard let questionTitle = dict["name"] as? String else { return }
guard let created_by = dict["email"] as? String else { return }
let question = Question(questionName: questionTitle, created_by: created_by)
self.questions.append(question)

Setting the value in NSDictionary from JSON response

I have parsed the JSON data, now while setting the JSON to my NSDictionary there might be certain keys in the JSON that might not be present. I want to check if the key is present in the JSON parsed to me and if not, set blank field as value to the key in the `NSDictionary' object.
jsonData = ["id": ((json["idnumber"]) as? String!)!,
"Name": ((json["name"]) as? String!)!,
"Roll Number": ((json["rollnumber"]) as? String!)!,
"Class": ((json["class"]) as? String!)!,
"Marks": ((json["marks"]) as? String!)!,
"Gender": ((json["gender"]) as? String!)!]
So in the above case, the marks field may or may not be present in the JSON. I want to check this and assign respective value to the corresponding key in jsonData.
I tried using "??", but it takes lot of compilation time.
You can use if let or guard for conditional optional wrapping for that like this.
var jsonData = [String : String]
if let num = json["idnumber"]) as? String {
jsonData["id"] = num
}
else {
jsonData["id"] = num
}
and so on for others.
Edit: You can also try like this.
var id = name = rollNum = class = ""
if let num = json["idnumber"]) as? String {
id = num
}
if let sname = json["name"]) as? String {
name = sname
}
...
Then at last create Dictionary
var jsonData = ["id": id, "name": name, ...]
You can set the value if present otherwise blank string (""). This will prevent crash. Try below code:-
let idValue = dictionary["idnumber"] as? String ?? ""
let name = dictionary["name"] as? String ?? ""
let rollNumber = dictionary["rollnumber"] as? String ?? ""
let className = dictionary["class"] as? String ?? ""
let marks = dictionary["marks"] as? String ?? ""
let gender = dictionary["gender"] as? String ?? ""
let jsonDic : NSMutableDictionary = NSMutableDictionary()
jsonDic.setObject(idValue, forKey: "id")
jsonDic.setObject(name, forKey: "name")
jsonDic.setObject(className, forKey: "class")
jsonDic.setObject(rollNumber, forKey: "rollNumber")
jsonDic.setObject(marks, forKey: "marks")
jsonDic.setObject(gender, forKey: "gender")

parse nested JSON attributes in swift

Having trouble which is probably so minor but my searches turn up nothing. I have a json model as follows :
//quick syntax to give you an idea of the model
{user:
{"name": "",
"password": "",
"medium" : {
{"title":"",
{"description":""}}}
I'm getting the above data model from a GET request to user and it returns all the info above but when i try to parse the "medium" information such as "title" & "description" I'm having no luck. I get these responses in Xcode that say
"Value of object 'AnyObject' not unwrapped, did you mean to use ! or ?"
and then when i click on the round red Xcode message to fix it it places !s and ?s everywhere but the error remains. Here is my parse method which worked perfectly fine when I was parsing only from the "medium". Any idea what I'm doing wrong?
a portion of the parse method where i get the same error for each attribute:
all lines with the same error indicated by *
// Parse JSON data
let jsonMedium = jsonResult?["user"] as! [AnyObject] //media where user is
for jsonMedia in jsonMedium {
let media = Media()
*media.title = jsonMedia["medium"]["title"] as! String
*media.description = jsonMedia["medium"]["description"] as! String
*media.category = jsonMedia["medium"]["category"] as! String
*media.image = jsonMedia["medium"]["image"] as! String
*if let IDer = jsonMedia["medium"]["id"] as? Int{
var stringIder = String(IDer)
media.identifierString = stringIder
}
Still no luck with anything. I don't understand why it works with regular JSON but Xcode won't accept anything when I try to obtain nested. All of your help has been appreciated. In the meantime here's the full method if it helps any further
func parseJsonData(data: NSData) -> [Media] {
var medium = [Media]()
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
// Parse JSON data
let jsonMedium = jsonResult?["media"] as! [AnyObject]
for jsonMedia in jsonMedium {
let media = Media()
media.title = jsonMedia["medium"]["title"] as! String
media.description = jsonMedia["medium"]["description"] as! String
media.category = jsonMedia["medium"]["category"] as! String
media.image = jsonMedia["medium"]["image"] as! String
if let IDer = jsonMedia["medium"]["id"] as? Int{
var stringIder = String(IDer)
media.identifierString = stringIder
}
medium.append(media)
}
} catch {
print(error)
}
return medium
}
let json = [
"user" : [
"name" : "My Name",
"password" : "My Password",
"medium" : [
"title" : "My Title",
"description" : "My Description"
]
]
]
if let userJson = json["user"] as? [String : AnyObject] {
if let name = userJson["name"] as? String {
print("name: \(name)")
}
if let password = userJson["password"] as? String {
print("password: \(password)")
}
if let mediumJson = userJson["medium"] as? [String : AnyObject] {
if let title = mediumJson["title"] as? String {
print("title: \(title)")
}
if let description = mediumJson["description"] as? String {
print("description: \(description)")
}
}
}
Maybe it helps
let request : ASIFormDataRequest = ...your request
if request.responseString() != nil {
var jsonResponse : Dictionary<String, AnyObject>?
do{
jsonResponse = try NSJSONSerialization.JSONObjectWithData(request.responseData(), options: NSJSONReadingOptions.AllowFragments) as? Dictionary<String, AnyObject>
} catch _ {
//some error
}
}
FIXED IT! Took an entire day of deep thought and google/youtube/stack/brainstorming and your help but it was one changed line that got the whole thing going
// Parse JSON data
let jsonMedium = jsonResult?["user"]!["medium"] as? [AnyObject]
for jsonMedia in jsonMedium! {
let media = Media()
media.title = jsonMedia["title"] as! String
media.description = jsonMedia["description"] as! String
instead of :
let jsonMedium = jsonResult?["user"] as! [AnyObject] //media where user is
for jsonMedia in jsonMedium {
let media = Media()
*media.title = jsonMedia["medium"]["title"] as! String
*media.description = jsonMedia["medium"]["description"] as! String

Resources