Future and DispatchQueue generating build error

43 views Asked by At

I am trying to build a swift app, and I am opening a JSON file. As it could be time-consuming on the main thread, I want to do it in background.

Here is the call I am doing using Future and DispatchQueue

func getFeed() -> Future<[ios_foodloose_app.FeedItemEntity], Error> {
        return Future<[FeedItemEntity], Error> { promise in
            
            guard let url = Bundle(for: MockedFeedService.self).url(forResource: "basic-test-feed-response",withExtension: "json"),
                  let data = try? Data(contentsOf: url) else {
                return promise(.failure(NSError(domain: "", code: 401, userInfo: [ NSLocalizedDescriptionKey: "Error"])))
            }
            
            do {
                DispatchQueue.global(qos: .background).async {
                    let response = try JSONDecoder().decode( [FeedItemEntity].self, from: data)
                    DispatchQueue.main.async {
                        promise(.success(response))
                    }
                }
            } catch {
                print("JSON Decode error: \(error)")
                promise(.failure(error))
            }
        }
            
    }

but Xcode still complain on the line DispatchQueue.global(qos: .background).async { by saying

Invalid conversion from throwing function of type '@Sendable () throws -> Void' to non-throwing function type '@Sendable @convention(block) () -> Void'

Any idea how to fix it ?

Thanks

2

There are 2 answers

0
maik19631996 On

I guess from just locking at it you have to do the do and catch operation inside of the async block. i would try this:

DispatchQueue.global(qos: .background).async {
    do {
        let response = try JSONDecoder().decode( [FeedItemEntity].self, from: data)
    } catch {
        print("JSON Decode error: \(error)")
        promise(.failure(error))
    }
    DispatchQueue.main.async {
        promise(.success(response))
    }
}
0
Sweeper On

For reading data from a URL and decoding, you can create a dataTaskPublisher with URLSession. The code can be a lot shorter.

func getFeed() -> AnyPublisher<[FeedItemEntity], Error> {
    let error = NSError(domain: "", code: 401, userInfo: [ NSLocalizedDescriptionKey: "Error"])
    guard let url = Bundle(for: MockedFeedService.self).url(forResource: "basic-test-feed-response",withExtension: "json") else {
        return Fail<[FeedItemEntity], Error>(error: error).eraseToAnyPublisher()
    }

    return URLSession.shared.dataTaskPublisher(for: url)
     // add this if you want to still throw the code: 401 error if the file-reading fails
     // .mapError { _ in error }
        .map(\.data)
        .decode([FeedItemEntity].self, decoder: JSONDecoder())
        .receive(on: DispatchQueue.main)
        .eraseToAnyPublisher()

}