Firebase 将快照值转换为对象

问题描述 投票:0回答:9

所以我有一个

postDict
作为
[String: AnyObject]
并且我有一个模型类
Post

是否有一种快速方法将

postDict
转换为
Post
对象数组,以便在使单元出队时,它将是:

cell.textLabel.text = posts[indexPath.item].author

import UIKit
import Firebase

class ViewController: UIViewController {

var posts = [Post]()

override func viewDidLoad() {
    super.viewDidLoad()

    let ref = FIRDatabase.database().reference().child("posts").queryLimitedToFirst(5)

    ref.observeEventType(FIRDataEventType.ChildAdded, withBlock: { (snapshot) in
        let postDict = snapshot.value as! [String : AnyObject]

        print(postDict)          

        //convert postDict to array of Post objects
    })
  }
}

class Post: NSObject {
    var author: String = ""
    var body: String = ""
    var imageURL: String = ""
    var uid: String = ""
}

这是打印 postDict 时的输出:

enter image description here

swift firebase firebase-realtime-database
9个回答
22
投票

尝试使用我在下面创建的类、协议和扩展,它将为您节省大量尝试将快照映射到对象的时间。

//
//  FIRDataObject.swift
//
//  Created by Callam Poynter on 24/06/2016.
//

import Firebase

class FIRDataObject: NSObject {

    let snapshot: FIRDataSnapshot
    var key: String { return snapshot.key }
    var ref: FIRDatabaseReference { return snapshot.ref }

    required init(snapshot: FIRDataSnapshot) {

        self.snapshot = snapshot

        super.init()

        for child in in snapshot.children.allObjects as? [FIRDataSnapshot] ?? [] {
            if respondsToSelector(Selector(child.key)) {
                setValue(child.value, forKey: child.key)
            }
        }
    }
}

protocol FIRDatabaseReferenceable {
    var ref: FIRDatabaseReference { get }
}

extension FIRDatabaseReferenceable {
    var ref: FIRDatabaseReference {
        return FIRDatabase.database().reference()
    }
}

现在您可以创建一个继承 FIRDataObject 类并可以使用 FIRDataSnapshot 进行初始化的模型。然后将 FIRDatabaseReferenceable 协议添加到您的 ViewController 以访问您的基本引用。

import Firebase
import UIKit

class ViewController: UIViewController, FIRDatabaseReferenceable {

    var posts: [Post] = []

    override func viewDidLoad() {

        super.viewDidLoad()

        ref.child("posts").observeEventType(.ChildAdded, withBlock: {
            self.posts.append(Post(snapshot: $0))
        })
    }
}

class Post: FIRDataObject {

    var author: String = ""
    var body: String = ""
    var imageURL: String = ""
}

更新 适用于 Swift 3

class FIRDataObject: NSObject {

    let snapshot: FIRDataSnapshot
    var key: String { return snapshot.key }
    var ref: FIRDatabaseReference { return snapshot.ref }

    required init(snapshot: FIRDataSnapshot) {

        self.snapshot = snapshot

        super.init()

        for child in snapshot.children.allObjects as? [FIRDataSnapshot] ?? [] {
            if responds(to: Selector(child.key)) {
                setValue(child.value, forKey: child.key)
            }
        }
    }
}

9
投票

感谢上面所有的评论和提示。 他们当然有帮助。 所以我使用 setValuesForKeysWithDictionary 的方法。 它让他们进入一系列帖子。

import UIKit
import Firebase
class ViewController: UIViewController {

var posts = [Post]()

override func viewDidLoad() {
    super.viewDidLoad()

    let ref = FIRDatabase.database().reference().child("posts").queryLimitedToFirst(3)

    ref.observeEventType(.Value, withBlock: { snapshot in
        print(snapshot.value)
        self.posts = []
        if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
            for snap in snapshots {
                if let postDict = snap.value as? Dictionary<String, AnyObject> {
                    let post = Post()
                    post.setValuesForKeysWithDictionary(postDict)
                    self.posts.append(post)
                }
            }
        }
        print("post 0: \(self.posts[0].body)")
        print("post 1: \(self.posts[1].body)")
        print("post 2: \(self.posts[2].body)")
      })
   }
}

