Where in Swift is defined that we can use `[]` as Array literal? - ios

I try to find in Swift documentation where it is defined, but I couldn't.
I expect something like following:
typealias [] = Array
typealias [Element] = Array<Element>
typealias [AnyObject] = Array<AnyObject>
So why it is possible to use [] instead of Array as initializer?
Edit depending on answer
I try to do the same with my custom Person class but it doesn't work:
class Person: ArrayLiteralConvertible {
typealias Element
public init(arrayLiteral elements: Self.Element...)
}
let personNames: Person = ["John", "Mark", "Kris"]

It's defined in the Summary of the Grammar, in the definition of array-literal:
array-literal → [ array-literal-items(opt) ]
array-literal-items → array-literal-item ,(opt) | array-literal-item , array-literal-items
array-literal-item → expression
In more "descriptive" text, this particular case is defined in The Swift Programming Language, Collection Types in the section on Arrays:
Creating an Empty Array
You can create an empty array of a certain type using initializer syntax:
var someInts: [Int] = []
print("someInts is of type [Int] with \(someInts.count) items.")
// Prints "someInts is of type [Int] with 0 items."
Note that the type of the someInts variable is inferred to be
[Int] from the type of the initializer.
To your updated question, the correct syntax (circa Swift 5, though your question is from and older version), would be:
class Person: ExpressibleByArrayLiteral {
required public init(arrayLiteral elements: String...) { ... }
}

You can write it a as a literal everything that conforms to literal protocols . Sets as Array conforms to ArrayLiteralConvertible protocol, you can declare a set by using the same array literal sintax and specifing the type to avoid compiler confusion or they still will be inferred as Array:
let set: Set = ["Hello","Bye"]
To conforms array literal protocol you must provide an initializer:
protocol ArrayLiteralConvertible {
typealias Element
init(arrayLiteral elements: Element...)
}
There is a nice post on NSHipster
Documentation define a literal as:
A literal is the source code representation of a value of a type, such
as a number or string.
Where it a literal first defined? I guess it's the duty of the compiler detect them and substitute with the proper type.

Related

Can #dynamicMemberLookup be used to call methods?

