Ambiguous reference to member 'subscript' on Dictionary - ios

I am trying to create a failable initializer for a class. My class will be initialized using input from a network request. Networks being unreliable, I want to create an initializer the checks for the presence on all properties, and for it to fail otherwise.
I am trying to make use of guard here, so please feel free to point any obvious mistakes in the approach:
public class JobModel {
let jobId: String
let status: String
let toName: String
let toAddress: String
let description: String
let fee: Int
let jobDate: NSDate
let fromName: String
let fromAddress: String
init?(job: [String:AnyObject]) throws {
guard self.jobId = job["jobid"] as! String else {
throw InitializationError.MissingJobId
}
}
}
The guard self.jobId line is failing to compile, with error:
Ambiguous reference to member 'subscript'
Any ideas on how to correct this error?

guard requires a condition that conforms to BooleanType. Simple assignment doesn't. You would need something like this.
guard let j = job["jobid"] as? String else {
throw InitializationError.MissingJobId
}
self.jobId = j
However, then you'll get the error "all stored properties of a class instance must be initialized before throwing from an initializer." This is expected and documented in the The Swift Programming Language:
For classes, however, a failable initializer can trigger an
initialization failure only after all stored properties introduced by
that class have been set to an initial value and any initializer
delegation has taken place.
Chris Lattner mentions the current behavior is undesirable here: http://swift-language.2336117.n4.nabble.com/Swift-2-throwing-in-initializers-td439.html

Related

Unclear use of Storage

