如何在Swift中使用循环异步创建函数?

时间:2018-01-17 20:32:36

标签: swift firebase asynchronous

我正在为库创建一个应用程序。我正在尝试从Firebase中获取用户已检出的所有图书,但我尝试使用DispatchGroup使该函数异步似乎并不起作用。我怀疑这是因为在函数内部找到了for-in循环。

# supposing x contains variables A, B, C, etc.
interacting_variables <- c('B', 'D', 'E', 'F')

my_glm.hex <- h2o.glm(y=y_idx,x=x_idx,
                      training_frame = "my_train",
                      validation_frame = "my_valid",
                      model_id = "my_glm.hex",
                      family = "binomial",
                      lambda_search = TRUE,
                      balance_classes = TRUE,
                      interactions = interacting_variables)

以下是print()语句的输出:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.coderz.creative.music.Fragment.HomeFragment">
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:id="@+id/chart_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <android.support.v7.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="250dp"
                android:layout_margin="10dp">

                <com.github.mikephil.charting.charts.PieChart
                    android:id="@+id/pie_chart_spend"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">

                </com.github.mikephil.charting.charts.PieChart>
            </android.support.v7.widget.CardView>

            <android.support.v7.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="250dp"
                android:layout_margin="10dp">

                <com.github.mikephil.charting.charts.BarChart
                    android:id="@+id/bar_chart_spend"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">

                </com.github.mikephil.charting.charts.BarChart>
            </android.support.v7.widget.CardView>

            <android.support.v7.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="250dp"
                android:layout_margin="10dp">

                <com.github.mikephil.charting.charts.BarChart
                    android:id="@+id/pie_chart_spend_on_click"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">

                </com.github.mikephil.charting.charts.BarChart>
            </android.support.v7.widget.CardView>
        </LinearLayout>
    </ScrollView>

</FrameLayout>

输出的前两行应在其他所有内容打印后打印。我真的需要一些帮助,我已经坚持了几个小时。谢谢!

编辑: 根据要求,这是我的Firebase结构:

    func fetchHistory() {

    if items.count > 0 {
        items.removeAll()
    }

    let myGroup = DispatchGroup()
    myGroup.enter()

    var itemNames = [String]() // this holds the names of the child values of /users/uid/items/ <-- located in Firebase Database
    guard let uid = fAuth.currentUser?.uid else {return}
    fData.child("users").child(uid).child("items").observe(.value, with: { snapshot in

        // make sure there is at least ONE item in the history
        if snapshot.childrenCount > 0 {
            let values = snapshot.value as! NSDictionary
            for i in values.allKeys {
                itemNames.append(i as! String)
            }

            print(itemNames)
            let uid = fAuth.currentUser!.uid // get the UID of the user
            for item in itemNames {
                fData.child("users").child(uid).child("items").child(item).observe(.value, with: { snapshot in
                    let values = snapshot.value as! NSDictionary
                    let bookTitle = values["title"] as! String
                    print(bookTitle)
                    let bookAuthor = values["author"] as! String
                    print(bookAuthor)
                    let bookCoverUrl = values["coverUrl"] as! String
                    print(bookCoverUrl)
                    let bookStatus = values["status"] as! String
                    print(bookStatus)
                    let bookDueDate = values["dueDate"] as! String
                    print(bookDueDate)

                    let book = Book(name: bookTitle, author: bookAuthor, coverUrl: bookCoverUrl, status: bookStatus, dueDate: bookDueDate)
                    self.items.append(book)
                })
            }
            self.booksTable.isHidden = false
        } else {
            self.booksTable.isHidden = true
        }

    })

    myGroup.leave()
    myGroup.notify(queue: DispatchQueue.main, execute: {
        self.booksTable.reloadData()
        print("Reloading table")
    })

}

2 个答案:

答案 0 :(得分:2)

