-1

When I try to run the following code on a Simulator (also on a Device, the result is the same) I have a problem: the function executes first the part 2 of the following code (the comments are there just to have a reference, the code itself is entirely in the viewDidLoad. Just to be clear) and then it executes the part 1 (the firebase part). I need the ref.child.... function to be performed before the other function because the "createAlbums" func needs the albums taken from my database (So a completion block would be perfect, but that function doesn't allow me to put any).

I thought about a pair of solutions:

  1. A way to add a completion block (remember that the ref.child... func is like a cycle so it will print the entire "albums" array for every single value that it finds on the database.

  2. Create a Int var and make the 2 part of the code run only if the value of the var is the same as the number of values of "Albums" in the database, so it will fill up the local "Albums" array.

  3. Use two different functions: the the viewDidLoad and the viewDidAppear. As I know, the second one will be performed after the first one.

Which one is the best solution? Is there any better solution? I also ask you to make suggests on how I can create my own solutions, because I can logically arrive to the point, but I'm not sure to be able to develop them in Xcode.

Just to summarize the entire thread: how can I make the 2 part of the following code run after the 1 part? And why does it run before, even if the order of writing is correct and the compiler runs from the line 1 to the last line?

Thank you for your help and sorry for my bad English, I hope that you can understand everything, unfortunately trying to explain a "complex" problem in English is not always easy.

But I think that the moderators will be great as always to make some corrections!

// 1
let ref = Database.database().reference()
var albums: [String] = [String]()

override func viewDidLoad() {
    super.viewDidLoad()
    ref.child("Albums").observe(.childAdded) { (snapshot) in
        albums.append(snapshot.key)

        print("\(albums)")
        print("Test print")
    }

// 2   
    createAlbums()
    setupConstraints()
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • 2
    The word you need to research is "asynchronous". – rmaddy Mar 08 '19 at 21:39
  • Use the completion block. – El Tomato Mar 08 '19 at 21:48
  • @ElTomato The func doesn't have any, as I said in the thread – Nicolò Padovan Mar 08 '19 at 21:49
  • @rmaddy Is that the fact that the first part comes after the second? Thanks – Nicolò Padovan Mar 08 '19 at 21:49
  • @NicolòPadovan Yes and the line `albums.append(snapshot.key)` and the two `print` lines after it are inside the completion block mentioned by El Tomato. – rmaddy Mar 08 '19 at 21:56
  • Have you ran it using breakpoints to make sure it comes in the order you say? An easy way to solve it is to add the functions inside the ref.child block. (Maybe not so good, but atleast it would work) but try using breakpoints and make sure it’s coming in the order you say. Because the pc should read the code from top to bottom. And seems weird if it’s not – Putte Mar 08 '19 at 21:57
  • @rmaddy As I said in the question: that "completion block" (it's not a real completion block btw) is being performed multiple times, and I need my two func to be performed only once. So albums.append and my prints are going to be performed several times. – Nicolò Padovan Mar 08 '19 at 21:58
  • @Putte Already tried to use breakpoints, and they confirmed what I said. I can't put things into the ref.child block because it's going to be performed multiple times during the viewDidLoad, and I need the other 2 func to be performed only once. – Nicolò Padovan Mar 08 '19 at 22:00
  • If you won’t find a solution till tomorrow morning (clock is currently 23:04) i’ll try to take a look at it. I’m on my phone so I cannot try it out on a playground (sorry for that) but I hope you will find a solution for it, good luck for now :) – Putte Mar 08 '19 at 22:05
  • @Putte Thank you very much! Currently it's 23.45 in my city so I don't think I'm going to have a solution tomorrow morning. Unfortunately, this weekend will be very busy for me. I hope to find you on this thread also the next week..! I'll stay tuned from my phone – Nicolò Padovan Mar 08 '19 at 22:46
  • I’m daily active. But i’m not so good at swift yet, so i can’t promies you the best result :) – Putte Mar 08 '19 at 22:50
  • Data is loaded from Firebase asynchronously, and the code after the listener continues straight away. That means that your `createAlbums()` runs before the data is loaded. Once the data is availably the closure you pass in is called. So any code that needs the data, needs to be inside the closure, or be called from there. See https://stackoverflow.com/questions/38364288/getting-data-out-of-a-closure-that-retrieves-data-from-firebase/38364861#38364861 (with their `success` being similar to your `createAlbums()`) and my longer explanation here: https://stackoverflow.com/a/37925384. – Frank van Puffelen Mar 09 '19 at 01:47
  • @FrankvanPuffelen I repeat: the albums.append(...) and the two prints are performed multiple times and I need that function to be performed only one time. The observe will run for each different node that it finds on the database, and I have a lot of data in there. – Nicolò Padovan Mar 09 '19 at 07:44
  • In that case, observe the `.value` event on the same reference/query. In your closure, loop over the children as shown here https://stackoverflow.com/a/27342233, and at the end of the loop call `createAlbums()`. – Frank van Puffelen Mar 09 '19 at 14:51
  • http://www.programmingios.net/what-asynchronous-means/ – matt Apr 07 '19 at 19:17

2 Answers2

0

You'll need to move createAlbums() and setupConstraints() into where you are doing Test Print.

Although it looks like your code is all happening in one place, it's really not.

These lines, between the curly braces...

albums.append(snapshot.key)

print("\(albums)")
print("Test print")

... are happening asynchronously, when firebase calls you back because it has data.

So you need something more like this:

// 1
let ref = Database.database().reference()
var albums: [String] = [String]()

override func viewDidLoad() {
    super.viewDidLoad()
    var createdAlbums = false
    ref.child("Albums").observe(.childAdded) { (snapshot) in
        albums.append(snapshot.key)

        print("\(albums)")
        print("Test print")

        if !createdAlbums {
            // Only run this part once, when we
            // get our first callback from firebase
            self.createAlbums()
            self.setupConstraints()
            createdAlbums = true
        }
    }

// 2
}
drewster
  • 5,460
  • 5
  • 40
  • 50
0

OTHER WORKING SOLUTION

In that case, observe the .value event on the same reference/query. In your closure, loop over the children as shown here stackoverflow.com/a/27342233, and at the end of the loop call createAlbums().

From a Frank van Puffelen comment