0

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)
                }
            }
            
        }
Pjaks
  • 251
  • 1
  • 11
  • `didRun` is bad, `NSLock` is worse, semaphore is worst. Run the second request in the completion handler of the first or use `async/await` (recommended in Swift 5.5+) – vadian Jul 29 '22 at 12:52
  • I use async/await (you see in my function declaration) but somehow it doesn't wait till the call is completely done. I have no clue. I used to think that async/await WAITS till it is completely done but somehow not. Maybe it's because I get a stream back and not a single response?? – Pjaks Jul 29 '22 at 12:55
  • Using `async/await` means to use the [`async` API of dataTask](https://developer.apple.com/documentation/foundation/urlsession/3767352-data). – vadian Jul 29 '22 at 12:56
  • Yeah but i am already using async/await and it's not working correctly. I added the function call to my question if it helps – Pjaks Jul 29 '22 at 12:59
  • 1
    No you don’t. The API with completion handler is not `async` and does not support `await`. – vadian Jul 29 '22 at 13:03
  • If I understand correctly, you want me to use async/await not only on the function but on the URLSession task? – Pjaks Jul 29 '22 at 13:05
  • 1
    Yes, exactly, you are using `async` but not `async/await`. Marking a function `async` means that it runs in an async context but not necessarily that it waits. – vadian Jul 29 '22 at 13:08

1 Answers1

0

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
    }
    
    
}
Pjaks
  • 251
  • 1
  • 11