aircraft
aircraft

Reputation: 26896

How can I get String from Any in swift3

Such as I get a dic from fetchData:

(lldb) po dic

▿ 3 elements
  ▿ 0 : 2 elements
    - .0 : "total"
    - .1 : 0.00
  ▿ 1 : 2 elements
    - .0 : "year"
    - .1 : 2016
  ▿ 2 : 2 elements
    - .0 : "month"
    - .1 : 12

(lldb) po dic["year"]
▿ Optional<Any>


(lldb) po dic["year"]!
2016

Is there a function to get String form Any?

The function's usage is like below:

let total = UtilSwift.getStrFromAny(dic["total"] as Any )

In objective-c, I written a method:

+ (NSString*)getStringWithoutNull:(id)value
{
    NSString *strValue = @"";
   if(value != nil){
        strValue = [NSString stringWithFormat:@"%@", value];
    }

    if([strValue isEqualToString:@"null"])
    {
        return @"";
    }

    if ([strValue isEqual:[NSNull null]]) {

        return @"";
    }

    return strValue;
}

Is in swift could write a method like this to get String form Any?


The Any maybe Int, String, "", Double, or other type.


EDIT - 1

After the tried in Playground. :

import UIKit

let dict:[String:Any]  = ["aa": 123, "year":1994, "month":"12"]

let string = String(describing:dict["year"]) ?? ""   // Try to turn result into a String, o

print(string)  // If I print(string!), and there will report error.

The warning:

the test


EDIT 2

I know the edit -2 maybe paint the lily, but if when use the func below, when I deliver a Opitional value to the func, the return String will be Opitinal too, how to avoid that?

This below is my test in Playground, dic["b"] as Any convert the parameter to Opitional:

   let dic:[String:Any] = [  // ["b": 12, "A": "A"]

    "A":"A",
    "b":12
]

func stringFromAny(_ value:Any?) -> String {

    if let nonNil = value, !(nonNil is NSNull) {

        return String(describing: nonNil) // "Optional(12)"
    }
    return ""
}

let str = stringFromAny(dic["b"] as Any)  // "Optional(12)"

Upvotes: 9

Views: 8304

Answers (3)

6rchid
6rchid

Reputation: 1292

I was facing a similar issue with the change: [NSKeyValueChangeKey: Any]? paramater in the observeValue function.

Here is how I get String from the Any value:

let urlValue = change?[.newKey]
let url = String(describing: urlValue!) // Unwrap urlValue here to prevent optional String
print(url) // This is now a String

Upvotes: -1

sweet-2
sweet-2

Reputation: 157

Use ! if the value is an optional

String(describing: nonNil)  // "Optional(12)"
String(describing: nonNil!) // "12"

Upvotes: 3

Daniel Hall
Daniel Hall

Reputation: 13679

Try this one:

func stringFromAny(_ value:Any?) -> String {
    if let nonNil = value, !(nonNil is NSNull) {
        return String(describing: nonNil)
    }
    return ""
}

Update:

If the calling code invokes the above function with an Any? parameter that is explicitly cast to Any (a strange scenario which the Swift 3 compiler allows), then it will consider the final type of the parameter to be a non-optional optional, i.e. an Any value where the type Any represents is Any?. Or, in other terms, the value would be considered to be Optional<Any>.some(value:Any?).

In this case, the if let to unwrap the "some" case returns an optional value as the result in the function implementation. Which means that the final string description will include the "Optional" designation.

Because of the various oddities around the fact that the Swift 3 compiler will happily cast between Any and Any? and consider a value of type Any to be a value of type Any? and vice versa, it's actually pretty complicated to detect if an Any really contains an `Any?' or not, and to unwrap accordingly.

A version of this same function, along with some necessary additional extensions is provided below. This version will recursively flatten an Any value containing any number of nested Any? cases inside to retrieve the innermost non-optional value.

While this is what you seem to be looking for, I am of the opinion that it's a lot of hassle to work around something a programmer should not be doing anyway, namely miscasting a known Any? value to be Any because the compiler has a weird exception for that, even when it is not actually true.

Here's the "developer-proof" version of the code:

protocol OptionalType {
    var unsafelyUnwrapped: Any { get }
    var unsafelyFlattened: Any { get }
}

extension Optional: OptionalType {
    var unsafelyUnwrapped: Any { return self.unsafelyUnwrapped }
    var unsafelyFlattened: Any { return (self.unsafelyUnwrapped as? OptionalType)?.unsafelyFlattened ?? self.unsafelyUnwrapped }
}

func stringFromAny(_ value:Any?) -> String {
    switch value {
    case .some(let wrapped):
        if let notNil =  wrapped as? OptionalType, !(notNil.unsafelyFlattened is NSNull) {
            return String(describing: notNil.unsafelyFlattened)
        } else if !(wrapped is OptionalType) {
            return String(describing: wrapped)
        }
        return ""
    case .none :
        return ""
    }
}

Upvotes: 12

Related Questions