class Post: NSObject {
    var author: String = ""
    var body: String = ""
    var imageURL: String = ""
    var uid: String = ""
}

8
投票

我编写了一个名为 CodableFirebase 的小框架,它有助于在 swift 4 中将 Firebase 实时数据库与

Codable
一起使用。因此,就您而言,您需要使您的
Post
模型符合
Codable
:

class Post: NSObject, Codable {
    var author: String = ""
    var body: String = ""
    var imageURL: String = ""
    var uid: String = ""
}

然后就可以使用库来解析对象了:

import CodableFirebase

ref.observeEventType(.сhildAdded, withBlock: { (snapshot) in
    guard let value = snapshot.value else { return }
    do {
        let posts = try FirebaseDecoder().decode([Post].self, from: value)
        print(posts)
    } catch let error {
        print(error)
    }
})

就是这样:)我认为这是最短、最优雅的方式。


2
投票

此代码在 swift 4 中不再有效,因为默认情况下禁用 @objc 推理。

更新适用于 Swift 4

class FIRDataObject: NSObject {

    let snapshot: FIRDataSnapshot
    @objc var key: String { return snapshot.key }
    var ref: FIRDatabaseReference { return snapshot.ref }

    required init(snapshot: FIRDataSnapshot) {

        self.snapshot = snapshot

        super.init()

        for child in snapshot.children.allObjects as? [FIRDataSnapshot] ?? [] {
            if responds(to: Selector(child.key)) {
                setValue(child.value, forKey: child.key)
            }
        }
    }
}

class Post: FIRDataObject {

    @objc var author: String = ""
    @objc var body: String = ""
    @objc var imageURL: String = ""
}

或者您可以通过以下方式将 @objc 推理设置为项目的默认值(警告:性能损失): 在 Swift 4 模式下使用 Swift 3 @objc 推理已被弃用?


0
投票

我正在创建一个助手来轻松将快照转换为对象,反之亦然。我还没有完成我的项目,但到目前为止它正在运行,每当我进行更改时我都会更新。

类的作用是自动为键分配值,但是如果键代表一个字典,那么它会再次映射到另一个对象(可能是另一个类对象)

getMap 方法非常简单,将每个属性转换为字典或对象。当属性是另一个对象时。您不能分配 nil 值,因此必须对 [NSNull] 进行转换。

我找不到自动检测 BOOL / Double / int 等的方法,因此它们应该在 getMap 方法上正确映射,或者简单地在属性模型中使用 NSNumbers 。

界面

#import <Foundation/Foundation.h>
@import FirebaseDatabase;

#ifndef FIRModel_m

#define FIRModel_m

#define IS_OBJECT(T) _Generic( (T), id: YES, default: NO)

#endif



/** Firebase model that helps converting Firebase Snapshot to object, and converting the object
 * to a dictionary mapping for updates */
@interface FIRModel : NSObject

/** Parses the snapshot data into the object */
- (void) parseFromSnapshot: (FIRDataSnapshot*) snapshot;

/** Returns a new model for the given key */
- (FIRModel*) modelForKey: (NSString*) key;

/** Returns the dictionary representation of this object */
- (NSMutableDictionary*) getMap;

/** Returns an object value for the given preference
 * If the property is null, then NSNUll is returned
 */
- (NSObject*) objFor: (id) value;

@end

实施

#import "FIRModel.h"

@implementation FIRModel
/** Parses the snapshot data into the object */
- (void) parseFromSnapshot: (FIRDataSnapshot*) snapshot {

    [self setValuesFromDictionary: snapshot.value];
}

/** Custom implementation for setValuesForKeysWithDictionary 
 *  Whenever it finds a Dictionary, it is transformed to the corresponding model object
 */
