💡 Remember This
Boxed protocol types store a concrete value inside a container while exposing only the protocol's interface.
Existential types are Swift's name for boxed protocol values whose concrete type has been erased from the surrounding code.
Opaque types hide a concrete type from the caller while still preserving that exact concrete type throughout the program.
What are Opaque Protocol Types?
💡 Three Important Protocol Types
Boxed protocol types store a concrete value inside a container while exposing only the protocol's interface.
Existential types are Swift's name for boxed protocol values whose concrete type has been hidden from the surrounding code.
Opaque protocol types hide a concrete type from the caller while preserving that exact concrete type throughout its lifetime.
An opaque protocol type allows a function or property to return a value that conforms to a protocol without revealing its concrete type.
The caller knows that the returned value satisfies a protocol, but does not know the concrete type that was returned.
Unlike boxed protocol types, the concrete type has not been erased.
Swift continues to preserve that concrete type internally.
Starting With a Concrete Type
Suppose we have a protocol describing an animal.
protocol Animal {
var name: String { get }
func makeSound()
}
A concrete type can conform to that protocol.
struct Dog: Animal {
let name: String
func makeSound() {
print("Woof!")
}
}
Dog is the concrete type.
Animal describes only the behaviour.
Returning a Concrete Type
A normal function can return Dog directly.
func favouritePet() -> Dog {
Dog(name: "Milo")
}
Everyone calling the function knows that a Dog will always be returned.
Hiding the Concrete Type
Sometimes we want callers to know only that the returned value conforms to Animal.
We may not want them to depend upon the concrete type.
Swift allows us to hide that type using the some keyword.
func favouritePet() -> some Animal {
Dog(name: "Milo")
}
The caller no longer knows that the function returns Dog.
Instead, the caller knows only that whatever is returned conforms to Animal.
Why Is It Called Opaque?
The word opaque means that something cannot be seen through.
The concrete type still exists.
It has simply become invisible to code outside the function.
Swift knows exactly which concrete type is returned.
The caller does not.
Important
An opaque protocol type hides a concrete type.
It does not erase that concrete type.
Swift continues to preserve it internally.
The Concrete Type Never Changes
This is the most important rule when working with opaque protocol types.
Every execution path must return the same concrete type.
func favouritePet() -> some Animal {
Dog(name: "Milo")
}
This is perfectly valid because every call returns Dog.
However, the following code produces an error.
func favouritePet(prefersCats: Bool) -> some Animal {
if prefersCats {
return Cat(name: "Luna")
}
return Dog(name: "Milo")
}
Although both Dog and Cat conform to Animal, they are different concrete types.
An opaque protocol type must always represent one single concrete type.
Why Would You Hide the Type?
Hiding the concrete type allows implementation details to change without affecting callers.
Suppose an API currently returns Dog.
Later, the implementation changes to use a different concrete type.
As long as that type still conforms to Animal, callers remain unaffected because they never depended upon the original concrete type.
SwiftUI Uses Opaque Types Everywhere
One of the best-known examples is SwiftUI.
var body: some View {
Text("Hello")
}
The property returns some View.
SwiftUI hides the enormous concrete type used to build the view hierarchy.
Developers interact only with the View protocol.
What Can the Caller Use?
The caller can use everything declared by the protocol.
let pet = favouritePet()
pet.makeSound()
The caller cannot use members that belong only to Dog.
pet.fetchStick() // Error
The concrete type remains hidden.
Opaque Does Not Mean Unknown
It is tempting to think that an opaque protocol type means Swift does not know the concrete type.
The opposite is true.
Swift always knows the concrete type.
The only code that does not know is the code calling the function.
Thinking About Opaque Types
You can think of an opaque protocol type as making the following promise:
I will always return the same concrete type.
I'm simply choosing not to tell you what that type is.
This is very different from a boxed protocol type, where different concrete types may appear over time.
Interview Questions
What is an opaque protocol type?
An opaque protocol type hides a concrete type from callers while preserving that concrete type internally.
Which keyword creates an opaque protocol type?
The some keyword.
Does Swift know the concrete type?
Yes.
Swift always knows the concrete type.
Only the caller is prevented from seeing it.
Can an opaque protocol type return different concrete types?
No.
Every execution path must return the same concrete type.
Where are opaque protocol types commonly used?
SwiftUI uses them extensively through some View.
Final Revision Note
An opaque protocol type hides a concrete type from callers while preserving that exact concrete type inside the program.
Unlike boxed protocol types, the concrete type is never erased.
It is simply hidden from code outside the function or property.
Remember
some Animalmeans:"I will always return one concrete Animal type, but I'm choosing not to reveal which one."
