Issues retrieving Branch deep link parameters in Swift - ios

I'm having trouble with this line:
let isBranchLinkKey = "+clicked_branch_link"
guard let didClickBranchLink = params[isBranchLinkKey] as? Int else { return }
didClickBranchLink is nil even though in the debugger the parameter is there.
Below is a screenshot of my debugger:
.

As mentioned in the edited screenshot, paramBranchLink is of a boolean value(Notice __NSCFBoolean in debugger on left side) but you are trying to cast as Int in guard statement. Since in swift both are two different types, the guard statement will fail. You should change your guard statement to get it as Bool value.
guard let didClickBranchLink = params[isBranchLinkKey] as? Bool else { return }
This should give a boolean value in didClickBranchLink which you can use it in your later part of the code.

Related

Failed to get the 'some' field from optional

astoundingly, typing this error into the Stack Overflow search returns no results that actually mention this error 0.0
So I have an Event object in my Swift IOS App which is saved as a document on Firestore that looks like this
The start and end fields are Timestamps.
Over on xcode when the Event collection is queried, the results are decoded into Events with this initialiser
init(document: DocumentSnapshot) {
self.id = document.documentID
let d = document.data()
self.title = d!["title"] as? String
let stamp = d!["start"] as? Timestamp
let estamp = d!["end"] as? Timestamp
self.start = stamp?.dateValue()
self.end = estamp?.dateValue()
/*
There is a breakpoint here!
*/
self.creator = d!["user"] as? String
self.isAllDay = (d!["isAllDay"] as? Bool)!
self.isPrivate = d!["isPrivate"] as! Bool
self.count = (d!["count"] as? String)!
self.date = d?["day"] as? String
self.month = d?["month"] as? String
self.year = d?["year"] as? String
self.bridgesDays = doesEventBridgeDays()
//MARK: re-implement these functions
isInvitee()
}
I've just swapped this over from using Strings to Timestamps and i now have unexpectedly found nil errors for the start and end fields on the app.
a breakpoint shows me this:
As you can see, the start and end fields now say Failed to get the 'some' field from optional start/end (start and end are now both Date objects)
I either don't understand what i'm reading online, or there are no questions/blog posts etc about this on the internet so
what does this mean?
and how do I fix it?
happy to answer any further questions to help resolve this issue :)
Thanks :)
Extra information*
This is failing because your Event object in code is trying to store your 'start' and 'end' properties as a Date? but you're retrieving them from Firebase Firestore as a Timestamp.
You'll need to do an intermediate step to unwrap the values and then get the date object.
if let end = data["end"] as? Timestamp {
self.end = end.dateValue()
}
Also, you should really be unwrapping the document.data() safely to avoid using the d! and risking a crash.
if let data = document.data(){
// extract values
}
Finally, it may be worth reading the document on sending / retrieving custom objects with Firestore. It makes everything a lot easier.
Firestore.firestore().collection("Event").document(id).getDocument { (document, error) in
if error != nil {
print(error?.localizedDescription as Any)
}
guard let document = document else {
// failed to unwrap document
return
}
if document.exists {
let result = Result {
try document.data(as: Event.self)
}
switch result {
case .success(let event):
if let event = event {
// do something with Event object
} else {
// failed to unwrap Event
}
case .failure:
// failed to form Event
}
} else {
// document doesn't exist
}
}

Value of optional type 'String?' must be unwrapped to a value of type 'String' to an array?

