Updating to Xcode 10 made value wrapped with “Opti

2019-08-20 12:55发布

问题:

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

回答1:

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



回答2:

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:

  1. 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 }
    
  2. 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

  1. use default value:

    let branchId = profile.value(forKey: "branchId") as? String ?? "default value"



回答3:

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 ?? ""
                      ]
    }
}