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.
CodePudding user response:
Note that UIImage(named:)
is a failable initialiser, and so the expressions UIImage(named: "DiceOne")
etc are of type UIImage?
. This makes the array diceRollImages
of type [UIImage?]
. Each Element
of the array is UIImage?
, alternatively written as Optional<UIImage>
.
As you may know, Optional
is just an enum with two cases, .some
and .none
(aka nil
). 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 name
randomElement
is declared to return Element?
(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
is Optional<UIImage>
in the case of diceRollImages
, and that the array elements (elementOfTheArray
) could have values .some(image)
or .none
.
Therefore, we can say that randomElement
returns a value of type Optional<Optional<UIImage>>
, aka UIImage??
, 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 selected
Since 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:
diceOneImageView.image = diceRollImages.randomElement()!
CodePudding user response:
The answer has been given already in the comments: UIImage(named:)
returns (optional) UIImage?
and the result of calling randomElement()
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
let diceRollImages = [
UIImage(named: "DiceOne")!,
UIImage(named: "DiceTwo")!,
UIImage(named: "DiceThree")!,
UIImage(named: "DiceFour")!,
UIImage(named: "DiceFive")!,
UIImage(named: "DiceSix")!
]
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.