- Secrets.swift entra no .gitignore; Secrets.swift.example fica como modelo - Adiciona .gitignore (xcuserdata, DerivedData) e README com instruções Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
89 lines
2.4 KiB
Swift
89 lines
2.4 KiB
Swift
//
|
|
// BibleAPI.swift
|
|
// Bible-Week
|
|
//
|
|
// Cliente da API da Bíblia (NBV - Nova Bíblia Viva).
|
|
//
|
|
|
|
import Foundation
|
|
|
|
// MARK: - Modelos
|
|
|
|
/// Um livro da Bíblia (ex.: Gênesis, João).
|
|
struct Book: Decodable, Identifiable, Hashable {
|
|
let code: String
|
|
let name: String
|
|
let testament: String
|
|
let bookOrder: Int
|
|
let chapterCount: Int
|
|
|
|
var id: String { code }
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case code, name, testament
|
|
case bookOrder = "book_order"
|
|
case chapterCount = "chapter_count"
|
|
}
|
|
}
|
|
|
|
/// Resposta de `GET /v1/books`.
|
|
struct BooksResponse: Decodable {
|
|
let books: [Book]
|
|
}
|
|
|
|
/// Um versículo com seu número e texto.
|
|
struct Verse: Decodable, Identifiable, Hashable {
|
|
let verse: Int
|
|
let text: String
|
|
|
|
var id: Int { verse }
|
|
}
|
|
|
|
/// Resposta de um capítulo completo.
|
|
struct Chapter: Decodable, Hashable {
|
|
let translation: String
|
|
let book: String
|
|
let bookName: String
|
|
let chapter: Int
|
|
let verses: [Verse]
|
|
}
|
|
|
|
// MARK: - Serviço
|
|
|
|
/// Cliente mínimo, somente leitura, para a API da Bíblia.
|
|
enum BibleAPI {
|
|
static let baseURL = URL(string: "https://bible.falehandix.com.br")!
|
|
|
|
// A chave vive em Secrets.swift (gitignored) — ver Secrets.swift.example.
|
|
static let apiKey = Secrets.bibleAPIKey
|
|
|
|
/// Faz um GET na API e decodifica a resposta JSON.
|
|
static func get<T: Decodable>(_ path: String, query: [URLQueryItem] = []) async throws -> T {
|
|
var components = URLComponents(
|
|
url: baseURL.appendingPathComponent(path),
|
|
resolvingAgainstBaseURL: false
|
|
)!
|
|
if !query.isEmpty { components.queryItems = query }
|
|
|
|
var request = URLRequest(url: components.url!)
|
|
request.setValue(apiKey, forHTTPHeaderField: "X-API-Key")
|
|
|
|
let (data, response) = try await URLSession.shared.data(for: request)
|
|
guard let http = response as? HTTPURLResponse, 200..<300 ~= http.statusCode else {
|
|
throw URLError(.badServerResponse)
|
|
}
|
|
return try JSONDecoder().decode(T.self, from: data)
|
|
}
|
|
|
|
/// Busca todos os 66 livros em ordem canônica.
|
|
static func books() async throws -> [Book] {
|
|
let response: BooksResponse = try await get("/v1/books")
|
|
return response.books
|
|
}
|
|
|
|
/// Busca o texto completo de um capítulo.
|
|
static func chapter(book code: String, chapter: Int) async throws -> Chapter {
|
|
try await get("/v1/books/\(code)/chapters/\(chapter)")
|
|
}
|
|
}
|