Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using JSONDecoder on a background thread in Swift 5.5+

The following URL session is performed asynchronously. But what about the JSON decoding step? Is that happening on the main thread?

func fetchFavorites() async throws -> [Int] {
    let url = URL(string: "https://hws.dev/user-favorites.json")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode([Int].self, from: data)
}
like image 217
bobby123uk Avatar asked Aug 30 '25 18:08

bobby123uk


1 Answers

If the function is isolated to a particular actor (whether explicitly, or as a member of a type that is actor-isolated), it will run on that actor. So, if isolated to the main actor, then decode will run on the main actor (which causes it to run on the main thread).

If the function is not isolated to any particular, in Swift 5.7 and later, this runs on a generic executor (i.e., not the main thread), thanks to SE-0338). In short, if not isolated to the main actor, it will not run on the main thread.

If this method is a member of a type isolated to the main actor, you can get this function off the actor with the nonisolated qualifier. E.g.:

nonisolated func fetchFavorites() async throws -> [Int] {
    let url = URL(string: "https://hws.dev/user-favorites.json")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode([Int].self, from: data)
}

We can make this non-isolated because it is not interacting with any isolated properties or methods. If it needed to be isolated to the main actor for some reason, we could alternatively move just the decoding into a non-isolated method (or other actor, or detached task, etc.) but keep this function on the main actor.

like image 107
Rob Avatar answered Sep 02 '25 18:09

Rob