JSONDecoderを使用してデコードできるように、Firebase DataSnapshot からデータをデコードしようとしています。
URLを使用してネットワークリクエスト(Dataオブジェクトを取得)でアクセスすると、このデータを細かくデコードできます。
ただし、Firebase APIを使用して、 (このページ で説明されているとおり、observeSingleEventを使用してデータを直接取得したいと考えています。
しかし、これを行うと、結果をDataオブジェクトに変換するようには見えないため、JSONDecoderを使用する必要があります。
DataSnapshotで新しいスタイルのJSONデコードを行うことは可能ですか?どのようにして可能ですか?理解できないようです。
Firebase専用に設計されたEncoders
およびDecoders
を提供する CodableFirebase というライブラリを作成しました。
したがって、上記の例の場合:
import Firebase
import CodableFirebase
let item: GroceryItem = // here you will create an instance of GroceryItem
let data = try! FirebaseEncoder().encode(item)
Database.database().reference().child("pathToGraceryItem").setValue(data)
そして、同じデータを読み取る方法は次のとおりです。
Database.database().reference().child("pathToGraceryItem").observeSingleEvent(of: .value, with: { (snapshot) in
guard let value = snapshot.value else { return }
do {
let item = try FirebaseDecoder().decode(GroceryItem.self, from: value)
print(item)
} catch let error {
print(error)
}
})
スナップショットをデータ形式のJSONに戻すことにより、JSONDecoderを使用してFirebaseスナップショットを変換しました。構造体はDecodableまたはCodableに準拠する必要があります。 SwiftyJSONでこれを実行しましたが、この例ではJSONSerializationを使用しており、引き続き機能します。
JSONSnapshotPotatoes {
"name": "Potatoes",
"price": 5,
}
JSONSnapshotChicken {
"name": "Chicken",
"price": 10,
"onSale": true
}
struct GroceryItem: Decodable {
var name: String
var price: Double
var onSale: Bool? //Use optionals for keys that may or may not exist
}
Database.database().reference().child("grocery_item").observeSingleEvent(of: .value, with: { (snapshot) in
guard let value = snapshot.value as? [String: Any] else { return }
do {
let jsonData = try JSONSerialization.data(withJSONObject: value, options: [])
let groceryItem = try JSONDecoder().decode(GroceryItem.self, from: jsonData)
print(groceryItem)
} catch let error {
print(error)
}
})
JSONキーがDecodable構造体と同じでない場合は注意してください。 CodingKeysを使用する必要があります。例:
JSONSnapshotSpinach {
"title": "Spinach",
"price": 10,
"onSale": true
}
struct GroceryItem: Decodable {
var name: String
var price: Double
var onSale: Bool?
enum CodingKeys: String, CodingKey {
case name = "title"
case price
case onSale
}
}
この詳細については、Apple Docs here を使用してください。
または、このソリューションを子供たちに使用できます
extension DatabaseReference {
func makeSimpleRequest<U: Decodable>(completion: @escaping (U) -> Void) {
self.observeSingleEvent(of: .value, with: { snapshot in
guard let object = snapshot.children.allObjects as? [DataSnapshot] else { return }
let dict = object.compactMap { $0.value as? [String: Any] }
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
let parsedObjects = try JSONDecoder().decode(U.self, from: jsonData)
completion(parsedObjects)
} catch let error {
print(error)
}
})
}
}
そして使う
self.refPriceStatistics.child(productId).makeSimpleRequest { (parsedArray: [YourArray]) in
callback(parsedArray)
}
いいえ。Firebaseは、デコードできないFIRDataSnapshotを返します。ただし、この構造は非常にシンプルで理解しやすいものです。
struct GroceryItem {
let key: String
let name: String
let addedByUser: String
let ref: FIRDatabaseReference?
var completed: Bool
init(name: String, addedByUser: String, completed: Bool, key: String = "") {
self.key = key
self.name = name
self.addedByUser = addedByUser
self.completed = completed
self.ref = nil
}
init(snapshot: FIRDataSnapshot) {
key = snapshot.key
let snapshotValue = snapshot.value as! [String: AnyObject]
name = snapshotValue["name"] as! String
addedByUser = snapshotValue["addedByUser"] as! String
completed = snapshotValue["completed"] as! Bool
ref = snapshot.ref
}
func toAnyObject() -> Any {
return [
"name": name,
"addedByUser": addedByUser,
"completed": completed
]
}
}
そして、toAnyObject()を使用してアイテムを保存します。
let groceryItemRef = ref.child("items")
groceryItemRef.setValue(groceryItem.toAnyObject())
フォント: https://www.raywenderlich.com/139322/firebase-tutorial-getting-started-2
Firebaseから返された値をデータに変換し、それをデコードできます。
この拡張機能をプロジェクトに追加します。
extension Collection {
//Designed for use with Dictionary and Array types
var jsonData: Data? {
return try? JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
}
}
次に、それを使用して、観測されたスナップショットの値をデータに変換します。これをデコードできます。
yourRef.observe(.value) { (snapshot) in
guard snapshot.exists(),
let value = snapshot.value as? [String],
let data = value.jsonData else {
return
}
//cast to expected type
do {
let yourNewObject = try JSONDecoder().decode([YourClass].self, from: data)
} catch let decodeError {
print("decodable error")
}
}
このライブラリ CodableFirebase を使用するか、次の拡張機能が役立ちます。
extension JSONDecoder {
func decode<T>(_ type: T.Type, from value: Any) throws -> T where T : Decodable {
do {
let data = try JSONSerialization.data(withJSONObject: value, options: .prettyPrinted)
let decoded = try decode(type, from: data)
return decoded
} catch {
throw error
}
}