web-dev-qa-db-ja.com

Swift 4のJSONDecoderをFirebase Realtime Databaseで使用できますか?

JSONDecoderを使用してデコードできるように、Firebase DataSnapshot からデータをデコードしようとしています。

URLを使用してネットワークリクエスト(Dataオブジェクトを取得)でアクセスすると、このデータを細かくデコードできます。

ただし、Firebase APIを使用して、 (このページ で説明されているとおり、observeSingleEventを使用してデータを直接取得したいと考えています。

しかし、これを行うと、結果をDataオブジェクトに変換するようには見えないため、JSONDecoderを使用する必要があります。

DataSnapshotで新しいスタイルのJSONデコードを行うことは可能ですか?どのようにして可能ですか?理解できないようです。

13
wazawoo

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)
    }
})
21
Noobass

スナップショットをデータ形式の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 を使用してください。

8
Errol

または、このソリューションを子供たちに使用できます

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)
}
4
Leonif

いいえ。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

2
Ricardo Daniel

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")
    }
}
2
vikzilla

このライブラリ 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
    }
}
0
Manas Sharma