I am writing a CLI tool and SPM package that needs to perform lots of networking requests. In the context of writing CLI tools it is fairly common to perform network requests synchronously (I think...I have only really done Ruby CLI dev and networking seems to by synchronous since I never had to deal with callbacks). Anyways I am not sure if this would be worth adding to Moya but here is an extension I wrote for adding synchronous requests to Moya for my CLI tool.
public extension MoyaProvider {
@discardableResult
public func synchronousRequest<T: Decodable>(_ target: Target) throws -> T {
return try synchronousRequest(target).map(T.self)
}
@discardableResult
public func synchronousRequest(_ target: Target) throws -> Moya.Response {
let semaphore = DispatchSemaphore(value: 0)
var response: Moya.Response? = nil
var error: Error? = nil
request(target) { (result: Result) in
defer { semaphore.signal() }
switch result {
case .success(let res):
response = res
case .failure(let err):
error = err
}
}
semaphore.wait()
guard error == nil else {
throw error!
}
return response!
}
}
Is there a way that this could be added to Moya as a plugin?
I wouldn鈥檛 mind this addition. If the community does agree to add it I would kind of prefer it to be named requestSync so that it stays in line with the current request method.
Sent with GitHawk
@kdawgwilk What do you mean by a plugin? You could make a small supporting library for Moya, but let鈥檚 see what some others say first. IMO it makes sense to have if we鈥檙e supporting SPM at this stage and it doesn鈥檛 provide a ton of new overhead. It saves people boilerplate of wrapping a request with the semaphore.
Sent with GitHawk
I saw this line in the examples and wondered if the blocking could happen through a plugin
let gitHubProvider = MoyaProvider<GitHub>(plugins: [NetworkLoggerPlugin(verbose: true, responseDataFormatter: JSONResponseDataFormatter)])
Plugin wouldn't work cause the API still requires callbacks to pass the data back
Yeah, I don't think it's possible in a plugin
I don't know exactly what you're trying to build, but I'd recommend re-visiting your assumption that a CLI has to have blocking requests. Great CLIs, from my experience, don't block. They show loading indicators and update with content when they finish loading.
They also make it possible to run multiple tasks at once, instead of doing them one at a time, which is probably what an architecture that incorporates blocking requests will result in. I'm thinking of tools like npm, buck, fastlane, etc.
I did think about that possibility and I would like to add that in the future. I understand if this isn't something that would be wanted in Moya so for my small rough project I can stick with my extension. I did find that when running the CLI tool that I needed to change one line or the semaphore would block everything from running:
request(target, callbackQueue: .global(qos: .background)) { (result: Result) in
Hey @kdawgwilk! I don't think that adding a synchronous version of a request is something that we should add to Moya. This is not seen as a good practice to use blocking requests. You can take a look at both NSURLSession and Alamofire - none of them support this behavior. You can also look at Alamofire#1147 for more detailed answer. (we support queues as well now!)
@sunshinejr are you talking about our callbackQueue? Or something else
@AndrewSB Yeah I'm talking about the queues that Alamofire uses as well.
I really do think a relatively simple CLI tool could benefit from having a synchronous request. I guess touching on what @AndrewSB mentioned: considering an asynchronous architecture may allow for greater expandability in the future, but it is also a hindrance for wanting to create something simple quickly.
However, my greatest fear is that by adding this to Moya, we'll encourage people to write bad code as they use this to solve the issue of dependencies between requests in iOS apps. See #1426
Considering Moya is primarily used for iOS, I'd change my position to be against this addition solely for the reason described above.
I'm going to close this because it seems like we all agree that adding a blocking request would be encouraging bad practice.
@kdawgwilk Hi ! I used the code you provided, but it doesn't work. The request callback doesn't invoke, and the main thread seems like be locked.
Having the same problem @canny09, also don't know what's going on ...
For future reference. I had the same problem as @canny09 and @Jeehut and solved it by setting the
callbackQueue: .global(qos: .background) when calling the request function.
Most helpful comment
@kdawgwilk Hi ! I used the code you provided, but it doesn't work. The request callback doesn't invoke, and the main thread seems like be locked.