- (void)setValuesFromDictionary:(NSDictionary*)dict
{

    NSLog(@"Parsing in %@ the following received info: %@", [self class], dict);


    for (NSString* key in dict) {

        NSObject* value = [dict objectForKey:key];

        if(!value || [value isKindOfClass: [NSNull class]]) {
            //do nothing, value stays null
        }


        //TODO: Do the same for arrays
        else if(value && [value isKindOfClass: [NSDictionary class]]) {

            FIRModel* submodel = [self modelForKey: key];

            if(submodel) {
                [submodel setValuesFromDictionary: (NSDictionary*)value];
                [self setValue: submodel forKey: key];
            } else {
                NSLog(@"ERROR - *** Nil model returned from modelForKey for key: %@ ***", key );
            }
        }
        else {
            [self setValue: value forKey:key];
        }

    }
}

/** Override for added firebase properties**/
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    NSLog(@"Unknown key: %@ on object: %@", key, [self class] );
}

/** Returns a new model for the given key */
- (FIRModel*) modelForKey: (NSString*) key {
    return nil; //to be implemented by subclasses
}

/** Returns the dictionary representation of this object */
- (NSMutableDictionary*) getMap {
    [NSException raise:@"getMap not implmented" format:@"ERROR - Not implementing getMap for %@", self.class];
    return [NSMutableDictionary dictionary];
}

/** Returns an object value for the given preference
 * If the property is null, then NSNUll is returned
 */
- (NSObject*) objFor: (id) value {

    if(!value || !IS_OBJECT(value)) {
        return [NSNull null];
    }

    return value;


}


@end

用法示例:

#import <Foundation/Foundation.h>
#import "FIRModel.h"


/** The user object */
@class PublicInfo;


@interface User : FIRModel

@property (nonatomic, strong) NSString* email;

@property (nonatomic, strong) NSString* phone;

@property (nonatomic, strong) PublicInfo* publicInfo;

@property (nonatomic, assign) double aDoubleValue;  

@property (nonatomic, assign) BOOL aBoolValue;  

@property (nonatomic, strong) id timestampJoined;   //Map or NSNumber

@property (nonatomic, strong) id timestampLastLogin;  //Map or NSNumber


@end



@interface PublicInfo : FIRModel

@property (nonatomic, strong) NSString* key;

@property (nonatomic, strong) NSString* name;

@property (nonatomic, strong) NSString* pic;

@end

实施

#import "User.h"


@implementation User

/** Returns a new model for the given key */
- (FIRModel*) modelForKey: (NSString*) key {
    if ([key isEqualToString: @"publicInfo"]) {
        return [[PublicInfo alloc] init];
    }
    return nil;
}

- (NSMutableDictionary *)getMap {

    NSMutableDictionary* map = [NSMutableDictionary dictionary];
    map[@"email"] =  [self objFor: self.email];
    map[@"phone"] = [self objFor: self.phone];
    map[@"aDoubleValue"] = @(self.aDoubleValue);
    map[@"aBoolValue"] = @(self.aBoolValue);
    map[@"publicInfo"] = self.publicInfo ? [self.publicInfo getMap] : [NSNull null];
    map[@"timestampJoined"] =  [self objFor: self.timestampJoined];
    map[@"timestampLastLogin"] = [self objFor: self.timestampLastLogin];

    return map;
}

@end


#pragma mark -

@implementation PublicInfo

- (NSMutableDictionary *)getMap {

    NSMutableDictionary* map = [NSMutableDictionary dictionary];
    map[@"name"] =  [self objFor: self.name];
    map[@"pic"] =  [self objFor: self.pic];
    map[@"key"] = [self objFor: self.key];

    return map;
}

@end

用法

//Parsing model
User *user = [[User alloc] init];
[user parseFromSnapshot: snapshot];

//Getting map for updateChildValues method
[user getMap]

0
投票

这是上面 Callam 代码的 Objective-C 版本。

@import Firebase;

@interface FIRDataObject : NSObject

