Why can you not initialize an empty Array in Swift? - swift

This code works,
let people = ["Tom","Dick","Harry"]
but this code doesn't work, for no apparent reason
let people = []
and nor does this (mutability matters not):
var people = []
The error is "Cannot convert expression's type Array to type 'ArrayLiteralConvertible'", but that makes no sense to me, and none of the other questions that show up in a search address this question.
At first I thought it had to do with type inference, but that proves not to be the issue (at least not simply that!) since although this works (with type specified)
var people:Array = [""]
this does not (with type specified as above but no String given inside the Array):
var people:Array = []
Since the last of these two has the type specified explicitly, it shouldn't need a String passed inside the Array.
Some languages (weirdly!) consider the type of the variable to refer to the type of item inside the array, so I also tried specifying String instead of Array, but got the same results. This first one works and the second doesn't:
var people:String = [""]
var people:String = []

The syntax you are looking for is
let people : [String] = []
or
let people = [String]()
in both case, you can substitute [String] with Array<String>
For this code
let people = []
It is impossible for compiler to figure it out the type of people. What type do you expect it have?
Note Array is not a complete type because it require the generic part. A complete type is like Array<String>

You can do this
let blankArray = [String]()
or ny other type you need.

Related

Cannot convert value of type 'String?' to expected element type 'Array<String>.ArrayLiteralElement' (aka 'String')

A field in my firebase document is an array of strings which contains user ids.
When I try to initialise it in one of my views, I get the error
Cannot convert value of type 'String?' to expected element type 'Array<String>.ArrayLiteralElement' (aka 'String')
Here's what I am doing
#State var playerIds: [String] = [Auth.auth().currentUser?.uid, "SomeRandomHardcodedUserID"]
The line below works but the one mentioned above does not.
#State var playerIds: [String] = ["SomeHardcodedUID", "SomeOtherHardcodedUserID"]
My intention is to use playerIds to construct a codeable objet to send to Firestore.
The below line works with hardcoded userIds.
let newGame = Game(gameTime: gameTime, playerIds: playerIds, inProgress: gameInProgress, winnerId: winnerId)
What's the right way to go about putting current user's userId in my array?
So I figured out that the fix is to define the type of array as [String?] rather than [String], which fixed my issue.
Passing playerIds as an argument was fixed by changing my Game structs model for playerIds to also use [String?] rather than [String]

Swift generic collection of Element cannot convert to collection of Any

I'm facing a problem I don't understand.
In my project, I want to make a collection of elements with some customised methods (like an update from a server). But when I try to group all these collections in an array, I get an error: "Cannot convert value of type MyCollection<someElement> to expected argument type MyCollection<Any>"
What I don't understand is that the same code with Array is working... Array isn't a collection?
// My collection which would contain an update method
class MyCollection<Element> {
var object:Element? = nil
}
let x = MyCollection<Int>()
var list = [MyCollection<Any>]()
list.append(x) //Cannot convert value of type 'MyCollection<In>' to expected argument type 'MyCollection<Any>'
let a = Array<Int>()
var lista = [Array<Any>]()
lista.append(a) //Doesn't get error at all...
I know I can do this with an array of the specific type but by grouping all of MyCollection in an array, I wish to use a code like :
func update() {
for e in list { // array of MyCollection<Any>
e.update()
}
}
Thank you in advance for your help ;)
Being able to convert from SomeType<Subtype> to SomeType<Supertype> is called covariance. In Swift, Array<T> is covariant on T by "compiler magic", and you can't do the same for your own types.
The type checker hardcodes conversions from Array to Array if there is a conversion from T to U. Similar rules exist for Optional and Dictionary. There's no mechanism for doing this with your own types.
Your own generic types are always invariant, meaning that there is never a conversion between SomeType<T> to SomeType<U>, as long as T and U are different types.
Let's imagine what would happen if the conversion on MyCollection were allowed. You could do:
let myCollectionInt = MyCollection<Int>()
let myCollectionAny: MyCollection<Any> = myCollectionInt // suppose you can do this
myCollectionAny.object = "string" // myCollectionAny.object is of type Any?, so this should be ok
We've set myCollectionAny.object to "string", but MyCollection is a reference type, so myCollectionInt.object should also be "string". But myCollectionInt.object is an Int?!
Of course this type-unsafety is also a problem with arrays, but the language designers have decided that casting arrays is a common enough thing to do, that disallowing it would do more hard than good.

