How to handle multiple network call in Alamofire - swift

I need to call 2 apis in a view controller to fetch some data from server, I want them to start at the same time, but next step will only be triggered if both of them are returned(doesn't matter it's a success or failure).
I can come up with 2 solutions :
1. Chain them together. Call api1, call api2 in api1's result handler, wait for api2's result
2. Set 2 Bool indicator variables, create a check function, if both of these indicators are true, do next. In both Apis result handler, set corresponding indicator variable, then call check function to decide if it's good to go
First one is not sufficient enough, and I can't say the second one is a elegant solution. Does Alamofire has something like combine signal in Reactivecocoa? Or any better solution?

Your assessment is 100% correct. At the moment, the two options you laid out are really the only possible approaches. I agree with you that your second option is much better than the first given your use case.
If you wish to combine ReactiveCocoa with Alamofire, then that's certainly possible, but hasn't been done yet to my knowledge. You could also investigate whether PromiseKit would be able to offer some assistance, but it hasn't been glued together with Alamofire yet either. Trying to combine either of these libraries with the Alamofire response serializers will not be a trivial task by any means.
Switching gears a bit, I don't really think ReactiveCocoa or PromiseKit are very well suited for your use case since you aren't chaining service calls, you are running them in parallel. Additionally, you still need to run all your parsing logic and determine whether each one succeeded or failed and then update your application accordingly. What I'm getting at is that Option 2 is going to be your best bet by far unless you want to go to all the effort of combining PromiseKit or ReactiveCocoa with Alamofire's response serializers.
Here's what I would suggest to keep things less complicated.
import Foundation
import Alamofire
class ParallelServiceCaller {
var firstServiceCallComplete = false
var secondServiceCallComplete = false
func startServiceCalls() {
let firstRequest = Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["first": "request"])
firstRequest.responseString { request, response, dataString, error in
self.firstServiceCallComplete = true
self.handleServiceCallCompletion()
}
let secondRequest = Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["second": "request"])
secondRequest.responseString { request, response, dataString, error in
self.secondServiceCallComplete = true
self.handleServiceCallCompletion()
}
}
private func handleServiceCallCompletion() {
if self.firstServiceCallComplete && self.secondServiceCallComplete {
// Handle the fact that you're finished
}
}
}
The implementation is really clean and simple to follow. While I understand your desire to get rid of the completion flags and callback function, the other options such as ReactiveCocoa and/or PromiseKit are still going to have additional logic as well and may end up making things more complicated.
Another possible option is to use dispatch groups and semaphores, but that really adds complexity, but could get you much closer to a ReactiveCocoa or PromiseKit styled approach.
I hope that helps shed some light.

DispatchGroup would be a good option to handle multiple dependent requests in parallel
func loadData() {
let dispatchGroup = DispatchGroup()
func startRequests() {
dispatchGroup.enter()
loadDataRequest1()
dispatchGroup.enter()
loadDataRequest2()
dispatchGroup.notify(queue: .main) { [weak self] in
// Process your responses
}
loadDataRequest1() {
// Save your response
dispatchGroup.leave()
}
loadDataRequest2() {
// Save your response
dispatchGroup.leave()
}
}
startRequests()
}

Related

Best practice with asynchronous functions Swift & Combine

I'm converting my Swift app to use Combine as well as async/await and I'm trying to understand what's the best way to handle interactions between asynchronous functions and the main thread.
Here's an asynchronous function that loads a user:
class AccountManager {
static func fetchOrLoadUser() async throws -> AppUser {
if let user = AppUser.current.value {
return user
}
let syncUser = try await loadUser()
let user = try AppUser(syncUser: syncUser)
AppUser.current.value = user // [warning]: "Publishing changes from background threads is not allowed"
return user
}
}
And a class:
class AppUser {
static var current = CurrentValueSubject<AppUser?,Never>(nil)
// ...
}
Note: I chose to use CurrentValueSubject because it allows me to both (1) read this value synchronously whenever I need it and (2) subscribe for changes.
Now, on the line marked above I get the error Publishing changes from background threads is not allowed, which I understand. I see different ways to solve this issue:
1. Mark whole AccountManager class as #MainActor
Since most of the work done in asynchronous functions is to wait for network results, I'm wondering if there is an issue with simply running everything on the main thread. Would that cause performance issues or not?
2. Englobe error line in DispatchQueue.main.sync
Is that a reasonable solution, or would that cause threading problems like deadlocks?
3. Use DispatchGroup with enter(), leave() and wait()
Like in this answer. Is there a difference at all with solution #2? Because this solution needs more lines of code so I'd rather not use it if possible —I prefer clean code.
You can wrap the call in an await MainActor.run { } block. I think this is the most Swifty way of doing that.
You should not use Dispatch mechanism while using Swift Concurrency, event though I think DispatchQueue.main.async { } is safe to use here.
The #MainActor attribute is safe but shouldn’t be used on anObservableObject and could potentially slow down the UI if CPU-bound code is run in a method of the annotated type.

