在后台线程问题中将服务器数据保存到核心数据

时间:2016-12-08 09:23:50

标签: ios json swift multithreading core-data

我知道并发真的很头疼。我读了很多关于如何解析和保存服务器数据到核心数据的文章。但很多教程都非常基础,不适用于线程。但是当我们开发应用程序时,需要多线程。

我的应用程序流程如下

1.使用URLSession从服务器获取数据 2.解析
3.保存核心数据 - 在后台,需要检查重复数据 4.然后获取数据
5.更新UI。

陷入第3步保存到核心数据 - 在后台,需要检查重复

我看到,URLSession正在后台线程中运行。苹果说需要使用performblock吗?https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/Concurrency.html

我尝试了以下方式。但有时它会崩溃。任何人都建议正确的编程流程会更好。有很多问题但是对于这个问题没有正确的解决方案。

func DownloadFrom_server(dict:Any,urlstring_get:String)  {
    if JSONSerialization.isValidJSONObject(dict) {
        do{
            let json:Data = try JSONSerialization.data(withJSONObject: dict, options: JSONSerialization.WritingOptions.prettyPrinted)

            var urlString = urlstring_get
            urlString =   urlString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlPathAllowed)!
            var httpRequest = URLRequest(url: URL(string: urlString)!)
            httpRequest.httpMethod = "POST"
            httpRequest.httpBody = json
            let sessionConfig = URLSessionConfiguration.default
            sessionConfig.httpAdditionalHeaders = ["Accept" : "application/json", "api-key" : "API_KEY"]
            let session = URLSession(configuration: sessionConfig)
            let task = session.dataTask(with: httpRequest) { data, response, error in
                guard let data_get = data, error == nil else{// check for fundamental networking error
                    print("data nil\(error)")
                    //1.network connection lost
                    //save offline data
                    return
                }
                do {
                    let jsonResult = try JSONSerialization.jsonObject(with: data_get, options: JSONSerialization.ReadingOptions.mutableContainers)
                    print("cloud data",jsonResult)
                    self.Save_server_Data_ToCoreData(jsonResult: jsonResult)
                }
                catch {
                    print("json result parsing error ",error)
                }  
            }
            task.resume()
        }   
        catch{ 
            print("InValidJSONObject json:Data error - ",error)
        }
    }
}

将服务器josn保存到核心数据,如下所示

func Save_server_Data_ToCoreData(jsonResult:Any)  {
    let context = getContext()
    let privateMOC = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
    privateMOC.parent = context
    privateMOC.perform {
        if let data = jsonResult  as? [[String: AnyObject]]{
            //Heree prepare MOC
            // if i need to check data alredy exits or not means how can i check
        }
        do {
            try privateMOC.save()
          context.performAndWait {
                do {
                    try context.save()
                } catch {
                    fatalError("Failure to save context: \(error)")
                }
            }
        } catch {
            fatalError("Failure to save context: \(error)")
        }
   }

   DispatchQueue.main.async(execute: {
                        print("update main")
                    })
}

我想再按照以下方式做事

             DispatchQueue.global(qos: .background).async {
//saving core data
 //fetching data
DispatchQueue.main.async {
   //update UI
}
     }

不确定需要使用哪个上下文,私有或主要。如果任何回答这个问题的人对像我这样的Multithread中的那么多初学者都有用。

2 个答案:

答案 0 :(得分:1)

用于核心数据的重复检查。在ios9之上,Coredata中有一个独特的约束功能。

在实体描述的约束中设置属性名称,如id,name等

enter image description here

并在managedobjectcontext中将合并策略设置为NSMergeByPropertyObjectTrumpMergePolicy

managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

请记住,如果您的实体中有关系(我已在xcode7.3中查看),则上述欺骗性检查功能不起作用。

答案 1 :(得分:0)

您可以使用id等唯一字段检查或创建新功能的功能。如果说记录已经存在,则可以跳过或更新字段,但不能创建新记录。 (确保您的实体具有主键的唯一字段)

您可以在循环从服务器API

收到的数据时调用此方法
func findOrCreate(byId objectId: String) -> NSManagedObject {
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Document")

    // Create a new predicate that filters out any object that equal to this id
    print("looking for id: \(objectId)")

    let predicate = NSPredicate(format: "objectId == %@", objectId)

    // Set the predicate on the fetch request
    fetchRequest.predicate = predicate

    do {

        // if found in core data
        let results = try managedObjectContext.fetch(fetchRequest)
        if !results.isEmpty {
            return results.first as! NSManagedObject
        }

    } catch let error as NSError {
        print("Could not create object \(error), \(error.userInfo)")
    }

    // else create new object
    let entity =  NSEntityDescription.entity(forEntityName: "DocumentEntity", in:managedObjectContext)

    let newObject = NSManagedObject(entity: entity!, insertInto: managedObjectContext)

    return newObject
}