repository pattern in swift
TRANSCRIPT
Naoto Kaneko
Repository pattern in Swift
リポジトリパターン•リポジトリとは、データの取得、保存、削除のためのインターフェイス
• リポジトリを使ってデータにアクセスする。
• メモリ、Realm、APIといったバックエンドを知る必要がなくなる。
ViewController
RecipeRepository
WebAPIRecipeRepository RealmRecipeRepository
query model
params JSON NSPredicate RealmObject
in Swiftprotocol RecipeRepository { func find(id: UInt) -> Recipe? func findAll(query: Query, sort: Sort) -> [Recipe] func save(recipes: [Recipe]) -> Recipe? func delete(recipes: [Recipe]) -> Recipe?}
in Swiftclass MemoryRecipeRepository: RecipeRepository { let recipes = [ Recipe(id: 1, name: “…”), Recipe(id: 2, name: “…”), Recipe(id: 3, name: “…”), ]
func find(id: UInt) -> Recipe? { recipes.filter { $0.id == id }.first }}
問題点•データへのアクセスはだいたい非同期
• クエリやソートはどうやって指定するのか
• RecipeRepository, ChefRepository, UserRepository…と似たようなコードをコピペすることになりそう
リポジトリで非同期処理をあつかう
Promiseprotocol RecipeRepository { func find(id: UInt) -> Task<Void, Recipe, ErrorType> func findAll(query: Query, sort: Sort) -> Task<Void, [Recipe], ErrorType> func save(recipes: [Recipe]) -> Task<Void, Recipe, ErrorType> func delete(recipes: [Recipe]) -> Task<Void, Recipe, ErrorType>}
ViewController
RecipeRepository
WebAPIRecipeRepository RealmRecipeRepository
query model
params JSON NSPredicate RealmObject
ViewController
RecipeRepository
WebAPIRecipeRepository RealmRecipeRepository
Task<Void, Model, Error>
params JSON NSPredicate RealmObject
query
汎用的なクエリを定義する
naoty/AnyQuerylet query = AnyQuery.Equal(key: “name”, value: “naoty”)query.predicate //=> NSPredicate(format: "name == ‘naoty’")query.dictionary //=> ["name": “naoty”]
let sort = AnySort.Ascending(key: “id”)sort.sortDescriptors //=> [NSSortDescriptor(key: "id", ascending: true)]sort.dictionary //=> ["sort": ["id"]]
naoty/AnyQueryprotocol RecipeRepository { func find(id: UInt) -> Task<Void, Recipe, ErrorType> func findAll(query: AnyQuery, sort: AnySort) -> Task<Void, [Recipe], ErrorType> func save(recipes: [Recipe]) -> Task<Void, Recipe, ErrorType> func delete(recipes: [Recipe]) -> Task<Void, Recipe, ErrorType>}
ViewController
RecipeRepository
WebAPIRecipeRepository RealmRecipeRepository
Task<Void, Model, Error>
params JSON NSPredicate RealmObject
query
ViewController
RecipeRepository
WebAPIRecipeRepository RealmRecipeRepository
Task<Void, Model, Error>
[String: AnyObject] JSON NSPredicate RealmObject
AnyQuery
汎用的なリポジトリ型 を定義する
これはできないprotocol Repository { associatedtype Domain
func find(id: Uint) -> Task<Void, Domain, ErrorType>}
let repository: Repository = MemoryRecipeRepository()
型消去struct AnyRepository<DomainType>: Repository { let _find: (id: UInt) -> Task<Void, DomainType, ErrorType>
init<T: Repository where T.Domain == DomainType>(_ repository: T) { _find = repository.find }
func find(id: UInt) -> Task<Void, DomainType, ErrorType> { return _find(id) }}
let repository = AnyRepository(MemoryRecipeRepository())// repository: AnyRepository<Recipe>
ViewController
RecipeRepository
WebAPIRecipeRepository RealmRecipeRepository
Task<Void, Model, Error>
[String: AnyObject] JSON NSPredicate RealmObject
AnyQuery
ViewController
AnyRepository<Recipe>
WebAPIRecipeRepository RealmRecipeRepository
Task<Void, Model, Error>
[String: AnyObject] JSON NSPredicate RealmObject
AnyQuery
実装例• https://github.com/naoty/Playground/tree/master/Repository
応用例: キャッシュ
キャッシュ•リポジトリから取得した結果をキャッシュしたい。
• キャッシュになければ別のリポジトリから取得して、キャッシュに保存した上で返す。
• キャッシュにあれば返す。
• 例: ログインユーザーの取得など
ありがとうございました