Nullable Int in Swift - swift

I am attempting to use a hash that contains a nullable int as a value. The below is the code.
var facs:[(pk:Int,phone:Int?,name:String)] = []
var phone: AnyObject? = fac["phone"]!
var phoneLong:Int?;
if(phone == nil){
phoneLong = nil
}
else{
phoneLong = phone as? Int
}
var id = fac["id"]! as Int
var name = fac["name"]! as String
facs.append(pk:id, phone:phoneLong, name:name)
However, I get a compile error on the facs.append line that states Type 'T' does not conform to protocol 'IntegerLiteralConvertible'. I've tried a few variations, and the only way I can get rid of the error is to make phone a non-nullable int, which is not what I need. Thoughts?

It looks like the append method doesn't correctly detect the parameters as a tuple - just make that explicit by assigning the tuple to a variable:
let params = (pk:id, phone:phoneLong, name:name)
facs.append(params)

Related

How to solve "String interpolation produces a debug description for an optional value; did you mean to make this explicit?" in Xcode 8.3 beta?

Since beta 8.3, zillions warnings "String interpolation produces a debug description for an optional value; did you mean to make this explicit?" appeared in my code.
For example, the warning popped in the following situation up, where options could lead to nil:
let msg = "*** Error \(options["taskDescription"]): cannot load \(sUrl) \(error)"
As previously designed, it was ok for me (and the compiler) the optionals to be interpolated as 'nil'. But compiler changed its mind.
What the compiler suggests is to add a String constructor with description as follows:
let msg = "*** Error \(String(describing: options["taskDescription"])): cannot load \(sUrl) \(error)"
Obviously, the results is explicit but also very very cumbersome in my opinion. Is there a better option? Do I have to fix all those warning or better wait for the next beta?
This is a change that was made in this pull request due to the fact that interpolating Optional(...) into the resultant string is often undesirable, and can be especially surprising in cases with implicitly unwrapped optionals. You can see the full discussion of this change on the mailing list here.
As mentioned in the pull request discussion (although unfortunately not by Xcode) – one slightly nicer way to silence the warning than the use of String(describing:) is to add a cast to the optional type of whatever you're interpolating, so for example:
var i: Int? = 5
var d: Double? = nil
print("description of i: \(i as Int?)") // description of i: Optional(5)
print("description of d: \(d as Double?)") // description of d: nil
Which can also be generalised to as Optional:
print("description of i: \(i as Optional)") // description of i: Optional(5)
print("description of d: \(d as Optional)") // description of d: nil
In Swift 5, with the new string interpolation system introduced by SE-0228, another option is to add a custom appendInterpolation overload for DefaultStringInterpolation:
extension DefaultStringInterpolation {
mutating func appendInterpolation<T>(optional: T?) {
appendInterpolation(String(describing: optional))
}
}
var i: Int? = 5
var d: Double? = nil
print("description of i: \(optional: i)") // description of i: Optional(5)
print("description of d: \(optional: d)") // description of d: nil
And, if desired, you could even remove the argument label to disable the warning entirely within a module (or within a particular file if you mark it as fileprivate):
extension DefaultStringInterpolation {
mutating func appendInterpolation<T>(_ optional: T?) {
appendInterpolation(String(describing: optional))
}
}
var i: Int? = 5
var d: Double? = nil
print("description of i: \(i)") // description of i: Optional(5)
print("description of d: \(d)") // description of d: nil
Though personally I would prefer to keep the argument label.
Two easier ways of dealing with this issue.
Option 1:
The first would be by "force-unwrapping" the value you would like to return using a bang (!)
var someValue: Int? = 5
print(someValue!)
Output:
5
Option 2:
The other way, which could be the better way - is to "safely-unwrap" the value you want returned.
var someValue: Int? = 5
if let newValue = someValue {
print(newValue)
}
Output:
5
Would recommend to go with option 2.
Tip: Avoid force unwrapping (!) where possible as we are not sure if we will always have the value to be unwrapped.
seems using String(describing:optional) is simplest.
default value ?? makes no sense for non-Strings e.g Int.
If Int is nil then you want the log to show 'nil' not default to another Int e.g. 0.
Some playground code to test:
var optionalString : String? = nil
var optionalInt : Int? = nil
var description_ = ""
description_ = description_ + "optionalString: \(String(describing: optionalString))\r"
description_ = description_ + " optionalInt: \(String(describing: optionalInt))\r"
print(description_)
Output
optionalString: nil
optionalInt: nil
After updating to Xcode 8.3 and getting a lot of warning messages, I came up with the following that is more like the original output behavior, easy to add in, reduces the verboseness of using "String(describing:)" both in code and output.
Basically, add an Optional extension that gives a String describing the thing in the optional, or simply "nil" if not set. In addition, if the thing in the optional is a String, put it in quotes.
extension Optional {
var orNil : String {
if self == nil {
return "nil"
}
if "\(Wrapped.self)" == "String" {
return "\"\(self!)\""
}
return "\(self!)"
}
}
And usage in a playground:
var s : String?
var i : Int?
var d : Double?
var mixed = "s = \(s.orNil) i = \(i.orNil) d = \(d.orNil)" // "s = nil i = nil d = nil"
d = 3
i = 5
s = ""
mixed = "s = \(s.orNil) i = \(i.orNil) d = \(d.orNil)" // "s = "" i = 5 d = 3.0"
s = "Test"
d = nil
mixed = "s = \(s.orNil) i = \(i.orNil) d = \(d.orNil)" // "s = "Test" i = 5 d = nil"
Thanks for help from following link:
check-if-variable-is-an-optional-and-what-type-it-wraps
See Ole Begeman's fix for this. I love it. It creates a ??? operator which you can then use like this:
var someValue: Int? = 5
print("The value is \(someValue ??? "unknown")")
// → "The value is 5"
someValue = nil
print("The value is \(someValue ??? "unknown")")
// → "The value is unknown"
Double click on the yellow triangle displayed on line containing this warning. This will show FixIt with two solutions.
Use String(describing:) to silence this warning :
Using this it will become String(describing:<Variable>)
Eg. : String(describing: employeeName)
Provide a default value to avoid this warning :
Using this it will become (<Variable> ?? default value)
Eg.: employeeName ?? “Anonymous” as! String
Swift 5
My solution is making an extension which unwrap Optional object to Any.
When you log the object or print it out, you can see the actual object or <nil>⭕️ (combination from text and visual character). It's useful to look at, especially in the console log.
extension Optional {
var logable: Any {
switch self {
case .none:
return "<nil>|⭕️"
case let .some(value):
return value
}
}
}
// sample
var x: Int?
print("Logging optional without warning: \(x.logable)")
// → Logging optional without warning: <nil>|⭕️
Create an interpolation method that accepts an optional generic Type with an unnamed parameter. All your annoying warnings will magically disappear.
extension DefaultStringInterpolation {
mutating func appendInterpolation<T>(_ optional: T?) {
appendInterpolation(String(describing: optional))
}
}