有几个问题:

  1. 模式是leave必须在异步调用的完成处理程序中调用 。您希望这是在闭包内执行的最后一件事,因此您可以将其添加为完成处理程序闭包中的最后一行。

    或者我更喜欢使用defer子句,这样你不仅知道它将是闭包中执行的最后一件事,而且还是:

    • 即使您稍后在封闭内添加任何“早期退出”,您也可以确保leave;和
    • enterleave调用在视觉中直接显示在代码中,使您无需在闭包底部进行视觉搜索,以确保正确调用它。
  2. 如果你想等待for循环中的异步调用,你也必须在那里添加它。

  3. 非常小的一点,但在成功解开uid之前,您可能不想创建该组。如果可能DispatchGroup而不执行任何异步代码,为什么要创建return

  4. 因此,或许:

    func fetchHistory() {
    
        if items.count > 0 {
            items.removeAll()
        }
    
        var itemNames = [String]()
        guard let uid = fAuth.currentUser?.uid else {return}
    
        let group = DispatchGroup()
    
        group.enter()
    
        fData.child("users").child(uid).child("items").observe(.value, with: { snapshot in
    
            defer { group.leave() }               // in case you add any early exits, this will safely capture
    
            if snapshot.childrenCount > 0 {
                ...
                for item in itemNames {
    
                    group.enter()                 // also enter before we do this secondary async call
    
                    fData.child("users").child(uid).child("items").child(item).observe(.value, with: { snapshot in
    
                        defer { group.leave() }   // and, again, defer the `leave`
    
                        ...
                    })
                }
                ...
            } else {
                ...
            }
        })
    
        group.notify(queue: .main) {
            self.booksTable.reloadData()
            print("Reloading table")
        }    
    }
    

答案 1 :(得分:0)

虽然Rob有一个很好的答案,但我会从不同的方向寻求解决方案。

一本书只能让一个人检查出来(一次),但借款人可以拥有多本书。由于这种关系,只需将拥有该书的人与书本身结合起来:

这是一个建议的用户结构

users
  uid_0
    name: "Rob"
  uid_1
    name: "Bill"

然后是书籍节点

books
   78DFB90A-DE5B-47DE-ADCA-2DAB9D43B9C8
     author: "Suzanne Collins"
     coverUrl: "https://images.gr assets.com/books/1358275419s/..."
     dueDate: "Date"
     status: "Checked"
     title: "Mockingjay (The Hunger Games, #3)"
     checked_out_by: "uid_0"
     check_date: "20180118"

然后获取Rob已检出的所有书籍并使用这些结果填充数组并将其显示在tableview中变得非常简单:

//var bookArray = [Book]() //defined as a class var

let booksRef = self.ref.child("books")
let query = booksRef.queryOrdered(byChild: "checked_out_by").queryEqual(toValue: "uid_0")
booksRef.observeSingleEvent(of: .value, with: { snapshot in
    for child in snapshot.children {
        let snap = child as! DataSnapshot
        let book = Book(initWithSnap: snap) //take the fields from the snapshot and populate the book
        self.bookArray.append(book)
    }
    self.tableView.reloadData()
})

然后你问自己,&#34;自我,如果我想要一本谁签出这本书的记录怎么办?&#34;

如果您需要该功能,只需稍微更改books节点,以便我们可以利用深层查询;

books
   78DFB90A-DE5B-47DE-ADCA-2DAB9D43B9C8
     author: "Suzanne Collins"
     coverUrl: "https://images.gr assets.com/books/1358275419s/..."
     dueDate: "Date"
     status: "Checked"
     title: "Mockingjay (The Hunger Games, #3)"
     check_out_history
        "uid_0" : true
        "uid_1" : true

并将签出日期移至用户节点。然后,您可以查询任何图书的任何用户,并查看谁签出该图书的历史记录。 (需要有逻辑来确定当前谁有这本书所以这只是一个起点)

或者,如果您想要其他选项,请保留一个单独的图书历史记录节点

book_history
   78DFB90A-DE5B-47DE-ADCA-2DAB9D43B9C8
      -9j9jasd9jasjd4    //key is created with childByAutoId
         uid: "uid_0"
         check_out_date: "20180118"
         check_in_date: "20180122"
         condition: "excellent"
      -Yuhuasijdijiji    //key is created with childByAutoId
         uid: "uid_1"
         check_out_date: "20180123"
         check_in_date: "20180125"
         condition: "good"

这个概念是让Firebase为您完成工作,而不是反复迭代数组,并且必须发出数十个调用来获取您需要的数据。调整结构使得将来维护和扩展变得更加简单 - 它避免了异步代码的所有问题,因为它都在闭包内;干净整洁。