Nitisha Sharma
Nitisha Sharma

Reputation: 259

Completion handler not working as expected with Dispatch Group and Concurrent Queue

i've created a Dispatch group in which three concurrent queues are running and then notifies group for update, which is going good and all this i've put in a function with completion handler.now issue i'm facing is completion handler get called before queue execution completes .how can i resolve this, please advice?

   func loadCompaniesFromSynch(_ data: Data, completionHandler: @escaping(String) -> ())
{

    var companyFile = ""
    companies = [Company]()
    let batchGroup = DispatchGroup()
    let queue = DispatchQueue(label: "Batch Queue", qos: .background, attributes: .concurrent)

    if !FileManager.default.fileExists(atPath: self.fileMgr.getDocumentPath()) {
        self.fileMgr.createFileDirectory(self.constants!.APP_FOLDER)
    }

    companyFile =    self.fileMgr.getDocumentFilePath(self.fileMgr.getCacheData(constants!.COMPANIES_LAST_SYNCH_DATE) as! String)
    let dataOld: Data = try! Data(contentsOf: URL(fileURLWithPath: companyFile),options: NSData.ReadingOptions.uncached)

    let oldCompanies: NSArray! = (try? JSONSerialization.jsonObject(with: dataOld, options: JSONSerialization.ReadingOptions.mutableContainers)) as? [[String:Any]] as NSArray!

    let newCompanyObj: NSDictionary? = (try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers)) as? NSDictionary

    var company: Company?

    if newCompanyObj?.count > 0 {

        if let companies = oldCompanies
        {
            for com in  companies as! [[String: AnyObject]]
            {

                company = Company()
                company!.orgCode = com["ORG_CODE"] as? String
                company!.orgDescription = com["ORG_DESCRIPTION"] as? String

                if let btlOrg = com["OBX_BTL_CODE"] as? String
                {
                    company!.orgBtlCode = btlOrg
                    company!.orgBtlDescription = com["BTL_DESCRIPTION"] as? String
                }
                company!.orgStatus = com["ORG_STATUS"] as! String?

                self.companies!.append(company!)
                company = nil
            }



        }

    print("loadCompaniesFromSynch >>  oldCompanies >>\(oldCompanies.count) Comapnies Count  \(self.companies!.count)")

        var dataDict = Dictionary<String,String>()

        if let json =  newCompanyObj  as NSDictionary!
        {
            if let companies = json["RESULTS"] as? NSDictionary
            {
                if let companiesNew = companies["COMPANIES"] as? [[String: AnyObject]]
                {
                   // for com in companiesNew 

                    let addArray = companiesNew.filter { $0["ORG_STATUS"] as! String == ComapnyStatus.ADD.rawValue}
                    let deleteArray = companiesNew.filter { $0["ORG_STATUS"] as! String == ComapnyStatus.DELETED.rawValue}
                    let updateArray = companiesNew.filter { $0["ORG_STATUS"] as! String == ComapnyStatus.UPDATE.rawValue}

                    print(addArray.count)
                    print(deleteArray.count)
                    print(updateArray.count)



                    var addCompanies: [Company]?
                    var updateCompanies:[Company]?
                     var comapnySet = Set(self.companies!)


                    batchGroup.enter()
                    queue.async(group: batchGroup)
                    {
                        if (addArray.count > 0 )
                        {
                            addCompanies = [Company]()
                            for (index,item) in addArray.enumerated()
                            {

                                let company =       self.returnComapnyOjectfromDictionary(item as NSDictionary)
                                addCompanies!.append(company)
                                print("add loop----\(index)")
                            }

                        }
                        batchGroup.leave()

                    }

                    batchGroup.enter()
                    queue.async(group: batchGroup) {

                        if updateArray.count > 0
                        {
                            updateCompanies = [Company]()


                        for (index,item) in updateArray.enumerated()
                        {
                            let company = self.returnComapnyOjectfromDictionary(item as NSDictionary)
                             updateCompanies!.append(company)
                                print("update loop----\(index)")

                        }
                        }
                        batchGroup.leave()
                    }

                   batchGroup.enter()
                    queue.async(group: batchGroup) {

                        for (_,item) in deleteArray.enumerated()
                        {

                             let company = self.returnComapnyOjectfromDictionary(item as NSDictionary)
                            _ = self.removeObject(&self.companies!,object: company)
                            print("looop2")

                        }

                        batchGroup.leave()
                    }


                    batchGroup.notify(queue: .global(qos: .background))
                    {

                        if updateCompanies?.count == updateArray.count{

                            //self.companies = Array(comapnySet)
                            print("count before \(self.companies?.count)")

                            comapnySet.subtract(Set(updateCompanies!))
                            self.companies = Array(comapnySet)

                          //  self.companies =  Array(comapnySet.intersection(Set(updateCompanies!)))
                            print("after delete \(self.companies?.count)")

                            self.companies!.append(contentsOf: updateCompanies!)


                            print("update array count \(updateArray.count) ----- and update Companies count --\(self.companies?.count)")
                            updateCompanies = nil

                        }
                        if addCompanies?.count == addArray.count
                        {
                        self.companies!.append(contentsOf: addCompanies!)
                            print("add array count \(addArray.count) ----- and add Companies count --\(addCompanies?.count)")

                            addCompanies = nil
                        }

                    }

                    batchGroup.wait()
                 }
            }

//**Below code is executed before queue completion**
            if let status = json["STATUS"] as? String
            {
                dataDict[self.constants!.defaultsKeys.RESPONSE_STATUS]      = status
            }
            if let message = json["MESSAGE"] as? String
            {
                dataDict[self.constants!.defaultsKeys.RESPONSE_MESSAGE] = message
            }
        }

        var newCompanyArray:Array<AnyObject> = []
        var dict = Dictionary<String,String>()


        for cmp in self.companies!
        {
            dict["ORG_CODE"] = cmp.orgCode
            dict["ORG_DESCRIPTION"] = cmp.orgDescription
            dict["OBX_BTL_CODE"] = cmp.orgBtlCode
            dict["BTL_DESCRIPTION"] = cmp.orgBtlDescription
            dict["ORG_STATUS"] = cmp.orgStatus

            newCompanyArray.append(dict as AnyObject)
        }

        let isValidJson = JSONSerialization.isValidJSONObject(newCompanyArray)


        if newCompanyArray.count > 0 && isValidJson
        {
            let companyCount = newCompanyArray.count - oldCompanies.count

            let replaceComCount = self.utility!.replace(self.constants!.logs.LOG_COMPANY_SYNC_END,originalString: "<COUNT>",withString: "\(companyCount)")


            self.parser!.setLogValueToXml(replaceComCount, logType:
                self.constants!.logs.LOG_TYPE_ACTIVITY, fileLogType: "")


            let dataFinal:Data =  try! JSONSerialization.data(withJSONObject: newCompanyArray, options: [])
            self.fileMgr.removeFile(self.fileMgr.getDocumentFilePath(self.fileMgr.getCacheData(self.constants!.COMPANIES_LAST_SYNCH_DATE) as! String))
            let compniesFileName = "Companies_\(self.dateUtil.getCurrentDateTime())" //logic is to be use in synch
            self.fileMgr.setCacheData(compniesFileName as AnyObject, key: self.constants!.COMPANIES_LAST_SYNCH_DATE)

            self.fileMgr.writeFile(NSString(data: dataFinal, encoding: String.Encoding.utf8.rawValue)!,fileName :self.fileMgr.getCacheData(self.constants!.COMPANIES_LAST_SYNCH_DATE) as! String,documentDir:self.fileMgr.getDocumentPath())

        }

    }

    completionHandler(companyFile)
 }
 DispatchQueue.global(qos: .background).async
                                {
                                    self.loadCompaniesFromSynch(jNsData, completionHandler:
                                    {
                                        companyFile in

                                            if !companyFile.isEmpty
                                            {
                                            self.doPropertySync()
                                            }
                                            else
                                            {

                                            }
                                    })
                                }

Upvotes: 2

Views: 870

Answers (1)

shallowThought
shallowThought

Reputation: 19602

You are mixing up a lot of things. You are trying to get notified, but also you are trying to wait.

Your completion()handler and the code marked with

//Below code is executed before queue completion"

is called/running outside your notification block...

AFAICS, there is no reason to use notify or wait whithin loadCompaniesFromSynch(), as you do do not call any async tasks in it.

How I understand, what you want to do is to do your heavy io stuff in the background. In this case, delete all the DispatchGroup stuff and dispatch the hole function. No need to wait/notify, as you are using a completion handler. Check the following to get the idea:

    func loadCompaniesFromSynch(_ data: Data, completionHandler: @escaping(String) -> ()) {
    // dispatch the whole thing to the global background queue
    DispatchQueue.global(qos: .background).async {

        // your original code with all DispatchGroup stuff deleted

        // call completion handler on main queue, so the caller does not have to care
        DispatchQueue.main.async {
            completionHandler(companyFile)
        }        
    } 
}

self.loadCompaniesFromSynch(jNsData) {
    companyFile in

    // do stuff after loadCompaniesFromSynch finished
}

Hope this helps.

Upvotes: 2

Related Questions