Files
stickercloner/StickerCloner/Shared/StickerClonerAPI.swift
Felix Förtsch 31040c3ca5 restructure as standalone iOS app + iMessage extension
convert container from Messages-only app to regular iOS app,
move shared code (models, api client, store) to Shared/ group,
add app group entitlements for cross-process data sharing,
rewrite StickerStore for shared UserDefaults + container,
create SwiftUI app entry point, simplify extension to read-only browser

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 15:40:52 +01:00

60 lines
1.9 KiB
Swift

import Foundation
enum StickerClonerAPIError: LocalizedError {
case invalidResponse(Int)
case decodingFailed(Error)
var errorDescription: String? {
switch self {
case .invalidResponse(let code):
return "Server returned status \(code)"
case .decodingFailed(let error):
return "Failed to decode response: \(error.localizedDescription)"
}
}
}
enum StickerClonerAPI {
private static let baseURL = URL(string: "https://serve.uber.space/sticker-cloner")!
private static let decoder: JSONDecoder = {
let d = JSONDecoder()
d.keyDecodingStrategy = .convertFromSnakeCase
return d
}()
static func fetchStickerSet(name: String) async throws -> StickerSetResponse {
let url = baseURL.appendingPathComponent("api/stickersets/\(name)")
let (data, response) = try await URLSession.shared.data(from: url)
guard let http = response as? HTTPURLResponse, 200..<300 ~= http.statusCode else {
let code = (response as? HTTPURLResponse)?.statusCode ?? -1
throw StickerClonerAPIError.invalidResponse(code)
}
do {
return try decoder.decode(StickerSetResponse.self, from: data)
} catch {
throw StickerClonerAPIError.decodingFailed(error)
}
}
static func downloadSticker(from relativePath: String, to localURL: URL) async throws {
let url = baseURL.appendingPathComponent(relativePath)
let (tempURL, response) = try await URLSession.shared.download(from: url)
guard let http = response as? HTTPURLResponse, 200..<300 ~= http.statusCode else {
let code = (response as? HTTPURLResponse)?.statusCode ?? -1
throw StickerClonerAPIError.invalidResponse(code)
}
let directory = localURL.deletingLastPathComponent()
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
if FileManager.default.fileExists(atPath: localURL.path) {
try FileManager.default.removeItem(at: localURL)
}
try FileManager.default.moveItem(at: tempURL, to: localURL)
}
}