Different ways to initialize a dictionary in Swift?

As far as I know, there are 4 ways to declare a dictionary in Swift:
var dict1: Dictionary<String, Double> = [:]
var dict2 = Dictionary<String, Double>()
var dict3: [String:Double] = [:]
var dict4 = [String:Double]()
It seems these four options yields the same result.
What's the difference between these?
All you're doing is noticing that you can:
Use explicit variable typing, or let Swift infer the type of the variable based on the value assigned to it.
Use the formal specified generic struct notation Dictionary<String,Double>, or use the built-in "syntactic sugar" for describing a dictionary type [String:Double].
Two times two is four.
And then there are in fact some possibilities you've omitted; for example, you could say
var dict5 : [String:Double] = [String:Double]()
And of course in real life you are liable to do none of these things, but just assign an actual dictionary to your variable:
var dict6 = ["howdy":1.0]

Array of String printing Optional, why?

I am playing with Arrays in playground and I am bit confused. Here is code:
var players = ["tob", "cindy", "mindy"] //["tob", "cindy", "mindy"]
print(players.isEmpty) // False
var currentPlayer = players.first // "tob"
print(currentPlayer) // "Optional("tob")\n"
Why does it says "Optional"?
I found explanation: "The property first actually returns an optional, because if the array were empty, first would return nil."
But it is not empty. .isEmpty //false, So I am not understanding this.
Thanks for help in advance.
The correct way to think of Optional is that this may or may not have a value. What is the first element of an empty list? There is no such thing. It is not a value. We call that lack of a value nil or .None.
In Swift a variable must have a specific type. So your example:
let currentPlayer = players.first
What is the type of currentPlayer? It may be a String, or it may be nothing at all. It is a "maybe string" and in Swift that's called an Optional<String>. Whether players has elements or doesn't have elements doesn't change the type of currentPlayer.
If you want to do something if-and-only-if the variable has a value, then there are many ways. The simplest is if-let.
let players = ["tob", "cindy", "mindy"] //["tob", "cindy", "mindy"]
print(players.isEmpty) // False
if let currentPlayer = players.first {
print(currentPlayer)
}
This will print tob as you're expecting.
Another very common approach is the guard let
let players = ["tob", "cindy", "mindy"] //["tob", "cindy", "mindy"]
guard let currentPlayer = players.first else { return }
print(currentPlayer)
This lets you avoid nesting the rest of your function inside of curly braces, but otherwise is the same approach.
It is possible to convert an Optional into its underlying type using !, but this is very dangerous and should be avoided except where absolutely necessary. Tools like if-let and guard-let (and also Optional.map) are almost always preferred.
But the key here is to understand that all Swift variables have a single type, and sometimes that type is "maybe it has a value, maybe it doesn't."
If we look at the description of first, we will see that it always returns optional type:
public var first: Self.Generator.Element? { get }

Swift various ways of creating empty array what are differences between them?

I looked through docs some forums and found I can create an array in various ways. I am confused which one should we be using?
var testArray = [Int]()
testArray.append(1)
var anotherTestArray: [Int] = []
anotherTestArray.append(1)
var yetAnotherTestArray: Array<Int> = []
yetAnotherTestArray.append(1)
var yetYetYetAnotherTestArray = Array<Int>()
yetYetYetAnotherTestArray.append(1)
This is not empty array but It keeps it's type for each element to be strictly to an Int
var yetYetAnotherTestArray = [1]
I think the cleanest way to create an array is var testArray: [Int] = []
In swift array objects must be the same type. If you want to store different objects of different types use [AnyObject]. However, you should always know what is coming out of it. Since the returning type value will be AnyObject, you have to cast down the value to the type you want.
I really don't recommend using AnyObject as the type of your arrays unless you really know what you are doing.
Here is an example anyway:
let a: [AnyObject] = [1, "a"]
let b = a[0] // An Int
let c = a[1] // A String