📑

[ios] Apollo Codegenで別々のOperationを同じObjectとして扱う方法

2023/02/07に公開

apollo codegenで同じSchemaに対して同じ結果セットが返ってくる、別々のQueryをGenerateすると別名Objectが生成される。
Swiftで同じ構造だけども、別々のObjectとして認識されてしまうとコードが冗長になるのでどうにか回避したい。

todoAPI
query Todo {
  todo {
    id
    name
    ...
  }
}
todo1API
query Todo1($name: name!) {
  todo({where: {name: {"_eq": $name}}}) {
    id
    name
    ...
  }
}

取得する中身が一緒でも別々のQueryOperationとして判別される。
apollo codegen:generate すると

  • TodoQuery Class
  • Todo1Query Class
    ができあがる。別ファイルに吐き出されるので、同じClass(Operation名は付けられない...)

最初にやったこと

if文で分岐。受取側のMethodを複数用意してオーバーロードで区別する

codeA
// 結果セットは同じ。Classが違うだけ
var todo: TodoQuery.Data.Todo?
var todo1: Todo1Query.Data.Todo?

// なんとかしてセットされているクラスを見分ける。
if ('todo == TodoQuery') {
	obj.setTodo(todo as! TodoQuery.Data.Todo)
} else if ('todo == Todo1Query') {
	obj.setTodo(todo as! Todo1Query.Data.Todo)
}

class obj {
  func setTodo(todo: TodoQuery.Data.Todo) {...}
  func setTodo(todo: Todo1Query.Data.Todo) {...}
}

まぁ、分岐がすくないうちはまだいいかもだけど(初心者コード丸出しだけど...) スマートじゃない…

次にやってこと

引数の型をprotocolにしてまとめて受け入れてみる
https://zenn.dev/smpeotn/articles/df7efc325f4ead

codeB
protocol pTodo {}
extension TodoQuery.Data.Todo: pTodo {}
extension Todo1Query.Data.Todo: pTodo {}

// 結果セットは同じ。Classが違うだけ
var todo: TodoQuery.Data.Todo?
var todo1: Todo1Query.Data.Todo?

// 引数の型がprotocolなのでextensionしている型は受け入れる
obj.setTodo(todo)
obj.setTodo(todo1)

class obj {
  func setTodo(todo: pTodo) {...}
}

受け入れることはできた。ただ setTodo method 内の処理でPropertyアクセスがうまくできなかった。
あくまでも protocol pTodo なので pTodo として定義してある必要があったのだ

extensionで拡張(Replace)してみよう

codegensされたClassObjectそのものを拡張してreplace してしまえばいいのでは?
と考えてみました。

codeV
extension Todo1Query.Data.Todo {
  func replaceClass() -> TodoQuery.Data.Todo {
    // 同じ結果セットなので、resultMapをreplaceしちゃう!
    return TodoQuery.Data.Todo(unsafeResultMap: self.resultMap);
    }  
}

// 結果セットは同じ。Classが違うだけ
var todo: TodoQuery.Data.Todo?
var todo1: Todo1Query.Data.Todo?

// TodoQuery.Data.TodoにReplace
var rTodo: TodoQuery.Data.Todo = todo1.replaceClass()

setTodo(todo)
setTodo(rTodo)

class obj {
  func setTodo(todo: TodoQuery.Data.Todo) {...}
}

ちゃんと動きました!これで同じ結果セットのObjectClassを揃えることに成功!
TodoQuery.Data.Todo を拡張すれば更に色々と共通処理がかけるようになります。

※ サンプルコードは 適当に記述しています。このままでは動きません…
ほかにスマートな方法があればぜひ教えていただきたいです。
最後まで読んでいただきありがとうございました!

Discussion