# type erasure

[Swift](https://lochiwei.gitbook.io/ios/swift) ⟩ [type](https://lochiwei.gitbook.io/ios/swift/type) ⟩ erasure

{% hint style="success" %}
🚧
{% endhint %}

{% tabs %}
{% tab title="👥 相關" %}

* [generic-types](https://lochiwei.gitbook.io/ios/swift/type/category/generic-types "mention")
* [protocol](https://lochiwei.gitbook.io/ios/swift/type/category/protocol "mention")
* [some](https://lochiwei.gitbook.io/ios/swift/type/category/some-any-generics/some "mention")
* [associated-type](https://lochiwei.gitbook.io/ios/swift/type/category/protocol/associated-type "mention")
* :package: [anyshape](https://lochiwei.gitbook.io/ios/swiftui/shapes/shape/anyshape "mention")：*<mark style="color:purple;">type-erased</mark>* shape value.
* :bulb: [wrap-types](https://lochiwei.gitbook.io/ios/swift/type/category/basic/enum/wrap-types "mention")：達到 *<mark style="color:purple;">type erasure</mark>* 的效果。
  {% endtab %}

{% tab title="⭐️ 重點" %}
{% hint style="info" %}
A use case to use an [**opaque return type**](https://lochiwei.gitbook.io/ios/swift/type/category/some-any-generics/some) is to perform <mark style="color:yellow;">automatic</mark> <mark style="color:purple;">type erasure</mark>. :point\_right: [Sundell](https://www.swiftbysundell.com/articles/opaque-return-types-in-swift/#type-erasure-beyond-swiftui)
{% endhint %}
{% endtab %}

{% tab title="📗 參考" %}

* [x] Yates ⟩ [What is type erasure in Swift](https://blog.devgenius.io/what-is-type-erasure-in-swift-6e53fe27145) (💈例一) ⭐️
* [ ] Sundell ⟩&#x20;
  * [ ] [Different flavors of type erasure in Swift](https://www.swiftbysundell.com/articles/different-flavors-of-type-erasure-in-swift/)
  * [ ] [Type erasure beyond SwiftUI](https://www.swiftbysundell.com/articles/opaque-return-types-in-swift/#type-erasure-beyond-swiftui)  ⭐️
* [x] Ray ⟩ [Opaque Return Types and Type Erasure](https://www.raywenderlich.com/24942207-opaque-return-types-and-type-erasure) (💈例二) ⭐️
* [x] Pacheco ⟩ [Type erasure in Swift: How to avoid it completely](https://triplebyte.com/blog/the-perils-of-type-erasure-in-swift-and-how-to-avoid-it-completely) - use <mark style="color:red;">**enum**</mark>.
  {% endtab %}
  {% endtabs %}

## 💈範例

{% tabs %}
{% tab title="例一" %}

```swift
// --------------------------------------------------
//     ⭐️ generic protocol (with associated type)
// --------------------------------------------------

protocol Fetcher {
    associatedtype Data
    typealias ResultHandler = (Result<Data, Error>) -> Void
    func fetch(completion: ResultHandler)
}

// conforming type
struct User {
    let id: Int
    let name: String
}

// protocol conformance
struct UserFetcher: Fetcher {
    typealias Data = User
    func fetch(completion: ResultHandler) {
        let user = User(id: 1, name: "Phil")
        completion(.success(user))
    }
}

/*
     ⭐️ The Problem
     --------------
     How do we hold a reference to an object that has 
     implemented this protocol?
 */

//struct SomeStruct {
//    let fetcher: Fetcher
//}
// ⛔ Protocol ‘Fetcher’ can only be used as a generic constraint 
//    because it has Self or associated type requirements
// 💬 The compiler CANNOT determine the associated type `Data`
//    from the declaration.

// ------------------------------------------------
//     Type Erasure: turn PAT into generic type
// ------------------------------------------------

// 1.  define a generic type conforming to protocol
struct AnyFetcher<T>: Fetcher {
    
    // 2. turn associated types into type parameters.
    typealias Data = T
    
    // 3. create members with matching signatures.
    //    (for each member in the protocol)
    private let _fetch: (ResultHandler) -> Void
    
    // 4. erase the type of injected instance (⭐️)
    //    by storing a reference to all of the methods WITHOUT
    //    storing a reference to the injected instance (⭐️).
    //               ╭─── ⭐️ ───╮
    init<F: Fetcher>(_ fetcher: F) where F.Data == T {
        // store reference to methods, not the object.
        _fetch = fetcher.fetch
    }
    
    // 5. forward all protocol method calls to stored references
    func fetch(completion: ResultHandler) {
        _fetch(completion)
    }
}

// ----------------
//     use case
// ----------------

// turn `UserFetcher` into `AnyFetcher` (erase `UserFetch` type)
let fetcher = AnyFetcher(UserFetcher())

// let it do fetch
fetcher.fetch { result in
    switch result {
        case .success(let user) : print(user.name)
        case .failure(let error): print(error)
    }
}
```

{% endtab %}

{% tab title="例二" %}
:point\_right: Ray ⟩ [Enter Type Erasure](https://www.raywenderlich.com/24942207-opaque-return-types-and-type-erasure#toc-anchor-014)

```swift
import SwiftUI

/// protocol with Self requirement
public protocol Filter: 
    Equatable,             // with `Self` requirement
    Identifiable  
{
    var name: String { get }
    func apply(to image: Image) -> Image
}

// -------------------------------------------------
//    Type Erasure: turn PSR into nongeneric type
// -------------------------------------------------
//  • PSR: protocol with `Self` requirement

// 1. define a nongeneric type conforming to protocol
public struct AnyFilter: Filter {
    
    // 2. create members with matching signatures.
    //    (for each member in the protocol)
    public let name: String
    private let _apply: (Image) -> Image
    
    // 3. erase the type of injected instance (⭐️)
    //    by storing a reference to all of the methods WITHOUT
    //    storing a reference to the injected instance (⭐️).
    //                     ╭── ⭐️ ───╮
    public init<F: Filter>(_ filter: F) {
        name = filter.name
        _apply = filter.apply(to:)
    }
}

extension AnyFilter {
    
    // `Filter` protocol conformance
    
    // 4. forward all protocol method calls to stored references
    public func apply(to image: Image) -> Image {
        return _apply(image)
    }
    
    // `Equatable` 
    public static func == (lhs: AnyFilter, rhs: AnyFilter) -> Bool {
        lhs.id == rhs.id
    }
    
    // `Identifiable`
    public var id: String { "AnyFilter(\(name))" }
}

```

{% endtab %}
{% endtabs %}
