refactor: Enhance API error handling with logging, introduce minimum loading time, update app version to 1.2.1, and ignore .DS_Store.
This commit is contained in:
parent
1747bb110a
commit
c21057146d
5 changed files with 60 additions and 13 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -60,3 +60,4 @@ fastlane/report.xml
|
||||||
fastlane/Preview.html
|
fastlane/Preview.html
|
||||||
fastlane/screenshots/**/*.png
|
fastlane/screenshots/**/*.png
|
||||||
fastlane/test_output
|
fastlane/test_output
|
||||||
|
.DS_Store
|
||||||
|
|
|
||||||
BIN
MacTorn-v1.2.1.zip
Normal file
BIN
MacTorn-v1.2.1.zip
Normal file
Binary file not shown.
|
|
@ -17,8 +17,8 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.2</string>
|
<string>1.2.1</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.2</string>
|
<string>1.2.1</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import os.log
|
||||||
|
|
||||||
|
private let logger = Logger(subsystem: "com.mactorn", category: "AppState")
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
class AppState: ObservableObject {
|
class AppState: ObservableObject {
|
||||||
|
|
@ -234,18 +237,35 @@ class AppState: ObservableObject {
|
||||||
func fetchData() {
|
func fetchData() {
|
||||||
guard !apiKey.isEmpty else {
|
guard !apiKey.isEmpty else {
|
||||||
errorMsg = "API Key required"
|
errorMsg = "API Key required"
|
||||||
|
logger.warning("Fetch aborted: API Key required")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let url = TornAPI.url(for: apiKey) else {
|
guard let url = TornAPI.url(for: apiKey) else {
|
||||||
errorMsg = "Invalid URL"
|
errorMsg = "Invalid URL"
|
||||||
|
logger.error("Fetch aborted: Invalid URL")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
isLoading = true
|
isLoading = true
|
||||||
errorMsg = nil
|
errorMsg = nil
|
||||||
|
|
||||||
|
logger.info("Starting data fetch...")
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
|
let startTime = Date()
|
||||||
|
|
||||||
|
// Ensure minimum loading time for UX, then set isLoading = false
|
||||||
|
defer {
|
||||||
|
Task { @MainActor in
|
||||||
|
let elapsed = Date().timeIntervalSince(startTime)
|
||||||
|
if elapsed < 0.5 {
|
||||||
|
try? await Task.sleep(nanoseconds: UInt64((0.5 - elapsed) * 1_000_000_000))
|
||||||
|
}
|
||||||
|
self.isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let (data, response) = try await URLSession.shared.data(from: url)
|
let (data, response) = try await URLSession.shared.data(from: url)
|
||||||
|
|
||||||
|
|
@ -253,35 +273,61 @@ class AppState: ObservableObject {
|
||||||
throw APIError.invalidResponse
|
throw APIError.invalidResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.info("HTTP response: \(httpResponse.statusCode)")
|
||||||
|
|
||||||
switch httpResponse.statusCode {
|
switch httpResponse.statusCode {
|
||||||
case 200:
|
case 200:
|
||||||
|
// Check for Torn API error in response (API returns 200 even on errors)
|
||||||
|
if let tornError = checkForTornAPIError(data: data) {
|
||||||
|
await MainActor.run {
|
||||||
|
self.errorMsg = tornError
|
||||||
|
}
|
||||||
|
logger.error("Torn API error: \(tornError)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Parse on background thread
|
// Parse on background thread
|
||||||
try await parseDataInBackground(data: data)
|
try await parseDataInBackground(data: data)
|
||||||
|
|
||||||
// Fetch faction data separately
|
// Fetch faction data separately
|
||||||
await fetchFactionData()
|
await fetchFactionData()
|
||||||
|
|
||||||
|
logger.info("Data fetch completed successfully")
|
||||||
|
|
||||||
case 403, 404:
|
case 403, 404:
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
self.errorMsg = "Invalid API Key"
|
self.errorMsg = "Invalid API Key"
|
||||||
self.data = nil
|
self.data = nil
|
||||||
self.isLoading = false
|
|
||||||
}
|
}
|
||||||
|
logger.error("HTTP \(httpResponse.statusCode): Invalid API Key")
|
||||||
default:
|
default:
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
self.errorMsg = "HTTP Error: \(httpResponse.statusCode)"
|
self.errorMsg = "HTTP Error: \(httpResponse.statusCode)"
|
||||||
self.isLoading = false
|
|
||||||
}
|
}
|
||||||
|
logger.error("HTTP Error: \(httpResponse.statusCode)")
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
self.errorMsg = error.localizedDescription
|
self.errorMsg = "Network error: \(error.localizedDescription)"
|
||||||
self.isLoading = false
|
|
||||||
}
|
}
|
||||||
|
logger.error("Network error: \(error.localizedDescription)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if Torn API returned an error (API returns HTTP 200 even on errors like rate limiting)
|
||||||
|
private func checkForTornAPIError(data: Data) -> String? {
|
||||||
|
guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
||||||
|
let error = json["error"] as? [String: Any],
|
||||||
|
let errorMessage = error["error"] as? String else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let errorCode = error["code"] as? Int ?? 0
|
||||||
|
logger.warning("Torn API error code \(errorCode): \(errorMessage)")
|
||||||
|
return "API Error: \(errorMessage)"
|
||||||
|
}
|
||||||
|
|
||||||
// Move parsing logic here and mark as non-isolated or detached
|
// Move parsing logic here and mark as non-isolated or detached
|
||||||
private func parseDataInBackground(data: Data) async throws {
|
private func parseDataInBackground(data: Data) async throws {
|
||||||
// Run CPU-heavy parsing detached from MainActor
|
// Run CPU-heavy parsing detached from MainActor
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue