moved bible integration to local pocketbase backend and cleaned up unused code

This commit is contained in:
RTSDA 2025-04-04 01:36:28 -04:00
parent 49f9a9eb91
commit 3dd687ba30
6 changed files with 366 additions and 64 deletions

View file

@ -532,7 +532,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.2.1;
MARKETING_VERSION = 1.2.2;
PRODUCT_BUNDLE_IDENTIFIER = com.rtsda.appr;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
@ -574,7 +574,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.2.1;
MARKETING_VERSION = 1.2.2;
PRODUCT_BUNDLE_IDENTIFIER = com.rtsda.appr;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;

View file

@ -3,86 +3,156 @@ import Foundation
@MainActor
class BibleService {
static let shared = BibleService()
private let configService = ConfigService.shared
private let baseURL = "https://api.scripture.api.bible/v1"
private let pocketBaseService = PocketBaseService.shared
private init() {}
// API Response structures
struct BibleAPIResponse: Codable {
let data: VerseData
}
struct VerseData: Codable {
struct Verse: Identifiable, Codable {
let id: String
let orgId: String
let bibleId: String
let bookId: String
let chapterId: String
let reference: String
let content: String
let text: String
let isActive: Bool
// The API returns HTML content, so we'll clean it up
var cleanContent: String {
return content
.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression)
.replacingOccurrences(of: "&quot;", with: "\"")
.replacingOccurrences(of: "&#39;", 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)
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]?
func getRandomVerse() async throws -> (verse: String, reference: String) {
// List of popular and uplifting Bible verses
let references = [
//"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"
]
let verses = try await getVerses()
print("Total verses available: \(verses.count)")
// Randomly select a reference
let randomReference = references.randomElement() ?? "JHN.3.16"
guard let apiKey = configService.bibleApiKey else {
throw NSError(domain: "BibleService", code: -1, userInfo: [NSLocalizedDescriptionKey: "Bible API key not found"])
guard !verses.isEmpty else {
print("No verses available")
throw NSError(domain: "BibleService", code: -1, userInfo: [NSLocalizedDescriptionKey: "No verses available"])
}
// Construct the API URL
let urlString = "\(baseURL)/bibles/de4e12af7f28f599-01/verses/\(randomReference)"
var request = URLRequest(url: URL(string: urlString)!)
request.addValue(apiKey, forHTTPHeaderField: "api-key")
let randomVerse = verses.randomElement()!
print("Selected random verse: \(randomVerse.reference)")
return (verse: randomVerse.text, reference: randomVerse.reference)
}
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)
// Log raw response
if let rawResponse = String(data: data, encoding: .utf8) {
print("Raw Bible API Response:")
print(rawResponse)
print("\n")
guard let httpResponse = response as? HTTPURLResponse else {
print("Invalid response type")
throw URLError(.badServerResponse)
}
// Check for successful response
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw NSError(domain: "BibleService", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to fetch verse"])
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)
}
// Print raw response for debugging
if let rawResponse = String(data: data, encoding: .utf8) {
print("Raw response from PocketBase:")
print(rawResponse)
}
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:")
print(apiResponse.data.cleanContent)
print("\nReference:", apiResponse.data.reference)
print("\n")
// Cache the verses
cachedVerses = versesRecord.verses.verses
print("Successfully fetched and cached \(versesRecord.verses.verses.count) verses")
return (verse: apiResponse.data.cleanContent, reference: apiResponse.data.reference)
return versesRecord.verses.verses
} 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")
}
}

View file

@ -95,7 +95,7 @@ struct Bulletin: Identifiable, Codable {
class PocketBaseService {
static let shared = PocketBaseService()
private let baseURL = "https://pocketbase.rockvilletollandsda.church/api/collections"
let baseURL = "https://pocketbase.rockvilletollandsda.church/api/collections"
private init() {}

View file

@ -495,7 +495,7 @@ struct MoreView: View {
fallbackURL: AppAvailabilityService.AppStoreURLs.hymnal
)
} label: {
Label("SDA Hymnal", systemImage: "music.note")
Label("Adventist Hymnal", systemImage: "music.note")
}
}

125
bible_verses.json Normal file
View 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
View 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()