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.
Related
I am declaring a constant in this line of code but if I don't put the `! after it, xXcode gives an error saying:
value of optional type string must be unwrapped.
Why does Xcode think this is an optional? I am just declaring a constant of type String and assigning it to a key that the user will set in the setting section.
Maybe because I am using the UserDefault settings and it's not set yet? If so, how do I get around that?
let jbEmail: String = userDefaults.string(forKey: "JBemail_preference")!
Look at the documentation for UserDefaults string(forKey:). It has a return type of String?. It returns an optional because there might not be a value for the given key.
So your attempt to assign a String? to a String results in the error. The forced unwrap (adding !) resolves the error but it is the worst possible solution because now your app will crash if there is no value for the key.
You should properly handle the situation where there is no value for the key in UserDefaults.
You can assign a default value:
let jbEmail = userDefaults.string(forKey: "JBemail_preference") ?? "Some Default"
Or you can conditional deal with there being no value:
if let jbEmail = userDefaults.string(forKey: "JBemail_preference") {
// Do something with jbEmail
} else {
// There is no value, do something else
}
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.
I have tried
self.adc_role_id = String(res["adc_role_id"])
self.adc_role_id = "\(res["adc_role_id']"
self.adc_role_id = (\(res["adc_role_id"] as? String)!
but still get
Could not cast value of type '__NSCFNumber' to 'NSString'
I added the dump of res[4] below
As new as I am to Swift, I don't know anything else to try
In Swift 4, the String initializer requires the describing: argument label.
I don't know if this will solve your problem, but your first line of code should be written:
self.adc_role_id = String(describing: res["adc_role_id"])
In your screenshot we can see that res["adc_role_id"] is an NSNumber.
To transform an NSNumber to a String you should use its stringValue property.
And since a dictionary gives an Optional, you should use optional binding to safely unwrap it.
Example:
if let val = res["adc_role_id"] {
self.adc_role_id = val.stringValue
}
You could also, if you want, use string interpolation instead of the property:
if let val = res["adc_role_id"] {
self.adc_role_id = "\(val)"
}
but I think using the property is more relevant.
If for some reason the compiler complains about the type of the content, cast it:
if let val = res["adc_role_id"] as? NSNumber {
self.adc_role_id = val.stringValue
}
Note that you should not use String(describing:) because this initializer will try to represent the string in many ways, and some of them will give inaccurate and unexpected results (for example, if String(describing:) resolves to use the debugDescription property, as explained in the documentation, you may get a totally different string than the one you want).
It's also worth noting that using String(describing:) with an optional value such as your dictionary will resolve to a wrong string: String(describing: res["adc_role_id"]) will give Optional(yourNumber)! This is why Mike's answer is wrong. Be careful about this. My advice is to avoid using String(describing:) altogether unless for debugging purposes.
The error message is clear and the dump is clear, too.
The value is not a String, it's an Int(64) wrapped in NSNumber
Optional bind the value directly to Int (NSNumber is implicit bridged to Int) and use the String initializer.
if let roleID = res["adc_role_id"] as? Int {
self.adc_role_id = String(roleID)
}
Please conform to the naming convention that variable names are camelCased rather than snake_cased
This question already has an answer here:
Constant unassigned optional will not be nil by default
(1 answer)
Closed 6 years ago.
The following code works fine
struct carConfi {
var owner: String?
let brand: String = "BMW"
var currentMile: Double = 2000
}
let tomCar = carConfi()
However, if I change the type of the property owner to constant, there will be an error at the initializer
struct carConfi {
let owner: String? // Change to constant
let brand: String = "BMW"
var currentMile: Double = 2000
}
let tomCar = carConfi() //error: missing argument for parameter 'owner' in call
I did a bit search, it turns out that it is because the optional variables automatically have a default value of nil
I guess: Because once the constant is set, it then cannot be changed, if the optional constant automatically received an nil then it will keep as an unchangeable nil that's very silly and may against the users will
Question: My college doesn't fully convinced by the guess, he told me there must be more reasons for that. I would very appreciate if someone can explain that to me
Thx
Not setting a read-only (constant) field with either an:
initialization expression
initializer
is almost certainly an indication of an error in your program.
Since you have no other opportunity to set the value of your let field, the value of the field is going to remain nil (or some other default). It is rather unlikely that a programmer would find such behavior desirable, and request it on purpose.
That is why Swift marks this situation as an error. On the other hand, if you actually wanted your String constant to remain nil, you could add an expression to set it to nil, and silence the error:
let owner: String? = nil // Pretty useless, but allowed
Constants are set once, and once only. If you wanted it to be null or 0, then you would set it. You always define a constant on initiation.
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.