How to use URLSession in MainActor

I have a ViewModel that is isolated with #MainActor and I assume that every part of this ViewModel class should run on the main actor (main thread), right?
So the question is, what about the async call of URLSession.shared.data?
Does it also run on main thread? Isn't that a bad approach?
#MainActor
class ViewModel {
#Published var name: String = ""
func fetchName() async {
guard let (data, _) = try? await URLSession.shared.data(from: URL(string: "http://....")!),
let response = try? JSONDecoder().decode(Response.self, from: data) else {
return
}
self.name = response.name
}
}
Does it also run on main thread
No. That's the whole point of saying await. At that moment, the system can switch contexts to a background thread without you knowing or caring. Moreover, at await your code pauses without blocking — meaning that while you're waiting for the download, the main thread is free to do other work, the user can interact with your app, etc. Don't worry be happy.
The issue is not the URLSession code that you await. That runs on a separate thread managed by URLSession. Whenever you see await, that means that the current execution is suspended while the asynchronous task runs, and that the current actor is free to run other code. All is well.
The only potential concern is code that runs synchronously on the main actor (i.e., everything besides what we await). In this case, the only relevant portion is the code after the await, inside what is called the “continuation”.
In this case, the continuation consists of the decode of the JSON and the updating of the property. That is modest and you are generally fine running that on the main actor.
But if the continuation consisted of anything anything more substantial (e.g. decoding many megabytes of JSON or decoding an image or the like), then you would want to move that off the main actor. But in this case, you are fine.
For more information, see WWDC 2021 video Swift concurrency: Behind the scenes.

Should I put my UserDefaults saving process into ViewModel? (good architecture)

I'm creating a simple NewsApp. I want to create the best app architecture I can made. So my question is that if I want save really simple data like username and maybe 5-6 tags as strings, should I put userDefaults logic into my viewModel or should I create a layer between ViewModel and UserDefaultsAPI which will take care about saving data?
I mean I will create StoreData protocol which UserDefaultsAPI will implement. And if I should do it how I can achieve that? I am using RxSwift and I don't now how to subscribe changing data in UserDefaults by UserDefaultsAPI.
You should create a layer between, but given an Rx/functional architecture, it shouldn't be something heavy weight like a protocol.
Learn How to Control the World and do something like this instead:
struct Storage {
static var shared = Storage()
var saveProperty: (Property) -> Void = { property in
UserDefaults.standard.set(try? JSONEncoder().encode(property), forKey: "property")
}
var deleteProperty: () -> Void = {
UserDefaults.standard.removeObject(forKey: "property")
}
var currentProperty: () -> Observable<Property?> = {
UserDefaults.standard.rx.observe(Data.self, "property")
.map { $0.flatMap { try? JSONDecoder().decode(Property.self, from: $0) } }
.distinctUntilChanged()
}
}
struct Property: Codable, Equatable { }
It depends what your doing creating a seperate layer gives you, the opportunity to have different sources for data that implement the same protocol could be useful, and your data may be complex types than need to be encoded and decoded, so it makes sense to encapsulate that out, you may also want to provide some limit range to some of your values, but UserDefaults is just a service like NotificationCenter, you are not going to automatically wrap NotificationCenter in ever class, just do what is simplest, but doesn't run the risk of painting yourself in a corner later on. You are not going to get every issue decided at the get go right, the skill is to make sure you can quickly change if the need arises, and knowing about what possibility's you will need to take advantage of in the future and how can you avoid needing to make complex changes in the moment that don't add squat. There are lots of things you need to do, and being able to prioritise them is an important part of what you do, don't try make cleaver designs, be cleaver in making designs that can be easy for other to understand and modify.

