swifthttprequestsynchronousnslockdispatchsemaphore

Block HTTP Requests or make them run synchron


I have two HTTP-Requests and i have to execute them in the ordert that I execute the first one, wait till its done and then execute the second one. I used to do this with a loop and a variable inside the call and only if the variable inside the call got changed, it was able to skip the loop and execute the second call. But this is very "ugly" and not very efficient. So I read about a few things you can do but I can't figure out what's the "standard practice" or the best one fitting for my problem. This is the code of my call:

func doHTTPRecentProjectsCall(employeeId: String) async{
    let date : Date = Date()
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyyMMdd"
    let todaysDate = dateFormatter.string(from: date)
    
    
    var url = "http://192.168.179.185:8160/api/v1/project/recent/"
    url += String(employeeId) + "?"
    url += "date="
    url += todaysDate
    guard let reqUrl = URL(string: url) else {
        print("Invalid URL")
        return()
    }
    
    
    var req = URLRequest(url: reqUrl)
    req.httpMethod = "GET"
    req.setValue("CC0001", forHTTPHeaderField: "CC-Tenant")
    req.setValue("BE", forHTTPHeaderField: "CC-Product")
    
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd"
    formatter.timeZone = TimeZone(abbreviation: "ETC")
    
    
    let task = URLSession.shared.dataTask(with: req) { data, response, error in
        if let data = data {
            do{
                let decoder = JSONDecoder()
                decoder.dateDecodingStrategy = .formatted(formatter)
                recentProjects = try decoder.decode(Array<Project>.self, from: data)
                for val in recentProjects{
                    recentProjectsDic[val.name] = val
                    recent.append(val.name)
                }
                didRun = true
                print("test1")
            } catch{
                print(error)
            }
        } else if let error = error {
            print("HTTP Request Failed \(error)")
        }
        if let response = response as? HTTPURLResponse {
                //print("Response HTTP Status code: \(response.statusCode)")
                
            }
    }
    task.resume()
}

As you can see, in there is a variable called "didRun" that gets set to true if the call finished. After that the second call gets executed. ("Similar code") The solutions I found:

  1. Similar construct to mine where you have a variable inside your call and a loop after your "task.resume()" that gets only skipped if the variable gets set to true or something.
  2. I read about a "DispatchSemaphore" but I heard that if you want to change data from the mainQueue inside your call, semaphore.wait() blocks this. Since I have to change something inside my call, I don't think this option fits for me.
  3. I read about something called "NSLock" that works similar than the "DispatchSemaphore" I think.

So does anyone ones a solution that fits my problem? Thanks in advance

UPDATE: This is the code how I call my functions

while !Task.isCancelled && projects.isEmpty {
            await doHTTPRecentProjectsCall(employeeId: self.employeeId)
            await doHTTPProjectsCall(token: apiKey, employeeId: self.employeeId)
            /*if (!didRun) {
                
            }
            if (didRun) {
                
            }*/
            
            
            if (projects.isEmpty) {
                do{
                    try await Task.sleep(nanoseconds: 2_000_000_000)
                } catch {
                    print(error)
                }
            }
            
        }

Solution

  • I simply had to change my calls to this:

    func doHTTPRecentProjectsCall(employeeId: String) async{
        let date : Date = Date()
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyyMMdd"
        let todaysDate = dateFormatter.string(from: date)
        
        
        var url = "http://192.168.179.185:8160/api/v1/project/recent/"
        url += String(employeeId) + "?"
        url += "date="
        url += todaysDate
        guard let reqUrl = URL(string: url) else {
            print("Invalid URL")
            return()
        }
        
        
        var req = URLRequest(url: reqUrl)
        req.httpMethod = "GET"
        req.setValue("CC0001", forHTTPHeaderField: "CC-Tenant")
        req.setValue("BE", forHTTPHeaderField: "CC-Product")
        
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd"
        formatter.timeZone = TimeZone(abbreviation: "ETC")
        
        do {
            let (data, response) = try await URLSession.shared.data(for: req)
            guard (response as? HTTPURLResponse)?.statusCode == 200 else { fatalError("Error while fetching data") }
            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .formatted(formatter)
            recentProjects = try decoder.decode(Array<Project>.self, from: data)
            for val in recentProjects{
                recentProjectsDic[val.name] = val
                recent.append(val.name)
            }
        } catch {
            print(error)
            return
        }
        
        
    }