So I have a postDict
as [String: AnyObject]
and I have a model class Post
.
我有一个postDict类型为[String: AnyObject],我有一个模型类Post。
Is there a quick way to convert postDict
to an array of Post
objects so that when dequeuing the cell, it will be:
是否有一种快速的方法将postDict转换为Post对象数组,以便在删除单元格时,它将是:
cell.textLabel.text = posts[indexPath.item].author
cell.textLabel。文本=帖子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 = ""
}
This is the output when printing out postDict:
这是打印postDict时的输出:
20
Try using the class, protocol and extension I have created below, it will save you a lot of time trying to map the snapshots to objects.
尝试使用我在下面创建的类、协议和扩展,它将节省您将快照映射到对象的大量时间。
//
// 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()
}
}
Now you can create a model that inherits the FIRDataObject class and can be initialised with a FIRDataSnapshot. Then add the FIRDatabaseReferenceable protocol to your ViewController to get access to your base 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 = ""
}
UPDATE for Swift 3
更新快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)
}
}
}
}
8
Thanks for all the comments and hints above. They certainly helped. So I am using the method with setValuesForKeysWithDictionary. It gets them into an array of posts.
感谢以上所有的评论和提示。他们肯定有帮助。我使用的方法是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 {
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 = ""
}
6
I wrote a small framework called CodableFirebase that helps using Firebase Realtime Database with Codable
in swift 4. So in your case, you need to conform your Post
model to Codable
:
我编写了一个名为CodableFirebase的小框架,它可以帮助使用在swift 4中具有可编程性的Firebase实时数据库。因此,在你的案例中,你需要将你的Post模型符合可编程性:
class Post: NSObject, Codable {
var author: String = ""
var body: String = ""
var imageURL: String = ""
var uid: String = ""
}
And then you can use the library to parse the object:
然后你可以使用库来解析对象:
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)
}
})
And that's it :) I think it's the shortest and most elegant way.
我认为这是最短、最优雅的方式。
2
This code no longer works in swift 4 because @objc inference is disabled by default.
此代码在swift 4中不再工作,因为默认情况下禁用了@objc推断。
UPDATE for Swift 4
更新迅速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 = ""
}
Or you can just make @objc inferencing a default on your project by (WARNING: loss of performance): The use of Swift 3 @objc inference in Swift 4 mode is deprecated?
或者您可以将@objc推断为您的项目的默认值(警告:性能损失):不赞成在Swift 4模式中使用Swift 3 @objc推断?
0
I am creating a helper to ease transforming snapshots to objects and viceversa. I haven't finished my project but it is working so far, I will update whenever I do changes.
我正在创建一个助手来简化将快照转换为对象和viceversa。我还没有完成我的项目,但是到目前为止它还在工作,只要我做了更改,我就会更新。
What the class does is assign automatically the value for key, but if the key represents a dictionary, then it is mapped to another object again (which may be another class object)
类的作用是自动为键赋值,但是如果键代表一个字典,那么它又被映射到另一个对象(可能是另一个类对象)
The getMap method is pretty straight forward, converting each property to a dictionary or object. When the property is another object. You can't assign nil values so the transformation must be done to [NSNull].
getMap方法非常直接,将每个属性转换为字典或对象。当属性是另一个对象时。不能赋值为nil,所以必须对[NSNull]进行转换。
I couldn't find a way to auto-detect BOOL / Double / int etc, so they should be mapped correctly on getMap method, or simply use NSNumbers in model the properties.
我找不到自动检测BOOL / Double / int等的方法,所以它们应该在getMap方法上正确地映射,或者简单地在模型中使用nsnumber。
Interface
接口
#import
@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
Implementation
实现
#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
Example usage:
使用示例:
#import
#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
Implementation
实现
#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
Usage
使用
//Parsing model
User *user = [[User alloc] init];
[user parseFromSnapshot: snapshot];
//Getting map for updateChildValues method
[user getMap]
0
Here's an Objective-C version of Callam's code above.
这是上面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;
}
Now all we need is model cascading and property type enforcement.
现在我们需要的是模型级联和属性类型强制。