moved bible integration to local pocketbase backend and cleaned up unused code
This commit is contained in:
parent
49f9a9eb91
commit
3dd687ba30
|
@ -532,7 +532,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.2.1;
|
MARKETING_VERSION = 1.2.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.rtsda.appr;
|
PRODUCT_BUNDLE_IDENTIFIER = com.rtsda.appr;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
@ -574,7 +574,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.2.1;
|
MARKETING_VERSION = 1.2.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.rtsda.appr;
|
PRODUCT_BUNDLE_IDENTIFIER = com.rtsda.appr;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
|
|
@ -3,86 +3,156 @@ import Foundation
|
||||||
@MainActor
|
@MainActor
|
||||||
class BibleService {
|
class BibleService {
|
||||||
static let shared = BibleService()
|
static let shared = BibleService()
|
||||||
private let configService = ConfigService.shared
|
private let pocketBaseService = PocketBaseService.shared
|
||||||
private let baseURL = "https://api.scripture.api.bible/v1"
|
|
||||||
|
|
||||||
private init() {}
|
private init() {}
|
||||||
|
|
||||||
// API Response structures
|
struct Verse: Identifiable, Codable {
|
||||||
struct BibleAPIResponse: Codable {
|
|
||||||
let data: VerseData
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VerseData: Codable {
|
|
||||||
let id: String
|
let id: String
|
||||||
let orgId: String
|
|
||||||
let bibleId: String
|
|
||||||
let bookId: String
|
|
||||||
let chapterId: String
|
|
||||||
let reference: String
|
let reference: String
|
||||||
let content: String
|
let text: String
|
||||||
|
let isActive: Bool
|
||||||
|
|
||||||
// The API returns HTML content, so we'll clean it up
|
enum CodingKeys: String, CodingKey {
|
||||||
var cleanContent: String {
|
case id
|
||||||
return content
|
case reference
|
||||||
.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression)
|
case text
|
||||||
.replacingOccurrences(of: """, with: "\"")
|
case isActive = "is_active"
|
||||||
.replacingOccurrences(of: "'", with: "'")
|
|
||||||
.replacingOccurrences(of: "¶\\s*", with: "", options: .regularExpression)
|
|
||||||
.replacingOccurrences(of: "^\\d+\\s*", with: "", options: .regularExpression) // Remove verse numbers at start
|
|
||||||
.replacingOccurrences(of: "\\((.*?)\\)", with: "$1", options: .regularExpression) // Keep text inside parentheses
|
|
||||||
.replacingOccurrences(of: "\\s+", with: " ", options: .regularExpression)
|
|
||||||
.trimmingCharacters(in: .whitespaces)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct VersesRecord: Codable {
|
||||||
|
let collectionId: String
|
||||||
|
let collectionName: String
|
||||||
|
let created: String
|
||||||
|
let id: String
|
||||||
|
let updated: String
|
||||||
|
let verses: VersesData
|
||||||
|
|
||||||
|
struct VersesData: Codable {
|
||||||
|
let id: String
|
||||||
|
let verses: [Verse]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var cachedVerses: [Verse]?
|
||||||
|
|
||||||
func getRandomVerse() async throws -> (verse: String, reference: String) {
|
func getRandomVerse() async throws -> (verse: String, reference: String) {
|
||||||
// List of popular and uplifting Bible verses
|
let verses = try await getVerses()
|
||||||
let references = [
|
print("Total verses available: \(verses.count)")
|
||||||
//"JER.29.11", "PRO.3.5", "PHP.4.13", "JOS.1.9",
|
|
||||||
"PSA.23.1",
|
|
||||||
//"ISA.40.31", "MAT.11.28", "ROM.8.28", "PSA.27.1", "PSA.46.10",
|
|
||||||
//"JHN.3.16", "ROM.15.13",
|
|
||||||
"2CO.5.7", //"DEU.31.6", "ROM.8.31",
|
|
||||||
//"1JN.4.19", "PHP.4.6", "MAT.6.33", "HEB.11.1", "PSA.37.4"
|
|
||||||
]
|
|
||||||
|
|
||||||
// Randomly select a reference
|
guard !verses.isEmpty else {
|
||||||
let randomReference = references.randomElement() ?? "JHN.3.16"
|
print("No verses available")
|
||||||
|
throw NSError(domain: "BibleService", code: -1, userInfo: [NSLocalizedDescriptionKey: "No verses available"])
|
||||||
guard let apiKey = configService.bibleApiKey else {
|
|
||||||
throw NSError(domain: "BibleService", code: -1, userInfo: [NSLocalizedDescriptionKey: "Bible API key not found"])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the API URL
|
let randomVerse = verses.randomElement()!
|
||||||
let urlString = "\(baseURL)/bibles/de4e12af7f28f599-01/verses/\(randomReference)"
|
print("Selected random verse: \(randomVerse.reference)")
|
||||||
var request = URLRequest(url: URL(string: urlString)!)
|
return (verse: randomVerse.text, reference: randomVerse.reference)
|
||||||
request.addValue(apiKey, forHTTPHeaderField: "api-key")
|
}
|
||||||
|
|
||||||
|
func getVerse(reference: String) async throws -> (verse: String, reference: String) {
|
||||||
|
print("Looking up verse with reference: \(reference)")
|
||||||
|
|
||||||
|
// Convert API-style reference (e.g., "JER.29.11") to display format ("Jeremiah 29:11")
|
||||||
|
let displayReference = reference
|
||||||
|
.replacingOccurrences(of: "\\.", with: " ", options: .regularExpression)
|
||||||
|
.replacingOccurrences(of: "([A-Z]+)", with: "$1 ", options: .regularExpression)
|
||||||
|
.trimmingCharacters(in: .whitespaces)
|
||||||
|
|
||||||
|
print("Converted reference to: \(displayReference)")
|
||||||
|
|
||||||
|
let verses = try await getVerses()
|
||||||
|
print("Found \(verses.count) verses")
|
||||||
|
|
||||||
|
if let verse = verses.first(where: { $0.reference.lowercased() == displayReference.lowercased() }) {
|
||||||
|
print("Found matching verse: \(verse.reference)")
|
||||||
|
return (verse: verse.text, reference: verse.reference)
|
||||||
|
}
|
||||||
|
|
||||||
|
print("No matching verse found for reference: \(displayReference)")
|
||||||
|
throw NSError(domain: "BibleService", code: -1, userInfo: [NSLocalizedDescriptionKey: "Verse not found"])
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getVerses() async throws -> [Verse] {
|
||||||
|
// Return cached verses if available
|
||||||
|
if let cached = cachedVerses {
|
||||||
|
print("Returning cached verses")
|
||||||
|
return cached
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Fetching verses from PocketBase")
|
||||||
|
// Fetch from PocketBase
|
||||||
|
let endpoint = "\(PocketBaseService.shared.baseURL)/bible_verses/records/nkf01o1q3456flr"
|
||||||
|
|
||||||
|
guard let url = URL(string: endpoint) else {
|
||||||
|
print("Invalid URL: \(endpoint)")
|
||||||
|
throw URLError(.badURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = URLRequest(url: url)
|
||||||
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||||
|
|
||||||
|
print("Making request to: \(endpoint)")
|
||||||
let (data, response) = try await URLSession.shared.data(for: request)
|
let (data, response) = try await URLSession.shared.data(for: request)
|
||||||
|
|
||||||
// Log raw response
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
if let rawResponse = String(data: data, encoding: .utf8) {
|
print("Invalid response type")
|
||||||
print("Raw Bible API Response:")
|
throw URLError(.badServerResponse)
|
||||||
print(rawResponse)
|
|
||||||
print("\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for successful response
|
print("Response status code: \(httpResponse.statusCode)")
|
||||||
guard let httpResponse = response as? HTTPURLResponse,
|
|
||||||
(200...299).contains(httpResponse.statusCode) else {
|
guard httpResponse.statusCode == 200 else {
|
||||||
throw NSError(domain: "BibleService", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to fetch verse"])
|
if let errorString = String(data: data, encoding: .utf8) {
|
||||||
|
print("Error response from server: \(errorString)")
|
||||||
|
}
|
||||||
|
throw URLError(.badServerResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print raw response for debugging
|
||||||
|
if let rawResponse = String(data: data, encoding: .utf8) {
|
||||||
|
print("Raw response from PocketBase:")
|
||||||
|
print(rawResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
let decoder = JSONDecoder()
|
let decoder = JSONDecoder()
|
||||||
let apiResponse = try decoder.decode(BibleAPIResponse.self, from: data)
|
do {
|
||||||
|
let versesRecord = try decoder.decode(VersesRecord.self, from: data)
|
||||||
// Log cleaned content
|
|
||||||
print("Cleaned verse content:")
|
// Cache the verses
|
||||||
print(apiResponse.data.cleanContent)
|
cachedVerses = versesRecord.verses.verses
|
||||||
print("\nReference:", apiResponse.data.reference)
|
print("Successfully fetched and cached \(versesRecord.verses.verses.count) verses")
|
||||||
print("\n")
|
|
||||||
|
return versesRecord.verses.verses
|
||||||
return (verse: apiResponse.data.cleanContent, reference: apiResponse.data.reference)
|
} catch {
|
||||||
|
print("Failed to decode response: \(error)")
|
||||||
|
if let decodingError = error as? DecodingError {
|
||||||
|
switch decodingError {
|
||||||
|
case .keyNotFound(let key, let context):
|
||||||
|
print("Missing key: \(key.stringValue) in \(context.debugDescription)")
|
||||||
|
case .typeMismatch(let type, let context):
|
||||||
|
print("Type mismatch: expected \(type) in \(context.debugDescription)")
|
||||||
|
case .valueNotFound(let type, let context):
|
||||||
|
print("Value not found: expected \(type) in \(context.debugDescription)")
|
||||||
|
case .dataCorrupted(let context):
|
||||||
|
print("Data corrupted: \(context.debugDescription)")
|
||||||
|
@unknown default:
|
||||||
|
print("Unknown decoding error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAllVerses() async throws {
|
||||||
|
print("\n=== Testing All Verses ===\n")
|
||||||
|
let verses = try await getVerses()
|
||||||
|
for verse in verses {
|
||||||
|
print("Reference: \(verse.reference)")
|
||||||
|
print("Verse: \(verse.text)")
|
||||||
|
print("-------------------\n")
|
||||||
|
}
|
||||||
|
print("=== Test Complete ===\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ struct Bulletin: Identifiable, Codable {
|
||||||
|
|
||||||
class PocketBaseService {
|
class PocketBaseService {
|
||||||
static let shared = PocketBaseService()
|
static let shared = PocketBaseService()
|
||||||
private let baseURL = "https://pocketbase.rockvilletollandsda.church/api/collections"
|
let baseURL = "https://pocketbase.rockvilletollandsda.church/api/collections"
|
||||||
|
|
||||||
private init() {}
|
private init() {}
|
||||||
|
|
||||||
|
|
|
@ -495,7 +495,7 @@ struct MoreView: View {
|
||||||
fallbackURL: AppAvailabilityService.AppStoreURLs.hymnal
|
fallbackURL: AppAvailabilityService.AppStoreURLs.hymnal
|
||||||
)
|
)
|
||||||
} label: {
|
} label: {
|
||||||
Label("SDA Hymnal", systemImage: "music.note")
|
Label("Adventist Hymnal", systemImage: "music.note")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
125
bible_verses.json
Normal file
125
bible_verses.json
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
{
|
||||||
|
"id": "verses",
|
||||||
|
"verses": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"reference": "Jeremiah 29:11",
|
||||||
|
"text": "For I know the thoughts that I think toward you, saith the LORD, thoughts of peace, and not of evil, to give you an expected end.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"reference": "Proverbs 3:5",
|
||||||
|
"text": "Trust in the LORD with all thine heart; and lean not unto thine own understanding.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"reference": "Philippians 4:13",
|
||||||
|
"text": "I can do all things through Christ which strengtheneth me.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4",
|
||||||
|
"reference": "John 3:16",
|
||||||
|
"text": "For God so loved the world, that he gave his only begotten Son, that whosoever believeth in him should not perish, but have everlasting life.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5",
|
||||||
|
"reference": "Romans 8:28",
|
||||||
|
"text": "And we know that all things work together for good to them that love God, to them who are the called according to his purpose.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6",
|
||||||
|
"reference": "Matthew 6:33",
|
||||||
|
"text": "But seek ye first the kingdom of God, and his righteousness; and all these things shall be added unto you.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7",
|
||||||
|
"reference": "Isaiah 41:10",
|
||||||
|
"text": "Fear thou not; for I am with thee: be not dismayed; for I am thy God: I will strengthen thee; yea, I will help thee; yea, I will uphold thee with the right hand of my righteousness.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8",
|
||||||
|
"reference": "Joshua 1:9",
|
||||||
|
"text": "Have not I commanded thee? Be strong and of a good courage; be not afraid, neither be thou dismayed: for the LORD thy God is with thee whithersoever thou goest.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9",
|
||||||
|
"reference": "Psalm 23:1",
|
||||||
|
"text": "The LORD is my shepherd; I shall not want.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "10",
|
||||||
|
"reference": "Psalm 27:1",
|
||||||
|
"text": "The LORD is my light and my salvation; whom shall I fear? the LORD is the strength of my life; of whom shall I be afraid?",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "11",
|
||||||
|
"reference": "Psalm 46:1",
|
||||||
|
"text": "God is our refuge and strength, a very present help in trouble.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "12",
|
||||||
|
"reference": "Psalm 91:1",
|
||||||
|
"text": "He that dwelleth in the secret place of the most High shall abide under the shadow of the Almighty.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "13",
|
||||||
|
"reference": "Psalm 119:105",
|
||||||
|
"text": "Thy word is a lamp unto my feet, and a light unto my path.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "14",
|
||||||
|
"reference": "Psalm 121:1",
|
||||||
|
"text": "I will lift up mine eyes unto the hills, from whence cometh my help.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "15",
|
||||||
|
"reference": "Psalm 139:14",
|
||||||
|
"text": "I will praise thee; for I am fearfully and wonderfully made: marvellous are thy works; and that my soul knoweth right well.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "16",
|
||||||
|
"reference": "Proverbs 16:3",
|
||||||
|
"text": "Commit thy works unto the LORD, and thy thoughts shall be established.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "17",
|
||||||
|
"reference": "Isaiah 40:31",
|
||||||
|
"text": "But they that wait upon the LORD shall renew their strength; they shall mount up with wings as eagles; they shall run, and not be weary; and they shall walk, and not faint.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "18",
|
||||||
|
"reference": "Matthew 11:28",
|
||||||
|
"text": "Come unto me, all ye that labour and are heavy laden, and I will give you rest.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "19",
|
||||||
|
"reference": "John 14:27",
|
||||||
|
"text": "Peace I leave with you, my peace I give unto you: not as the world giveth, give I unto you. Let not your heart be troubled, neither let it be afraid.",
|
||||||
|
"is_active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "20",
|
||||||
|
"reference": "Romans 12:2",
|
||||||
|
"text": "And be not conformed to this world: but be ye transformed by the renewing of your mind, that ye may prove what is that good, and acceptable, and perfect, will of God.",
|
||||||
|
"is_active": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
107
test_verses.swift
Normal file
107
test_verses.swift
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class PocketBaseService {
|
||||||
|
static let shared = PocketBaseService()
|
||||||
|
let baseURL = "https://pocketbase.rockvilletollandsda.church/api/collections"
|
||||||
|
private init() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
class BibleService {
|
||||||
|
static let shared = BibleService()
|
||||||
|
private let pocketBaseService = PocketBaseService.shared
|
||||||
|
|
||||||
|
private init() {}
|
||||||
|
|
||||||
|
struct Verse: Identifiable, Codable {
|
||||||
|
let id: String
|
||||||
|
let reference: String
|
||||||
|
let text: String
|
||||||
|
let isActive: Bool
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case id
|
||||||
|
case reference
|
||||||
|
case text
|
||||||
|
case isActive = "is_active"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VersesRecord: Codable {
|
||||||
|
let collectionId: String
|
||||||
|
let collectionName: String
|
||||||
|
let created: String
|
||||||
|
let id: String
|
||||||
|
let updated: String
|
||||||
|
let verses: VersesData
|
||||||
|
|
||||||
|
struct VersesData: Codable {
|
||||||
|
let id: String
|
||||||
|
let verses: [Verse]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var cachedVerses: [Verse]?
|
||||||
|
|
||||||
|
private func getVerses() async throws -> [Verse] {
|
||||||
|
print("Fetching verses from PocketBase")
|
||||||
|
let endpoint = "\(PocketBaseService.shared.baseURL)/bible_verses/records/nkf01o1q3456flr"
|
||||||
|
|
||||||
|
guard let url = URL(string: endpoint) else {
|
||||||
|
print("Invalid URL: \(endpoint)")
|
||||||
|
throw URLError(.badURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = URLRequest(url: url)
|
||||||
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||||
|
|
||||||
|
print("Making request to: \(endpoint)")
|
||||||
|
let (data, response) = try await URLSession.shared.data(for: request)
|
||||||
|
|
||||||
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
|
print("Invalid response type")
|
||||||
|
throw URLError(.badServerResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Response status code: \(httpResponse.statusCode)")
|
||||||
|
|
||||||
|
guard httpResponse.statusCode == 200 else {
|
||||||
|
if let errorString = String(data: data, encoding: .utf8) {
|
||||||
|
print("Error response from server: \(errorString)")
|
||||||
|
}
|
||||||
|
throw URLError(.badServerResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
let decoder = JSONDecoder()
|
||||||
|
let versesRecord = try decoder.decode(VersesRecord.self, from: data)
|
||||||
|
return versesRecord.verses.verses
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAllVerses() async throws {
|
||||||
|
print("\n=== Testing All Verses ===\n")
|
||||||
|
let verses = try await getVerses()
|
||||||
|
for verse in verses {
|
||||||
|
print("Reference: \(verse.reference)")
|
||||||
|
print("Text: \(verse.text)")
|
||||||
|
print("-------------------\n")
|
||||||
|
}
|
||||||
|
print("=== Test Complete ===\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Starting Bible Verses Test...")
|
||||||
|
|
||||||
|
// Create a semaphore to signal when the task is complete
|
||||||
|
let semaphore = DispatchSemaphore(value: 0)
|
||||||
|
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
try await BibleService.shared.testAllVerses()
|
||||||
|
} catch {
|
||||||
|
print("Error testing verses: \(error)")
|
||||||
|
}
|
||||||
|
semaphore.signal()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the task to complete
|
||||||
|
semaphore.wait()
|
Loading…
Reference in a new issue