1

How do I load data from an async callback into the parent class attributes?

I have a database in Firebase which holds my user profiles. I want to be able to pass in the node key and have it populate the object with the given values.

Because it works async, I am getting the default values from the object because the callback completes after the class init.

Obviously I'm new to Swift so apologies if the terminology is sketchy.

I read this thread which talked about inversion of control pattern, and whilst it made sense, I couldn't work out how to actually initiate the class.

UserProfile.swift:

import UIKit
import Firebase
import FBSDKCoreKit

class UserProfile{

    var ref: DatabaseReference!

    var uid : String = ""
    var name : String = ""
    var location : String = ""
    //var posts = [String]()

    init(uid : String) {
        self.ref = Database.database().reference().child("user_profiles").child(uid)
        self.ref.observeSingleEvent(of: .value, with: { (snapshot) in
            let userObj = snapshot.value as? [String : AnyObject] ?? [:]
            self.uid = uid
            self.name = userObj["name"] as! String
            self.location = userObj["location"] as! String
        })
    } 
}

ViewController.swift

let user = UserProfile(uid : "NODEKEYINFIREBASE")
print(user)

Output:

()
1
  • 2
    I think you should consider changing your approach completely on this and instead of loading data in initializer you should think of network service that will initialize your model only when all data are loaded Commented Jul 4, 2017 at 12:20

2 Answers 2

1

I'm not sure, that it is best practice, but I'm using completion handler for it.

Example:

1) Data item:

class Item {
   // properties...

   init(snapshot: [String: Any], _ key: String,
        completion: @escaping (_ item: Item) -> Void) {
     // some inits

     // some async task starts 
         // completion(item)
   }
}

2) Use:

let _ = Item(snapshot: value!, key) { item in
            // some tasks.. like
            self.newItem = item // or other
        }

I think you should get the idea.

Hope it helps

Sign up to request clarification or add additional context in comments.

1 Comment

I like this idea.
0

You can make a protocol that will be called after the object is initialized.

import UIKit
import Firebase
import FBSDKCoreKit

protocol UserProfileProtocol {
    func userLoaded(userProfile: UserProfile);
}

class UserProfile{

var ref: DatabaseReference!

var uid : String = ""
var name : String = ""
var location : String = ""
//var posts = [String]()

init(uid : String, listener: UserProfileProtocol) {
    self.ref = Database.database().reference().child("user_profiles").child(uid)
    self.ref.observeSingleEvent(of: .value, with: { (snapshot) in
        let userObj = snapshot.value as? [String : AnyObject] ?? [:]
        self.uid = uid
        self.name = userObj["name"] as! String
        self.location = userObj["location"] as! String
        listener.userLoaded(userProfile: self)
    })
  } 
}

Add the protocol to the parent class as UserProfileProtocol

class parentViewController: UIViewController, UserProfileProtocol

I hope this works. Otherwise try the solution of @Vlad Pulichev

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.