What's the correct way to create a dictionary typealias?

If I want to define a typealias for a Dictionary, which accept String as the key, and String for the value, but I also want it to can accept nil, which one of these definition is correct?
typealias dictString = Dictionary<String, String?>
or
typealias dictString = Dictionary<String, String>
or
typealias dictString = [String:String?]
or
typealias dictString = [String:String]
Because I use the first one, and now each time I want to unwrap a value, I need to unwrap it twice (e.g. dict["name"]!!) so I began to wondering whether I really need to add the question mark or not (but I need the dictionary to still able to take nil value). Thanks.
The second and fourth are okay.
You don't need to make the value optional in most cases. You can set a key's value to nil even if you did not declare the value as an optional type:
var dict = [String: String]()
dict["Hello"] = nil
And when you access "Hello", it returns nil. Fantastic!
In fact, setting a key's value to nil means to "delete the key". This can cause unexpected results if you don't know this.
For example,
var dict = [String: String]()
dict["Hello"] = nil
print(dict.keys.contains("Hello"))
prints false.
If you need to check if a key exists even if its value is nil, then you have to make the value an optional type.
A dictionary will return nil for any key that's not present. Now, if you want to be able to actually have the key present but have the value nil then version 1 and 3 are the way to go and you keep the double unwrapping. Otherwise use 2 or 4.
Update
Example:
Version 1 & 3:
typealias DictString = Dictionary<String, String?> // or [String: String?]
var myDict: DictString = ["MyKey1": "MyVal1", "MyKey2": nil]
var value1: String? = myDict["MyKey1"]! // This is an Optional String so you'd need to unwrap it
var value2: String? = myDict["MyKey2"]! // This will return nil
Version 2 & 4:
typealias DictString = Dictionary<String, String> // or [String: String]
var myDict: DictString = ["MyKey1": "MyVal1"]
var value1: String? = myDict["MyKey1"] // This is an Optional String so you'd need to unwrap it
var value2: String? = myDict["MyKey2"] // This will return nil
Now the difference between the two is that the first case actually stores the keys. So if you ever do this:
var allKeys: [String] = Array(myDict.keys)
You'll get two different results:
1 & 3: allKeys will be ["MyKey1", "MyKey2"]
2 & 4: allKeys will be ["MyKey1"]
P.S: A good practice is to use upper cases for types' names. So I would suggest to changes your typealias to DictString.

