2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Swiftで型消去を利用したAPIClientDelegateの実装の話

Posted at

APIClientの実装

APIClientの実装をしたくて、とりあえず適当に簡単なprotocolを実装してみた。

protocol APIClient {
    associatedtype GetDataType
    func get()
}

get()の中で、URLSessionDataTaskを使って非同期でGETする前提のAPIClientを考えた。
このprotocolを使って、https://google.com をただ単にGETするだけのAPIClientを作ってみた。

class GetGoogleDataClient: APIClient {
    typealias GetDataType = String
    
    private let urlString: String = "https://google.com/"
    
    func get() {
        let dataTask = URLSession.shared.dataTask(with: URL(string: urlString)!) { [weak self] data, error, response in
            if let data = data, let stringData = String(data: data, encoding: .utf8) {
                // dataにGETしたデータが入る
            }
        }
        dataTask.resume()   
    }
}

APIClientDelegateの実装?

dataTaskでGETされたデータを、クライアントを使っているclassに通知したい、と思ったので簡単にprotocolであるAPIClientDelegateを実装してみる。

protocol APIClientDelegate: class {
    associatedtype GetDataType
    func getDataIsFinished(data: GetDataType)
}

この時点での理想形は以下。

class GetGoogleDataClient: APIClient {
    typealias GetDataType = String

    weak var delegate: APIClientDelegate? // NG
    private let urlString: String = "https://google.com/"
    
    func get() {
        let dataTask = URLSession.shared.dataTask(with: URL(string: urlString)!) { [weak self] data, error, response in
            if let data = data, let stringData = String(data: data, encoding: .utf8) {
                self?.delegate.getDataIsFinished(data: data)
            }
        }
        dataTask.resume()   
    }
}

しかし、このままではAPIClientDelegate型を利用した変数は宣言できない。

class GetGoogleDataClient: APIClient {
   ...
   weak var delegate: APIClientDelegate? // NG
   ...
}

変数として宣言するときにassociatedtypeであるGetDataTypeが抽象的なままであるからだ。(以下エラー文)

Protocol 'APIClientDelegate' can only be used as a generic constraint because it has Self or associated type requirements

型消去によるAPIClientDelegateの問題点の解決

今回は、APIClientDelegateGetDataTypeによる問題を解決したいので、早速型消去を利用して問題点の解決を試みた。
少々古いドキュメントであるが、https://qiita.com/S_Shimotori/items/458a50d8f8e54e39de59 を見れば型消去の概要はだいたい理解できる。

実装したAnyAPIClientDelegateはこちら。
ジェネリクスGetTypeAPIClientDelegate中のGetDataTypeにあたる、と考えれば良い。

class AnyAPIClientDelegate<GetType>: APIClientDelegate {
    private let _getDataIsFinished: ((GetType) -> Void)
    
    required init<U: APIClientDelegate>(_ delegate: U) where U.GetDataType == GetType {
        self._getDataIsFinished = delegate.getDataIsFinished
    }
    
    func getDataIsFinished(data: GetType) {
        _getDataIsFinished(data)
    }
}

実際にAnyAPIClientDelegate型のメンバ変数を宣言すると、エラーが出なくなる。

GetGoogleDataClientの完成形は以下。

class GetGoogleDataClient: APIClient {
    typealias GetDataType = String

    weak var delegate: AnyAPIClientDelegate<GetDataType>? // OK
    private let urlString: String = "https://google.com/"
    
    func get() {
        let dataTask = URLSession.shared.dataTask(with: URL(string: urlString)!) { [weak self] data, error, response in
            if let data = data, let stringData = String(data: data, encoding: .utf8) {
                self?.delegate.getDataIsFinished(data: data)
            }
        }
        dataTask.resume()   
    }
}

AnyAPIClientDelegateを利用してみる

では、GetGoogleDataClientを利用するHogeクラスを実装する。

class Hoge {
    let dataClient = GetGoogleDataClient()
    
    init() {
    }
    
    func showSomeData() {
        dataClient.get()
    }
}

dataClientが受け取ったデータを通知できるように、HogeAPIClientDelegateに準拠させる。
今回はただ単に受け取ったデータをprintしてみる。

extension Hoge: APIClientDelegate {
    func getDataIsFinished(data: String) {
        print(data)
    }
}

その後、dataClientのdelegateとして、自身をAnyAPIClientDelegateで初期化して渡す。

class Hoge {
   ...
   init() {
        dataClient.delegate = .init(self)
   }
   ...
}

後は以下のように利用すれば、データがprintされる。

let hoge = Hoge()
hoge.showSomeData() // データがprintされる

まとめ

  • 型消去による、protocolの不自由から解き放たれたAnyAPIClientDelegateを実装した。
  • 正直Rxとか使ったほうが楽かもしれないが、何かしらのこだわりがあるときは是非参考に。

今回利用したソースコードは以下で公開している、
https://gist.github.com/freddi-kit/47746f60259c16e134413d6aa15b6ecc

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?