Opaque types in Swift introduces developers to a powerful feature that enhances code abstraction, flexibility, and performance. By using the some keyword, opaque types allow developers to hide implementation details while maintaining type safety, unlike other techniques like Any or AnyObject.
The aim of thi post is clarify their usage in real-world scenarios, help bridge the knowledge gap for developers who are familiar with protocols and generics but not opaque types, and showcase how this feature leads to cleaner, more maintainable code.
Opaque types
Opaque types in Swift, introduced with the some
keyword, allow you to define a type that hides its concrete implementation while still conforming to a specific protocol or set of constraints. This enables you to return a value from a function without exposing the exact type, providing flexibility and abstraction while maintaining type safety. Essentially, the compiler knows the specific type behind the opaque type, but users of the type only know that it conforms to the expected protocol, which helps in writing cleaner, more maintainable code without sacrificing performance or type safety.
Lets start by a generic sample:
protocol Animal {
func makeSound() -> String
}
struct Dog: Animal {
func makeSound() -> String { "Woof!" }
}
struct Cat: Animal {
func makeSound() -> String { "Meow!" }
}
func getAnimal() -> some Animal {
return Dog()
}
In this example:
The
getAnimal()
function returns an opaque type (some Animal
), indicating that it returns a type conforming to theAnimal
protocol.The specific type (
Dog
) is hidden from the caller.
Abstraction: Hides implementation details while exposing only necessary behavior.
Type Safety: Ensures that the returned type is consistent and conforms to the specified protocol.
Optimization: Allows the compiler to optimize code by knowing the exact underlying type.
Now let me introduce you another example that you most probably have seen in swiftUI:
struct ContentView: View {
var body: some View {
VStack {
myCustomSomeViewA()
myCustomSomeViewB()
}
}
func myCustomSomeViewA() -> some View {
Text("myCustomSomeViewA")
}
func myCustomSomeViewB() -> some View {
Text("myCustomSomeViewB")
}
}
The some
keyword in this SwiftUI code is used to specify opaque return types for the body
property and the helper functions myCustomSomeViewA()
and myCustomSomeViewB()
, which return SwiftUI views without exposing their exact types. This allows the compiler to enforce type consistency while keeping implementation details hidden. In practice, it ensures that each function returns a single, consistent view type, improving type safety and optimization.
any keyword
In Swift, the any
keyword is used to explicitly indicate existential types, meaning a value can be of any type that conforms to a given protocol. Introduced in Swift 5.6, it improves clarity by distinguishing between existentials (any Protocol
) and generics (<T: Protocol>
), helping developers understand when dynamic dispatch and runtime type checking are involved. Using any
makes code more explicit but may introduce performance overhead compared to generics, which enable static dispatch. It is recommended when working with heterogeneous types but should be avoided when generics provide a more efficient alternative.
We will continue with previous generic example:
func printAnimal(_ animal: any Animal) -> String { // Explicit existential
animal.makeSound()
}
The function printAnimal(_ animal: any Animal) -> String
takes an existential any Animal
as a parameter and calls its makeSound()
method but has a compilation error because it declares a return type of String
without returning a value. To fix it, makeSound()
should return a String
, and the function should return that value. For example, if Animal
is a protocol with func makeSound() -> String
, and a Dog
struct implements it by returning "Woof!"
, calling printAnimal(Dog())
would correctly return "Woof!"
.
On extending previous SwiftUI example with any:

The error …this expreession cannot conform to ‘View’ occurs because the any
keyword creates an existential type, which cannot directly conform to the View
protocol in SwiftUI. This is a common issue when working with protocols and generic types in SwiftUI.
For fixing this issue we have to address in the following way:
let views: [AnyView] = [AnyView(myCustomAnyViewA()),
AnyView(myCustomAnyViewB())]
Conclusions
In this post, I clarify the concept of Opaque Types in Swift and the usage of the some
and any
keywords. You can find source code used for writing this post in following repository.
References
- Opaque and Boxed Protocol Types
Swift.org