Swift AnyObject Conversion - swift

I created some test code to show the problem I am having.
This compiles fine in a playground, however, when I try to put it into a project, Xcode gives the following warning: Treating a forced downcast to 'String' as optional will never produce 'nil' on line 30. I am given two suggestions to fix the problem:
Use 'as?' to perform a conditional downcast to 'String', which makes absolutely no sense. However, it does compile without warnings/errors, which seems strange because it is assigning an optional value to a non-optional type of String.
Use the conditional form of the type cast operator (as?) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This enables you to check for a successful downcast.
From the Swift language guide.
Unless it thinks I might want to assign nil if the conversion fails (therefore removing the dictionary entry), this makes no sense. Especially because I am sure it will succeed because I literally just checked to see if it was a String.
Add parentheses around the cast to silence this warning, which seems pointless, but does silence the warning. This seems like a strange thing to do, but then again, it may just be a poor way of confirming that you really want to do what you are trying to do.
Which option is right, or neither? What is causing this warning?

The correct solution is to use optional binding instead of the
forced unwrap operator !. Actually you can incorporate the check
value != nil into the switch statement:
for (key, value) in dict {
switch value {
case let s as String:
newDict[key] = s
case let i as Int:
newDict[key] = String(i)
case let b as Bool:
newDict[key] = b ? "1" : "0"
case let v?: // value is not `nil`
newDict[key] = String(v)
default: // value is `nil`
break
}
}

Here is your code, modified to show how you can cast a function result to match AnyObject? and avoid explicitly unwrapped optionals:
func returnsAString() -> AnyObject? {
return "I am a String." as? AnyObject
}
func returnsAnInt() -> AnyObject? {
return Int(123) as? AnyObject
}
func returnsABool() -> AnyObject? {
return true as? AnyObject
}
func returnsNilBool() -> AnyObject? {
return nil as Bool? as? AnyObject
}
var dict : [String : AnyObject?] = [String : AnyObject?]()
var newDict : [String : String ] = [String : String ]()
dict["string"] = returnsAString()
dict["int"] = returnsAnInt()
dict["bool"] = returnsABool()
dict["nil"] = returnsNilBool()
for (key, value) in dict {
switch value {
case let value as String:
newDict[key] = value
case let value as Int:
newDict[key] = String(value)
case let value as Bool:
newDict[key] = (value ? "1" : "0")
default:
newDict[key] = "nil"
}
}
print("Dict: \(dict)")
print("newDict: \(newDict)")
// Dict: ["nil": nil, "int": Optional(123), "bool": Optional(1), "string": Optional(I am a String.)]
// newDict: ["nil": "nil", "int": "123", "bool": "1", "string": "I am a String."]

Related

How to get the First Character in a name

I have below func in my class.
static func getFirstCharInName(strName: String) -> String {
let firstCharInName = String(strName.first)
return firstCharInName.trim()
}
I encountered this err:
Value of optional type 'Character?' must be unwrapped to a value of type 'Character'
What seems to be the problem?
Thanks
func getFirstCharInName(strName: String) -> String {
let indexStartOfText = strName.index(strName.startIndex, offsetBy: 0)
let indexEndOfText = strName.index(strName.startIndex, offsetBy: 0)
let firstChar = String(strName[indexStartOfText...indexEndOfText])
return firstChar
}
This error means that the expression has optional value (the value can be nil) that is not yet unwrapped, strName.first returns an optional value of Character?, but your function demands a returning type of String which is not an optional type.
So, in order to fix this, you need to unwrap the optional value strName.first, it seems like you are not familiar with optionals, here's the code for your case (choose one from two options):
func getFirstCharInName(strName: String) -> String {
// option 1: force unwrap - can cause fatal error
return String(strName.first!)
// option 2: optional binding
if let firstCharInName = strName.first {
return String(firstCharInName)
} else {
// if the optional value is nil, return an empty string
return ""
}
}
PS. I don't really understand the function trim() in your question, but if you mean to strip away the blank spaces like " ", you can do:
firstCharInName.trimmingCharacters(in: .whitespaces)
Avoid the optional simply with prefix, it's totally safe. if there is no first character you'll get an empty string.
static func getFirstChar(in name: String) -> String { // the function name getFirstChar(in name is swiftier
return String(name.prefix(1))
}
I don't know what the trim function is supposed to do.
It means that value of optional type 'Character?' (as result of your part of code strName.first) must be unwrapped to a value of type 'Character' before you will be gonna cast it to String type.
You may use this variant:
func getFirstCharInName(strName: String) -> String {
return strName.count != 0 ? String(strName.first!) : ""
}
As you can see, the exclamation point is in the string strName.first! retrieves the optional variable as it was needed.
you can do something like that:
extension String {
var firstLetter: String {
guard !self.isEmpty else { return "" }
return String(self[self.startIndex...self.startIndex])
}
}
then
let name = "MilkBottle"
let first = name.firstLetter // "M"

