所以我有一个
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 时的输出:
尝试使用我在下面创建的类、协议和扩展,它将为您节省大量尝试将快照映射到对象的时间。
//
// 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)
}
}
}
}
感谢上面所有的评论和提示。 他们当然有帮助。 所以我使用 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 = ""
}
我编写了一个名为 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)
}
})
就是这样:)我认为这是最短、最优雅的方式。
此代码在 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 推理已被弃用?
我正在创建一个助手来轻松将快照转换为对象,反之亦然。我还没有完成我的项目,但到目前为止它正在运行,每当我进行更改时我都会更新。
类的作用是自动为键分配值,但是如果键代表一个字典,那么它会再次映射到另一个对象(可能是另一个类对象)
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]
这是上面 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;
}
现在我们需要的只是模型级联和属性类型强制。
我发现了一个更简单的方法。
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!)")
})
我发现的最简单的解决方案是将对象转换为 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"
}
}
> 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
> }
>
> }
我使用这样的泛型,如果你愿意,你可以发送你的模型并将其从完成块中转义。