我正在使用CoreData在加载新数据然后将其更新到tableView时显示缓存的数据

缓存的数据可以很好地加载,但是问题在于,一旦调用API来加载新数据,tableView就变得无响应,即用户无法在表上滚动或单击任何内容,并且在新数据完全加载后,它将更新tableView,它再次变得敏感

我要实现的是该应用程序立即显示缓存数据,并调用api并在后台加载数据。在加载数据时以及从API加载数据完成时,tableView不应无响应单击刷新按钮或向上滑动以更新数据


 // DID LOAD
    override func viewDidLoad() {
        super.viewDidLoad()
        print("did load")
        getAvatar()
        tableView.separatorStyle = .none
        updateTableContents()
        self.tableView.delegate = self
        self.tableView.dataSource = self
    }


显示缓存数据并调用API的函数


 func updateTableContents()
    {
        do {
            try self.fetchedhResultController.performFetch()
            print("COUNT FETCHED FIRST: \(self.fetchedhResultController.sections?[0].numberOfObjects)")
        } catch let error  {
            print("ERROR: \(error)")
        }

        print("function called")
        let retrievedToken: String? = KeychainWrapper.standard.string(forKey: "acessTokenKey")
        let headers = [
            "Authorization" : "Bearer "+retrievedToken!,
            "Content-Type"  : "application/json"
        ]


        let url = "someURL"
        Alamofire.request(url, method: .get , headers: headers).responseJSON { response in
            switch response.result {
            case .success:
                let json = response.result.value as! [String:Any]
                let data = json["data"] as! [[String : Any]]
                self.clearData()
                self.saveInCoreDataWith(array: data)
                self.nextToken = json["nextPageToken"] as? String ?? "empty"
                print("Token = "+self.nextToken!)
                for dic in data{
                    self.news.append(News(dictionary: dic))
                    print(self.news.count)

                }
                DispatchQueue.main.async {
                    self.loadingIndicator.stopAnimating()
                    self.tableView.reloadData()
                }


            case .failure: break
            }
        }

    }



我的桌子查看代码



    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        if let count = fetchedhResultController.sections?.first?.numberOfObjects {
            return count
        }
        return 0
    }

     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "NewsCell") as! NewsCell

        if let fetchedNews = fetchedhResultController.object(at: indexPath) as? NewsObject {
            cell.test(object : fetchedNews)

            print(self.count)
            self.count+=1;
        }



以及所有核心数据存储和提取功能

  // Creating an Object
    private func createNewsEntityFrom(dictionary: [String: Any]) -> NewsObject {
        let context = CoreDataStack.sharedInstance.managedObjectContext
        let newsEntity = NewsObject(context: context)
        newsEntity.newsAuthor = dictionary["author"] as? String ?? "default"
        newsEntity.newsTitle = dictionary["title"] as? String ?? "default"
        let images = dictionary["image"] as? [String: Any]
        newsEntity.newsImageURL = images?["link"] as? String ?? "default"
        newsEntity.newsID = dictionary["_id"] as? String ?? "default"
        newsEntity.newsPublisher = dictionary["publisher"] as? String ?? "default"
        newsEntity.newsPublishorIconURL = dictionary["shortenedLogo"] as? String ?? "default"
        newsEntity.liked = dictionary["liked"] as? Bool ?? false
        newsEntity.bookmarked = dictionary["bookmarked"] as? Bool ?? false
        return newsEntity
    }

    // Saving Data in Core Data
    private func saveInCoreDataWith(array: [[String: Any]]) {
        for dict in array {
            _ = self.createNewsEntityFrom(dictionary: dict)
        }
        do {
            try CoreDataStack.sharedInstance.persistentContainer.viewContext.save()
        } catch let error {
            print(error)
        }
    }

    // Fetching Data from Core Data
    lazy var fetchedhResultController: NSFetchedResultsController<NSFetchRequestResult> = {
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "NewsObject")
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "newsID", ascending: true)]
        let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: CoreDataStack.sharedInstance.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
         frc.delegate = self
        return frc
    }()

    // Function used to Clear Data from Core Data
    private func clearData() {
        do {
            let context = CoreDataStack.sharedInstance.persistentContainer.viewContext
            let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "NewsObject")
            do {
                let objects  = try context.fetch(fetchRequest) as? [NSManagedObject]
                _ = objects.map{$0.map{context.delete($0)}}
                CoreDataStack.sharedInstance.saveContext()
            } catch let error {
                print("ERROR DELETING : \(error)")
            }
        }
    }



