feat(ios): share extension ile Ratebubble iOS istemcisini ekle ve paylaşım akışını düzelt
This commit is contained in:
65
ios/Ratebubble/Shared/APIClient.swift
Normal file
65
ios/Ratebubble/Shared/APIClient.swift
Normal file
@@ -0,0 +1,65 @@
|
||||
import Foundation
|
||||
|
||||
enum APIClientError: LocalizedError {
|
||||
case invalidBaseURL
|
||||
case invalidResponse
|
||||
case server(String)
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .invalidBaseURL:
|
||||
return "API_BASE_URL geçersiz."
|
||||
case .invalidResponse:
|
||||
return "Sunucudan geçerli yanıt alınamadı."
|
||||
case .server(let message):
|
||||
return message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class APIClient {
|
||||
static let shared = APIClient()
|
||||
|
||||
private init() {}
|
||||
|
||||
private var baseURL: URL? {
|
||||
guard let raw = Bundle.main.object(forInfoDictionaryKey: "API_BASE_URL") as? String else {
|
||||
return nil
|
||||
}
|
||||
return URL(string: raw)
|
||||
}
|
||||
|
||||
private var mobileAPIKey: String {
|
||||
Bundle.main.object(forInfoDictionaryKey: "MOBILE_API_KEY") as? String
|
||||
?? "mobile-dev-key-change-me"
|
||||
}
|
||||
|
||||
func getInfo(url: String) async throws -> GetInfoResponse {
|
||||
guard let baseURL else { throw APIClientError.invalidBaseURL }
|
||||
|
||||
var request = URLRequest(url: baseURL.appending(path: "/api/getinfo"))
|
||||
request.httpMethod = "POST"
|
||||
request.timeoutInterval = 20
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.setValue(mobileAPIKey, forHTTPHeaderField: "X-API-Key")
|
||||
request.httpBody = try JSONEncoder().encode(GetInfoRequest(url: url))
|
||||
|
||||
let (data, response) = try await URLSession.shared.data(for: request)
|
||||
guard let http = response as? HTTPURLResponse else {
|
||||
throw APIClientError.invalidResponse
|
||||
}
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
let envelope = try decoder.decode(APIEnvelope<GetInfoResponse>.self, from: data)
|
||||
|
||||
if (200..<300).contains(http.statusCode), envelope.success, let payload = envelope.data {
|
||||
return payload
|
||||
}
|
||||
|
||||
if let errorMessage = envelope.error?.message {
|
||||
throw APIClientError.server(errorMessage)
|
||||
}
|
||||
|
||||
throw APIClientError.server("İstek başarısız oldu (\(http.statusCode)).")
|
||||
}
|
||||
}
|
||||
29
ios/Ratebubble/Shared/Models.swift
Normal file
29
ios/Ratebubble/Shared/Models.swift
Normal file
@@ -0,0 +1,29 @@
|
||||
import Foundation
|
||||
|
||||
struct GetInfoRequest: Encodable {
|
||||
let url: String
|
||||
}
|
||||
|
||||
struct APIErrorPayload: Decodable, Error {
|
||||
let code: String
|
||||
let message: String
|
||||
}
|
||||
|
||||
struct APIEnvelope<T: Decodable>: Decodable {
|
||||
let success: Bool
|
||||
let data: T?
|
||||
let error: APIErrorPayload?
|
||||
}
|
||||
|
||||
struct GetInfoResponse: Decodable {
|
||||
let provider: String
|
||||
let title: String
|
||||
let year: Int?
|
||||
let plot: String?
|
||||
let ageRating: String?
|
||||
let type: String
|
||||
let genres: [String]
|
||||
let cast: [String]
|
||||
let backdrop: String?
|
||||
let currentSeason: Int?
|
||||
}
|
||||
31
ios/Ratebubble/Shared/SharedPayloadStore.swift
Normal file
31
ios/Ratebubble/Shared/SharedPayloadStore.swift
Normal file
@@ -0,0 +1,31 @@
|
||||
import Foundation
|
||||
|
||||
enum SharedConfig {
|
||||
static var appGroupID: String {
|
||||
Bundle.main.object(forInfoDictionaryKey: "APP_GROUP_ID") as? String
|
||||
?? "group.net.wisecolt.ratebubble"
|
||||
}
|
||||
|
||||
static var appURLScheme: String {
|
||||
Bundle.main.object(forInfoDictionaryKey: "APP_URL_SCHEME") as? String
|
||||
?? "ratebubble"
|
||||
}
|
||||
}
|
||||
|
||||
enum SharedKeys {
|
||||
static let incomingURL = "incoming_shared_url"
|
||||
}
|
||||
|
||||
enum SharedPayloadStore {
|
||||
static func saveIncomingURL(_ url: String) {
|
||||
guard let defaults = UserDefaults(suiteName: SharedConfig.appGroupID) else { return }
|
||||
defaults.set(url, forKey: SharedKeys.incomingURL)
|
||||
defaults.synchronize()
|
||||
}
|
||||
|
||||
static func consumeIncomingURL() -> String? {
|
||||
guard let defaults = UserDefaults(suiteName: SharedConfig.appGroupID) else { return nil }
|
||||
defer { defaults.removeObject(forKey: SharedKeys.incomingURL) }
|
||||
return defaults.string(forKey: SharedKeys.incomingURL)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user