Nested DispatchGroup - ios

I try use nested DispatchGroup, there is my code :
class TranslateService {
private let myGroup = DispatchGroup()
func translateText(text:[String],closure:#escaping ((_ success:String?,_ error:Error?) -> Void)) {
var translateString: String = ""
var responseError: Error?
for index in 0...text.count - 1 {
let urlString = "https://translate.yandex.net/api/v1.5/tr.json/translate?key=trnsl.1.1.20171105T134956Z.795c7a0141d3061b.dc25bae76fa5740b2cdecb02396644dea58edd24&text=\(text[index])&lang=fa&format=plain&options=1"
if let allowString = Utilities.shareInstance.getQueryAllowedString(url: urlString) {
if let url = URL(string:allowString){
myGroup.enter()
Alamofire.request(url).responseJSON { response in
guard let responseData = response.data else {
self.myGroup.leave()
return
}
do {
let json = try JSONSerialization.jsonObject(with: responseData, options: [])
if let res = json as? [String:Any] {
if let code = res["code"] as? Int {
if code == 200 {
if let textArr = res["text"] as? [AnyObject] {
let flattArr = Utilities.shareInstance.flatStringMapArray(textArr)
if flattArr.count > 0 {
translateString += "،" + flattArr[0]
}
}
}
}
self.myGroup.leave()
}
}catch {
responseError = error
self.myGroup.leave()
}
}
self.myGroup.notify(queue: .main) {
print("Finished all requests.")
print(translateString)
closure(translateString, responseError)
}
}
}
}
}
}
class AddressService {
private let translateService: TranslateService = TranslateService()
private let myGroup = DispatchGroup()
func fetchAddressFromGeographicalLocation(latitude: Double, longitude: Double,closure:#escaping ((_ success:String,_ name:String,_ error:Error?) -> Void)) {
var address: String = ""
let name: String = ""
var responseError: Error?
if let url = URL(string:"https://maps.googleapis.com/maps/api/geocode/json?latlng=\(latitude),\(longitude)&key=AIzaSyAdEzHZfZWyjLMuuW92w5fkR86S3-opIF0&language=fa&region=IR&locale=fa"){
self.myGroup.enter()
Alamofire.request(url).responseJSON { response in
guard let responseData = response.data else {
self.myGroup.leave()
return
}
do {
let json = try JSONSerialization.jsonObject(with: responseData, options: [])
if let addressDic = json as? [String:Any] {
if let result = addressDic["results"] as? [AnyObject] {
if result.count > 0 {
let flattRes = Utilities.shareInstance.flatMapArray(result)
let item = flattRes[0]
address = item["formatted_address"] as? String ?? ""
var res = address
if res.isContainEnglishCharachter {
self.myGroup.enter()
let resArr = res.components(separatedBy: ",")
var all : [String] = []
for item in resArr {
if item != " " {
all.append(item)
}}
self.translateService.translateText(text: all, closure: {stringAdd,error in
self.myGroup.enter()
if error != nil {
self.myGroup.leave()
}else {
address = stringAdd ?? ""
self.myGroup.leave()
} })
}else {
self.myGroup.leave()
}
}
}
}
}catch {
responseError = error
self.myGroup.leave()
}
self.myGroup.notify(queue: .main) {
// print("Finished all requests.")
closure(address, name, responseError)
}
}
}
}
}
All I want is that the myGroup I put in the class AddressService waiting for the myGroup that I put in the class TranslateService.
but now self.myGroup.notify not call in the AddressService class, So closure not work.
How can solve this problem, Thank you for all the answers.