How do I use Storage in Vapor 4?
I tried the following:
if let someValue = req.storage.get(theKey) as? String {
// do something
} else {
req.storage.set(theKey, to: "Some Value")
}
However I get the following Errors:
error: type of expression is ambiguous without more context
if let original: String = req.storage.get(theKey) {
~~~~~~~~~~~~^~~~~~~~~~~
error: type of expression is ambiguous without more context
req.storage.set(theKey, to: "Some Value")
~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I also did not find any documentation on this topic.
The input key is actually a Protocol use must implement. If you want to see examples of how Store is used, do like #imike says and look for usages of the Storage class.
Storage is not string based key object storage. You have to declare a struct conforming to StorageKey, implement the typealias Value representing the object type you want to store.
If you need to store a string let's take this example :
struct MyStringKey: StorageKey {
typealias Value = String
}
...
request.storage.set(MyStringKey.self, to: "my string")
var myString: String? = request.storage.get(MyStringKey.self)
It is NOT possible for the Key to be any arbitrary String. The key must be predefined.

iOS Swift4 how to reconcile T.Type and type(of:) to pass dynamic class type as function parameter?

I'm trying implement generic storage of configuration parameters by using class type string as a dictionary key. The idea is that the retrieve function will return an object of proper type. Each type is unique in the storage. However, when I call the function, I'm getting a Swift compiler error and am not sure how interpret it:
Compiler Error:
Cannot invoke 'retrieve' with an argument list of type '(type: Any.Type)
I checked the documentation, and it seems like the type(of:) method is supposed to return runtime class, while the compiler makes it look like it's complaining because it thinks I'm passing a type of Any
How do I pass Swift class name as a function parameter without using an instance of that class?
func retrieve<T>(type: T.Type) -> T? {
let valueKey = String(describing: type)
print("retrieving: \(valueKey)")
return dictionary[valueKey] as? T
}
func update(with value: Any) {
let valueKey = String(describing: type(of: value))
print("Updating: \(valueKey)")
dictionary[valueKey] = value
}
let testCases: [Any] = [1, 2.0, "text"]
for testCase in testCases {
subject.update(with: testCase)
//Compiler Error: Cannot invoke 'retrieve' with an argument list of type '(type: Any.Type)
let t = type(of: testCase)
let retrieved = subject.retrieve(type: t)
//check for equality
}
//this works
expect(subject.retrieve(type: Int.self)).to(equal(1))
expect(subject.retrieve(type: Double.self)).to(equal(2.0))
expect(subject.retrieve(type: String.self)).to(equal("text"))
I've done more testing, and see that it appears my array does not honor the type(of: ) documentation, and this function returns the same object as "Any":
func retrieve<T>(sample: T) -> T? {
let valueKey = String(describing: type(of: sample))
print("retrieving: \(valueKey)") //always retrieves same object "Any"
return dictionary[valueKey] as? T
}
Updated: Thank you for responses, to clarify - test cases were intended to begin with simple types, then progress to more complex classes. The actual implementation would store completely unique instances of custom types, not Strings or Ints.
let testCases: [Any] = [ConnectionConfig(...),
AccountID("testID"),
AccountName("testName")]
The tests recognize the generic nature of the retrieve function and assign appropriate types, as evidenced by code completion:
expect(subject.retrieve(type: ConnectionConfig.self)?.ip).to(equal("defaultIP"))
expect(subject.retrieve(type: AccountID.self)?.value).to(equal("testId"))
The intended end use within RxSwift context: provide the generic storage to a class and allow it to pull the appropriate values for configuration parameters. If no value exists, an error is thrown and is handled by a separate error handler:
class RxConfigConsumer: ConfigConsumer {
var connection: ConnectionConfig?
var accountID: AccountID?
init(with provider: ConfigProvider) {
connection = provider.retrieve(type: ConnectionConfig.self)
accountID = provider.retrieve(type: AccountID.self)
//etc
}
}
The combination of a generic with a metatype (.Type) is very weird and is probably what's tripping you up. If you get rid of the generic things work as you would expect:
func retrieve(_ T:Any.Type) {
print(type(of:T))
}
let testCases: [Any] = [1, 2.0, "text"]
for testCase in testCases {
retrieve(type(of:testCase))
}
// Int.Type, Double.Type, String.Type
If you really want the generic, then get rid of the .Type and write it like this:
func retrieve<T>(_ t:T) {
print(type(of:t))
}
let testCases: [Any] = [1, 2.0, "text"]
for testCase in testCases {
retrieve(type(of:testCase))
}
// Int.Type, Double.Type, String.Type
Even then, however, it's unclear to me what the point is of passing the metatype.
The short answer is what you're trying to do is impossible, because there is no way to type-annotate the following line of code:
let retrieved = subject.retrieve(type: t)
What is the static type, known at compile-time, of retrieved? It can't change at run-time. It certainly can't change from iteration to iteration. The compiler needs to allocate space for it. How much space does it require? Does it require space on the stack or heap? There's no way to know. The best we can say is that it's Any and put a box around it. 1 doesn't even have a proper type anyway. It's just an integer literal. It could be a Float or many other things (try let x: Float = 1 and see).
The answer is you can't build a loop like this. Your individual test cases are the right ones. Once you create an [Any], it is very difficult to get "real" types back out. Avoid it. If you have a more concrete problem beyond the example you've given, we can discuss how to deal with that, but I believe outside of a unit test, this specific problem shouldn't come up anyway.
This is an interesting question and you can run the following codes in a playground.
The first step is to solve the T.Type parameter. It's hard to put it into a function call. So to achieve your goal, we can use T but T.Type.
class MySubject {
var dictionary : [String : Any] = [:]
func retrieve<T>(type1: T) -> T? {
let valueKey = String(describing: (type(of: type1)))
print("retrieving: \(valueKey)")
return dictionary[valueKey] as? T
}
func update(with value: Any) {
let valueKey = String(describing: type(of: value))
print("Updating: \(valueKey)")
dictionary[valueKey] = value
}
}
var subject : MySubject = MySubject()
let testCases: [Any] = [1, 2.0, "text"]
for testCase in testCases {
subject.update(with: testCase)
//Compiler Error: Cannot invoke 'retrieve' with an argument list of type '(type: Any.Type)
let retrieved = subject.retrieve(type1: testCase)
//check for equality
}
The compilation is correct. But as you said, the return value is nil as a result of a generic retrieve Function. In order to achieve your goal, we may skip the generic way, use Any directly.
class MySubject {
var dictionary : [String : Any] = [:]
func retrieve(type1: Any) -> Any? {
let valueKey = String(describing: (type(of: type1)))
print("retrieving: \(valueKey)")
return dictionary[valueKey]
}
func update(with value: Any) {
let valueKey = String(describing: type(of: value))
print("Updating: \(valueKey)")
dictionary[valueKey] = value
}
}
var subject : MySubject = MySubject()
let testCases: [Any] = [1, 2.0, "text"]
for testCase in testCases {
subject.update(with: testCase)
//Compiler Error: Cannot invoke 'retrieve' with an argument list of type '(type: Any.Type)
let retrieved = subject.retrieve(type1: testCase)
//check for equality
}
Currently everything is perfect as you wish. But this brings up an interesting thought about generic. Is it O.K. or right to use generic here? As we know, there is a presumption in generic, which is represented by letters T, U, V. They have one common meaning: Type. When we try to use generic, we assume every parameter should have only one unique type. So in first case, Any is the only type and should be accepted without question in a generic call. There is no other type will be revealed during function call.
This kind of misunderstanding roots from the use of "let testCases: [Any] = [1, 2.0, "text"]. Although swift allows you to write this way, they are not a normal array. They are a list which contains different type essentially. So you can ignore fancy generic here without any regrets. Just pick the Any to solve your problem.

Can't perform methods of objects stored in Array[Any]

I want to store objects of different types in an array.
The program below is only a minimum demo. In the anyArray:[Any] an instance of Object1 is stored. The print statement prints out the expected object type. In the following line the test of the stored object's type returns true. This means, during run time the correct object type is known and every thing seems to be fine.
class Object1 {
var name = "Object1"
}
var anyArray:[Any] = [Object1()]
print("\(type(of: anyArray[0]))")
let testResult = anyArray[0] is Object1
print("Test result:\(testResult)")
//print("Name:\((anyArray[0]).name)")
Console output:
Object1
Test result:true
However, if I try to print out the name property of the object, I get an error message from the editor:
Value of type 'Any' has no member 'name'
Well, at compile time the object's type is unknown. That's why the compiler complains. How can I tell the compiler that it is OK to access the properties of the stored object?
The difference comes from the difference from Type Checking in:
runtime, or
compile time
The is operator checks at runtime whether the expression can be cast to the specified type. type(of:) checks, at runtime, the exact type, without consideration for subclasses.
anyArray[0].name doesn't compile since the Type Any doesn't have a name property.
If you're sure anyArray[0] is an Object1, you could use the downcast operator as!:
print("\((anyArray[0] as! Object1).name)")
To check at runtime if an element from anyArray could be an Object1 use optional binding, using the conditional casting operator as?:
if let:
if let object = anyArray[0] as? Object1 {
print(object.name)
}
Or use the guard statement, if you want to use that object in the rest of the scope:
guard let object = anyArray[0] as? Object1 else {
fatalError("The first element is not an Object1")
}
print(object.name)
If all objects in your array have a name property, and you don't want to go through all the hoops of optional binding repeatedly, then use a protocol. Your code will look like this:
protocol Named {
var name: String {get set}
}
class Object1: Named {
var name = "Object1"
}
var anyArray:[Named] = [Object1()]
print("\(type(of: anyArray[0]))")
let testResult = anyArray[0] is Object1
print("Test result:\(testResult)")
print("Name:\(anyArray[0].name)")
Notice that anyArray is now an array of Named objects, and that Object1 conforms to the Named protocol.
To learn more about protocols, have a look here.
You object is still of type Any. You just checked if it can be of type Object1, but you did not cast it. If you want the object as Object1, you need to cast it.
Also if multiple classes can have name, you need to use Protocol like #vadian has mentioned in his comment and cast it to that protocol.
protocol NameProtocol {
var name: String {get set}
}
class Object1: NameProtocol {
var name = "Object1"
}
if let testResult = anyArray[0] as? NameProtocol {
print(testResult.name)
}
Edit: "I want to store objects of different types in an array". The solution that you have marked as correct will not work if all the objects that you have do not conform to the protocol.

Get the class type name of a UIView subclass in Swift [duplicate]

Is there a way to print the runtime type of a variable in swift? For example:
var now = NSDate()
var soon = now.dateByAddingTimeInterval(5.0)
println("\(now.dynamicType)")
// Prints "(Metatype)"
println("\(now.dynamicType.description()")
// Prints "__NSDate" since objective-c Class objects have a "description" selector
println("\(soon.dynamicType.description()")
// Compile-time error since ImplicitlyUnwrappedOptional<NSDate> has no "description" method
In the example above, I'm looking for a way to show that the variable "soon" is of type ImplicitlyUnwrappedOptional<NSDate>, or at least NSDate!.
Update September 2016
Swift 3.0: Use type(of:), e.g. type(of: someThing) (since the dynamicType keyword has been removed)
Update October 2015:
I updated the examples below to the new Swift 2.0 syntax (e.g. println was replaced with print, toString() is now String()).
From the Xcode 6.3 release notes:
#nschum points out in the comments that the Xcode 6.3 release notes show another way:
Type values now print as the full demangled type name when used with
println or string interpolation.
import Foundation
class PureSwiftClass { }
var myvar0 = NSString() // Objective-C class
var myvar1 = PureSwiftClass()
var myvar2 = 42
var myvar3 = "Hans"
print( "String(myvar0.dynamicType) -> \(myvar0.dynamicType)")
print( "String(myvar1.dynamicType) -> \(myvar1.dynamicType)")
print( "String(myvar2.dynamicType) -> \(myvar2.dynamicType)")
print( "String(myvar3.dynamicType) -> \(myvar3.dynamicType)")
print( "String(Int.self) -> \(Int.self)")
print( "String((Int?).self -> \((Int?).self)")
print( "String(NSString.self) -> \(NSString.self)")
print( "String(Array<String>.self) -> \(Array<String>.self)")
Which outputs:
String(myvar0.dynamicType) -> __NSCFConstantString
String(myvar1.dynamicType) -> PureSwiftClass
String(myvar2.dynamicType) -> Int
String(myvar3.dynamicType) -> String
String(Int.self) -> Int
String((Int?).self -> Optional<Int>
String(NSString.self) -> NSString
String(Array<String>.self) -> Array<String>
Update for Xcode 6.3:
You can use the _stdlib_getDemangledTypeName():
print( "TypeName0 = \(_stdlib_getDemangledTypeName(myvar0))")
print( "TypeName1 = \(_stdlib_getDemangledTypeName(myvar1))")
print( "TypeName2 = \(_stdlib_getDemangledTypeName(myvar2))")
print( "TypeName3 = \(_stdlib_getDemangledTypeName(myvar3))")
and get this as output:
TypeName0 = NSString
TypeName1 = __lldb_expr_26.PureSwiftClass
TypeName2 = Swift.Int
TypeName3 = Swift.String
Original answer:
Prior to Xcode 6.3 _stdlib_getTypeName got the mangled type name of a variable. Ewan Swick's blog entry helps to decipher these strings:
e.g. _TtSi stands for Swift's internal Int type.
Mike Ash has a great blog entry covering the same topic.
Edit: A new toString function has been introduced in Swift 1.2 (Xcode 6.3).
You can now print the demangled type of any type using .self and any instance using .dynamicType:
struct Box<T> {}
toString("foo".dynamicType) // Swift.String
toString([1, 23, 456].dynamicType) // Swift.Array<Swift.Int>
toString((7 as NSNumber).dynamicType) // __NSCFNumber
toString((Bool?).self) // Swift.Optional<Swift.Bool>
toString(Box<SinkOf<Character>>.self) // __lldb_expr_1.Box<Swift.SinkOf<Swift.Character>>
toString(NSStream.self) // NSStream
Try calling YourClass.self and yourObject.dynamicType.
Reference: https://devforums.apple.com/thread/227425.
Swift 3.0
let string = "Hello"
let stringArray = ["one", "two"]
let dictionary = ["key": 2]
print(type(of: string)) // "String"
// Get type name as a string
String(describing: type(of: string)) // "String"
String(describing: type(of: stringArray)) // "Array<String>"
String(describing: type(of: dictionary)) // "Dictionary<String, Int>"
// Get full type as a string
String(reflecting: type(of: string)) // "Swift.String"
String(reflecting: type(of: stringArray)) // "Swift.Array<Swift.String>"
String(reflecting: type(of: dictionary)) // "Swift.Dictionary<Swift.String, Swift.Int>"
Is this what you're looking for?
println("\(object_getClassName(now))");
It prints "__NSDate"
UPDATE: Please note this no longer seems to work as of Beta05
My current Xcode is Version 6.0 (6A280e).
import Foundation
class Person { var name: String; init(name: String) { self.name = name }}
class Patient: Person {}
class Doctor: Person {}
var variables:[Any] = [
5,
7.5,
true,
"maple",
Person(name:"Sarah"),
Patient(name:"Pat"),
Doctor(name:"Sandy")
]
for variable in variables {
let typeLongName = _stdlib_getDemangledTypeName(variable)
let tokens = split(typeLongName, { $0 == "." })
if let typeName = tokens.last {
println("Variable \(variable) is of Type \(typeName).")
}
}
Output:
Variable 5 is of Type Int.
Variable 7.5 is of Type Double.
Variable true is of Type Bool.
Variable maple is of Type String.
Variable Swift001.Person is of Type Person.
Variable Swift001.Patient is of Type Patient.
Variable Swift001.Doctor is of Type Doctor.
As of Xcode 6.3 with Swift 1.2, you can simply convert type values into the full demangled String.
toString(Int) // "Swift.Int"
toString(Int.Type) // "Swift.Int.Type"
toString((10).dynamicType) // "Swift.Int"
println(Bool.self) // "Swift.Bool"
println([UTF8].self) // "Swift.Array<Swift.UTF8>"
println((Int, String).self) // "(Swift.Int, Swift.String)"
println((String?()).dynamicType)// "Swift.Optional<Swift.String>"
println(NSDate) // "NSDate"
println(NSDate.Type) // "NSDate.Type"
println(WKWebView) // "WKWebView"
toString(MyClass) // "[Module Name].MyClass"
toString(MyClass().dynamicType) // "[Module Name].MyClass"
You can still access the class, through className (which returns a String).
There are actually several ways to get the class, for example classForArchiver, classForCoder, classForKeyedArchiver (all return AnyClass!).
You can't get the type of a primitive (a primitive is not a class).
Example:
var ivar = [:]
ivar.className // __NSDictionaryI
var i = 1
i.className // error: 'Int' does not have a member named 'className'
If you want to get the type of a primitive, you have to use bridgeToObjectiveC(). Example:
var i = 1
i.bridgeToObjectiveC().className // __NSCFNumber
You can use reflect to get information about object.
For example name of object class:
var classname = reflect(now).summary
Xcode 8 Swift 3.0 use type(of:)
let className = "\(type(of: instance))"
I had luck with:
let className = NSStringFromClass(obj.dynamicType)
SWIFT 5
With the latest release of Swift 3 we can get pretty descriptions of type names through the String initializer. Like, for example print(String(describing: type(of: object))). Where object can be an instance variable like array, a dictionary, an Int, a NSDate, an instance of a custom class, etc.
Here is my complete answer: Get class name of object as string in Swift
That question is looking for a way to getting the class name of an object as string but, also i proposed another way to getting the class name of a variable that isn't subclass of NSObject. Here it is:
class Utility{
class func classNameAsString(obj: Any) -> String {
//prints more readable results for dictionaries, arrays, Int, etc
return String(describing: type(of: obj))
}
}
I made a static function which takes as parameter an object of type Any and returns its class name as String :) .
I tested this function with some variables like:
let diccionary: [String: CGFloat] = [:]
let array: [Int] = []
let numInt = 9
let numFloat: CGFloat = 3.0
let numDouble: Double = 1.0
let classOne = ClassOne()
let classTwo: ClassTwo? = ClassTwo()
let now = NSDate()
let lbl = UILabel()
and the output was:
diccionary is of type Dictionary
array is of type Array
numInt is of type Int
numFloat is of type CGFloat
numDouble is of type Double
classOne is of type: ClassOne
classTwo is of type: ClassTwo
now is of type: Date
lbl is of type: UILabel
In Xcode 8, Swift 3.0
Steps:
1. Get the Type:
Option 1:
let type : Type = MyClass.self //Determines Type from Class
Option 2:
let type : Type = type(of:self) //Determines Type from self
2. Convert Type to String:
let string : String = "\(type)" //String
In Swift 3.0, you can use type(of:), as dynamicType keyword has been removed.
To get a type of object or class of object in Swift, you must need to use a type(of: yourObject)
type(of: yourObject)
When using Cocoa (not CocoaTouch), you can use the className property for objects that are subclasses of NSObject.
println(now.className)
This property is not available for normal Swift objects, which aren't subclasses of NSObject (and in fact, there is no root id or object type in Swift).
class Person {
var name: String?
}
var p = Person()
println(person.className) // <- Compiler error
In CocoaTouch, at this time there is not a way to get a string description of the type of a given variable. Similar functionality also does not exist for primitive types in either Cocoa or CocoaTouch.
The Swift REPL is able to print out a summary of values including its type, so it is possible this manner of introspection will be possible via an API in the future.
EDIT: dump(object) seems to do the trick.
The top answer doesn't have a working example of the new way of doing this using type(of:. So to help rookies like me, here is a working example, taken mostly from Apple's docs here - https://developer.apple.com/documentation/swift/2885064-type
doubleNum = 30.1
func printInfo(_ value: Any) {
let varType = type(of: value)
print("'\(value)' of type '\(varType)'")
}
printInfo(doubleNum)
//'30.1' of type 'Double'
I've tried some of the other answers here but milage seems to very on what the underling object is.
However I did found a way you can get the Object-C class name for an object by doing the following:
now?.superclass as AnyObject! //replace now with the object you are trying to get the class name for
Here is and example of how you would use it:
let now = NSDate()
println("what is this = \(now?.superclass as AnyObject!)")
In this case it will print NSDate in the console.
I found this solution which hopefully might work for someone else.
I created a class method to access the value. Please bear in mind this will work for NSObject subclass only. But at least is a clean and tidy solution.
class var className: String!{
let classString : String = NSStringFromClass(self.classForCoder())
return classString.componentsSeparatedByString(".").last;
}
In the latest XCode 6.3 with Swift 1.2, this is the only way I found:
if view.classForCoder.description() == "UISegment" {
...
}
Many of the answers here do not work with the latest Swift (Xcode 7.1.1 at time of writing).
The current way of getting the information is to create a Mirror and interrogate that. For the classname it is as simple as:
let mirror = Mirror(reflecting: instanceToInspect)
let classname:String = mirror.description
Additional information about the object can also be retrieved from the Mirror. See http://swiftdoc.org/v2.1/type/Mirror/ for details.
Swift version 4:
print("\(type(of: self)) ,\(#function)")
// within a function of a class
Thanks #Joshua Dance
In lldb as of beta 5, you can see the class of an object with the command:
fr v -d r shipDate
which outputs something like:
(DBSalesOrderShipDate_DBSalesOrderShipDate_ *) shipDate = 0x7f859940
The command expanded out means something like:
Frame Variable (print a frame variable) -d run_target (expand dynamic types)
Something useful to know is that using "Frame Variable" to output variable values guarantees no code is executed.
I've found a solution for self-developed classes (or such you have access to).
Place the following computed property within your objects class definition:
var className: String? {
return __FILE__.lastPathComponent.stringByDeletingPathExtension
}
Now you can simply call the class name on your object like so:
myObject.className
Please note that this will only work if your class definition is made within a file that is named exactly like the class you want the name of.
As this is commonly the case the above answer should do it for most cases. But in some special cases you might need to figure out a different solution.
If you need the class name within the class (file) itself you can simply use this line:
let className = __FILE__.lastPathComponent.stringByDeletingPathExtension
Maybe this method helps some people out there.
Based on the answers and comments given by Klass and Kevin Ballard above, I would go with:
println(_stdlib_getDemangledTypeName(now).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(soon).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(soon?).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(soon!).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar0).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar1).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar2).componentsSeparatedByString(".").last!)
println(_stdlib_getDemangledTypeName(myvar3).componentsSeparatedByString(".").last!)
which will print out:
"NSDate"
"ImplicitlyUnwrappedOptional"
"Optional"
"NSDate"
"NSString"
"PureSwiftClass"
"Int"
"Double"
let i: Int = 20
func getTypeName(v: Any) -> String {
let fullName = _stdlib_demangleName(_stdlib_getTypeName(i))
if let range = fullName.rangeOfString(".") {
return fullName.substringFromIndex(range.endIndex)
}
return fullName
}
println("Var type is \(getTypeName(i)) = \(i)")
Swift 4:
// "TypeName"
func stringType(of some: Any) -> String {
let string = (some is Any.Type) ? String(describing: some) : String(describing: type(of: some))
return string
}
// "ModuleName.TypeName"
func fullStringType(of some: Any) -> String {
let string = (some is Any.Type) ? String(reflecting: some) : String(reflecting: type(of: some))
return string
}
Usage:
print(stringType(of: SomeClass())) // "SomeClass"
print(stringType(of: SomeClass.self)) // "SomeClass"
print(stringType(of: String())) // "String"
print(fullStringType(of: String())) // "Swift.String"
There appears to be no generic way to print the type name of an arbitrary value's type. As others have noted, for class instances you can print value.className but for primitive values it appears that at runtime, the type information is gone.
For instance, it looks as if there's not a way to type: 1.something() and get out Int for any value of something. (You can, as another answer suggested, use i.bridgeToObjectiveC().className to give you a hint, but __NSCFNumber is not actually the type of i -- just what it will be converted to when it crosses the boundary of an Objective-C function call.)
I would be happy to be proven wrong, but it looks like the type checking is all done at compile time, and like C++ (with RTTI disabled) much of the type information is gone at runtime.
This is how you get a type string of your object or Type which is consistent and takes into account to which module the object definition belongs to or nested in. Works in Swift 4.x.
#inline(__always) func typeString(for _type: Any.Type) -> String {
return String(reflecting: type(of: _type))
}
#inline(__always) func typeString(for object: Any) -> String {
return String(reflecting: type(of: type(of: object)))
}
struct Lol {
struct Kek {}
}
// if you run this in playground the results will be something like
typeString(for: Lol.self) // __lldb_expr_74.Lol.Type
typeString(for: Lol()) // __lldb_expr_74.Lol.Type
typeString(for: Lol.Kek.self)// __lldb_expr_74.Lol.Kek.Type
typeString(for: Lol.Kek()) // __lldb_expr_74.Lol.Kek.Type
Not exactly what you are after, but you can also check the type of the variable against Swift types like so:
let object: AnyObject = 1
if object is Int {
}
else if object is String {
}
For example.
Xcode 7.3.1, Swift 2.2:
String(instanceToPrint.self).componentsSeparatedByString(".").last