Total new beginner here in Swift, I'm trying to understand typecasting here
I have the below code
#IBAction func randomImage(_ sender: Any) {
let path = Bundle.main.path(forResource: "imageList", ofType: "plist")
let dict = NSDictionary(contentsOfFile: path!)
let data = dict!.object(forKey: "Images") as! [String]
imageView.image = UIImage(named: data.randomElement())
}
As shown above, first I have dict! to ensure that dict is available, then data will be typecasted into [String] which is an array of string.
Now the part that I dont understand is why data.randomElement() giving me error
Value of optional type 'String?' must be unwrapped to a value of type 'String'
Coalesce using '??' to provide a default when the optional value contains 'nil'
Force-unwrap using '!' to abort execution if the optional value contains 'nil'
sure enough based on the suggestion, i can get away with data.randomElement()!, but why is that needed?
The randomElement() function returns an Optional because the collection you are fetching from might be empty. if it is, the function returns nil.
Get out of the habit of using the ! force-unwrap operator (and variants like implicitly unwrapped optionals.) I call ! the "crash-if-nil" operator. That's what it does.
You should rewrite your code using if let syntax (optional binding)
#IBAction func randomImage(_ sender: Any) {
if let path = Bundle.main.path(forResource: "imageList", ofType: "plist"),
let dict = NSDictionary(contentsOfFile: path),
let data = dict.object(forKey: "Images") as? [String],
let image = UIImage(named: data.randomElement()) {
imageView.image = image
} else {
// Unable to load image
}
}
With a compound if let like that the expression quits on each step if the result is nil. If the result is valid, it keeps going on the if statement and the new temporary variable is now a non-optional.
Once you make it through the last let in the compound if let, you have a UIImage in image that you can install into your image view.
If any of the steps fail, the else clause runs. you can do whatever you want there - install a default image, install nil into the imageView to remove it, print to the console, whatever. (Although you could simplify the code a little if you were going to install a nil image into the image view.)
Edit:
In addition to if let optional binding, you can also use guard statements.
if let optional binding says "try to unwrap this optional/optionals. If it succeeds, create a variable that's only defined inside the body of the if statement, and execute the if statement.
In contrast, guard says "try to unwrap this optional/optionals. If it succeeds, continue. If it fails, execute a block of code that exits the current scope.
Guard statements are useful when you need to check a whole series o things and want to keep going when everything is good, but bail out when if something goes wrong.
if let optional binding can lead to ever-increasing levels of indentation:
func foo() {
if let a = anOpitonal {
// Do stuff
if let b = a.someOtherOptional {
// Do more stuff
if let c = b.yetAnotherOptional {
// Still more code that only runs if all 3 unwraps work
}
}
}
}
In contrast, you could write that with guard like this:
func foo() {
guard let a = anOpitonal else { return }
// Do stuff
guard let b = a.someOtherOptional else { return }
// Do more stuff
guard let c = b.yetAnotherOptional else { return }
// Still more code that only runs if all 3 guard statements work
}

Unable to infer closure type in the current context

On the 3rd line in the function below I get the following error:
Unable to infer closure type in the current context
How do I fix this?
func fetchAllUsersImages() {
print("inside func")
self.ref.child("Posts").child(self.userID).child(self.postNum).observe(.childAdded, with: { snapshot in //error here
var images: [URL] = []
if let snapShotValue = snapshot.value as? [String: String] {
for (_, value) in snapShotValue {
if let imageURL = URL(string: value) {
print(imageURL, "image url here")
let imageAsData = try Data(contentsOf: imageURL)
let image = UIImage(data: imageAsData)
let ImageObject = Image()
ImageObject.image = image
self.arrayOfImgObj.append(ImageObject)
self.tableView.reloadData()
}
}
}
})
}
The reason why it is not inferring the closure type is because the try statement is not handled. This means that the closure expected to "catch" the error, but in your case, you forgot the do-try-catch rule.
Therefore you can try the following answer which will catch your errors:
do {
let imageAsData = try Data(contentsOf: imageURL)
let image = UIImage(data: imageAsData)
let ImageObject = Image()
ImageObject.image = image
self.arrayOfImgObj.append(ImageObject)
} catch {
print("imageURL was not able to be converted into data") // Assert or add an alert
}
You can then assert an error (for testing), or what I would personally do, is set up an alert.
This way the app wouldn't crash, but instead, notify the user. I find this very helpful when on the go and my device isn't plugged in - so I can see the error messages instead of a blank crash with no idea what happened.
This error can also happen if you have a non related compilation error in your closure body. For example, you may be trying to compare two or more non-boolean types.
extension Array where Element == Resistance {
init(_ points: [Point]) {
let peaks = points.beforeAndAfter { (before, current, after) -> Bool in
before < current && current > after
}
self = []
}
}
will produce Unable to infer closure type in the current context.
The correct code:
extension Array where Element == Resistance {
init(_ points: [Point]) {
let peaks = points.beforeAndAfter { (before, current, after) -> Bool in
before.value < current.value && current.value > after.value
}
self = []
}
}
In addition to ScottyBlades answer, I'd like to add two data points to the "experience". It looks like referencing a non-existent property using self inside the block is not handled nicely by the compiler.
Nice error inside the block:
// Setting a handler for an NWListener instance:
self.nwListener?.newConnectionHandler = { (_ connection: NWConnection) -> Void in
// Results in "Cannot find 'nonExistentProperty' in scope"
// in the line below:
guard let delegate = nonExistentProperty else { return }
}
Weird "Type of expression is ambiguous without more context" error: (note the new self in front of nonExistentProperty)
// Setting a handler for an NWListener instance:
// Results in "Type of expression is ambiguous without more context"
// at the equals sign below:
self.nwListener?.newConnectionHandler = { (_ connection: NWConnection) -> Void in
guard let delegate = self.nonExistentProperty else { return }
}

