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