Switft Dictionary problem 'String?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?

I'm new to Swift and ObjC and can't get this simple bit of code to work. I get the error 'String?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
guard let data = context as? Dictionary<String,String> else {return}
var str : String
str = data["Score"] as String //<<<I get the error here
When I change it to as! I know get this warning: Forced cast from 'String?' to 'String' only unwraps optionals; did you mean to use '!'?
Any ideas how to extract the string from the dictionary so I can use it?
It might be possible, that the key "Score" is not set in your dictionary and may return nil. Use this code to unwrap the optional:
if let score = data["Score"] {
str = score
}
Since you already unwrapped context to Dictionary<String, String>, Swift will automatically infer the type of score as String.
You don't need to cast because data dictionary would always return optional String type and you can use guard statement
guard let str = data[Score] else { return }
// Your code here
The type returned by subscripting a Dictionary is an optional, since it is valid to subscript it with non-existinent keys. So, as per the error message, in a dictionary with String values, the return type is String?.
If you know for sure that the key always exist in the dictionary, you can force-unwrap it as data["Score"]! to make the type String (without casting, but crashing if it doesn't exist). Otherwise handle the nil in some way, e.g., if let str = data["Score"] or str = data["Score"] ?? "0".
Use like this:
if let dataDictionary = context as? [String: Any] {
if let someString = dataDictionary["score"] as? String {
print(someString)
}
}
We don't know what kind of data you're working with but you're likely either getting back a dictionary actually of type [String: String], in which case you would:
func getData(context: [String: String]) {
guard let str = context["Score"] else {
return
}
print(str)
}
...or you're really getting back a dictionary of type [String: Any] (more common with database dictionaries), in which case you would:
func getData(context: [String: Any]) {
guard let str = context["Score"] as? String else {
return
}
print(str)
}

Function throws AND returns optional.. possible to conditionally unwrap in one line?

I am using an SQLite library in which queries return optional values as well as can throw errors. I would like to conditionally unwrap the value, or receive nil if it returns an error. I'm not totally sure how to word this, this code will explain, this is what it looks like:
func getSomething() throws -> Value? {
//example function from library, returns optional or throws errors
}
func myFunctionToGetSpecificDate() -> Date? {
if let specificValue = db!.getSomething() {
let returnedValue = specificValue!
// it says I need to force unwrap specificValue,
// shouldn't it be unwrapped already?
let specificDate = Date.init(timeIntervalSinceReferenceDate: TimeInterval(returnedValue))
return time
} else {
return nil
}
}
Is there a way to avoid having to force unwrap there? Prior to updating to Swift3, I wasn't forced to force unwrap here.
The following is the actual code. Just trying to get the latest timestamp from all entries:
func getLastDateWithData() -> Date? {
if let max = try? db!.scalar(eventTable.select(timestamp.max)){
let time = Date.init(timeIntervalSinceReferenceDate: TimeInterval(max!))
// will max ever be nil here? I don't want to force unwrap!
return time
} else {
return nil
}
}
Update: As of Swift 5, try? applied to an optional expression does not add another level of optionality, so that a “simple” optional binding is sufficient. It succeeds if the function did not throw an error and did not return nil. val is then bound to the unwrapped result:
if let val = try? getSomething() {
// ...
}
(Previous answer for Swift ≤ 4:) If a function throws and returns an optional
func getSomething() throws -> Value? { ... }
then try? getSomething() returns a "double optional" of the
type Value?? and you have to unwrap twice:
if let optval = try? getSomething(), let val = optval {
}
Here the first binding let optval = ... succeeds if the function did
not throw, and the second binding let val = optval succeeds
if the return value is not nil.
This can be shortened with case let pattern matching to
if case let val?? = try? getSomething() {
}
where val?? is a shortcut for .some(.some(val)).
I like Martin's answer but wanted to show another option:
if let value = (try? getSomething()) ?? nil {
}
This has the advantage of working outside of if, guard, or switch statements. The type specifier Any? isn't necessary but just included to show that it returns an optional:
let value: Any? = (try? getSomething()) ?? nil

Swift - matching class dynamically

I am trying to write an extension method on Dictionary with the following signature:
func firstNonNilObjectForKey(keys: [Key], ofClass aClass: Any.Type = String.self) -> Value?
This is meant to help me deal with JSON dictionaries, where I often need the first non-null value, but sometimes the JSON includes null itself as value, which is converted to NSNull in Cocoa.
The usage would be something like:
let dict: [String:AnyObject] = [...]
let myValue = dict.firstNonNilObjectForKey([ "key1", "key2" ]) as? String
The issue is implementational - how to match the class:
if let value = self[key] {
if value is aClass { ... } <--- error: aClass is not a type
if let castedValue = value as? aClass { ... } <--- error: ditto
let type = value.dynamicType
if type == aClass { ... } <--- no error, but doesn't handle subclasses!
// Cannot use value.isKindOfClass() since value may be Any
}
I have thought of an alternative:
func firstNonNilObjectForKey<ReturnValueType>(keys: [Key]) -> ReturnValueType?
which allows to be implemented as
if let value = self[key] as? ReturnValueType { ... }
But:
- Here I cannot set the default type (I mostly need String.Type).
- I'm not really sure how to invoke this:
let value = dict.firstNonNilObjectForKey([ "key1", "key2" ]) as? String <--- error: Cannot invoke 'firstNonNilObjectForKey' with an argument list of type '([String])'
let value = dict.firstNonNilObjectForKey<String>([ ... ]) <--- error: Cannot explicitly specialize a generic function
I hope this isn't a duplicate, but most similar questions here are simply handling a situation where you are matching against a known class.
I'm not sure I got the requirements exactly, but can something like this work for you?
extension Dictionary {
func firstNonNilObjectForKey(keys: [Key]) -> String? {
return self.firstNonNilObjectForKey(keys, ofClass: String.self)
}
func firstNonNilObjectForKey<T>(keys: [Key], ofClass aClass: T.Type) -> T? {
for k in keys {
if let v = self[k] as? T {
return v
}
}
return nil
}
}
let dict = ["key1": 2, "key2": "Foo"]
let aString = dict.firstNonNilObjectForKey(["key1", "key2"]) // "Foo"
let anInt = dict.firstNonNilObjectForKey(["key1", "key2"], ofClass: Int.self) // 2
The main gotcha here is that I'm using overloading as opposed to default parameters, which don't seem to get along well with the swift type checker.

Grabbing values from a dictionary in a more elegant way

I've been playing with swift and am getting quite tortured! Consider:
var myDict : Dictionary <String, String>
//DO SOME MAGIC TO POPULATE myDict with values
<magic being done>
//Now myDict has values. Let's parse out the values of myDict
//This doesn't work
let title : String = myDict["title"]
//This does
let title : String? myDict["title"]
This is because it isn't known whether the key is in the dictionary. What I want to say, though, is "If the title key is in the dictionary, give me that value, else, just give me an empty string"
I could probably write:
var myTitle : String
if let title : String = myDict["title"] {
myTitle = title
} else {
myTitle = ""
}
I believe that works...BUT...it's quite a lot of code for EACH key of the dictionary. Does anyone have any ideas in the swift world on how this is supposed to be written?
RD
You could write an extension on optional:
extension Optional {
/// Unwrap the value returning 'defaultValue' if the value is currently nil
func or(defaultValue: T) -> T {
switch(self) {
case .None:
return defaultValue
case .Some(let value):
return value
}
}
}
Then you can do:
myDict["title"].or("")
This would also work for all optionals.
Note: I started a module to add common helpers like this or on Optional to swift.
You unwrap the value either explicitly:
let title : String = myDict["title"]!
or implicitly:
let title : String! = myDict["title"]
Note that you still have to check whether title is nil or not unless you are really sure it's there.
Edit:
Here's a sample global operator overload for any optional for type T:
#infix func | <T: Any>(lhs: T?, rhs: T!) -> T! {
if lhs {
return lhs!
}
return rhs
}
var myDict : Dictionary <String, String> = ["a": "b"]
let title1 = (myDict["a"] | "") // "b"
let title2 = (myDict["title"] | "") // ""