How to check nil for variables in swift and avoid app crashing?

I'm having my string declared as,
var firstName = String()
and I'm assigning value from parsed JSON content like,
firstName = json["first_name"].stringValue
But sometimes, there might be empty values in the JSON response and the app is crashing, I read about guard statement and if statement to check empty values, but that requires the declaration format to be changed, couldn't find a right way to handle this error without changing the declaration format.
since I have declared all the variables in my app with similar formats, changing that requires time, I'm in the verge of uploading my app, this is my first swift app, if my declaration format is wrong please answer why it is, can someone help me out of this?
Code as of Swift 4:
Keep in mind that when you are using ".stringValue", it is almost the same as using a "!" which will force a crash on nil.
if let firstName = json["first_name"]as? String {
//do stuff like
self.firstName = firstName
}
This will unwrap it to where you can get at the value if it isn't null and can be a string.
Guard let's are really good for this though as you can account for it in the beginning and you can assume that it is not optional for the entire scope.
guard let firstName = json["first_name"]as? String else {return}
self.firstName = firstName
In addition, you could always check for nulls in one line and assign a default value if a nil value occurs.
self.firstName = (json["first_name"]as? String) ?? "Default String"
You can use next statement:
guard let firstName = json["first_name"].stringValue else { // Do sth if nil }
// Do sth if not nil
Or you could use statement, which you wrote, but you should check variable
firstName like this:
guard firstName != nil else { // Do sth if nil }
// Do sth if not nil
Or
if firstName != nil {
// Do sth if not nil
}
You can use guard statement also,
guard let firstName = json["first_name"] else {
print("FirstName is Empty")
return
}
or you can check with if also,
if let firstName = json["first_name"] {
//Your code goes here
}
You can do that the following way:
if let dictionary = json {
if let fName = dictionary["first_name"] {
firstName = fName as? String
}
}
I guest you use SwiftyJSON. The function stringValue always return String object. It's impossible to be nil. It sounds like the response data which is not valid JSON format, so it crashed.
My snippet codes.
// Alarmofire + SwiftyJSON
let request: DataRequest = ...//< Configure Alarmofire DataRequest
request.response() { (resp) in
if let error = resp.error {
// TODO: Error handle
return
}
if (response.data == nil) {
// TODO: error handle for empty data?
return
}
assert(response.data != nil, "In this place, the `data` MUST not be nil.")
let json = try? JSON(data: response.data!)
if (json == nil) {
// TODO: Error handle for invalid JSON format.
// If json is nil, then `json["first_name"]` will lead to crash.
return
}
// TODO: parse json object
let firstNameStr = json["first_name"].stringValue
}

How to write swift code to display Yelp Star Images? [duplicate]

This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 5 years ago.
Hey I'm trying to use Yelp's Review API and am having trouble structuring/writing the code necessary to display the different Yelp Star Ratings. I have no problem getting the response (it's successful). Yelp has provided image assets of all their different star ratings (5, 4.5, 4 etc. stars). Because the rating response is as a Double, I converted that into a String value. As for knowing which to call, I created an enum class so that it knows which image name to use. Using that name, I can then use it to find the image asset I need.
Now that I structure the code this way, my app crashes. Xcode will build it but upon opening the app, it crashes.
Enum class:
import Foundation
import UIKit
enum Rating: String {
case five = "regular_5"
case fourAndAHalf = "regular_4_half"
case four = "regular_4"
case threeAndAHalf = "regular_3_half"
case three = "regular_3"
case twoAndAHalf = "regular_2_half"
case two = "regular_2"
case oneAndAHalf = "regular_1_half"
case one = "regular_1"
case zero = "regular_0"
}
Yelp Client Service class:
import Foundation
import Alamofire
import SwiftyJSON
class YelpClientService {
static func getReviews(url: String, completionHandler: #escaping ([Review]?)-> Void)
{
let httpHeaders: HTTPHeaders = ["Authorization": "Bearer \(UserDefaults.standard.string(forKey: "token") ?? "")"]
//removing diacritics from the URL
if let requestUrl = URL(string: url.folding(options: .diacriticInsensitive, locale: .current))
{
Alamofire.request(requestUrl, encoding: URLEncoding.default, headers: httpHeaders).responseJSON { (returnedResponse) in
let returnedJson = JSON(with: returnedResponse.data as Any)
let reviewArray = returnedJson["reviews"].array
print(reviewArray as Any)
var reviews = [Review]()
for review in reviewArray! {
let userName = review["user"]["name"].stringValue
let ratingDouble = review["rating"].doubleValue
let rating = String(ratingDouble)
let text = review["text"].stringValue
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let timeCreated = formatter.date(from: review["time_created"].stringValue)
let url = review["url"].stringValue
let review = Review(rating: Rating(rawValue: rating)!, userName: userName, text: text, timeCreated: timeCreated!, url: url)
reviews.append(review)
}
completionHandler(reviews)
}
}
else
{
print("invalid url")
completionHandler(nil)
}
}
}
Func in View Controller thats displaying the Star:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reviewCell", for: indexPath) as! ReviewCell
let review = reviewList[indexPath.row]
print(review.userName)
cell.userName.text = review.userName
cell.reviewText.text = review.text
cell.yelpStars.image = UIImage(named: review.rating.rawValue)
//cell.date.text = review.timeCreated
return cell
}
The error when I build is: fatal error: unexpectedly found nil while unwrapping an Optional value.
I'm not sure what went wrong. Is it correct of me to instantiate rating as a Rating type? Should I keep it String?
I realize this is long code but I hope someone can help! Thank you!
I am sure it would crash. The way you have written it. let ratingDouble = review["rating"].doubleValue you are expecting double. It would be 0, 4.5, 3.0 etc. Which would get converted to string "0","4.5" "3.0" etc.Then you try to initialise rating with Rating(rawValue : rating), Rating enum does not have these raw values as "0", "4.5" etc, so nil will be returned. You are force unwrapping it with '!", no doubt its crashing.
You will need to format your enum like this
enum Rating: String {
case five = "5.0"
case fourAndAHalf = "4.5"
case four = "4.0"
case threeAndAHalf = "3.5"
case three = "3.0"
case twoAndAHalf = "2.5"
case two = "2.0"
case oneAndAHalf = "1.5"
case one = "1.0"
case zero = "0.0"
getImageName()-> String {
switch self {
case five:
return "ImageNameForFive"
case fourAndHalf:
return "ImageNameForFourAndHalf.
......
}
}
}
and change
let rating = String(ratingDouble)
to
let rating = String.init(format: "%.1f", ratingDouble)
The error fatal error: unexpectedly found nil while unwrapping an Optional value. is thrown when Swift is unable to do an action usually when the targets are nil, empty, non-existent or undefined.
If a value could potentially be nil, empty, non-existent or undefined; it is known as an optional value. In order to use these values in our code, we must unwrap them. If the value is either nil, empty, non-existent or undefined, our app would most likely crash if it was not unwrapped safely.
To unwrap an object safely in swift we can either use if let or a guard statement. The code is written inside of this only runs if the object is not null.
It's good practice to safely unwrap all your objects in swift to prevent crashes. An example can be found below:
Safe Unwrapping
// initalize a string that may be nil (an optional)
var string: String? = nil
// create a random number between 0-2
let randomNum:UInt32 = arc4random_uniform(3)
// create a switch to set the string value based on our number
switch (randomNum) {
case 0:
string = "Some String"
default:
break
}
// attempt to print out our string
// using a guard statement
guard let string = string else {
// handle if string is null
return
}
// print the string from the guard
print(string)
// using if let
if let string = string {
print(string)
} else {
// handle string is null
}
Unsafe Unwrapping
// initalize a string that may be nil (an optional)
var string: String? = nil
// create a random number between 0-2
let randomNum:UInt32 = arc4random_uniform(3)
// create a switch to set the string value based on our number
switch (randomNum) {
case 0:
string = "Some String"
default:
break
}
// attempt to print our string by forcefully unwrapping it even if it is null causing a crash if it is null
print(string!)
So you can see the difference, in the second one the app would crash with the same error you are getting as it failed to unwrap an optional value in the event the random number is not 0.
It is possible to safely unwrap an object without using if let or guard. This is known as an inline conditional which is basically an if/else clause but quicker.
Inline Conditionals
// if the string is null, it will print the value beside the two `??`
print(string ?? "This is what is printed if the string is nil")
So now that you have all this knowledge, you can go ahead and take a look at your code to see if you are forcefully unwrapping any of your values. A hint is that you use the ! to do this.
Also, the enum that you made takes string values like "half" not double values even if it is a string like '0.5". So it could also crash
Some examples I picked out that may cause the crash are:
for review in reviewArray!
review["rating"].doubleValue
Rating(rawValue: rating)!
timeCreated!

Resources