0

I recently changed my http request method to async/await in swift.

func httpRequest(requestType:HTTPRequestType, parameter:Parameter = Parameter()) async throws -> Data {

    // do some preparation depending on requestType and parameter, 
    // e.G. find the right URL, setting http-method to POST / GET / DELETE / PUT and generate headers and body
    
    let (data, response):(Data, URLResponse) = try await {
        do {
            return try await self.httpSession.data(for: request)
        } catch {
            throw HTTPRequestError.noNetwork // check for network errors
        }
    }()

    // Handle Response and return Data-Object or throw, for example if login is not correct
}

It all works fine, as long as I call ONE function - for example

func login(username:String, password:String) async throws {
    let dataCredentials = try await httpRequest(requestType: .login)
    let credentials = try await decodeData(dataCredentials)

    // Request own userdata
    let dataUserdata = try await httpRequest(requestType: .requestOwnUserData)
    let userdata = try await decodeData(dataUserData)

    // Request users notifications
    let dataNotifications = try await httpRequest(requestType: .requestUserNotifications)
    let notifications = try await decodeData(dataNotifications)

    // and some more actions
}

This so far works very well - I do get a problem though as soon as the user presses another button. This - obviously - creates a new task that does not "queue" in the login, but creates a separate thread that is being worked on. It might happen though that there are dependencies - so I want to make sure that all task are being send to the server in that order that my user performed them. For now, I call my requests like this:

 Button(action: {
     Task {
         do { 
             try await login(username: username, password: password)
         } catch let error {
             // handle error
         }
     }
 }, label: {
     Text("Login")
 })

In a perfect world I could also count how many tasks are left there, but that's not essential. Though - in this case I could/would also split up my login() into severals tasks.

  • The problem is the same as before you had `async`/`await`. In traditional `URLSessionTask` patterns, when you start a network request, it is asynchronous. Start more requests, and they will run concurrently. Typically, you simply would not allow them to tap more buttons until they had successfully signed in. You wouldn't just add the requests before you successfully logged in, because what if login failed? – Rob Jan 24 '22 at 00:08
  • But if you want to wait for the login to complete, save the `Task` reference and `await` it, e.g., https://stackoverflow.com/a/70703295/1271826. – Rob Jan 24 '22 at 01:40
  • I get your point about the login failing - and maybe my example was not the best. To describe what my ‚worst case‘ is is a bit more complicated: Imagine an App where a host creates a Session, maybe like a powerpoint, and users can follow. The powerpoint might be big though and is uploaded slide by slide. So the host creates it, starts uploading them one after another. If he then sees a typo in slide 60 and wants to update it, it should also be updated with a PUT http-request. But if so far he just uploaded 30 of the 60 slides, the server will not know the ID of the slide and return a 404 – JacquesNorris Jan 25 '22 at 07:46

0 Answers0