所以我有一个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时的输出:
答案 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"
}
}