Swift AnyObject as? Set<T> crash - swift

I came across the following behaviour while programming in Swift:
If you run this snippet:
import Foundation
class A : Hashable, Equatable {
var hashValue: Int {
get { return ObjectIdentifier(self).hashValue }
}
}
func ==(a:A, b:A) -> Bool {
return a === b
}
typealias T = A
typealias TCollection = Array<T>
var element:TCollection = [T()]
var array:[AnyObject] = [element]
println(array[0] as? TCollection)
it prints:
Optional([SetCrashTest.A])
However if you change TCollection from Array<T> to Set<T>, it crashes with the following error:
Could not cast value of type 'SetCrashTest.A' (0x1001d1530) to 'NSObject' (0x7fff72a9d0f0).
Also, it runs correctly if I change T from A to some other type, like Int, String, CGFloat etc.
What causes this behaviour? And how can one do such optional downcasting without crashing?
Edit:
I experienced this crash when trying to read a Set<T> from an NSNotification's userInfo, which is of type [NSObject:AnyObject]. It was only after this, that I've constructed the above example; so please don't advise to use [TCollection] instead of [AnyObject], as AnyObject is dictated by the NSNotification API.

Related

Swift get struct properties from type reference

I'm trying to loop over the struct properties but I only have reference for struct type not fully initialized struct. if I were to have an initialized struct I can use the mirror methods but I only have a type reference from generics.
Here in my code I'm trying to generate other code for some reason 😅 I'll create function arguments from struct properties.
class LoginRequiredRule<T: Request>: Rule {
var validator: (T) -> Void
init(validator: #escaping (T) -> Void) {
self.validator = validator
}
let a = T.Type.self
func generateTree(_ depth: Int, path: String) -> String {
var o = "";
// i need to loop over the properities of T here but i only have access to T.self
return o;
}
}
I tried using the objc foundation functions to access struct but I don't know objc 🙃.
Is this even possible in swift runtime?
I know I can implement a protocol that contains static reference to properties but there has to be a swifty way 😀

Create Set of Array in Swift

I was trying to create a set out of an array to make elements unique.
I thought it would be an elegant approach to use the Set initializer to do this job. So I created an array and tried to initialize my new set using that array.
But the compiler was complaining. I tried it using Integer values and it works like a charm. But with my own class it does not work. I also thought that the Equatable Protocol is the right way to go .. but as you see, it does not solve my Problem.
Consider the following Playground:
import UIKit
internal struct Object: Equatable {
var a: Int
}
internal func == (lhs:Object,rhs: Object) -> Bool {
return lhs.a == rhs.a
}
let array = [Object(a:1),Object(a:1),Object(a:1),Object(a:1),Object(a:2)]
let set = Set(array) // ERROR
The Compiler is complaining with
can not invoke initializer Set<_> with argument of type [Object]
Which Protocol do I need to implement to makes things work?
If you 'command-click' on Set in Xcode, it takes you to the definition of the Set type. And there you go:
/// A collection of unique `Element` instances with no defined ordering.
public struct Set<Element : Hashable> : Hashable, CollectionType ...
As mentioned by Rob the elements need to confirm to Hashable (which in turn requires Equatable).
Adjusting your code:
import Foundation
internal struct Object: Hashable {
var a: Int
var hashValue: Int { return a.hash }
}
internal func == (lhs:Object,rhs: Object) -> Bool {
return lhs.a == rhs.a
}
let array = [Object(a:1),Object(a:1),Object(a:1),Object(a:1),Object(a:2)]
let set = Set(array) // SUCCESS

Using a Type Variable in a Generic

