Files
bookibra/ios/Bookibra/Models/BookRemote.swift

136 lines
4.1 KiB
Swift

import Foundation
struct BookRemote: Codable, Identifiable, Hashable {
var id: String { isbn13 ?? isbn10 ?? title + (authors.first ?? "") }
let remoteId: String?
let title: String
let authors: [String]
let publishedYear: Int?
let isbn10: String?
let isbn13: String?
let coverImageUrl: URL?
let language: String?
let description: String?
let pageCount: Int?
let categories: [String]
let publisher: String?
let sourceLocale: String?
init(
remoteId: String? = nil,
title: String,
authors: [String] = [],
publishedYear: Int? = nil,
isbn10: String? = nil,
isbn13: String? = nil,
coverImageUrl: URL? = nil,
language: String? = nil,
description: String? = nil,
pageCount: Int? = nil,
categories: [String] = [],
publisher: String? = nil,
sourceLocale: String? = nil
) {
self.remoteId = remoteId
self.title = title
self.authors = authors
self.publishedYear = publishedYear
self.isbn10 = isbn10
self.isbn13 = isbn13
self.coverImageUrl = coverImageUrl
self.language = language
self.description = description
self.pageCount = pageCount
self.categories = categories
self.publisher = publisher
self.sourceLocale = sourceLocale
}
}
struct BookSearchResponse: Decodable {
let items: [BookRemote]
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Endpoint 1: /isbn returns { data: { tr: {...}, en: {...} } }
if let localeMap = try? container.decode([String: RawBook].self, forKey: .data) {
self.items = localeMap.map { locale, raw in raw.toBook(locale: locale) }
return
}
// Endpoint 2/3: /title and /filter returns { data: [{ locale, items: [...] }] }
if let groups = try? container.decode([LocaleGroup].self, forKey: .data) {
self.items = groups.flatMap { group in
group.items.map { $0.toBook(locale: group.locale) }
}
return
}
self.items = []
}
private enum CodingKeys: String, CodingKey {
case data
}
}
private struct LocaleGroup: Decodable {
let locale: String
let items: [RawBook]
}
private struct RawBook: Decodable {
let asin: String?
let title: String?
let authorName: String?
let author: String?
let isbn: String?
let thumbImage: String?
let image: String?
let date: String?
let publisher: String?
let page: Int?
let description: String?
let categories: [String]?
let locale: String?
func toBook(locale: String?) -> BookRemote {
let coverRaw = thumbImage ?? image
let authorsText = authorName ?? author ?? ""
let authors = authorsText
.replacingOccurrences(of: "[Yazar]", with: "")
.split(separator: ",")
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter { !$0.isEmpty }
let isbnClean = isbn?.filter { $0.isNumber || $0.uppercased() == "X" }
let isbn10 = isbnClean?.count == 10 ? isbnClean : nil
let isbn13 = isbnClean?.count == 13 ? isbnClean : nil
return BookRemote(
remoteId: asin,
title: title ?? "Untitled",
authors: authors,
publishedYear: Self.extractYear(from: date),
isbn10: isbn10,
isbn13: isbn13,
coverImageUrl: coverRaw.flatMap(URL.init(string:)),
language: locale,
description: description,
pageCount: page,
categories: categories ?? [],
publisher: publisher,
sourceLocale: locale
)
}
private static func extractYear(from value: String?) -> Int? {
guard let value else { return nil }
let digits = value.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
guard digits.count >= 4 else { return nil }
return Int(String(digits.prefix(4)))
}
}