ios - 加载新数据时使缓存数据显示为响应式

标签 ios swift core-data

我正在使用 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
            }
        }

    }

我的tableView代码



    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(数组:数据)

而且这个可能很贵:

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

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

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

关于ios - 加载新数据时使缓存数据显示为响应式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58199981/

相关文章:

javascript - 在移动设备上滚动时倒计时暂停,但在桌面上不

ios - 检测 "Done"按钮的点击事件(youtube视频)

ios - 符合协议(protocol)的元素集合 ? : Viewer Swift

iphone - Objective-C:与 CoreData 的多对多关系

ios - 核心数据 我将如何使用核心数据来保持用户登录?

ios - 如何更改 UIBarButtonItem 的上边距

iphone - 调整 ASIWebPageRequest 的 header 搜索路径

ios - 如何在我的应用程序正在使用时禁用所有 iOS 通知横幅?

ios - 在后台 Context 上保存 NSManagedObject 时出现问题

swift - 扩展通用类型,其中 T 具有另一个通用类型