I'm trying to call substringWithRange on an option String but after multiple experiments am still not able to get it to compile:
var mdn:String?
var subscriber = CTSubscriber()
var carrierToken = subscriber.carrierToken
mdn = NSString(data:carrierToken, encoding:NSUTF8StringEncoding)
let range:NSRange = NSRange(location: 0, length: 10)
if mdn
{
let subString = mdn!.substringWithRange(range)
}
This will result in the compilation error saying the value of the optional NSString is not unwrapped.
I thought it already was unwrapped due to the !.
If I remove ! then I get an error saying String? does not have a member named substringWithRange.
Replace
var mdn:String?
with
var mdn:NSString?
You are using the var to store a NSString so you should give it the correct type. Although String and NSString are mutually assignable, it's not the same type.
String doesn't have the substringWithRange method.
I think this might actually be a compiler bug. I would report it to Apple and see how they come back.
However, the proper approach to unwrapping an optional for use if you need to gain some information from it. (Like obtaining a substring, in your example) is:
if let theMdn = mdn {
let subString = theMdn.substringWithRange(range)
}
That way your codeblock will only be executed if the unwrapping was successful.
Edit: Sulthan's answer might be more accurate. However I still think if you can unwrap the optional in a let block, you should be able to do it manually.
Related
I am asking this hesitantly as I know this is probably a dumb question.
I am returning a Realm result and then have gone ahead and tried to cast it to a String as normal (to put in a text label).
However I'm getting an error 'init' has been renamed to 'init(describing:)'.
When I try use the describing method instead, the label prints "Optional" inside it which obviously isn't what I want.
Is there a reason I can't use :
previousTimeLabel.text = String(lastRecord?.time)
I'm sure I've done this before and it's been fine, am I missing something? (lastRecord.time is an Int).
I've checked the answer here about Interpolation Swift String Interpolation displaying optional? and tried changing to something like this :
if let previousRounds = String(lastRecord?.rounds) {
previousRoundsLabel.text = previousRounds
}
but get the same error + Initializer for conditional binding must have Optional type, not 'String'
The issue isn't String(lastRecord?.time) being Optional. The issue is lastRecord being Optional, so you have to unwrap lastRecord, not the return value of String(lastRecord?.time).
if let lastRecord = lastRecord {
previousRoundsLabel.text = "\(lastRecord.time)"
}
To summarize Dávid Pásztor's answer, here's a way you can fix it:
previousTimeLabel.text = String(lastRecord?.time ?? 0)
This may not be the best way for your application. The point Dávid was making is that you need to deal with lastRecord possibly being nil before trying to pass its time Int into the String initializer. So the above is one simple way to do that, if you're ok with having "0" string as your previousTimeLabel's text if there was no lastRecord.
So I am teaching myself Swift and I get optionals when I am declaring them, so for example:
var thisString:String?
I would have to either force unwrap thisString, or use
if let anotherString = thisString {
or use
guard if let another string = thisString else { return }
or nil coalesce it
let anotherString = thisString ?? "Something else"
But where I am getting hung up is there are times I create something that I don't think it an optional but the compiler does.
For example, why is the URL an optional here?
let myURL = URL(string: "https://itunes.apple.com/search?term=U2")
var myRequest = URLRequest(url: myURL!)
I didn't declare it as an optional and it clearly has a value. So why does the compiler see this as an optional? If I didn't force unwrap it I would get this error:
Value of optional type 'URL?' not unwrapped; did you mean to use '!' or '?'?
Is there some standard that I am missing? I've gone over both the docs and Swift Programming Book from Big Nerd and I still don't seem to get this part.
Thanks for the assist.
But where I am getting hung up is there are times I create something that I don't think it an optional but the compiler does.
But what you think is not what matters. What matters is what the API call you are making actually returns. You find that out by reading the docs.
So why does the compiler see this as an optional
Well, let's read the docs. You are calling URL(string:). That is the same as URL's init(string:). Look at the docs:
https://developer.apple.com/documentation/foundation/url/1779737-init
The declaration is
init?(string: String)
See the question mark? That means, "This call returns an Optional." So you should expect an Optional here.
The compiler can't determine if the url that you define in the string is valid or not.
Suppose instead of:
let myURL = URL(string: "https://itunes.apple.com/search?term=U2")
You miss typed the myURL definition as:
let myURL = URL(string: "https:/itunes.apple.com/search?term=U2")
The string contains a malformed URL, so the program would crash the moment you went to define myURL.
let myURL = URL(string: "https://itunes.apple.com/search?term=U2")
Here you are creating an url from string.That string might not be a valid string.All the strings are not valid url. So you are getting an optional because if that string can be turned in to a valid url then that url will be returned else nil will be returned. See the Apple documentation here.The initializer what you are using is a failable initializer itself.
init?(string: String)
#Keshav is correct, to get a better idea, hold the option button and click on the 'string' part of the init function for the URL class. You will see in the swift reference the init declaration is init?(string: String). This means that a optional is returned. Any function can return a optional, you can have func returnMyString(_ myString: String) -> String {} or func returnMyString(_ myString: String) -> String? {}. Both of those functions are pretty much the same, except the second one returns a optional.
URL has optional initializers. For example you can have
class A {
init?() {
return nil //You will get Optional<A>.none
}
}
A() === Optional<A>.none //true
which kind of implies that initialization failed. Such initializers wrap returned object into Optional. In Swift nil == Optional<Any>.none so you can speak of them interchangeably.
For example if you will attempt to construct a URL with something that is not an actual url, it will return nil.
let notAURL = URL(string: "{") //notAURL will be nil
On a side note: I believe optional initializers are a very poor design choice since they don't communicate anything about what went wrong inside the init. If init is fallible, it should throw. I don't understand why Swift designers allow optional initializers and I see why it births a lot of confusion.
I'm receiving a compiler error and I'm not really sure why. I'm sure there is a simple answer for this. I have a core data attribute I'm trying to assign before saving. In my Core Data Property file it's defined as this:
#NSManaged public var age: Int32
I am using a UIPicker to select it and put it into an inputView. That works fine, so ageTextField: UITextField! holds the value. As I try to assign this to the CoreData object just before saving I get the following
person.age = ageTextField.text -> Cannot assign String? to Int32.
Ok, I understand that, so I cast it
person.age = Int(ageTextField.text) -> Value of Optional String not unwrapped...
Ok, I get that, so I unwrapped it, it asks to unwrap again and I agree:
person.age = Int(ageTextField.text!)! -> Type of expression is ambiguous without more context
I'm not sure what is wrong here, just looking over some old Swift 2 code of mine and this worked. This is my first code with Swift 3 though.
That compiler error is obscure at best and misleading at worst. Change your cast to Int32:
person.age = Int32(ageTextField.text!)!
Also: unless you are absolutely sure that the user will always enter a valid number into the textfield, use optional binding instead of force unwrap:
if let text = ageTextField.text,
let age = Int32(text)
{
person.age = age
}
The immediate issue is the use of the wrong type. Use Int32, not Int. But even once that is fixed, you have lots of other issues.
You should safely unwrap the text and then the attempt to convert the string to an integer.
if let text = ageTextField.text, let num = Int32(text) {
person.age = num
}
The use of all of those ! will cause a crash if the text is nil or it contains a value that isn't a valid number. Always use safe unwrapping.
Just make sure to unwrap the optional before convert. Make your code safe.
I have an "if let" statement that is being executed, despite the "let" part being nil.
if let leftInc : Double? = self.analysis.inputs[self.btnLeftIncisor.dictionaryKey!]! {
println(leftInc)
let valueString : String = formatter.stringFromNumber(NSNumber(double: leftInc!))!
self.leftIncisorTextField?.text = valueString
self.btnLeftIncisor.associatedLabel?.text = valueString
}
// self.analysis.inputs is a Dictionary<String, Double?>
The inputs dictionary holds information entered by the user - either a number, or nil if they haven't entered anything in the matching field yet.
Under the previous version of Swift, the code was written as this:
if let leftInc : Double? = self.analysis.inputs[self.btnLeftIncisor.dictionaryKey!]?? {
and worked correctly.
I saw a similar question here, but in that instance the problem seemed to be the result of using Any?, which is not the case here.
Swift 2.2
In your if let you define another optional, that's why nil is a legitimate case. if let is intended mainly to extract (maybe) non optional value from an optional.
You might try:
if let leftInc : Double = self.analysis.inputs[self.btnLeftIncisor.dictionaryKey!].flatMap ({$0}) {
// leftInc is not an optional in this scope
...
}
Anyway I'd consider to not do it as a one liner but take advantage of guard case. Just in order to enhance readability. And avoid bang operator (!).
The if-let is for unwrapping optionals. You are allowing nil values by setting the type to an optional Double.
The if statement should be:
if let leftInc = self.analysis.inputs[self.btnLeftIncisor.dictionaryKey!] as? Double{
...
}
This will attempt to get an object out of inputs, if that fails it returns nil and skips it. If it does return something it will attempt to convert it to a Double. If that fails it skips the if statement as well.
if inputs is a dictionary like [Something:Double] then you don't need the last as? Double as indexing the dictionary will return a Double?
I recommend reading the swift book on optional chaining.
You could break it down further -
if let optionalDouble = self.analysis.inputs[self.btnLeftIncisor.dictionaryKey!], leftInc = optionalDouble {
....
}
as your dictionary has optional values - this way of writing it might make it clearer what's going on
if let k = dict["someKey"]{}, dict["someKey"] will be an object of type Any
this can bypass a nill
So do a typecast to get it correct like if let k = dict["someKey"] as! String {}
I am reading the following book, on Page #32 there is a code snippet. December 2014: First Edition.
Swift Development with CocoaJonathon Manning, Paris Buttfield-Addison,
and Tim Nugent
I know we can use ? to make a vairbale optional and ! to unwrap a optional vairbale in Swift
var str: String? = "string"
if let theStr = str? {
println("\(theStr)")
} else {
println("nil")
}
Why do we need ? in this line if let theStr = str? It seems working just fine with out it and making no difference.
You don't need it, and shouldn't have it. The optional binding evaluates the optional. If it's nil, it stops. If it's not nil, it assigns the value to your required variable and then executes the code inside the braces of the if statement.
EDIT:
The language has changed slightly since it was first given out in beta form. My guess is that the ? used to be required.
Some of the sample projects I've used from Github fail to compile and I've had to edit them to get them to work. this might be an example where the syntax has changed slightly.
The current version of Swift does not require it, as it is redundant since your variable is already an optional.
Whatever you put in the if let statement does have to be an optional though or you will receive an error
Bound Value in a conditional binding must be of Optional type
Furthermore, if you are casting to a type, you do need to use as? to cast to an optional type.
var str2: Any = ["some", "example"]
if let theStr = str2 as? [String] {
println("\(theStr)")
} else {
println("nil")
}