more error handling

This commit is contained in:
Alek Michelson 2026-04-09 23:48:10 -04:00
parent 354103fc17
commit e46d983111
3 changed files with 62 additions and 11 deletions

View file

@ -169,15 +169,27 @@ extension AppDelegate: MessagingDelegate {
Log.d(tag, "Firebase token received: \(String(describing: fcmToken))")
// Subscribe to ~poll topic
Messaging.messaging().subscribe(toTopic: pollTopic)
Messaging.messaging().subscribe(toTopic: pollTopic) { error in
if let error {
Log.e(self.tag, "Firebase subscribe failed for \(self.pollTopic)", error)
} else {
Log.d(self.tag, "Firebase subscribe succeeded for \(self.pollTopic)")
}
}
// Re-subscribe to Firebase for all topics
let store = Store.shared
let subscriptionManager = SubscriptionManager(store: store)
store.getSubscriptions()?.forEach{ subscription in
if let baseUrl = subscription.baseUrl, let topic = subscription.topic {
let firebaseTopicName = firebaseTopic(baseUrl: baseUrl, topic: topic)
Log.d(tag, "Re-subscribing to topic \(baseUrl)/\(topic)")
Messaging.messaging().subscribe(toTopic: firebaseTopic(baseUrl: baseUrl, topic: topic))
Messaging.messaging().subscribe(toTopic: firebaseTopicName) { error in
if let error {
Log.e(self.tag, "Firebase subscribe failed for \(firebaseTopicName)", error)
} else {
Log.d(self.tag, "Firebase subscribe succeeded for \(firebaseTopicName)")
}
}
}
}
}

View file

@ -9,8 +9,15 @@ struct SubscriptionManager {
func subscribe(baseUrl: String, topic: String) {
let normalizedBaseUrl = normalizeBaseUrl(baseUrl)
let firebaseTopicName = firebaseTopic(baseUrl: normalizedBaseUrl, topic: topic)
Log.d(tag, "Subscribing to \(topicUrl(baseUrl: normalizedBaseUrl, topic: topic))")
Messaging.messaging().subscribe(toTopic: firebaseTopic(baseUrl: normalizedBaseUrl, topic: topic))
Messaging.messaging().subscribe(toTopic: firebaseTopicName) { error in
if let error {
Log.e(tag, "Firebase subscribe failed for \(firebaseTopicName)", error)
} else {
Log.d(tag, "Firebase subscribe succeeded for \(firebaseTopicName)")
}
}
let subscription = store.saveSubscription(baseUrl: normalizedBaseUrl, topic: topic)
poll(subscription)
}
@ -19,7 +26,14 @@ struct SubscriptionManager {
Log.d(tag, "Unsubscribing from \(subscription.urlString())")
DispatchQueue.main.async {
if let baseUrl = subscription.baseUrl, let topic = subscription.topic {
Messaging.messaging().unsubscribe(fromTopic: firebaseTopic(baseUrl: baseUrl, topic: topic))
let firebaseTopicName = firebaseTopic(baseUrl: baseUrl, topic: topic)
Messaging.messaging().unsubscribe(fromTopic: firebaseTopicName) { error in
if let error {
Log.e(tag, "Firebase unsubscribe failed for \(firebaseTopicName)", error)
} else {
Log.d(tag, "Firebase unsubscribe succeeded for \(firebaseTopicName)")
}
}
}
store.delete(subscription: subscription)
}

View file

@ -19,7 +19,10 @@ class ApiService {
}
func poll(subscription: Subscription, messageId: String, user: BasicUser?, completionHandler: @escaping (Message?, Error?) -> Void) {
let url = URL(string: "\(subscription.urlString())/json?poll=1&id=\(messageId)")!
guard let url = URL(string: "\(subscription.urlString())/json?poll=1&id=\(messageId)") else {
completionHandler(nil, URLError(.badURL))
return
}
Log.d(tag, "Polling single message from \(url) with user \(user?.username ?? "anonymous")")
let request = newRequest(url: url, user: user)
@ -28,8 +31,16 @@ class ApiService {
completionHandler(nil, error)
return
}
guard let httpResponse = response as? HTTPURLResponse, (200..<300).contains(httpResponse.statusCode) else {
completionHandler(nil, URLError(.badServerResponse))
return
}
guard let data = data else {
completionHandler(nil, URLError(.badServerResponse))
return
}
do {
let message = try JSONDecoder().decode(Message.self, from: data!)
let message = try JSONDecoder().decode(Message.self, from: data)
completionHandler(message, nil)
} catch {
completionHandler(nil, error)
@ -98,19 +109,33 @@ class ApiService {
}
private func fetchJsonData<T: Decodable>(urlString: String, user: BasicUser?, completionHandler: @escaping ([T]?, Error?) -> ()) {
guard let url = URL(string: urlString) else { return }
guard let url = URL(string: urlString) else {
completionHandler(nil, URLError(.badURL))
return
}
let request = newRequest(url: url, user: user)
newSession(timeout: 30).dataTask(with: request) { (data, response, error) in
if let error = error {
if let error {
Log.e(self.tag, "Error fetching data", error)
completionHandler(nil, error)
return
}
guard let httpResponse = response as? HTTPURLResponse, (200..<300).contains(httpResponse.statusCode) else {
completionHandler(nil, URLError(.badServerResponse))
return
}
guard let data = data else {
completionHandler(nil, URLError(.badServerResponse))
return
}
do {
let lines = String(decoding: data!, as: UTF8.self).split(whereSeparator: \.isNewline)
let lines = String(decoding: data, as: UTF8.self).split(whereSeparator: \.isNewline)
var notifications: [T] = []
for jsonLine in lines {
notifications.append(try JSONDecoder().decode(T.self, from: jsonLine.data(using: .utf8)!))
guard let jsonData = jsonLine.data(using: .utf8) else {
throw URLError(.cannotDecodeContentData)
}
notifications.append(try JSONDecoder().decode(T.self, from: jsonData))
}
completionHandler(notifications, nil)
} catch {