我一直在遵循本教程,以了解如何实现CoreData(如果有帮助的话https://medium.com/@jamesrochabrun/parsing-json-response-and-save-it-in-coredata-step-by-step-fb58fc6ce16f

编辑:

尝试在后台线程中调用Alamofire完成处理程序


 func updateTableContents()
    {
        do {
            try self.fetchedhResultController.performFetch()
            print("COUNT FETCHED FIRST: \(self.fetchedhResultController.sections?[0].numberOfObjects)")
        } catch let error  {
            print("ERROR: \(error)")
        }

        print("function called")
        let retrievedToken: String? = KeychainWrapper.standard.string(forKey: "acessTokenKey")
        let headers = [
            "Authorization" : "Bearer "+retrievedToken!,
            "Content-Type"  : "application/json"
        ]


        let url = "https://api.tapin.news/v1/posts/home"
        Alamofire.request(url, method: .get , headers: headers).responseJSON { response in
                DispatchQueue.global(qos: .background).async {
                    switch response.result {
                        case .success:
                            let json = response.result.value as! [String:Any]
                            let data = json["data"] as! [[String : Any]]
                            self.nextToken = json["nextPageToken"] as? String ?? "empty"
                            print("Token = "+self.nextToken!)
                            self.clearData()
                            self.saveInCoreDataWith(array: data)
                        case .failure: break
                   }
                }
            self.loadingIndicator.stopAnimating()
            self.tableView.reloadData()
        }

    }



编辑2:所以我进行了一些测试,并且我认为这不是后台任务的问题,因为我将获取代码从updateTableContents移到了viewDidLoad并从viewDidLoad中删除了该函数。因此,甚至没有执行后台API调用并保存到coreData

用户界面仍然需要几秒钟才能响应,在此期间我看不到任何图像,然后在图像加载后立即响应


 override func viewDidLoad() {
        super.viewDidLoad()
        print("did load")
        getAvatar()
        tableView.separatorStyle = .none
        self.tableView.delegate = self
        self.tableView.dataSource = self
        do {
            try self.fetchedhResultController.performFetch()
            print("COUNT FETCHED FIRST: \(self.fetchedhResultController.sections?[0].numberOfObjects)")
        } catch let error  {
            print("ERROR: \(error)")
        }

    }




    // Fetching Data from Core Data
    lazy var fetchedhResultController: NSFetchedResultsController<NSFetchRequestResult> = {
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "NewsObject")
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "newsID", ascending: true)]
        let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: CoreDataStack.sharedInstance.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
         frc.delegate = self
        return frc
    }()



编辑3:这是我的CoreDataStack



import UIKit
import CoreData

class CoreDataStack: NSObject {
    static let sharedInstance = CoreDataStack()
    private override init() {}

    lazy var managedObjectContext : NSManagedObjectContext = {
        return self.persistentContainer.viewContext
    }()

    lazy var persistentContainer: NSPersistentContainer = {

        let container = NSPersistentContainer(name: "MyAppName")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {


                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()



    // MARK: - Core Data Saving support

    func saveContext () {
        let context = persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }


    func applicationDocumentsDirectory() {
        if let url = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).last {
            print(url.absoluteString)
        }
    }
}

最佳答案

在Alamofire的完成处理程序中,假定它正在后台线程中运行,然后调用主线程并更新tableView。
现实情况是:Alamofire的完成处理程序已经在主线程中运行。

这导致您的UI阻塞,因为那里有昂贵的方法,例如:
self.clearData()

self.saveInCoreDataWith(array: data)

另外,这可能很昂贵:

for dic in data {
   self.news.append(News(dictionary: dic))
   print(self.news.count)
}


尝试将Alamofire完成处理程序包装在后台线程中:

DispatchQueue.global(qos: .background).async {
   <#code#>
}

10-08 07:27
查看更多