In the documentation for #dynamicMemberLookup it says,
Apply this attribute to a class, structure, enumeration, or protocol to enable members to be looked up by name at runtime.
If I'm not mistaken, instance methods are considered members of a struct / class. However, when I try to call a function dynamically I get an error saying:
Dynamic key path member lookup cannot refer to instance method foo()
To reproduce the problem:
struct Person {
var name: String
var age: Int
func greet() {
print("hello, my name is \(name)")
}
}
#dynamicMemberLookup
struct Wrapper {
var value: Person
subscript<T>(dynamicMember keypath: KeyPath<Person, T>) -> T {
value[keyPath: keypath]
}
}
let person = Person(name: "John Doe", age: 21)
let wrapper = Wrapper(value: person)
wrapper.greet() // << Error: Dynamic key path member lookup cannot refer to instance method `greet()`
// Or
let function = wrapper.greet // << Error: Dynamic key path member lookup cannot refer to instance method `greet()`
function()
How can I dynamically call greet() using #dynamicMemberLookup? Is there any way to achieve what I'm trying to do?
Thanks in advance!
No, dynamicMemberLookup does not work for methods. As the signature of the subscript suggests, it only works for things that can be represented as a KeyPath. Method calls cannot be part of a key path. :(
Key-Path Expression
A key-path expression refers to a property or subscript of a type.
The path consists of property names, subscripts, optional-chaining
expressions, and forced unwrapping expressions. Each of these key-path
components can be repeated as many times as needed, in any order.
At compile time, a key-path expression is replaced by an instance of
the KeyPath class.
I suspect the reason why it is called "dynamic member lookup" is because it also works with subscripts. The alternative of dynamicPropertyOrSubscriptLookup is rather a mouthful isn't it?
One rather hacky fix would be to change greet into a computed property:
var greet: () -> Void { {
print("hello, my name is \(name)")
} }
If greet has had parameters, you could also change it into a subscript, but I think that is an even uglier solution.

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.

Combining two arrays of different types [duplicate]

I'm trying to find the best way to merge Swift arrays, that are not of same type, but they have same superclass. I've already read all the tutorials so I know I can use:
var array = ["Some", "Array", "of", "Strings"]
array += ["Another string", "and one more"]
array.append(["Or", "This"])
In this case array variable infers the type [String]. The problem I have relates to the next hypothetical situation with classes (properties do not matter in this case, just there to make a difference between classes):
class A {
var property1 : String?
}
class B : A {
var property2: String?
}
class C : A {
var property3: String?
}
So I create an array of class A and B objects:
var array = [ A(), B(), A(), B() ]
This array should now be of type [A], since this is the inferred type by both A and B classes.
Now I want to append objects to this array, that are of type C.
var anotherArray = [ C(), C(), C() ]
Since anotherArray is now of type [C], we should still be able to append it, since all C instances respond to A methods. So we try:
array += anotherArray
This fails due to:
Binary operator '+=' cannot be applied to operands of type '[A]' and '[C]'.
Similar story with using append method. While this does make sense, I cannot understand why this couldn't work.
Can someone explain why this is not working? What is the best solution to this problem?
The only sensible solution I found is to define the type of anotherArray to be [A], but are there any better ones or this is correct?
var anotherArray : [A] = [ C(), C(), C() ]
Thanks!
If C inherits from A then you can "upcast" an array of type [C] to an array of type [A]
array += anotherArray as [A]
Alternatively, use (tested with Swift 4)
array.append(contentsOf: anotherArray)
In addition to Martin's answer, you could create a protocol that all of the classes conform to and then when creating your array, make it's type that protocol.
Then you can add any of the classes to it without casting.
you can merge almost everything. the only requirement is that all elements of resulting array must conform to the same protocol.
let arr1 = [1,2,3] // [Int]
let arr2 = [4.0,5.0,6.0] // [Double]
let arr3 = ["a","b"] // [String]
import Foundation // NSDate
let arr4 = [NSDate()] // [NSDate]
// the only common protocol in this case is Any
var arr:[Any] = []
arr1.forEach { arr.append($0) }
arr2.forEach { arr.append($0) }
arr3.forEach { arr.append($0) }
arr4.forEach { arr.append($0) }
print(arr) // [1, 2, 3, 4.0, 5.0, 6.0, "a", "b", 2016-02-15 08:25:03 +0000]

How to put different values and keys into a dictionary [duplicate]

I want to create a Dictionary that does not limit the key type (like NSDictionary)
So I tried
var dict = Dictionary<Any, Int>()
and
var dict = Dictionary<AnyObject, Int>()
resulting
error: type 'Any' does not conform to protocol 'Hashable'
var dict = Dictionary<Any, Int>()
^
<REPL>:5:12: error: cannot convert the expression's type '<<error type>>' to type '$T1'
var dict = Dictionary<Any, Int>()
^~~~~~~~~~~~~~~~~~~~~~
OK, I will use Hashable
var dict = Dictionary<Hashable, Int>()
but
error: type 'Hashable' does not conform to protocol 'Equatable'
var dict = Dictionary<Hashable, Int>()
^
Swift.Equatable:2:8: note: '==' requirement refers to 'Self' type
func ==(lhs: Self, rhs: Self) -> Bool
^
Swift.Hashable:1:10: note: type 'Hashable' does not conform to inherited protocol 'Equatable.Protocol'
protocol Hashable : Equatable
^
<REPL>:5:12: error: cannot convert the expression's type '<<error type>>' to type '$T1'
var dict = Dictionary<Hashable, Int>()
^~~~~~~~~~~~~~~~~~~~~~~~~~~
So Hashable inherited from Equatable but it does not conform to Equatable??? I don't understand...
Anyway, keep trying
typealias KeyType = protocol<Hashable, Equatable> // KeyType is both Hashable and Equatable
var dict = Dictionary<KeyType, Int>() // now you happy?
with no luck
error: type 'KeyType' does not conform to protocol 'Equatable'
var dict = Dictionary<KeyType, Int>()
^
Swift.Equatable:2:8: note: '==' requirement refers to 'Self' type
func ==(lhs: Self, rhs: Self) -> Bool
^
Swift.Hashable:1:10: note: type 'KeyType' does not conform to inherited protocol 'Equatable.Protocol'
protocol Hashable : Equatable
^
<REPL>:6:12: error: cannot convert the expression's type '<<error type>>' to type '$T1'
var dict = Dictionary<KeyType, Int>()
^~~~~~~~~~~~~~~~~~~~~~~~~~
I am so lost now, how can I make compiler happy with my code?
I want to use the dictionary like
var dict = Dictionary<Any, Int>()
dict[1] = 2
dict["key"] = 3
dict[SomeEnum.SomeValue] = 4
I know I can use Dictionary<NSObject, Int>, but it is not really what I want.
Swift 3 update
You can now use AnyHashable which is a type-erased hashable value, created exactly for scenarios like this:
var dict = Dictionary<AnyHashable, Int>()
I believe that, as of Swift 1.2, you can use an ObjectIdentifier struct for this. It implements Hashable (and hence Equatable) as well as Comparable. You can use it to wrap any class instance. I'm guessing the implementation uses the wrapped object's underlying address for the hashValue, as well as within the == operator.
I took the liberty of cross-posting / linking to this question on a separate post on the Apple Dev forums and this question is answered here.
Edit
This answer from the above link works in 6.1 and greater:
struct AnyKey: Hashable {
private let underlying: Any
private let hashValueFunc: () -> Int
private let equalityFunc: (Any) -> Bool
init<T: Hashable>(_ key: T) {
underlying = key
// Capture the key's hashability and equatability using closures.
// The Key shares the hash of the underlying value.
hashValueFunc = { key.hashValue }
// The Key is equal to a Key of the same underlying type,
// whose underlying value is "==" to ours.
equalityFunc = {
if let other = $0 as? T {
return key == other
}
return false
}
}
var hashValue: Int { return hashValueFunc() }
}
func ==(x: AnyKey, y: AnyKey) -> Bool {
return x.equalityFunc(y.underlying)
}
Dictionary is struct Dictionary<Key : Hashable, Value>...
Which means that Value could be anything you want, and Key could be any type you want, but Key must conform to Hashable protocol.
You can't create Dictionary<Any, Int>() or Dictionary<AnyObject, Int>(), because Any and AnyObject can't guarantee that such a Key conforms Hashable
You can't create Dictionary<Hashable, Int>(), because Hashable is not a type it is just protocol which is describing needed type.
So Hashable inherited from Equatable but it does not conform to
Equatable??? I don't understand...
But you are wrong in terminology. Original error is
type 'Hashable' does not conform to inherited protocol 'Equatable.Protocol'
That means that Xcode assuming 'Hashable' as some type, but there is no such type. And Xcode treat it as some kind empty type, which obviously does not conform any protocol at all (in this case it does not conform to inherited protocol Equatable)
Something similar happens with KeyType.
A type alias declaration introduces a named alias of an existing type into your program.
You see existing type. protocol<Hashable, Equatable> is not a type it is protocol so Xcode again tells you that type 'KeyType' does not conform to protocol 'Equatable'
You can use Dictionary<NSObject, Int> just, because NSObject conforms Hashable protocol.
Swift is strong typing language and you can't do some things like creating Dictionary that can hold anything in Key. Actually dictionary already supports any can hold anything in Key, which conforms Hashable. But since you should specify particular class you can't do this for native Swift classes, because there is no such master class in Swift like in Objective-C, which conforms air could conform (with a help of extensions) to Hashable
Of course you can use some wrapper like chrisco suggested. But I really can't imagine why you need it. It is great that you have strong typing in Swift so you don't need to worry about types casting as you did in Objective-C
Hashable is just a protocol so you can't specify it directly as a type for the Key value. What you really need is a way of expressing "any type T, such that T implements Hashable. This is handled by type constraints in Swift:
func makeDict<T: Hashable>(arr: T[]) {
let x = Dictionary<T, Int>()
}
This code compiles.
AFAIK, you can only use type constraints on generic functions and classes.
This doesn't exactly answer the question, but has helped me.
The general answer would be implement Hashable for all your types, however that can be hard for Protocols because Hashable extends Equatable and Equatable uses Self which imposes severe limitations on what a protocol can be used for.
Instead implement Printable and then do:
var dict = [String: Int]
dict[key.description] = 3
The implementation of description has to be something like:
var description : String {
return "<TypeName>[\(<Field1>), \(<Field2>), ...]"
}
Not a perfect answer, but the best I have so far :(
This does not answer the OP's question, but is somewhat related, and may hopefully be of use for some situations. Suppose that what you really want to do is this:
public var classTypeToClassNumber = [Any.Type : Int]()
But Swift is telling you "Type 'Any.Type' does not conform to protocol Hashable".
Most of the above answers are about using object instances as a dictionary key, not using the type of the object. (Which is fair enough, that's what the OP was asking about.) It was the answer by Howard Lovatt that led me to a usable solution.
public class ClassNumberVsClassType {
public var classTypeToClassNumber = [String : Int]()
public init() {
classTypeToClassNumber[String(describing: ClassWithStringKey.self)] = 367622
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList3.self)] = 367629
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList2.self)] = 367626
classTypeToClassNumber[String(describing: ClassWithGuidKey.self)] = 367623
classTypeToClassNumber[String(describing: SimpleStruct.self)] = 367619
classTypeToClassNumber[String(describing: TestData.self)] = 367627
classTypeToClassNumber[String(describing: ETestEnum.self)] = 367617
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList0.self)] = 367624
classTypeToClassNumber[String(describing: ClassBasedOnKeyedItemList1.self)] = 367625
classTypeToClassNumber[String(describing: SimpleClass.self)] = 367620
classTypeToClassNumber[String(describing: DerivedClass.self)] = 367621
}
public func findClassNumber(_ theType : Any.Type) -> Int {
var s = String(describing: theType)
if s.hasSuffix(".Type") {
s = s.substring(to: s.index(s.endIndex, offsetBy: -5)) // Remove ".Type"
}
let classNumber = _classTypeToClassNumber[s]
return classNumber != nil ? classNumber! : -1
}
}
EDIT:
If the classes involved are defined in different modules, and may have conflicting class names if you neglect the module name, then substitute "String(reflecting:" for "String(describing:", both when building up the dictionary and when doing the lookup.
You can use the class name as a Hashable, e.g.:
var dict = [String: Int]
dict[object_getClassName("key")] = 3
See How do I print the type or class of a variable in Swift? for how you might get the class name.

