xmlやplistを使うことも考えたが、codableを使うことで独自のモデルにも容易に対応させることができた。
環境
- Xcode 11.4.1
- Swift 5
- iOS 13.4.1
StringやIntなどは自動Codableに準拠しているので、encodeやdecodeメソッドを使う必要はないが、
今回はその他のデータ型にも対応できるようにencode、decodeメソッドを実装した。
モデル[Recipe]
import Foundation
import RealmSwift
class Recipe: Object,Decodable {
@objc dynamic var name = ""
@objc dynamic var category = ""
@objc dynamic var country = ""
@objc dynamic var season = ""
var ingredients = List<Ingredient>() //RecipeとIngredientは1対多
override static func primaryKey() -> String? {
return "name"
}
private enum CodingKeys:String,CodingKey{
case name
case category
case country
case season
case ingredients
}
required convenience public init(from decoder:Decoder) throws{
self.init()
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
category = try container.decode(String.self, forKey: .category)
country = try container.decode(String.self, forKey: .country)
season = try container.decode(String.self, forKey: .season)
//一度配列に格納した後、Listに変換する
let ingredientsArray = try container.decode([Ingredient].self, forKey: .ingredients)
ingredients = ingredientsArray.reduce(List<Ingredient>()) {$0.append($1); return $0}
}
}
モデル[ingredient]
import Foundation
import RealmSwift
class Ingredient: Object,Decodable {
@objc dynamic var name = ""
let recipes = LinkingObjects(fromType: Recipe.self, property: "ingredients")
private enum CodingKeys: String, CodingKey {
case name
}
required convenience public init(from decoder: Decoder) throws {
self.init()
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
}
}
JSONファイル
[
{
"name": "トマトパスタ",
"category": "パスタ",
"country": "イタリアン",
"season": "Any",
"ingredients":[
{
"name": "トマト"
},
{
"name": "にんにく"
}
]
},
{
"name": "カツ丼",
"category": "丼ぶり",
"country": "和食",
"season": "Any",
"ingredients":[
{
"name": "たまねぎ"
},
{
"name": "卵"
},
{
"name": "豚肉"
}
]
},
{
"name": "ペペロンチーノ",
"category": "パスタ",
"country": "イタリアン",
"season": "Any",
"ingredients":[
{
"name": "にんにく"
},
{
"name": "鷹の爪"
}
]
},
{
"name": "カレー",
"category": "カレー",
"country": "和食",
"season": "Any",
"ingredients":[
{
"name": "人参"
},
{
"name": "たまねぎ"
},
{
"name": "じゃがいも"
},
{
"name": "牛肉"
}
]
},
{
"name": "シチュー",
"category": "シチュー",
"country": "和食",
"season": "Any",
"ingredients":[
{
"name": "人参"
},
{
"name": "たまねぎ"
},
{
"name": "じゃがいも"
},
{
"name": "牛肉"
}
]
},
{
"name": "炒飯",
"category": "ご飯",
"country": "中華",
"season": "Any",
"ingredients":[
{
"name": "人参"
},
{
"name": "たまねぎ"
},
{
"name": "卵"
},
{
"name": "にんにく"
}
]
},
{
"name": "炊き込みご飯",
"category": "ご飯",
"country": "和食",
"season": "Any",
"ingredients":[
{
"name": "人参"
},
{
"name": "椎茸"
},
{
"name": "鶏肉"
},
{
"name": "大葉"
}
]
}
]
JSONデータの取り込み
//JSONファイルのパスを取得
guard let path = Bundle.main.path(forResource: "RecipesList", ofType: "json") else { return }
let url = URL(fileURLWithPath: path)
do {
let realm = try! Realm()
let data = try Data(contentsOf: url)
let recipe_obj = try! JSONDecoder().decode([Recipe].self, from: data)
try! realm.write {
realm.deleteAll()
realm.add(recipe_obj, update: true)
//人参を使うレシピを取得・表示
var objects: Results<Recipe>
objects = realm.objects(Recipe.self).filter("SUBQUERY(ingredients, $ingredient, $ingredient.name = %@).@count >= 1","人参")
print("objectsの中",objects)
}
} catch {
print("エラー")
}