Assigning let variable in fallible initializer swift 1.2

I have a struct with a fallible initializer, not an instance method, but an initializer. After updating to 1.2, when I try to assign a let property inside the initializer, I receive the following error Cannot assign to 'aspectRatio' in self. My code below:
import Foundation
public struct MediaItem
{
public let url: NSURL!
public let aspectRatio: Double
public var description: String { return (url.absoluteString ?? "no url") + " (aspect ratio = \(aspectRatio))" }
// MARK: - Private Implementation
init?(data: NSDictionary?) {
var valid = false
if let urlString = data?.valueForKeyPath(TwitterKey.MediaURL) as? NSString {
if let url = NSURL(string: urlString as String) {
self.url = url
let h = data?.valueForKeyPath(TwitterKey.Height) as? NSNumber
let w = data?.valueForKeyPath(TwitterKey.Width) as? NSNumber
if h != nil && w != nil && h?.doubleValue != 0 {
aspectRatio = w!.doubleValue / h!.doubleValue
valid = true
}
}
}
if !valid {
return nil
}
}
struct TwitterKey {
static let MediaURL = "media_url_https"
static let Width = "sizes.small.w"
static let Height = "sizes.small.h"
}
}
My question is what do I do to fix this?
Swift 1.2 has closed a loophole having to do with let properties:
The new rule is that a let constant must be initialized before use (like a var), and that it may only be initialized, not reassigned or mutated after initialization.
That rule is exactly what you are trying to violate. aspectRatio is a let property and you have already given it a value in its declaration:
public let aspectRatio: Double = 0
So before we ever get to the initializer, aspectRatio has its initial value — 0. And that is the only value it can ever have. The new rule means that you can never assign to aspectRatio ever again, not even in an initializer.
The solution is (and this was always the right way): assign it no value in its declaration:
public let aspectRatio: Double
Now, in the initializer, either assign it 0 or assign it w!.doubleValue / h!.doubleValue. In other words, take care of every possibility in the initializer, once. That will be the only time, one way or another, that you get to assign aspectRatio a value.
If you think about it, you'll realize that this is a much more sensible and consistent approach; previously, you were sort of prevaricating on the meaning of let, and the new rule has stopped you, rightly, from doing that.
In your rewrite of the code, you are failing to initialize all properties in the situation where you intend to bail out and return nil. I know it may seem counterintuitive, but you cannot do that. You must initialize all properties even if you intend to bail out. I discuss this very clearly in my book:
A failable class initializer cannot say return nil until after it has completed all of its own initialization duties. Thus, for example, a failable subclass designated initializer must see to it that all the subclass’s properties are initialized and must call super.init(...) before it can say return nil. (There is a certain delicious irony here: before it can tear the instance down, the initializer must finish building the instance up.)
EDIT: Please note that starting in Swift 2.2, this requirement will be lifted. It will be legal to return nil before initializing properties. This will put class initializers on a par with struct initializers, where this was already legal.

Resources