Opaque Types in Swift: Hiding Details, Not Power

Have you ever wanted to return some kind of value from a function but didn’t want to expose exactly what type it is? Like telling the compiler, I’ll tell you it conforms to a protocol, but you don’t need to know the concrete type. Just trust me, it’ll work. Well, that’s what opaque types are for. Let’s break it down together.

So What Is an Opaque Type?

An opaque type in Swift is a return type that hides the concrete type behind a protocol. You’ve seen this syntax before:

Swift
func makeButton() -> some View {
    Text("Tap me!")
}

That some View is the magic. Instead of returning a Text, or a Button, or a ZStack, we say, I’m returning some type that conforms to View, you don’t need to know which one. That’s the whole idea.

Why Not Just Return View?

Let’s try that:

Swift
func makeButton() -> View {
    Text("Tap me!")
}

This will throw a compiler error.


“Use of protocol ‘View’ as a type must be written ‘any View’; this will be an error in a future Swift language mode.”
“Type ‘any View’ cannot conform to ‘View’”

That’s the problem. Most SwiftUI protocols (like View, Shape, Animatable, etc.) have associated types and Swift can’t just erase them like that. You can’t return a protocol with associated types directly, but you can return some type that conforms to that protocol. Hence:

Swift
func makeButton() -> some View

So What Does some View Actually Mean?

It means, This function returns a specific, single type that conforms to View but I’m not telling you what type that is. The type is opaque to the caller, but Swift still knows what it is under the hood and gives you all the safety. It’s like giving someone a sealed box and saying, What’s inside is definitely a toy car. You don’t need to know the brand.

A Real Example:

Swift
func primaryButton(label: String) -> some View {
    Text(label)
        .padding()
        .foregroundStyle(.white)
        .background(.blue)
        .clipShape(Capsule())
}

You don’t need to know the internals. All you care about is that it returns some View and Swift guarantees it works.

What if I Return Different Types?

You can’t do this:

Swift
func errorView(hasError: Bool) -> some View {
    if hasError {
        Text("An error occurred")
    } else {
        Image(systemName: "checkmark")
    }
}

You will get a compiler error like:


“Branches have mismatching types ‘Text’ and ‘Image’”

Why? Because Text and Image are different concrete types, even though both conform to View. And some View means “always returns one specific type.”

How to Fix It

Wrap them in something like AnyView:

Swift
func errorView(hasError: Bool) -> some View {
    if hasError {
        AnyView(Text("An error occurred"))
    } else {
        AnyView(Image(systemName: "checkmark"))
    }
}

Note that AnyView erases the type, but it has a performance cost so use it only when needed.

Opaque Return Types with Custom Protocols

You can use some T with your own protocols too. This works as long as the function always returns one concrete type.

Swift
protocol Weapon {
    func damage() -> Int
}

struct Sword: Weapon {
    func damage() -> Int { 20 }
}

struct Bow: Weapon {
    func damage() -> Int { 12 }
}

func equipSword() -> some Weapon {
    Sword()
}

Although this won’t compile:

Swift
func equipRandomWeapon() -> some Weapon {
    Bool.random() ? Sword() : Bow() // returns two types
}

You are going to see an error like:


Result values in ‘? :’ expression have mismatching types ‘Sword’ and ‘Bow’

some vs any: Know the Difference

Use some when:

  • You want to preserve performance
  • You always return the same concrete type
  • You’re working with SwiftUI or associated-type protocols

Use any when:

  • You want flexibility over performance
  • You need to return different conforming types
  • You’re ok with type-erased abstraction

Opaque types give Swift a rare blend of type safety, performance, and flexibility all without exposing internals. Whether you’re writing SwiftUI components, generic logic, or DSLs, you’ll run into some often. Now that you know how it works, you’ll never look at some View the same again 😉. That was all for this one i will see you in the next one 🙂


Posted

in