how can i convert any object type to a string?
let single_result = results[i]
var result = ""
result = single_result.valueForKey("Level")
now i get the error: could not assign a value of type any object to a value of type string.
and if i cast it:
result = single_result.valueForKey("Level") as! String
i get the error:
Could not cast value of type '__NSCFNumber' (0x103215cf0) to 'NSString' (0x1036a68e0).
How can i solve this issue?
You can't cast any random value to a string. A force cast (as!) will fail if the object can't be cast to a string.
If you know it will always contain an NSNumber then you need to add code that converts the NSNumber to a string. This code should work:
if let result_number = single_result.valueForKey("Level") as? NSNumber
{
let result_string = "\(result_number)"
}
If the object returned for the "Level" key can be different object types then you'll need to write more flexible code to deal with those other possible types.
Swift arrays and dictionaries are normally typed, which makes this kind of thing cleaner.
I'd say that #AirSpeedVelocity's answer (European or African?) is the best. Use the built-in toString function. It sounds like it works on ANY Swift type.
EDIT:
In Swift 3, the answer appears to have changed. Now, you want to use the String initializer
init(describing:)
Or, to use the code from the question:
result = single_result.valueForKey("Level")
let resultString = String(describing: result)
Note that usually you don't want valueForKey. That is a KVO method that will only work on NSObjects. Assuming single_result is a Dictionary, you probably want this syntax instead:
result = single_result["Level"]
This is the documentation for the String initializer provided here.
let s = String(describing: <AnyObject>)
Nothing else is needed. This works for a diverse range of objects.
The toString function accepts any type and will always produce a string.
If it’s a Swift type that implements the Printable protocol, or has overridden NSObject’s description property, you’ll get whatever the .description property returns. In the case of NSNumber, you’ll get a string representation of the number.
If it hasn’t, you’ll get a fairly unhelpful string of the class name plus the memory address. But most standard classes, including NSNumber, will produce something sensible.
import Foundation
class X: NSObject {
override var description: String {
return "Blah"
}
}
let x: AnyObject = X()
toString(x) // return "Blah"
"\(x)" // does the same thing but IMO is less clear
struct S: Printable {
var description: String {
return "asdf"
}
}
// doesn't matter if it's an Any or AnyObject
let s: Any = S()
toString(s) // reuturns "asdf"
let n = NSNumber(double: 123.45)
toString(n) // returns "123.45"
n.stringValue // also works, but is specific to NSNumber
(p.s. always use toString rather than testing for Printable. For one thing, String doesn’t conform to Printable...)
toString() doesn't seem to exist in Swift 3 anymore.
Looks like there's a failable initializer that will return the passed in value's description.
init?(_ description: String)
Docs here https://developer.apple.com/reference/swift/string/1540435-init
Related
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
I just joined a project that has a lot of existing code. The previous programmer was perhaps unfamiliar with Swift or began development in the early stages of the Swift language. They seemed to be using the if let statement in an odd way. They seemed to want to use the statement as a if is let. Before I edit the code I would like to know if there is any valid use for this:
// In JSON parser
if value is String, let string = value as? String {
document.createdBy = string
}
First checking if value is of type String seems redundant to me. Doesn't Swift check for this in the let string = value as? String portion of the statement?
QUESTION
Why would this need to be checked twice? Or would there be a reason for this?
You're correct, this is redundant. If value is not a string, then value as? String would return nil, and the conditional binding would fail.
To check the type, and not use the casted result:
if value is String {
// Do something that doesn't require `value` as a string
}
To check the type and use the result:
if let value = value as? String { // The new name can shadow the old name
document.createdBy = value
}
Doing both makes no sense.
I am trying to add BooleanLiteralConvertible support to my class so I can instantiate it with a boolean. The thing that's throwing me for a loop is the distinction between a boolean value and a boolean literal.
For example, after adding the protocol I attempted this:
func setSelected(value: Bool) {
var node: MyClass = value
}
But Swift complained that it cannot convert Bool to MyClass. It took me a while to realize it has to be a boolean literal. Oddly enough the following works fine:
func setSelected(value: Bool) {
var node: MyClass = value ? true : false
}
…which seems just absolutely silly to me. Is there a legitimate reason for this seemingly very bizarre requirement?
Types conforming to BooleanLiteralConvertible can be initialized with the Boolean literals true and false, e.g.
let mc : MyClass = true
This has nothing to do with initializing the type with a Boolean value:
let value : Bool = // ... some boolean value
let mc : MyClass = value // error: cannot convert value of type 'Bool' to specified type 'MyClass'
and there is – as far as I know – no way to make such an implicit
conversion work. You would have to write a custom init method
init(bool : Bool) {
// ...
}
and initialize the object as
let value : Bool = // ... some boolean value
let mc = MyClass(bool: value)
I like the question. Only the Swift team could definitively answer, but I can speculate as to why: converting a typed value into a variable of a different type without an explicit conversion or cast is very easy to confuse with a programmer error, and in many cases is something the compiler should warn about.
Example (and assume that Person is also a StringLiteralConvertible that can be initialized with a string variable as well as a literal as you pose in your question):
struct Person {
private static var idCounter = 1
var name:String
let id:Int
init(withName name:String) {
Person.idCounter += 1
self.name = name
self.id = Person.idCounter
}
}
var person = Person(withName:"Mary")
let name = "John"
person = name
The above code looks suspiciously like a mistake, where the programmer is assigning a value of the wrong type (String) to a variable of type Person. It may in fact be a mistake. Maybe the programmer only meant to change the name of the person (person.name = name) without creating a new Person with a new unique id. Or maybe the programmer intended to assign some other value to person but made a typo or code completion error. Hard to tell without either being the original programmer, or carefully studying all the context to see whether this conversion makes sense. And it gets harder the further the assignment is from the place where the variables are originally initialized Should the compiler warn here that a value of type String is being assigned to a variable of type Person?
The example would be far more clear, and more in line with Swift conventions as:
var person = Person(withName:"Mary")
let name = "John"
person = Person(withName:name)
The above version is completely unambiguous, both to the compiler and to any other programmers who read this later.
I'm trying to write a function that takes a parameter of type Printable:
func logMessage(message: Printable) {
// ...
}
Strangely, this doesn't work as expected when passing in Strings.
This doesn't compile:
logMessage("some string \(someVariable)")
// Neither does this:
let aString = "aString"
logMessage(aString)
This however compiles:
logMessage("A string")
// This works too:
let aString: Printable = "a string"
logMessage(aString)
This is quite confusing. It seems that in some cases String implements Printable and in others not.
In addition, it seems that string interpolation always produces a String that does not implement Printable. This crashes at runtime with a cast error:
let aString = "a string"
let interpolatedString = "contains \(aString)"
Any idea what's going on here?
You're right that String doesn't conform to Printable. The reason this compiles:
let aString: Printable = "Ceci n'est pas une String"
is that you aren't creating a String with that literal – you're creating an NSString (which is Printable).
Generally, in Swift, it’s usually better to write generic functions constrained by protocols. So instead of
func logMessage(message: Printable) {
// ...
}
you would probably be better off writing:
func logMessage<T: Printable>(message: T) {
// ...
}
This approach has a number of advantages – better type-safety and avoiding type erasure, more performant etc. You can read more about this stuff here.
But you'll still hit a problem because you can't pass in a String. You have two options here. First, just don't constraint it at all:
func logMessage<T>(message: T) {
// ...then use toString(message) to create a String if you need one,
// or use string interpolation or print()
}
This will work with String, and in fact will also work with anything that isn’t Printable as well (though you'll get quite a unhelpful output involving the mangled classname).
Or, you could use Streamable which strings do conform to:
func logMessage<T: Streamable>(message: T) {
println(message)
}
let s: String = "hello"
logMessage(s)
I think I read a while back on twitter one of the Swift team mention that the reason String doesn't conform to Printable is exactly because they didn't want people using Printable directly like this and that it’s better to always use toString or similar.
Consider this Person class, which simply implements StringLiteralConvertible and assigns the string literal to name:
class Person : StringLiteralConvertible {
var name : String?
typealias StringLiteralType = String
required init(stringLiteral value: StringLiteralType) {
println("stringLiteral \(value)")
name = value
}
typealias ExtendedGraphemeClusterLiteralType = String
required init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) {
println("extendedGraphemeClusterLiteral \(value)")
name = value
}
typealias UnicodeScalarLiteralType = Character
required init(unicodeScalarLiteral value: UnicodeScalarLiteralType) {
println("unicodeScalarLiteral \(value)")
name = "\(value)"
}
}
This allows me to create a Person instance using a string:
let aaron : Person = "Aaron"
I can even cast an array of Persons from an array of strings:
let names = ["John", "Jane"] as [Person]
However this only works with string literals. If I use a string variable, it fails:
let aaronString = "Aaron"
let aaron : Person = aaronString
// Error: 'NSString' is not a subtype of 'Person'
Similarly, trying to cast an array of non-literal strings fails:
let nameStrings = ["John", "Jane"]
let people : [Person] = nameStrings
// Error: 'String' is not identical to 'Person'
I have three questions:
Is there another protocol I can implement to cast a non-literal string to a Person? I'd like to do this so I can cast entire collections to convert the objects.
If no to #1, is map + an initializer the best way to perform the conversion myself?
let nameStrings = ["John", "Jane"]
let people = nameStrings.map{Person(name: $0)}
If yes to #1, is there a similar approach I can use to specify an approach to convert two objects which are unrelated in hierarchy? That is, can I work around this error without an initializer?
let rikerPerson : Person = "Riker"
let rikerEmployee = rikerPerson as Employee
// Error: 'Person' is not convertible to 'Employee'
What you are describing as “casting” isn’t really casting (in the way that, say, s = “fred”; ns = s as NSString is, or that casts in C++ are).
let names = ["John", "Jane"] as [Person]
is just another a way of writing:
let names: [Person] = ["John", "Jane"]
that is, a way of telling Swift which of the many possible versions of StringLiteralConvertible to use (and not the one for String, which is the default).
Put it another way – your as is fulfilling a similar function to the as in this snippet that disambiguates two overloaded functions that differ only by return type:
func f() -> String { return "foo" }
func f() -> Int { return 42 }
let i = f() as Int // i will be 42
let s = f() as String // s will be “foo"
No “conversion” is going on here – the as is just being used to disambiguate which f Swift calls. It’s the same with which init(stringLiteral:) is chosen.
Definitely (but only if you put a space between map and the { } ;-).
If you’re concerned about the waste of converting it all to an array just to do some other thing with it, check out lazy(a).map
Nope. In the betas, there used to be a __conversion() -> T method you could implement to do “casts” like this on your own classes – or more importantly, allowed you to pass your Person class into a function that took an Employee argument and have it be converted implicitly. But that got disappeared. Generally that kind of implicit conversion is antithetical to Swift’s style, except in rare cases (Obj-C and C interop, and implicit wrapping in optionals, being the main ones). You have to write an init for Employee that takes a Person (or some class or protocol that Person conforms to), and then call it.