Cannot assign value of type 'String?' to type 'String'

class C{
var b:[String]?
subscript(i:Int)->String?{
get{
return b?[i]
}
set{
b?[i] = newValue! // notice the unwrapped “!” here
}
}
}
In the code, I put a exclamation mark "!" to unwrap the newValue,
Because newValue is the same type of the return value of subscript which is String?.
If I don't put the exclamation mark "!" it will report error:
"
error: cannot assign value of type 'String?' to type 'String' b?[i] = newValue
"
Question: b?[i] is obviously an optional String value String?, how come it is of type String.
I wonder it was a bad error code
Your var b: [String]? is an optional array. However the string inside is not optional. If you changed it to: var b: [String?]? then it would work.
You property b is indeed an optional array, but its member elements are not optionals. Hence, when attempting to assign a new value to an existing member, the new value must be of concrete type String, and not the optional type String?.
// this is an Optional array, with non-optional elements
var foo: [String]? = ["foo"]
let optStr: String? = "bar"
let nonOptStr = "bar"
// ok, nonOptStr is not an optional
foo?[0] = nonOptStr
// as for 'nonOptStr', we need to unwrap
// it prior to attempting to assign it as a
// replacement member of 'foo'
if let str = optStr {
foo?[0] = str
}
Also, avoid explicit unwrapping, and consider validating the index prior to using it. E.g.:
class C {
var b: [String]?
subscript(i: Int) -> String? {
get {
if b?.indices.contains(i) ?? false { return b?[i] }
return nil
}
set {
if b?.indices.contains(i) ?? false, let newValue = newValue {
b?[i] = newValue
}
}
}
}
More generally, I found myself with this exact error after doing a split on a string and ended up printing out both variables. That clarified that I was trying to assign a two element variable to an indexed array that contained a string. So, that is the source of the error, which is essentially saying your trying to assign two elements of an array into a string inside your other array. Well, that makes sense out of the error.
Algorithmically it is:
1) array1[0] = array2Item1, array2Item2
That's why this was fine:
2) array1 = array2Item1, array2Item2
but the above was not.
So, the correct thing to do is 2) and then assign it by index:
array1 = array2Item1, array2Item2
array3 = array1[0] to do what I thought I was doing in 1).
Just another explanation for any others who venture here.

why swift think its optional let input = int(a)

I have following simple code after last line sum swift gives error
and var sum=input*2 should be changed to var sum=input!*2
I am not sure why since i didn't declare variable a as optional.
Does swift make input as optional ? Thanks
Var a="2"
let input = int(a)
var sum=input*2
Casting a string to an int returns and Optional because it could fail.
For example, let result = Int("foo") will return nil, because "foo" is not a valid Int.
What if you did
Var a = "This is most defiantly not a number and even if it were its too long to fit within an int 123457993849038409238490ff9f-09-0f9-09f dd0d0066646464646464349023849038490328 I'm a teapot".
let input = int(a)
Do you think that could be converted to an int? Now do you understand why its an optional?
What happens if the String can't be converted to an Int? Such as Int("A"). It becomes an optional because the compiler can't know for sure that the String you are passing in can become an Int.
guard let intVal = Int(a) else { return }
or
if let intVal = Int(a) {
//you have a valid Int here
}
is the way to go about handling this situation

set multiple values in one variable with different datatypes

I have this code:
var result = 0
func btnPressed(sender:UIButton!)
{
var btn = sender.titleForState(.Normal)
var value = btn!
result += value
println(result);
}
my result variable is an int, and my value variable is an uint8.
What i want to to is each time you press on the button set the title (this is an number) in the result variable. Now i get the error: int is not identical to uint8.
but my result must be an int, how can i solve this?
if i convert the value variable with this:
var value = Int(btn)
I get the following error: cannot invoke init with an argument of list type #lvalue string?
I don't know how to solve this issue.
Thank you!
What you need to do is cast the button title to int, so you can then add it to the result variable, like this:
var result = 0
func btnPressed(sender:UIButton!)
{
var btn = sender.titleForState(.Normal)
var value = btn?.toInt()
result += value!
println(result);
}