is there a library similar to Reentrant Lock in Java for Swift?

I am currently working on a function that can be used by multiple threads. The issue is that the function needs to complete first and store the result in the cache. In the meantime, other threads could be calling this function and I would need them to wait until is completed. We were able to accomplish this on Java using Reentrant Lock is there a similar library in swift? I saw that NSRecursiveLock approaches what we are trying to do, however, we want to keep it with swift only. I have also been seeing multiple articles such as this one that talks about using GCD, however, I believe this is for something similar but different: https://medium.com/#prasanna.aithal/multi-threading-in-ios-using-swift-82f3601f171c
Thank you in advance.
Recursion with locking is always a bit of a pain point. A clean solution would be to refactor your function that requires the lock into an external API that acquires the lock and forwards to an internal API that doesn't. Internally don't call the external API.
A simple example might be something like this (this is almost Swift code - parameters and actual work implementations need to be filled in)
extension DispatchSemaphore
{
func withLock<R>(_ block: () throws -> R) rethrows -> R
{
wait()
defer { signal() }
return try block()
}
}
let myLock = DispatchSemaphore(value: 1)
func recursiveLockingFunction(parameters)
{
func nonLockingFunc(parameters) {
if /* some terminating case goes here */ {
// Do the terminating case
return
}
// Do whatever you need to do to handle the partial problem and
// and reduce the parameters
nonLockingFunc(reducedParameters)
}
myLock.withLock { nonLockingFunc(parameters) }
}
Whether this will work for you depends on your design, but should work if the only problem is that the function you want to lock is recursive. And it only uses GCD (DispatchSemaphore) to achieve it.

How to assert an error is thrown async when testing?

We can test thrown errors with XCTAssertThrowsError. Async things can be tested with expectation. I have some method which dispatch work to a background thread and can at some point throw an error.
Is it possible to expect an error be thrown somewhere in the future? I need to combine expectation and XCTAssertThrowsError I think, but I do not know how.
Reproduction project: https://github.com/Jasperav/ThrowingAsyncError. Just clone the project and run the tests, one of them will fail. I made a class which will crash after a few seconds after it has been allocated. I want to make sure it keeps crashing after a few seconds, so I want a test case for it.
You could fulfill an expectation in the catch block of a call that is expected to fail.
func testFailingAsyncCode() async throws {
let expectation = expectation(description: "expect call to throw error")
let dataFetcher = DataFetcher()
do {
// This call is expected to fail
let data = try await dataFetcher.fetchData(withRequest: request, validStatusCodes: [200])
} catch {
// Expectation is fulfilled when call fails
expectation.fulfill()
}
wait(for: [expectation], timeout: 3)
}
I took the sample code from David B.'s answer and changed it, because expectations are not needed when the unit test method is annotated with async.
func testFailingAsyncCode() async { // async is important here
let dataFetcher = DataFetcher()
var didFailWithError: Error?
do {
// This call is expected to fail
_ = try await dataFetcher.fetchData(withRequest: request, validStatusCodes: [200])
} catch {
didFailWithError = error
// Here you could do more assertions with the non-nil error object
}
XCTAssertNotNil(didFailWithError)
}
I took a look at the reproduction project to see what you were trying to accomplish here...
To my understanding:
XCTAssertThrowsError are assertions that takes in a block that can throw. They just happen to assert that an error is thrown in a synchronous block when it's done running.
XCTestExpectation are classes that keep track of whether or not requested conditions are met. They are for keeping track of asynchronous code behavior objects/references need to be kept and checked later.
What you seem to be trying to do is make something like XCTestExpectation work the same way XCTAssertThrowsError does, as in make an synchronous assertion that an asynchronous block will throw. It won't work quite that way because of how the code runs and returns.
The asynchronous code you refer to does not throw (timer initializer). As far as I know, there aren't any asynchronous blocks that can throw. Perhaps the question you should be asking is how can we make a synchronous operation choose to run synchronously sometimes, but also asynchronously when it feels like...
Alternatively for some additional complexity in every class you would like to test I've made a solution with what is almost bare minimum to make this easily testable and portable...
https://github.com/Jasperav/ThrowingAsyncError/pull/1/files
May I ask why you would ever want to do something like this?