Firebase将快照值转换为对象

时间:2016-06-26 13:32:37

标签: swift firebase firebase-realtime-database

所以我有一个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

8 个答案:

答案 0 :(得分:21)

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

//
//  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)
            }
        }
    }
}

答案 1 :(得分:8)

感谢上面的所有评论和提示。他们肯定有帮助。所以我使用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 = ""
}

答案 2 :(得分:8)

我写了一个名为CodableFirebase的小框架,它有助于在swift 4中使用带有Codable的Firebase实时数据库。因此,在您的情况下,您需要将Post模型符合{{1 }}:

Codable

然后您可以使用该库来解析对象:

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

那就是它:)我认为它是最短最优雅的方式。

答案 3 :(得分: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推断项目的默认值: The use of Swift 3 @objc inference in Swift 4 mode is deprecated?

答案 4 :(得分: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]

答案 5 :(得分: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;
}

现在我们所需要的只是模型级联和属性类型实施。

答案 6 :(得分:0)

我找到了一种更简单的方法。

快速对象:

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!)")
        })

答案 7 :(得分: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"
   }
}