I’m learning Swift and some iOS development. Here is my code which I’m trying to figure out why XCode is complaining.
private func rollMyDice() {
let diceRollImages = [
UIImage(named: "DiceOne"),
UIImage(named: "DiceTwo"),
UIImage(named: "DiceThree"),
UIImage(named: "DiceFour"),
UIImage(named: "DiceFive"),
UIImage(named: "DiceSix")
]
diceOneImageView.image = diceRollImages.randomElement()
diceTwoImageView.image = diceRollImages.randomElement()
}
So in this case, diceOneImageView.image = diceRollImages.randomElement()
will complain that it cannot assign UIImage??
to UIImage?
.
ImageView.image
is of type UIImage?
. What I don’t understand is why is randomElement()
here is returning UIImage??
.
What does UIImage??
mean? I thought ??
was the nil coalescsing operator, so I’m not sure why it’s part of some return type.
Also reading the documentation on randomElement()
, it should return ?
. So in this case, I would expect diceRollImages.randomElement()
to return UIImage?
which should suit diceOneImageView.image
.
What is happening here? I know I can fix it by using !
or using nil coalescing etc. to make it work. Just don’t get what’s going on.
2
Answers
The answer has been given already in the comments:
UIImage(named:)
returns (optional)UIImage?
and the result of callingrandomElement()
on an optional is a double optional??
This is a good example where force unwrapping is welcome.
The images are part of the application bundle which is immutable at runtime and the app is useless if one of them is missing.
Declare
diceRollImages
If the code crashes nevertheless it reveals a design mistake which can be fixed immediately.
You can even force unwrap
randomElement()!
because the array is a constant and is clearly not empty.Note that
UIImage(named:)
is a failable initialiser, and so the expressionsUIImage(named: "DiceOne")
etc are of typeUIImage?
. This makes the arraydiceRollImages
of type[UIImage?]
. EachElement
of the array isUIImage?
, alternatively written asOptional<UIImage>
.As you may know,
Optional
is just an enum with two cases,.some
and.none
(akanil
). Each element of the array can be:.some(image)
, if aUIImage
is created successfully from the name.none
, if there is no images with that namerandomElement
is declared to returnElement?
(Optional<Element>
), because the array could have no elements, and hence, cannot give you a random element.randomElement
returns:.none
, when the array is empty.some(elementOfTheArray)
, when the array is non-empty andelementOfTheArray
is a random element in array.Recall that
Element
isOptional<UIImage>
in the case ofdiceRollImages
, and that the array elements (elementOfTheArray
) could have values.some(image)
or.none
.Therefore, we can say that
randomElement
returns a value of typeOptional<Optional<UIImage>>
, akaUIImage??
, and it could be one of 3 things:.none
when the array is empty.some(.some(image))
when the array is non-empty and a successfully-created image in the array is randomly selected.some(.none)
when the array is non-empty and a unsuccessfully-created image in the array is random selectedSince you are hardcoding the array of images, you know that the array is not empty, and so it is safe to force-unwrap the outer layer of the optional: