Type-Erasing in Swift | AnyView behind the scenes

What I’m trying to achieve

Hi! I’m currently working on a project that requires type-erasing, similar to SwiftUI’s AnyView. And I’ve been wondering how AnyView works behind the scenes and how that could help me with my project.

Limitations

I have tried to solve the problem of creating a type-erasing Type with many different approaches but I seem to always run into problems of type-checking. The main limitation is that I cannot use generics in my struct, because I want it to be type-erased, so that the following:

struct TypeErased<Value: MyProtocol> {
    let value: Value

    init(value: Value) {
        self.value = value
    }
}

does not create a type-erased Type. Instead, it creates something like a wrapper Type for Value. I think that I need generics but only in the initializer.

If anyone can help me or knows something about how AnyView works behind the scenes, then please let me know; I would really appreciate it! Thanks in advance!

If you have protocol that looks like this

protocol MyProtocol {
    associatedtype A
    func requirement() -> A
}

then there are two ways you can type-erase it that I know

the easier one:

struct TypeErased {
    typealias A = Any
    init<T: MyProtocol>(_ value: T) {
        self._requirement = value.requirement
    }

    private let _requirement: () -> Any
    func requirement() -> Any {
        return self._requirement()
    }
}

and the more complicated, but I think this one is used by standard library (sorry for meaningless names)

private protocol TypeErasedBox {
    var base: Any { get }
    func requirement() -> Any
}
private struct ConcreteTypeErased<Base: MyProtocol>: TypeErasedBox {
    let baseProto: Base
    var base: Any {
        return self.baseProto
    }
    func requirement() -> Any {
        return self.baseProto.requirement()
    }
}

struct TypeErased: MyProtocol {
    typealias A = Any
    private let box: TypeErasedBox
    init<T: MyProtocol>(_ value: T) {
        self.box = ConcreteTypeErased(baseProto: value)
    }
    var base: Any {
        self.box.base
    }
    func requirement() -> Any {
        return self.box.requirement()
    }
}
3 Likes

Oh sorry... I forgot to specify something important. Let’s assume that my protocol is called ‘Block’ I would want it to be something like this:

protocol Block {
    associatedtype Content: Block
    var content: Content { get }
}

struct AnyBlock: Block { 
    init<B: Block>(_ block: Block) {
        //... 
    }
    
    var content: //...
}

In other words I want the type-erased struct AnyBlock to be a block. Like SwiftUI’s AnyView.

I’m sorry about that. I’m new to the forums and I wasn’t specific enough. Thanks for the quick response by the way!

1 Like

Oh.
AnyView has Never as the type of the body which means that SwiftUI never grabs that directly, and instead uses some kind of black magic to unwrap the type-erased view. My sorcery level is too low to guess how they're doing it. :(

1 Like

After doing some more testing I discovered that SwiftUI’s AnyView uses a property of type (random letters).Storage, so you are right. Thanks for telling me that. I’d been stack trying to solve this problem for a couple of days.

1 Like
Terms of Service

Privacy Policy

Cookie Policy