Reputation: 25
I have 2 methods I call. I need to produce a model that contains the result of both and call another method.
I wanted to avoid placing 1 method inside another as this could expand out to 3 or 4 additional calls.
Essentially once I have the results for setUserFollowedState
and loadFollowersForTopic
I want to send both values to another function.
Coming from a JS land I would use async/await
but this does not exist in Swift.
func setUserFollowedState() {
following.load(for: userID, then: { [weak self, topic] result in
guard self != nil else { return }
let isFollowed = (try? result.get().contains(topic)) ?? false
// do something with isFollowed?
})
}
func loadFollowersForTopic() {
followers.load(topic, then: { [weak self, topic] result in
guard self != nil else { return }
let count = (try? result.get().first(where: { $0.tag == topic })?.followers) ?? 0
// do something with count?
})
}
Upvotes: 1
Views: 1296
Reputation: 402
Another approach (which I believe is a bit cleaner) would be to use a DispatchGroup
to combine the result of the mentioned methods.
You would modify your original methods to take a completion handler and then combine the two results where you actually need the data. See example below.
func setUserFollowedState(completion: @escaping ((Bool) -> Void)) {
following.load(for: userID, then: { [weak self, topic] result in
guard self != nil else { return }
let isFollowed = (try? result.get().contains(topic)) ?? false
// Call completion with isFollowed flag
completion(isFollowed)
})
}
func loadFollowersForTopic(completion: @escaping ((Int) -> Void)) {
followers.load(topic, then: { [weak self, topic] result in
guard self != nil else { return }
let count = (try? result.get().first(where: { $0.tag == topic })?.followers) ?? 0
// Call completion with follower count
completion(count)
})
}
func loadFollowedAndCount() {
let group = DispatchGroup()
var isFollowed: Bool?
// Enter group before triggering data fetch
group.enter()
setUserFollowedState { followed in
// Store the fetched followed flag
isFollowed = followed
// Leave group only after storing the data
group.leave()
}
var followCount: Int?
// Enter group before triggering data fetch
group.enter()
loadFollowersForTopic { count in
// Store the fetched follow count
followCount = count
// Leave group only after storing the data
group.leave()
}
// Wait for both methods to finish - enter/leave state comes back to 0
group.notify(queue: .main) {
// This is just a matter of preference - using optionals so we can avoid using default values
if let isFollowed = isFollowed, let followCount = followCount {
// Combined results of both methods
print("Is followed: \(isFollowed) by: \(followCount).")
}
}
}
Edit: always make sure that a group.enter()
is followed by a group.leave()
.
Upvotes: 1
Reputation: 1375
You can store both async call results as optional properties. When your callbacks happen, set these properties then check that both properties have been set. If they've been set, you know both async calls have returned.
private var isFollowed: Bool?
private var count: Int?
func setUserFollowedState() {
following.load(for: userID, then: { [weak self, topic] result in
guard let self = self else { return }
let isFollowed = (try? result.get().contains(topic)) ?? false
self.isFollowed = isFollowed
performPostAsyncCallFunctionality()
})
}
func loadFollowersForTopic() {
followers.load(topic, then: { [weak self, topic] result in
guard let self = self else { return }
let count = (try? result.get().first(where: { $0.tag == topic })?.followers) ?? 0
self.count = count
performPostAsyncCallFunctionality()
})
}
private func performPostAsyncCallFunctionality() {
// Check that both values have been set.
guard let isFollowed = isFollowed, let count = count else { return }
// Both calls have returned, do what you need to do.
}
The good thing about this approach is that you can easily add more async calls using the pattern. However, if you need to make that many async network calls at once, I would recommend you think about rewriting your server-side logic so you only need one network call for this functionality.
Upvotes: 2