I have this question except for Swift. How do I use a Type variable in a generic?
I tried this:
func intType() -> Int.Type {
return Int.self
}
func test() {
var t = self.intType()
var arr = Array<t>() // Error: "'t' is not a type". Uh... yeah, it is.
}
This didn't work either:
var arr = Array<t.Type>() // Error: "'t' is not a type"
var arr = Array<t.self>() // Swift doesn't seem to even understand this syntax at all.
Is there a way to do this? I get the feeling that Swift just doesn't support it and is giving me somewhat ambiguous error messages.
Edit: Here's a more complex example where the problem can't be circumvented using a generic function header. Of course it doesn't make sense, but I have a sensible use for this kind of functionality somewhere in my code and would rather post a clean example instead of my actual code:
func someTypes() -> [Any.Type] {
var ret = [Any.Type]()
for (var i = 0; i<rand()%10; i++) {
if (rand()%2 == 0){ ret.append(Int.self) }
else {ret.append(String.self) }
}
return ret
}
func test() {
var ts = self.someTypes()
for t in ts {
var arr = Array<t>()
}
}
Swift's static typing means the type of a variable must be known at compile time.
In the context of a generic function func foo<T>() { ... }, T looks like a variable, but its type is actually known at compile time based on where the function is called from. The behavior of Array<T>() depends on T, but this information is known at compile time.
When using protocols, Swift employs dynamic dispatch, so you can write Array<MyProtocol>(), and the array simply stores references to things which implement MyProtocol — so when you get something out of the array, you have access to all functions/variables/typealiases required by MyProtocol.
But if t is actually a variable of kind Any.Type, Array<t>() is meaningless since its type is actually not known at compile time. (Since Array is a generic struct, the compiler needs know which type to use as the generic parameter, but this is not possible.)
I would recommend watching some videos from WWDC this year:
Protocol-Oriented Programming in Swift
Building Better Apps with Value Types in Swift
I found this slide particularly helpful for understanding protocols and dynamic dispatch:
There is a way and it's called generics. You could do something like that.
class func foo() {
test(Int.self)
}
class func test<T>(t: T.Type) {
var arr = Array<T>()
}
You will need to hint the compiler at the type you want to specialize the function with, one way or another. Another way is with return param (discarded in that case):
class func foo() {
let _:Int = test()
}
class func test<T>() -> T {
var arr = Array<T>()
}
And using generics on a class (or struct) you don't need the extra param:
class Whatever<T> {
var array = [T]() // another way to init the array.
}
let we = Whatever<Int>()
jtbandes' answer - that you can't use your current approach because Swift is statically typed - is correct.
However, if you're willing to create a whitelist of allowable types in your array, for example in an enum, you can dynamically initialize different types at runtime.
First, create an enum of allowable types:
enum Types {
case Int
case String
}
Create an Example class. Implement your someTypes() function to use these enum values. (You could easily transform a JSON array of strings into an array of this enum.)
class Example {
func someTypes() -> [Types] {
var ret = [Types]()
for _ in 1...rand()%10 {
if (rand()%2 == 0){ ret.append(.Int) }
else {ret.append(.String) }
}
return ret
}
Now implement your test function, using switch to scope arr for each allowable type:
func test() {
let types = self.someTypes()
for type in types {
switch type {
case .Int:
var arr = [Int]()
arr += [4]
case .String:
var arr = [String]()
arr += ["hi"]
}
}
}
}
As you may know, you could alternatively declare arr as [Any] to mix types (the "heterogenous" case in jtbandes' answer):
var arr = [Any]()
for type in types {
switch type {
case .Int:
arr += [4]
case .String:
arr += ["hi"]
}
}
print(arr)
I would break it down with the things you already learned from the first answer. I took the liberty to refactor some code. Here it is:
func someTypes<T>(t: T.Type) -> [Any.Type] {
var ret = [Any.Type]()
for _ in 0..<rand()%10 {
if (rand()%2 == 0){ ret.append(T.self) }
else {
ret.append(String.self)
}
}
return ret
}
func makeArray<T>(t: T) -> [T] {
return [T]()
}
func test() {
let ts = someTypes(Int.self)
for t in ts {
print(t)
}
}
This is somewhat working but I believe the way of doing this is very unorthodox. Could you use reflection (mirroring) instead?
Its possible so long as you can provide "a hint" to the compiler about the type of... T. So in the example below one must use : String?.
func cast<T>(_ value: Any) -> T? {
return value as? T
}
let inputValue: Any = "this is a test"
let casted: String? = cast(inputValue)
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>
Why Swift doesn't just allow us to let casted = cast<String>(inputValue) I'll never know.
One annoying scenerio is when your func has no return value. Then its not always straightford to provide the necessary "hint". Lets look at this example...
func asyncCast<T>(_ value: Any, completion: (T?) -> Void) {
completion(value as? T)
}
The following client code DOES NOT COMPILE. It gives a "Generic parameter 'T' could not be inferred" error.
let inputValue: Any = "this is a test"
asyncCast(inputValue) { casted in
print(casted)
print(type(of: casted))
}
But you can solve this by providing a "hint" to compiler as follows:
asyncCast(inputValue) { (casted: String?) in
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>
}

Swift generic parameter cannot be bound to non-#objc protocol

The following code produces a compile error of "Generic parameter "T" cannot be bound to non-#objc protocol type 'AAA' on the fail line. When I use a class instead of a protocol, it works ok. Also, if I add an #objc to the protocol it also works, but only in 6.4 beta. Any suggestions would be helpful.
protocol AAA {
var id: String { get set }
}
class BBB: AAA {
var id: String = ""
}
class BBBService {
func getAll<T:AAA>() -> [T] {
var returnArray:[T] = [T]()
return returnArray
}
}
class TestIt
{
func myTest() {
var service = BBBService()
var fail:[AAA] = service.getAll() // fails with Generic parameter "T" cannot be bound to non-#objc protocol type AAA
var succeed:[BBB] = service.getAll()
}
}
this also fails:
<T where T:AAA>
Update - from a practical perspective, adding the #objc causes other problems in my app. So, that is not an option at this point and time.
The trouble is with this line:
getAll<T: AAA>() -> [T]
you are declaring that T must be a concrete type that implements the protocol AAA. It’s important to distinguish between the protocol AAA, i.e. code like this:
func getAll() -> [AAA] {
var returnArray: [AAA] = [BBB()]
return returnArray
}
which works fine (returns an array of references to AAA-conforming objects, which could be of type BBB, or type CCC), and this:
func getAll<T: AAA>() -> [T] {
var returnArray: [T] = [] // what is T? No idea.
return returnArray
}
in which you are saying to the compiler “write me a version of getAll, in which T can be replaced by any specific type that implements AAA”.
This is why your second version compiles - you’re fixing T to be the actual type BBB.
Bear in mind, T might be a struct. In which case the array returned must be sized specifically for whatever struct is being held, right there as a value within the array. As opposed to if the protocol was #objc in which case it would at least be known to only be a class reference of a fixed size.
If what you actually want is an array of protocols, you should remove the generic placeholder and just return an array of protocols:
func getAll() -> [AAA] {
var returnArray: [AAA] = []
return returnArray
}

Swift: Type does not conform to protocol 'ArrayLiteralConvertible'

Keep getting an error in my playground saying that Set does not conform to protocol 'ArrayLiteralConvertible'
struct Set<T: Hashable> : ArrayLiteralConvertible {
typealias Element = T
private var contents: [Element: Bool]
init() {
self.contents = [Element: Bool]()
}
// easier initialization
init<S: SequenceType where S.Generator.Element == Element>(_ sequence: S) {
self.contents = [Element: Bool]()
Swift.map(sequence) { self.contents[$0] = true }
}
// allow Set creation through simple assignment using ArrayLiteralConvertible protocol
internal static func convertFromArrayLiteral(elements: Element...) -> Set {
return Set(elements)
}
}
ArrayLiteralConvertible requires you to implement an initializer of type init(arrayLiteral: Element...). Something like this would re-use your initializer that takes a sequence:
init(arrayLiteral: Element...) {
self = Set(arrayLiteral)
}
If you’re doing this in a playground, hit opt-cmd-enter to see more details in the assistant editor than you get just from the error message in the source. It shows the details of all the protocol requirements you aren’t meeting.
By the way, if you declared contents like this:
private var contents: [Element: Bool] = [:]
you wouldn’t need to initialize it in each of your initializers.
ArrayLiteralConvertible protocol needs an initializer init(arrayLiteral elements: Element...) in the class
init(arrayLiteral elements: Element...) {
self = Set(elements)
}
You don't need to implement the convertFromArrayLiteral function anymore. It was in the old version of swift. It was deprecated in iOS 8.1 API. You just need the initializer above.
https://developer.apple.com/library/ios/releasenotes/General/iOS81APIDiffs/modules/Swift.html
http://swiftdoc.org/protocol/ArrayLiteralConvertible/
Thanks, you got me on the right track. I found a gist on the github with some ArrayLiteralConvertible changes in syntax. It went to this tune:
init(arrayLiteral elements: T...) {
self.init(elements)
}
This seems to work.