Swift Firebase加载记录延迟很长

时间:2016-12-11 02:18:56

标签: ios swift firebase firebase-realtime-database offline

我正在使用Firebase构建一个Swift应用程序,而且我是两个新手都很温柔。目前,当我打开应用程序时,它会再次同步整个数据库,并导致2或3秒滞后,用户盯着空的tableview。我怎样才能加快速度呢?

有什么想法吗?

我的代码:

我的loadContacts函数

import UIKit
import Firebase

class ContactTableViewController: UITableViewController, UISearchBarDelegate, UISearchDisplayDelegate {
// MARK: Properties
var contactSearchResults : [Contact] = []

// FIRDatabase.database().persistenceEnabled = true
let contactRef = FIRDatabase.database().reference().child("contacts")

override func viewDidLoad() {

    contactRef.queryOrdered(byChild: "Last Name").observe(.childAdded) { (snap: FIRDataSnapshot) in
        contacts.append(loadContact(snap: snap))
        self.tableView.reloadData()
    }

    contactRef.queryOrdered(byChild: "Last Name").observe(.childChanged) { (snap: FIRDataSnapshot) in
        // this code here is wrong, but it doesn't matter for demonstration purposes
        contacts.append(loadContact(snap: snap))
        self.tableView.reloadData()
    }

    // Uncomment the following line to preserve selection between presentations
    // self.clearsSelectionOnViewWillAppear = false

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem()
}

在我的联系表视图中

public class CustomInterceptor implements Interceptor {
    private static final Charset UTF8 = Charset.forName("UTF-8");

    @Override
    public Response intercept(Chain chain) throws IOException {
        final Request request = chain.request();
        final String urlStr = request.url().toString();
        final Request.Builder builder = request.newBuilder();

            if (request.body() != null && request.body() instanceof FormBody) {
                .
                .
                .
                .
                .
                final RequestBody newBody = RequestBody.create(mediaType, content);

                final Request newRequest = builder.url(urlStr)
                        .header("User-Agent", ApiUtils.getUserAgent())
                        .header("Content-Type", oldBody.contentType().toString())
                        .header("Content-Length", String.valueOf(content.length()))
                        .method(request.method(), newBody)
                        .build();


                //
                final Response response = chain.proceed(newRequest);
                final ResponseBody responseBody = response.body();
                final Headers headers = response.headers();
                BufferedSource source = responseBody.source();
                source.request(Long.MAX_VALUE); // Buffer the entire body.
                Buffer buffer = source.buffer();
                Charset charset = UTF8;
                MediaType contentType = responseBody.contentType();
                String realResult;
                try {
                    if (contentType != null) {
                        charset = contentType.charset(UTF8);
                    }
                    final String result = buffer.clone().readString(charset);//garbled
                    .
                    .
                    .
                    .
                    .
                } catch (Exception e) {
                    Logger.v(e.getMessage());
                    return response;
                }
                return response.newBuilder()
                        .body(ResponseBody.create(contentType != null ? contentType : MediaType.parse(oldBody.contentType().toString()),
                                realResult.getBytes()))
                        .build();
            }
    }

}

我的数据库结构类似

enter image description here

联系人(我的问题区域)中有大约4000条记录,每条记录有33个单独的子项值。

1 个答案:

答案 0 :(得分:6)

问题中的代码存在许多会影响性能的问题。

1)孩子最初为每个孩子添加事件火灾,然后为之后添加的任何孩子添加事件火灾。如果您有1000个联系人,这意味着在启动时,您将刷新表视图1000次。最好通过.value加载所有联系人并迭代它们,添加到数组,然后在完成后刷新tableView

2)与#1一起使用,如果您只想按姓氏排序,请按.value观察节点,迭代快照以填充数组然后排序,然后重新加载tableView。这将明显加快。

3)childChanged事件:没有理由按姓氏查询当孩子改变时,它会通知你那个孩子,如果需要,你可以再次对代码进行排序

4)与观察事件相比,Firebase查询非常“繁重”。在这种情况下,你真的不是要特别查询,所以应该被淘汰。只需使用观察事件来加载节点的数据,并在查找该数据的子集时使用查询。

*请注意,这很大程度上取决于您拥有多少联系人。对于几千个这些建议工作正常。

因此,有一个非常酷的设计模式,使初始数据集的填充真正干净。我认为其中一个Firebasers将其作为示例应用程序的一部分编写。

我们首先定义一个名为initialLoad的类级变量,并将其设置为true。然后我们使用childAdded观察加载所有联系人并填充我们的tableView dataSource数组。

var initialLoad = true

contactsRef.observeEventType(.ChildAdded, withBlock: { snapshot in
     self.handleChildAdded(withSnap: snapshot)
})

以及处理childAdded事件的函数,最初会一次加载一个孩子,并观察之后添加的孩子。

func handleChildAdded(withSnap: snapshot: FDataSnapshot! ) {
    let myContact = Contact()
    myContact.initWithSnap(snapshot)
    myDataSourceArray.append(myContact)

    //upon first load, don't reload the tableView until all children are loaded
    if ( self.initialLoad == false ) { 
            self.contactsTableView.reloadData()
    }    
}

现在棘手的一点

//this .Value event will fire AFTER the child added events to reload the tableView 
//  the first time and to set subsequent childAdded events to load after each child is
//    added in the future
contactsRef.observeSingleEventOfType(.Value, withBlock: { snapshot in      
     print("inital data loaded so reload tableView!")
     self.itemsTableView.reloadData()
     self.initialLoad = false
})

10k英尺的观点:

这里的关键是.value事件触发AFTER .childAdded事件,因此我们利用它来将所有子添加事件完成后的initialLoad变量设置为false。

以上代码是Swift 2/3,Firebase 2,但它为您提供了如何进行初始加载的概念。