Add own-instance API request retrying

This commit is contained in:
Shadowfacts 2021-04-04 15:11:29 -04:00
parent a896573a5e
commit 13a4221fce
1 changed files with 49 additions and 7 deletions

View File

@ -46,6 +46,9 @@ class MastodonController: ObservableObject {
@Published private(set) var instance: Instance! @Published private(set) var instance: Instance!
private(set) var customEmojis: [Emoji]? private(set) var customEmojis: [Emoji]?
private var pendingOwnInstanceRequestCallbacks = [(Instance) -> Void]()
private var ownInstanceRequest: URLSessionTask?
var loggedIn: Bool { var loggedIn: Bool {
accountInfo != nil accountInfo != nil
} }
@ -115,17 +118,56 @@ class MastodonController: ObservableObject {
} }
} }
// todo: this should dedup requests
func getOwnInstance(completion: ((Instance) -> Void)? = nil) { func getOwnInstance(completion: ((Instance) -> Void)? = nil) {
getOwnInstanceInternal(retryAttempt: 0, completion: completion)
}
private func getOwnInstanceInternal(retryAttempt: Int, completion: ((Instance) -> Void)?) {
// this is main thread only to prevent concurrent access to ownInstanceRequest and pendingOwnInstanceRequestCallbacks
assert(Thread.isMainThread)
if let instance = self.instance { if let instance = self.instance {
completion?(instance) completion?(instance)
} else { } else {
if let completion = completion {
pendingOwnInstanceRequestCallbacks.append(completion)
}
if ownInstanceRequest == nil {
let request = Client.getInstance() let request = Client.getInstance()
run(request) { (response) in ownInstanceRequest = run(request) { (response) in
guard case let .success(instance, _) = response else { fatalError() } switch response {
case .failure(_):
let delay: DispatchTimeInterval
switch retryAttempt {
case 0:
delay = .seconds(1)
case 1:
delay = .seconds(5)
case 2:
delay = .seconds(30)
case 3:
delay = .seconds(60)
default:
// if we've failed four times, just give up :/
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
// completion is nil because in this invocation of getOwnInstanceInternal we've already added it to the pending callbacks array
self.getOwnInstanceInternal(retryAttempt: retryAttempt + 1, completion: nil)
}
case let .success(instance, _):
DispatchQueue.main.async { DispatchQueue.main.async {
self.ownInstanceRequest = nil
self.instance = instance self.instance = instance
completion?(instance)
for completion in self.pendingOwnInstanceRequestCallbacks {
completion(instance)
}
self.pendingOwnInstanceRequestCallbacks = []
}
}
} }
} }
} }