Syntax explanation: square brackets in Swift

I'm studying Swift and got confusing with following syntax:
var treasures: [Treasure] = []
Treasure is custom class, declared as follow:
class Treasure: NSObject { }
In Objective-C square brackets mean method, but what do they mean in Swift?
Ok, this is the meaning of
var treasures: [Treasure] = []
var: you are declaring a variable
treasures: the name of your variable
[Treasure]: the type of your variable, in this case the type is Array of Treasure, the compiler will allow you to insert only object of type Treasure in your Array
[]: the actual object (Array) referenced by your variable, in this case an empty Array.
E.g. if you want the Array to hold 2 elements you can write
var treasures: [Treasure] = [Treasure(), Treasure()]
Hope this helps.
Update:
My example can also be written this way
var treasures = [Treasure(), Treasure()]
Infact thanks to the Type Inference the compiler can deduce the type of the variable treasures looking at the type of the assigned value.
[Treasure] is just a syntax sugar for Array<Treasure>.
The same way [String:Treasure] is just a syntax sugar for Dictionary<String,Treasure>.
[] is just an empty array of the type you defined. The same way [:] is an empty dictionary.
When it comes to Swift and square brackets, the rules are simple. They are used only in two situations:
1) working with Array and Dictionary types:
let vectors : [[Int]] = [[1,2,3],[4,5,6]]
let birthBook : [Int:[String]] = [1987:["John","William"], 1990: ["Mary"]]
2) for subscripting objects that support subscripting:
class RouteMapper {
private var routeMap : [String:String] = [:]
subscript(endpoint: String) -> String {
get {
if let route = routeMap[endpoint] {
return route
}
return "/"
}
set(newValue) {
routeMap[endpoint] = newValue
}
}
}
let routeMapper = RouteMapper()
routeMapper["users"] = "/v1/confirmed/users"
let url = routeMapper["admins"]
Since [ and ] are not allowed in custom operators, these are the only usages for now.

Resources