I-Swift
I-Swift SDK iyatholakala nge-Swift Package Manager njengomkhiqizo FreeUseBibleAPI we-repository ye bible-api-swift .
Swift client for the public Free Use Bible API:
https://bible.helloao.org
It supports iOS 13+, macOS 10.15+, tvOS 13+, watchOS 6+, visionOS, and Linux, and has no dependencies.
Installation
Add the package to your Package.swift:
dependencies: [
.package(url: "https://github.com/HelloAOLab/bible-api-swift.git", from: "v1.16.0"),
],
targets: [
.target(
name: "MyApp",
dependencies: [
.product(name: "FreeUseBibleAPI", package: "bible-api-swift"),
]
),
]Or in Xcode, choose File > Add Package Dependencies… and enter https://github.com/HelloAOLab/bible-api-swift.git.
The versions match the releases of the Free Use Bible API.
Quick Start
import FreeUseBibleAPI
let api = FreeUseBibleAPI()
let available = try await api.getAvailableTranslations()
print("Total translations:", available.translations.count)
let books = try await api.getTranslationBooks(translation: "BSB")
print("Books in BSB:", books.books.count)
let chapter = try await api.getTranslationBookChapter(translation: "BSB", book: .GEN, chapter: 1)
print("Verses in Genesis 1:", chapter.numberOfVerses)Client Options
let api = FreeUseBibleAPI(
endpoint: URL(string: "https://bible.helloao.org/")!,
useCache: true,
transport: URLSessionTransport(session: .shared)
)endpoint: Base API endpoint.useCache: Enables in-memory response caching (default:true). Concurrent requests for the same URL share a single request.transport: Performs the HTTP requests (default:URLSessionTransportwithURLSession.shared). Implement theFreeUseBibleAPITransportprotocol to use a different HTTP client, add logging, or return canned responses in tests.
Every request method also takes an optional endpoint: argument to use a different endpoint for a single request.
API Methods
All of the request methods are async throws.
Translations
getAvailableTranslations()getTranslationBooks(translation:)getTranslationBookChapter(translation:book:chapter:)getTranslationBookChapterAudioTimings(translation:book:chapter:reader:)getTranslationBookChapterWords(translation:book:chapter:)getCompleteTranslation(translation:)getSimpleTranslationBookChapter(translation:book:chapter:)getSimpleTranslationBookChapterWords(translation:book:chapter:)getSimpleCompleteTranslation(translation:)
getCompleteTranslation() and getSimpleCompleteTranslation() are never cached because their responses are very large.
Only some chapters have audio timings. The keys of a chapter's thisChapterAudioTimings property identify the readers available to pass to getTranslationBookChapterAudioTimings().
Commentaries
getAvailableCommentaries()getCommentaryBooks(commentary:)getCommentaryBookChapter(commentary:book:chapter:)getSimpleCommentaryBookChapter(commentary:book:chapter:)
Datasets
getAvailableDatasets()getDatasetBooks(dataset:)getDatasetBookChapter(dataset:book:chapter:)- returns anApiDatasetBookChapterResponse, which is either.datasetBookChapter(cross references for cross reference datasets, or the original-language words of each verse for interlinear datasets such asbsb-interlinear) or.datasetEntityBookChapter(the people, places, and events that appear in the chapter for entity datasets such astheographic)getDatasetPeople(dataset:)getDatasetPerson(dataset:person:)getDatasetPlaces(dataset:)getDatasetPlace(dataset:place:)getDatasetEvents(dataset:)getDatasetEvent(dataset:event:)getDatasetPeopleGroups(dataset:)getDatasetPeopleGroup(dataset:group:)getDatasetLexicon(dataset:)getDatasetLexiconEntry(dataset:strongs:)getDatasetVerses(dataset:)getVerseOfTheDay(date:seed:category:dataset:)- see Verse of the DaygetDatasetCategories(dataset:)getDatasetCategory(dataset:category:)
Chapter Navigation Helpers
getNextChapter(_:)/getPreviousChapter(_:)- work with any chapter response, and return the same type of chapter (ornilat the start or end of the translation).getFirstChapter(_:)/getLastChapter(_:)- get the first or last chapter of a translation, commentary, or dataset book.getSimpleChapter(_:)- follows a chapter'ssimpleChapterApiLink, or returnsnilif simplified chapters aren't available for it.
Chapter & Verse Helpers
getVerseText(_:)getChapterVerseText(_:options:)getSimpleChapterVerseText(_:options:)formatReference(_:_:)
Verse of the Day
getVerseOfTheDay(date:seed:category:dataset:)- gets the verse of the day for a date (today by default) from theverse-of-the-daydataset. The date is aYYYY-MM-DDstring or aDate(its calendar date in the current time zone is used).pickVerseOfTheDay(_:date:seed:category:holidays:)- picks the verse of the day from a list of verses you already have, such as theversesandholidaysfromgetDatasetVerses(dataset:).
The verse is picked by the Verse of the Day Convention, so every SDK picks the same verse on the same day. The same date always gives the same verse, a verse doesn't repeat within a calendar year, and holidays (such as Christmas and Resurrection Sunday) get verses of their own. Pass a seed (such as the name of your app) to get a different order with the same guarantees.
Word Annotations
Some translations include word-level annotations (Strong's numbers and related source data) for their chapters.
getChapterWords(_:)/getSimpleChapterWords(_:)- get the annotations for a chapter you already loaded, ornilif it doesn't have any.getWordText(_:_:)/getSimpleWordText(_:_:)- get the text that a single annotation applies to.getVerseWords(_:_:)/getSimpleVerseWords(_:_:)- get the annotations for a verse, each paired with the text it applies to.
The offsets in annotations (and in simplified chapters) count UTF-16 code units, like JavaScript string indexes. The helpers take care of this for you.
Types
The response types are generated from the API's OpenAPI document and use the same names as the schemas in it (ApiTranslationBookChapter, ChapterVerse, and so on). They are all Codable, Hashable, and Sendable.
- Content that can be one of several kinds is an enum. For example,
ChapterContentis.heading,.lineBreak,.verse, or.hebrewSubtitle, and the items in a verse'scontentare.text,.formattedText,.inlineHeading,.inlineLineBreak, or.verseFootnoteReference. - Values with a fixed set of options, like
BookIdandTextDirection, are structs with a static property for each known value (BookId.GEN,BookId._1SA), and can also be created from string literals ("GEN"). - Values that are newer than the SDK are kept instead of failing to decode: enums have an
.unknown(JSONValue)case, and option values keep theirrawValue.
Examples
Print the text of a chapter
let chapter = try await api.getTranslationBookChapter(translation: "BSB", book: .JHN, chapter: 1)
print(api.getChapterVerseText(chapter))
// John 1
// [1] In the beginning was the Word, and the Word was with God, and the Word was God. ...Read the verses of a chapter
let chapter = try await api.getTranslationBookChapter(translation: "BSB", book: .GEN, chapter: 1)
for content in chapter.chapter.content {
switch content {
case .heading(let heading):
print("##", heading.content.joined(separator: " "))
case .verse(let verse):
print(verse.number, api.getVerseText(verse))
default:
break
}
}Navigate to the next/previous chapter
let current = try await api.getTranslationBookChapter(translation: "BSB", book: .GEN, chapter: 1)
let next = try await api.getNextChapter(current)
let previous = try await api.getPreviousChapter(current)
print(next?.chapter.number as Any) // Optional(2)
print(previous?.chapter.number as Any) // nilRead the Strong's numbers for a chapter
let chapter = try await api.getTranslationBookChapter(translation: "engwebp", book: .JHN, chapter: 1)
if let words = try await api.getChapterWords(chapter) {
for case .verse(let verse) in chapter.chapter.content {
for annotated in api.getVerseWords(verse, words) {
print(annotated.text, annotated.word.strongs ?? [])
}
}
}
// In ["G1722"]
// the ["G1722"]
// beginning ["G0746"]
// ...Read a dataset chapter
let chapter = try await api.getDatasetBookChapter(dataset: "open-cross-ref", book: .JHN, chapter: 3)
if case .datasetBookChapter(let crossReferences) = chapter {
for case .references(let verse) in crossReferences.chapter.content {
print(verse.verse, verse.references.count)
}
}Get the verse of the day
if let verse = try await api.getVerseOfTheDay(date: "2026-12-25") {
let chapter = try await api.getTranslationBookChapter(translation: "BSB", book: verse.book, chapter: verse.chapter)
for case .verse(let content) in chapter.chapter.content where (verse.verse...(verse.endVerse ?? verse.verse)).contains(content.number) {
print(content.number, api.getVerseText(content))
}
}Error Handling
Request methods throw FreeUseBibleAPIError:
.requestFailed(url:statusCode:)for non-2xx responses. A 404 usually means that one of the path values is invalid, for example the translation, commentary, dataset, book, or chapter..decodingFailed(url:underlyingError:)if the response can't be decoded..invalidURL(_:)if a request URL can't be built.
Errors from the transport (such as URLError for network failures) are thrown as they are.