I think you are over complicating it.
If I understood a bit, what you want to do is the following:
Get an address from the Address service.
Translate some words of that address, one by one, using the translation service.
When using the Address service there is only one call being done, so there is no need to use Dispatch Groups at this point. Dispatch Groups are used to synchronize more than one async call.
For your Translation service you can make good use of the Dispatch groups, since you are doing calls to the service inside a for loop. The problem here is, that the implementation is slightly wrong. You are setting the notification block inside the for loop, and it should be outside, so that it gets only triggered once, when all the calls inside the loop are done.
So move this block outside the for loop in the Translation service:
self.myGroup.notify(queue: .main) {
print("Finished all requests.")
print(translateString)
closure(translateString, responseError)
}
Now "Finished all requests." will only be printed once, when all requests are done.
In the address service you do not need dispatch groups at all. Just wait until the completion block is called.
self.translateService.translateText(text: all, closure: {stringAdd,error in
Everything is done here already.
}

Related

Swift: For loop to synchronous one by one until all function response

I'm new to swift and practicing my best.
I have main function appointmentCall() when it executes and in response I may get multiple appointments. Then I pass appointmentId to appointmentDetail function for more details.
All I want to is how can I set For loop to synchronous process. Means it will not execute next appointment until first is finished. At the moment it executes all appointments.
I need appointment one by one executes all function once finished executes next appointment.
AppointmentCall
-> AppoinmentDetail -> processDetail -> Completed.
Code:
func appointmentCall(_ selectedDate:Date) {
DataProvider.main.serviceGetAppointment(date: selectedDate, callback: {success, result in
do{
if(success){
print(result as! Data)
let decoder = JSONDecoder()
let response = try decoder.decode(ResponseData.self, from: result! as! Data)
if let appointments = response.appointments {
self.appData = appointments.map { AppointmentDownloadModel(appointmentModel: $0)}
}
for eachApp in self.appData {
self.appointmentDetail(AppId: appId)
}
self.tableView.reloadData()
return true
}else{
return false
}
}catch let error {
DataProvider.main.token = nil
print(error as Any)
return false
}
})
}
func appointmentDetail(AppId: Int){
DataProvider.main.serviceGetAppointmentDetail(Id: AppId , callback: {success, result in
do{
if(success){
let decoder = JSONDecoder()
let resp = try decoder.decode(AppointmentDetail.self, from: result! as! Data)
self.AppDetailData = resp
self.processDetail(appId: AppId)
return true
}else{
return false
}
}catch let error {
print(error as Any)
return false
}
})
}
func processDetail(appId: Int) {
guard let detail = AppDetailData, AppDetailData?.appointmentId == appId else {
return
}
for firmParam in (detail.sectionList ?? []) {
for firmItem in firmParam.items! {
if firmItem.actionParamData != nil {
let str = firmItem.actionParamData
let param = str?.components(separatedBy: ":")
let final = param![1].replacingOccurrences(of: "}", with: "")
let fmId = final.components(separatedBy: ",")
let frmId = fmId[0]
self.firmDetails(actionParamData: Int(frmId) ?? 0)
}
//pdf download
if firmItem.actionType == 2 {
if firmItem.actionUrl != nil {
self.contentLength(link: firmItem.actionUrl!)
let fileURL = URL(fileURLWithPath: firmItem.actionUrl ?? "")
let fileTitle = firmItem.textField ?? ""
self.downloadPDFTask(pdfURL: firmItem.actionUrl ?? "")
}
}
}
}
}
If you use a recursive function with completion handler it would ensure you could essentially loop over an array and call a function to do something and know that it has completed before you do the same to your next value. See below to high level generic example
func doSomething()
{
// Call this function with the first index in the array
firstFunction(index: 0)
// Now were here and we know that every value in the member variable array has been processed by "secondFunction"
}
func firstFunction(index: Int)
{
let value = memberVariableArray[index]
secondFunction(String: value) { (returnValueFromSecondFunction) in
// do something with the return value from the second function
// Recall this function with the next index (if we have another one)
let newIndex = index + 1
if newIndex < memberVariableArray.count
{
self.firstFunction(index: newIndex)
}
}
}
func secondFunction(valueToDoSomethingWith: String, completion: #escaping (String) -> Void)
{
// Do something here
// Complete function
completion("The Value you want back")
}

How to make the for loop stop for the asynchronous request Swift 5

Here's the func that I want the for loop to wait for the completion from func getVenueDetails and if completion is true break out of the loop, and if not continue with the next venue id.
func searchVenues(lat: Double, lng: Double) {
let parameter: [String: String] = [
"ll": "\(lat),\(lng)",
"radius": "600",
"limit": "10",
"intent": "browse",
"categoryId": "4bf58dd8d48988d1e4931735,4bf58dd8d48988d1f1931735,4deefb944765f83613cdba6e,4bf58dd8d48988d17f941735,52e81612bcbc57f1066b79eb,4bf58dd8d48988d181941735,4bf58dd8d48988d1f4931735,4bf58dd8d48988d189941735,4bf58dd8d48988d182941735,4bf58dd8d48988d17b941735,4bf58dd8d48988d163941735,4bf58dd8d48988d164941735,4bf58dd8d48988d165941735,56aa371be4b08b9a8d57356a,4bf58dd8d48988d12f941735"
];
var isSent2: Bool = false
client.request(path: "venues/search", parameter: parameter) { result in
switch result {
case let .success(data):
let jsonResponse = try! JSONSerialization.jsonObject(with: data, options: [])
let json = JSON(jsonResponse)
let name = json["response"]["venues"][0]["name"].string
print("NAME FROM JSON: ", name)
let id = json["response"]["venues"][0]["id"].string
print("id of foursquare", id)
let group = DispatchGroup()
group.notify(queue: .main) {
for (key,subJson):(String, JSON) in json["response"]["venues"] {
//group.enter()
let placeName = subJson["name"].string
print("place name:",placeName.unsafelyUnwrapped)
let placeId = subJson["id"].string
print("place id:",placeId.unsafelyUnwrapped)
group.enter()
self.getVenueDetails(id: placeId!) { (isSent) in
print("isSent", isSent)
isSent2 = isSent
group.leave()
}
if (isSent2){
print("linaaa")
//group.leave()
break
}
}//end of for loop
}
// print("json == ", jsonResponse)
case let .failure(error):
// Error handling
switch error {
case let .connectionError(connectionError):
print(connectionError)
case let .responseParseError(responseParseError):
print(responseParseError) // e.g. JSON text did not start with array or object and option to allow fragments not set.
case let .apiError(apiError):
print(apiError.errorType) // e.g. endpoint_error
print(apiError.errorDetail) // e.g. The requested path does not exist.
}
}
}
}
here's the other function, where the request for venue details is made, so I want when the isSent returned true the for loop in searchVenues to stop (break) and if returned false to continue with next one.
func getVenueDetails(id: String, completionHandler: #escaping (Bool)->Void) {
var isSent: Bool = false
let parameter: [String: String] = [
"VENUE_ID": "\(id)",
];
client.request(path: "venues/\(id)", parameter: parameter) { result in
switch result {
case let .success(data):
let jsonResponse = try! JSONSerialization.jsonObject(with: data, options: [])
let json = JSON(jsonResponse)
// print("json == ", jsonResponse)
let name = json["response"]["venue"]["name"].string
if let rating:Double = json["response"]["venue"]["rating"].double {
print("rating from: ", rating)
//rat = rating
if (rating > 2) {
self.foursquareNotification(name: name!)
print("here inside lol")
isSent = true
DispatchQueue.main.async {
completionHandler(isSent)
print("isSent hereee", isSent)
}
} //end if
else {
isSent = false
DispatchQueue.main.async {
completionHandler(isSent)
}
}//end else
} //end if rating
// rat = json["response"]["venue"]["rating"].double!
// print("rating from: ", rat)
// //rat = rating.unsafelyUnwrapped
case let .failure(error):
// Error handling
switch error {
case let .connectionError(connectionError):
print(connectionError)
case let .responseParseError(responseParseError):
print(responseParseError) // e.g. JSON text did not start with array or object and option to allow fragments not set.
case let .apiError(apiError):
print(apiError.errorType) // e.g. endpoint_error
print(apiError.errorDetail) // e.g. The requested path does not exist.
}
}
}
//return isSent
}//end getVenueDetails
You need to remove the for loop
var res = json["response"]["venues"]
Then
var current = 0
func start(item:ItemType) {
let placeName = subJson["name"].string
let placeId = subJson["id"].string
self.getVenueDetails(id: placeId!) { (isSent) in
if isSent {
counter += 1
start(nextItem)
}
}

How to return a value within an if let statement in Swift 4? [duplicate]

This question already has an answer here:
Return a string from a web scraping function in swift
(1 answer)
Closed 4 years ago.
How can I return a value within an if let statement to be further returned within a function? Here is the code:
func loadUrl(url:String) -> String {
DispatchQueue.global().async {
do {
let appUrl = URL(string:url)!
let data = try Data(contentsOf:appUrl)
let json = try JSONSerialization.jsonObject(with: data) as! [String:Any]
print("Test from do")
if let results = json["results"] as? [[String:Any]] {
print("Test from if let 1")
if let first = results[0] as? [String:Any] {
print("Test from if let 2")
var cityStateLocation = first["formatted_address"]!
return cityStateLocation
//What needs to be returned
}
}
DispatchQueue.main.async {
print("No Error")
}
} catch {
DispatchQueue.main.async {
print("Cannot connect to the server.")
}
}
}
}
What I would like to be able to do is take cityStateLocation and return it in the func, but because it is a part of an if let statement within an .async method I don't know how to do that. Could someone please explain?
EDIT: I need the return value of cityStateLocation to equal a variable in a separate function. Here is the separate function:
#IBAction func continueButton(_ sender: Any) {
var cityState:String
if locationSwitch.isOn == true {
print(location.latitude)
print(location.longitude)
let url = "https://maps.googleapis.com/maps/api/geocode/json?latlng=\(location.latitude),\(location.longitude)&result_type=locality&key=AIzaSyDI-ZacHyPbLchRhkoaUTDokwj--z_a_jk"
loadUrl(url: url)
cityState = loadUrl(url: url)
} else {
cityState = ""
}
CoreDataHandler.saveObject(locationLocality: cityState)
}
Edit 2: The main reason why the "duplicate answer" is not a duplicate is that my code needs to call the return of this function within a separate function then save it to Core Data. Also, my code is not using an array.
You could modify your function to include a closure. For instance:
func loadUrl(url: String, completionHandler: #escaping (_ location: String?) -> (Void)) {
And then, where you want to return it, you'd pass it in as such.
completionHandler(cityStateLocation)
I made it an optional so that, in your fail paths, you could return nil.
Then, where you call the function would change. Using trailing closure syntax, it could look like this:
loadUrl(url: "someurl.com/filepath.txt") { optionalLocation in
guard let nonOptionalLocation = optionalLocation else {
// Location was nil; Handle error case here
return
}
// Do something with your location here, like setting UI or something
}
This is a fairly common pattern when dealing with asynchronous activity, such as working with network calls.
The simplest (perhaps no the prettiest), way of doing this would simply be to declare and instantiate a variable above the dispatch queue. Then you can set the variable equal to whatever you want, within the dispatch queue, and return it afterwards. You can change the type of ret, so that it suits your needs more directly.
func loadUrl(url:String) -> String {
var ret = NSObject()
DispatchQueue.global().async {
do {
let appUrl = URL(string:url)!
let data = try Data(contentsOf:appUrl)
let json = try JSONSerialization.jsonObject(with: data) as! [String:Any]
print("Test from do")
if let results = json["results"] as? [[String:Any]] {
print("Test from if let 1")
if let first = results[0] as? [String:Any] {
print("Test from if let 2")
var cityStateLocation = first["formatted_address"]!
ret = cityStateLocation
//What needs to be returned
}
}
DispatchQueue.main.async {
print("No Error")
}
} catch {
DispatchQueue.main.async {
print("Cannot connect to the server.")
}
}
}
return ret
}
DispatchQueue.global().async will cause the coded included in the closure to be executed at some point the future, meaning you loadUrl function will return (almost) immediately.
What you need is some kind of callback which can be called when you have a result (AKA closure)
This is just another way to approach the problem, the difference between this and Josh's example is simply, I provide an additional closure to handle the errors
func loadUrl(url:String, complition: #escaping (String?) -> Void, fail: #escaping (Error) -> Void) {
DispatchQueue.global().async {
do {
let appUrl = URL(string:url)!
let data = try Data(contentsOf:appUrl)
let json = try JSONSerialization.jsonObject(with: data) as! [String:Any]
print("Test from do")
if let results = json["results"] as? [[String:Any]], !results.isEmpty {
print("Test from if let 1")
let first = results[0]
print("Test from if let 2")
if let cityStateLocation = first["formatted_address"] as? String {
complition(cityStateLocation)
} else {
complition(nil)
}
} else {
complition(nil)
}
} catch let error {
fail(error)
}
}
}
Which you might call using something like...
loadUrl(url: "your awesome url", complition: { (value) in
guard let value = value else {
// No value
return
}
// process value
}) { (error) in
// Handle error
}

iOS swift how can I await an async task inside a function that needs a return value

I am using swift 3.0 and have created a function that returns an Array of Integers. The arrays of Integers are very specific and they are gotten from a database therefore the HTTP call is asynchronous . This is a function because I use it in 3 different controllers so it makes sense to write it once . My problem is that the Async code is returned after the return statement at the bottom therefore it is returning nil . I have tried the example here Waiting until the task finishes however it is not working mainly because I need to return the value . This is my code
func ColorSwitch(label: [UILabel]) -> [Int] {
for (index, _) in label.enumerated() {
label[index].isHidden = true
}
// I need the value of this variable in the return
// statement after the async is done
var placeArea_id = [Int]()
let urll:URL = URL(string:ConnectionString+"url")!
let sessionn = URLSession.shared
var requestt = URLRequest(url: urll)
requestt.httpMethod = "POST"
let group = DispatchGroup()
group.enter()
let parameterr = "http parameters"
requestt.httpBody = parameterr.data(using: String.Encoding.utf8)
let task = sessionn.dataTask(with:requestt, completionHandler: {(data, response, error) in
if error != nil {
print("check check error")
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any]
DispatchQueue.main.async {
if let Profiles = parsedData?["Results"] as? [AnyObject] {
if placeArea_id.count >= 0 {
placeArea_id = [Int]()
}
for Profiles in Profiles {
if let pictureS = Profiles["id"] as? Int {
placeArea_id.append(pictureS)
}
}
}
group.leave()
}
} catch let error as NSError {
print(error)
}
}
})
task.resume()
group.notify(queue: .main) {
// This is getting the value however can't return it here since it
// expects type Void
print(placeArea_id)
}
// this is nil
return placeArea_id
}
I already checked and the values are returning inside the async code now just need to return it any suggestions would be great .
You will want to use closures for this, or change your function to be synchronous.
func ColorSwitch(label: [UILabel], completion:#escaping ([Int])->Void) {
completion([1,2,3,4]) // when you want to return
}
ColorSwitch(label: [UILabel()]) { (output) in
// output is the array of ints
print("output: \(output)")
}
Here's a pretty good blog about closures http://goshdarnclosuresyntax.com/
You can't really have your function return a value from an asynchronous operation within that function. That would defeat the purpose of asynchronicity. In order to pass that data back outside of your ColorSwitch(label:) function, you'll need to also have it accept a closure that will be called on completion, which accepts an [Int] as a parameter. Your method declaration will need to look something like this:
func ColorSwitch(label: [UILabel], completion: #escaping ([Int]) -> Void) -> Void {
for (index, _) in label.enumerated() {
label[index].isHidden = true
}
var placeArea_id = [Int]()
let urll:URL = URL(string:ConnectionString+"url")!
let sessionn = URLSession.shared
var requestt = URLRequest(url: urll)
requestt.httpMethod = "POST"
let group = DispatchGroup()
group.enter()
let parameterr = "http parameters"
requestt.httpBody = parameterr.data(using: String.Encoding.utf8)
let task = sessionn.dataTask(with:requestt, completionHandler: {(data, response, error) in
if error != nil {
print("check check error")
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any]
DispatchQueue.main.async {
if let Profiles = parsedData?["Results"] as? [AnyObject] {
if placeArea_id.count >= 0 {
placeArea_id = [Int]()
}
for Profiles in Profiles {
if let pictureS = Profiles["id"] as? Int {
placeArea_id.append(pictureS)
}
}
}
group.leave()
completion(placeArea_id) // This is effectively your "return"
}
} catch let error as NSError {
print(error)
}
}
})
task.resume()
}
Later on, you can call it like this:
ColorSwitch(label: []) { (ids: [Int]) in
print(ids)
}

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
{
}
})
}
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.

Resources