@property (strong, nonatomic) FIRDataSnapshot *snapshot;
@property (strong, nonatomic, readonly) NSString *key;
@property (strong, nonatomic, readonly) FIRDatabaseReference *ref;

-(instancetype)initWithSnapshot:(FIRDataSnapshot *)snapshot;

@end

@implementation FIRDataObject

-(NSString *)key
{
    return _snapshot.key;
}

-(FIRDatabaseReference *)ref
{
    return _snapshot.ref;
}

-(instancetype)initWithSnapshot:(FIRDataSnapshot *)snapshot
{
 if (self = [super init])
 {
     _snapshot = snapshot;
     for (FIRDataSnapshot *child in snapshot.children.allObjects)
     {
         if ([self respondsToSelector:NSSelectorFromString(child.key)])
         {
             [self setValue:child.value forKey:child.key];
         }
     }
 }
    return self;
}

现在我们需要的只是模型级联和属性类型强制。


0
投票

我发现了一个更简单的方法。

Swift 对象:

import Foundation

class FirebaseTransactionData : NSObject{

    var customer : FirebaseTransactionDataCustomer!
    var driver : FirebaseTransactionDataCustomer!
    var status : String!

    init(fromDictionary dictionary: [String:Any]){
        status = dictionary["status"] as? String
        if let customerData = dictionary["customer"] as? [String:Any]{
            customer = FirebaseTransactionDataCustomer(fromDictionary: customerData)
        }
        if let driverData = dictionary["driver"] as? [String:Any]{
            driver = FirebaseTransactionDataCustomer(fromDictionary: driverData)
        }
    }
}

class FirebaseTransactionDataCustomer : NSObject{

    var lat : Double!
    var longField : Double!

    init(fromDictionary dictionary: [String:Any]){
        lat = dictionary["lat"] as? Double
        longField = dictionary["lng"] as? Double
    }
}

Firebase 方法

ref.observe(DataEventType.value, with: { (snapshot) in
            let value = snapshot.value as? [String:Any]
            let datt = FirebaseTransactionData(fromDictionary: value!)
            print("snapshot \(datt.status!)")
            print("snapshot \(datt.customer.lat!)")
        })

0
投票

我发现的最简单的解决方案是将对象转换为 JSON 数据,如下所示:

let jsonData =  try! JSONSerialization.data(withJSONObject: snapshot.value!, options: .prettyPrinted)

然后使用 JSONDecoder 解析对象:

try! JSONDecoder().decode(UserOtherDetails.self, from: jsonData)

完整的解决方案如下所示:

Database.database().reference().child("USER").observe(.value) { (snapshot) in
    let jsonData =  try! JSONSerialization.data(withJSONObject: snapshot.value!, options: .prettyPrinted)
    self.userOtherDetails = try! JSONDecoder().decode(UserOtherDetails.self, from: jsonData)
}

对象的一个示例是:

struct UserOtherDetails: Codable {
   let addressBlock, addressCity: String
   let addressIDS: [Int]
   let addressUnit, displayName, phone: String
   let rememberMe: Int

   enum CodingKeys: String, CodingKey {
      case addressBlock = "address_block"
      case addressCity = "address_city"
      case addressIDS = "address_ids"
      case addressUnit = "address_unit"
      case displayName = "display_name"
      case phone
      case rememberMe = "remember_me"
   }
}

0
投票
>     final class FirestoreManager {
>     
>         private func snapConvert<T: Decodable>(_ query: QuerySnapshot?, to type: T.Type) -> [T] {
>             guard let snapshot = query else {
>                 return [T]()
>             }
>             var models = [T]()
>             for document in snapshot.documents {
>                 let json = JSON(document.data())
>                 if let model = try? JSONDecoder().decode(T.self, from: try! json.rawData()) {
>                     models.append(model)
>                 }
>             }
>             return models
>         }
>     
>     }

我使用这样的泛型,如果你愿意,你可以发送你的模型并将其从完成块中转义。

© www.soinside.com 2019 - 2024. All rights reserved.