Skip to content

refactor(reader): model scroll intent as a single ScrollAction BL-1901 - #190

Merged
solomonstorts merged 2 commits into
ss/drive-reader-from-outside-BL-1901from
ae/scroll-action
Jul 10, 2026
Merged

solomonstorts merged 2 commits into
ss/drive-reader-from-outside-BL-1901from
ae/scroll-action

Conversation

@andrewse02

@andrewse02 andrewse02 commented Jul 10, 2026 •

Copy link
Copy Markdown
Member

What changes

  • Single source of truth for scroll intent. Folds scrollToTop: Bool + scrollTargetReference: BibleReference? into one ScrollAction enum (.none / .top / .toVerse). The two were mutually exclusive but independently mutable, so setScrollTarget() had to hand-maintain the invariant. As one value they can't get out of sync — and it closes the same-run-loop coalescing gap where scrollToTop = true (in onHeaderSelectionChange) was overwritten by scrollToTop = false (in setScrollTarget) before SwiftUI observed it. scrollTargetReference stays as a read-only derived accessor, so the coordinator and tests read unchanged.
  • Single exit point. Adds finishChapterChange() (clears the armed scroll + resets isChangingChapter) as the one place a navigation returns to rest. The coordinator's clearScrollState() and goToReference's error branch both route through it instead of poking isChangingChapter/clearScrollTarget() independently.
  • One onChange handler. The view's two handlers collapse into a single exhaustive switch on scrollAction.

Intentionally left alone

The Task.sleep(0.5s) scroll-to-top settle — it exists to ignore scroll-offset changes during the programmatic scroll animation (SwiftUI gives no scroll-completion callback), and fully event-driving it wants device testing. Flagged as a possible follow-up.

No public API change

All changes are internal/private on BibleReaderViewModel (a final internal class); ScrollAction is internal. API-stability gate unaffected.

Verification

  • Reader unit suites green (VerseScroll, Navigation, NavigationState, Interaction); SwiftLint --strict clean.
  • Driven end-to-end in the SampleApp on the simulator: full-chapter verse scroll (John 3:16), verse-range (Romans 8:28), whole chapter (Genesis 1), chapter nav, cold-launch restore (full chapter preserved), and chrome expand/collapse on scroll — including confirming isChangingChapter is released after the verse-scroll path, which is exactly the single-exit guarantee.

Totally your call whether to take it, adapt it, or toss it. Relates to BL-1901; follows up the review thread on #174.

Note: branch is named ae/scroll-action; happy to rename to the BL-1901-... convention if you'd prefer before merge.

Greptile Summary

This PR refactors the reader's scroll intent into a single ScrollAction enum (.none / .top / .toVerse), replacing the two independently-mutable fields scrollToTop: Bool and scrollTargetReference: BibleReference?. The key win is eliminating a same-run-loop coalescing bug where a .top write could be overwritten by a .toVerse arm before SwiftUI observed it, and introducing finishChapterChange() as the single exit point that returns the reader to rest.

  • ScrollAction enum eliminates the "can't arm both or neither" invariant that setScrollTarget() previously maintained by hand; scrollTargetReference is retained as a read-only computed accessor so existing callsites (coordinator, tests) are unaffected.
  • finishChapterChange(clearingScroll:) unifies what were three independent mutation sites into one function; the clearingScroll: false overload correctly protects a re-armed scroll during the 0.5 s animation settle window.
  • Single onChange(of: viewModel.scrollAction, initial: true) replaces two separate handlers and adds initial: true coverage for the cold-launch state, closing a minor gap in the previous implementation.

Confidence Score: 5/5

Clean refactor with no regressions; all scroll paths reviewed and the single-exit invariant holds throughout.

All scroll intent mutations now flow through ScrollAction and finishChapterChange(). The clearingScroll: false path correctly guards against clobbering a re-armed scroll during the async settle Task. The initial: true addition on the merged onChange is a net improvement. Unit suites cover the affected navigation paths.

No files require special attention.

Important Files Changed

Filename Overview
Sources/YouVersionPlatformReader/ViewModels/BibleReaderViewModel.swift Introduces ScrollAction enum and replaces two independent fields with a single source of truth; adds clearScrollAction() and finishChapterChange(clearingScroll:) as the unified resting-state exit point.
Sources/YouVersionPlatformReader/ViewModels/BibleReaderViewModel+Navigation.swift All scrollToTop = true sites replaced with scrollAction = .top; error-branch isChangingChapter = false in goToReference replaced with finishChapterChange(). Consistent and correct.
Sources/YouVersionPlatformReader/BibleReaderView.swift Two onChange handlers collapsed into one exhaustive switch over scrollAction with initial: true; finishChapterChange(clearingScroll: false) correctly used in the 0.5 s settle Task.
Sources/YouVersionPlatformReader/VerseScrollCoordinator.swift clearScrollState() simplified to a single finishChapterChange() call, removing the independent two-field mutation. No logic changes.
Tests/YouVersionPlatformReaderTests/BibleReaderViewModel+NavigationTests.swift scrollToTop assertions replaced with scrollAction == .top / scrollAction == .none expectations. Coverage unchanged.
Tests/YouVersionPlatformReaderTests/BibleReaderViewModelNavigationStateTests.swift Updated to assert scrollAction == .top after onHeaderSelectionChange; aligns correctly with the new model.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Nav as Navigation
    participant VM as BibleReaderViewModel
    participant View as BibleReaderView
    participant VSC as VerseScrollCoordinator

    Note over Nav,VSC: Chapter navigation (scroll to top)
    Nav->>VM: "scrollAction = .top"
    VM-->>View: onChange fires (.top)
    View->>View: scrollProxy.scrollTo(topOfContent)
    View->>VM: clearScrollAction()
    View->>View: Task.sleep(0.5s)
    View->>VM: finishChapterChange(clearingScroll: false)

    Note over Nav,VSC: Verse navigation
    Nav->>VM: "onHeaderSelectionChange -> scrollAction = .top"
    Nav->>VM: "setScrollTarget() -> scrollAction = .toVerse(ref)"
    VM-->>View: onChange fires (.toVerse)
    View->>VSC: handleScrollTarget(proxy)
    VSC->>VM: finishChapterChange()

    Note over Nav,VSC: Guard branch (reference mismatch)
    Nav->>VM: goToReference - guard fails
    VM->>VM: finishChapterChange()
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Nav as Navigation
    participant VM as BibleReaderViewModel
    participant View as BibleReaderView
    participant VSC as VerseScrollCoordinator

    Note over Nav,VSC: Chapter navigation (scroll to top)
    Nav->>VM: "scrollAction = .top"
    VM-->>View: onChange fires (.top)
    View->>View: scrollProxy.scrollTo(topOfContent)
    View->>VM: clearScrollAction()
    View->>View: Task.sleep(0.5s)
    View->>VM: finishChapterChange(clearingScroll: false)

    Note over Nav,VSC: Verse navigation
    Nav->>VM: "onHeaderSelectionChange -> scrollAction = .top"
    Nav->>VM: "setScrollTarget() -> scrollAction = .toVerse(ref)"
    VM-->>View: onChange fires (.toVerse)
    View->>VSC: handleScrollTarget(proxy)
    VSC->>VM: finishChapterChange()

    Note over Nav,VSC: Guard branch (reference mismatch)
    Nav->>VM: goToReference - guard fails
    VM->>VM: finishChapterChange()
Loading

Reviews (2): Last reviewed commit: "refactor(reader): route scroll-to-top se..." | Re-trigger Greptile

Replace the scrollToTop flag + scrollTargetReference pair with a single
ScrollAction enum (.none/.top/.toVerse) as the source of truth, removing the
same-run-loop coalescing hazard where scrollToTop=true was overwritten before
SwiftUI observed it. Add finishChapterChange() as the single navigation exit and
route the coordinator's clearScrollState() and goToReference's error path through
it. Collapse the two scroll onChange handlers into one. No public API change.
@github-actions

github-actions Bot commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

✅ Commit Lint: passed

All commit messages in this PR conform to Conventional Commits.

📦 Release preview: v5.2.3 (no bump)

These commits do not trigger a version bump. The published version on main would remain v5.2.3.

Release analyzer output
[analyzer] Analyzing commit: refactor(reader): model scroll intent as a single ScrollAction BL-1901

Replace the scrollToTop flag + scrollTargetReference pair with a single
ScrollAction enum (.none/.top/.toVerse) as the source of truth, removing the
same-run-loop coalescing hazard where scrollToTop=true was overwritten before
SwiftUI observed it. Add finishChapterChange() as the single navigation exit and
route the coordinator's clearScrollState() and goToReference's error path through
it. Collapse the two scroll onChange handlers into one. No public API change.
[analyzer] The commit should not trigger a release
[analyzer] Analyzing commit: refactor(reader): route scroll-to-top settle through finishChapterChange BL-1901
[analyzer] The commit should not trigger a release
[analyzer] Analysis of 2 commits complete: no release

@andrewse02
andrewse02 marked this pull request as ready for review July 10, 2026 14:32
@andrewse02
andrewse02 requested a review from solomonstorts July 10, 2026 14:32
Comment thread Sources/YouVersionPlatformReader/BibleReaderView.swift
Comment thread Sources/YouVersionPlatformReader/BibleReaderView.swift
@github-actions

Copy link
Copy Markdown
Contributor

Code Coverage Report

Coverage after merging ae/scroll-action into ss/drive-reader-from-outside-BL-1901 will be
92.10%
Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
/Users/runner/work/platform-sdk-swift/platform-sdk-swift/DerivedData/Build/Intermediates.noindex/YouVersionPlatform.build/Debug-iphonesimulator/YouVersionPlatformCoreTests.build/DerivedSources
   resource_bundle_accessor.swift87.50%100%75%88.89%19, 44–45
/Users/runner/work/platform-sdk-swift/platform-sdk-swift/Sources/YouVersionPlatformCore
   YouVersionPlatformConfiguration.swift90.99%100%80%92.71%145–147, 39–41
/Users/runner/work/platform-sdk-swift/platform-sdk-swift/Sources/YouVersionPlatformCore/APIs
   URLBuilder.swift88.70%100%90%88.54%110–119, 12–19
   URLRequest+YouVersion.swift100%100%100%100%
   YouVersionAPI.swift92.93%100%90%93.26%10–11, 75, 83, 85, 9
/Users/runner/work/platform-sdk-swift/platform-sdk-swift/Sources/YouVersionPlatformCore/APIs/Bible
   BibleVersionAPI.swift73.50%100%66.67%74.51%118–132, 37–39, 58, 65–67, 75, 87
   BibleVersions.swift93.06%100%100%92.06%32, 54, 60–61, 83
   Highlights.swift77.71%100%59.09%80.39%109–116, 158, 161, 180, 183, 212, 215, 223, 227, 260–262, 63, 67, 79, 97
/Users/runner/work/platform-sdk-swift/platform-sdk-swift/Sources/YouVersionPlatformCore/APIs/DataExchange
   DataExchange.swift97.37%100%100%97.14%39
/Users/runner/work/platform-sdk-swift/platform-sdk-swift/Sources/YouVersionPlatformCore/APIs/Languages
   Languages.swift92.75%100%85.71%93.55%47–49, 79
/Users/runner/work/platform-sdk-swift/platform-sdk-swift/Sources/YouVersionPlatformCore/APIs/Organizations
   Organizations.swift37.04%100%33.33%37.50%29–39, 41–43, 51
/Users/runner/work/platform-sdk-swift/platform-sdk-swift/Sources/YouVersionPlatformCore/APIs/Users
   SignInWithYouVersionPKCEAuthorizationRequest.swift81.25%100%73.68%82.40%100, 111, 122–129, 137, 75–81, 99
   SignInWithYouVersionPermission.swift69.77%100%60%71.05%18, 20, 22, 26, 39, 50–51, 53–56
   SignInWithYouVersionResult.swift39.68%100%20%43.40%15–17, 44–56, 83–91
   Users.swift74.13%100%67.74%75%112, 150–156, 16, 161, 17, 171, 18–20, 208, 21, 211, 228, 234, 252–254, 278–280, 282–284, 286–288, 290–292, 297–299, 31–40, 44, 61, 67–71, 85
   YouVersionUserInfo.swift70.59%100%50%73.33%18, 29–31
/Users/runner/work/platform-sdk-swift/platform-sdk-swift/Sources/YouVersionPlatformCore/APIs/VOTD
   VOTD.swift94.12%100%100%93.33%22
   YouVersionVerseOfTheDay.swift0%100%0%0%15–18, 20–25
/Users/runner/work/platform-sdk-swift/platform-sdk-swift/Sources/YouVersionPlatformCore/Bible
   BibleBook.swift100%100%100%100%
   BibleChapterRepository.swift85.26%100%82.76%85.83%111–113, 42, 49, 54–57, 65–68, 83
   BibleContentStorage.swift100%100%100%100%
   BibleHighlight.swift55.56%100%50%57.14%12–14
   BibleHighlightsCache.swift86.71%100%83.33%87.61%103–105, 117–118, 62, 65–67, 72
   BibleHighlightsRepository.swift77.78%100%69.23%78.98%144–148, 19–21, 220–221, 223–227, 23–25, 253–254, 256–260, 27–29, 31–32, 328–329, 33, 330–354, 381–383, 407–411
   BibleHighlightsViewModel.swift90.68%100%85.71%91.43%33–36, 66, 69, 73
   BibleReference.swift83.37%100%66.10%86.10%136, 231–232, 256, 260, 293, 306, 316, 326, 336, 338–349, 53–57, 67–69, 88, 90, 92
   BibleTextNode.swift92.86%100%75%95.83%
   BibleTextNodeParser.swift94.29%100%100%93.79%104, 150, 155, 173, 190, 44, 56–59
   BibleVersion.swift70.10%100%86.67%67.07%107–126, 61, 71–74, 80
   BibleVersionRepository.swift86.30%100%83.02%87.03%125–128, 173, 212–217, 243, 248, 276–278, 71–74, 82–85
   DeprecatedBibleAPI.swift0%100%0%0%100, 102–104, 106–108, 110–112, 114–116, 119–121, 126, 128, 131–133, 138, 140, 142–144, 147–149, 27, 29–31, 36, 38, 40–42, 44–46, 48–50, 52–54, 56–58, 65–67, 69–71, 73–75, 77–79, 81–83, 85–87, 94–96, 98–99
/Users/runner/work/platform-sdk-swift/platform-sdk-swift/Sources/YouVersionPlatformCore/Logging
   YouVersionPlatformLogger.swift62.50%100%50%65.91%100, 56–58, 77–79, 90, 92, 94, 98
/Users/runner/work/platform-sdk-swift/platform-sdk-swift/Sources/YouVersionPlatformCore/Utilities
   AbbreviationSplitting.swift100%100%100%100%
/Users/runner/work/platform-sdk-swift/platform-sdk-swift/Tests/YouVersionPlatformCoreTests
   AbbreviationSplittingTests.swift100%100%100%100%
   BibleChapterRepositoryTests.swift98.68%100%94.12%99.25%
   BibleContentStorageTests.swift100%100%100%100%
   BibleHighlightsCacheTests.swift100%100%100%100%
   BibleHighlightsRepositoryTests.swift99.63%100%100%99.60%77, 87
   BibleHighlightsViewModelTests.swift94.06%100%92.11%94.34%19–25, 30, 38, 46, 85–87, 89–90
   BibleMetadataAPITests.swift100%100%100%100%
   BibleReferenceTests.swift100%100%100%100%
   BibleReferenceTests_adjacency.swift100%100%100%100%
   

@andrewse02
andrewse02 requested a review from solomonstorts July 10, 2026 18:21
@solomonstorts
solomonstorts merged commit 6397fca into ss/drive-reader-from-outside-BL-1901 Jul 10, 2026
7 checks passed
@solomonstorts
solomonstorts deleted the ae/scroll-action branch July 10, 2026 19:42
solomonstorts pushed a commit that referenced this pull request Jul 13, 2026
#190)

* refactor(reader): model scroll intent as a single ScrollAction BL-1901

Replace the scrollToTop flag + scrollTargetReference pair with a single
ScrollAction enum (.none/.top/.toVerse) as the source of truth, removing the
same-run-loop coalescing hazard where scrollToTop=true was overwritten before
SwiftUI observed it. Add finishChapterChange() as the single navigation exit and
route the coordinator's clearScrollState() and goToReference's error path through
it. Collapse the two scroll onChange handlers into one. No public API change.

* refactor(reader): route scroll-to-top settle through finishChapterChange BL-1901
solomonstorts pushed a commit that referenced this pull request Jul 13, 2026
#190)

* refactor(reader): model scroll intent as a single ScrollAction BL-1901

Replace the scrollToTop flag + scrollTargetReference pair with a single
ScrollAction enum (.none/.top/.toVerse) as the source of truth, removing the
same-run-loop coalescing hazard where scrollToTop=true was overwritten before
SwiftUI observed it. Add finishChapterChange() as the single navigation exit and
route the coordinator's clearScrollState() and goToReference's error path through
it. Collapse the two scroll onChange handlers into one. No public API change.

* refactor(reader): route scroll-to-top settle through finishChapterChange BL-1901
solomonstorts pushed a commit that referenced this pull request Jul 16, 2026
#190)

* refactor(reader): model scroll intent as a single ScrollAction BL-1901

Replace the scrollToTop flag + scrollTargetReference pair with a single
ScrollAction enum (.none/.top/.toVerse) as the source of truth, removing the
same-run-loop coalescing hazard where scrollToTop=true was overwritten before
SwiftUI observed it. Add finishChapterChange() as the single navigation exit and
route the coordinator's clearScrollState() and goToReference's error path through
it. Collapse the two scroll onChange handlers into one. No public API change.

* refactor(reader): route scroll-to-top settle through finishChapterChange BL-1901
solomonstorts pushed a commit that referenced this pull request Jul 17, 2026
#190)

* refactor(reader): model scroll intent as a single ScrollAction BL-1901

Replace the scrollToTop flag + scrollTargetReference pair with a single
ScrollAction enum (.none/.top/.toVerse) as the source of truth, removing the
same-run-loop coalescing hazard where scrollToTop=true was overwritten before
SwiftUI observed it. Add finishChapterChange() as the single navigation exit and
route the coordinator's clearScrollState() and goToReference's error path through
it. Collapse the two scroll onChange handlers into one. No public API change.

* refactor(reader): route scroll-to-top settle through finishChapterChange BL-1901
solomonstorts added a commit that referenced this pull request Jul 17, 2026
…#174)

* feat(reader): drive the reader to a passage via BibleReaderNavigation

Add BibleReaderNavigation, a shared observable a host app uses to move an
on-screen reader to a new passage from elsewhere — e.g. a "Read" button in
another tab. Callers `request(reference, showsFullChapter:)`; the reader picks
up the pending reference, loads that chapter in place via goToReference, and
scrolls to the verse (building on the verse-scroll support from the prior
commit).

Adds the navigation init parameter to BibleReaderView, a Navigate tab to the
sample app demonstrating it, and README docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(reader): persist showsFullChapter across cold launch BL-1901

A reader restored from storage (no explicit reference) dropped its
showsFullChapter intent, so a passage last viewed as a full chapter
came back as just its verse range after a cold launch.

- Persist showsFullChapter to UserDefaults alongside reference
- Restore it on the cold-launch branch of init; no verse scroll is
  armed on restore, so a user who scrolled within the chapter is not
  pulled back to the saved verse
- Add persistence tests and clear the new key in test teardown

Co-authored-by: Claude (AI Assistant)

* docs(reader): fix navigation param label in README BL-1901

The BibleReaderView example used the old `navigation:` label; the
parameter is `readerNavigation:`, so the snippet would not compile.

Co-authored-by: Claude (AI Assistant)

* fix(reader): release isChangingChapter after a verse scroll BL-1901

A full-chapter verse navigation (goToReference) armed a verse scroll,
which suppresses the chapter-top scroll by setting scrollToTop back to
false in the same run-loop turn. SwiftUI never observed the transient
scrollToTop = true, so the onChange handler that clears isChangingChapter
never fired and the flag stayed true — wedging handleScroll and leaving
the header unresponsive to scrolling.

- Clear isChangingChapter from VerseScrollCoordinator when the pending
  scroll resolves (landed, abandoned, or timed out), the moment that
  actually ends the chapter change for a verse scroll
- Consolidate the scroll teardown into a single clearScrollState()
- Assert isChangingChapter stays engaged until the scroll settles

Co-authored-by: Claude (AI Assistant)

* fix(reader): handle failed chapter load in goToReference BL-1901

goToReference committed showsFullChapter and armed the verse scroll
around an await that can fail. On a cross-version navigation whose
version fetch fails, onHeaderSelectionChange swallows the error and
leaves the old reference in place, which caused three issues: the verse
scroll armed against a stale reference, the new showsFullChapter value
was committed (and persisted) despite the failed load, and
isChangingChapter stayed true — permanently gating handleScroll and
freezing the header chrome.

- Reorder so onHeaderSelectionChange runs first; commit showsFullChapter
  and arm the scroll only once the requested chapter actually loaded
- Reset isChangingChapter in the failure branch so the header stays
  responsive
- Assert showsFullChapter and isChangingChapter in the failure test

Co-authored-by: Claude (AI Assistant)

* refactor(reader): model scroll intent as a single ScrollAction BL-1901 (#190)

* refactor(reader): model scroll intent as a single ScrollAction BL-1901

Replace the scrollToTop flag + scrollTargetReference pair with a single
ScrollAction enum (.none/.top/.toVerse) as the source of truth, removing the
same-run-loop coalescing hazard where scrollToTop=true was overwritten before
SwiftUI observed it. Add finishChapterChange() as the single navigation exit and
route the coordinator's clearScrollState() and goToReference's error path through
it. Collapse the two scroll onChange handlers into one. No public API change.

* refactor(reader): route scroll-to-top settle through finishChapterChange BL-1901

* refactor(reader): apply review feedback on ScrollAction BL-1901

- Reword the ScrollAction doc comment to describe the type rather than
  the old scrollToTop/scrollTargetReference pattern it replaced
- Rename the verse case from .toVerse to .reference
- Say "reference" instead of "verse" in the scrollTargetReference doc

Co-authored-by: Claude (AI Assistant)

* fix: address review comments

* refactor(reader): split reader init into explicit and restore paths BL-1901

BibleReaderView previously took an optional reference plus a
showsFullChapter flag, and silently ignored the flag in favor of the
persisted value whenever reference was nil. Replace that shape with two
coherent entry points:

- init(reference:...) now requires a non-optional reference, so a
  display mode can no longer be passed without the reference it
  describes
- BibleReaderView.restoringLastPassage(...) is a self-describing
  factory that restores the last-viewed passage and its display mode
  (or falls back to John 1), with no showsFullChapter parameter to
  conflict with the restored state

BibleReaderViewModel mirrors the split with two convenience inits
delegating to a private designated init that keeps the original
resolution logic. No behavior change; the sample app adopts the
factory for its reader tab.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(reader): keep BibleReaderView(reference:) source-compatible BL-1901

Bryan's init split made BibleReaderView(reference:) non-optional, which
would have broken existing callers who relied on the nil default (e.g.
BibleReaderView(), BibleReaderView(onVerseTap:)). Restore source
compatibility instead of shipping a breaking change:

- Re-add the original init(reference: BibleReference? = nil, ...) as a
  deprecated overload that forwards to the designated init. Callers keep
  compiling; the deprecation message steers them to a non-optional
  reference or BibleReaderView.restoringLastPassage().
- Update README examples to use restoringLastPassage() for the restore case.

Verified with swift-api-digester: no breaking changes against the shipped
baseline, so this remains a minor release. The baseline file is refreshed
separately in the post-release chore, per repo convention (e.g. commit
051a318 "chore: update api-baseline files after 5.3.0 release").

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(reader): rename clear-request test to a verb phrase BL-1901

Rename clearsPendingRequest() to clearPendingRequestSetsItToNil() so the
test name reads as an action and outcome, matching the sibling tests
(requestSetsPendingRequest, requestDefaultsToVerseRange).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Andrew Elliott <26266444+andrewse02@users.noreply.github.com>
Co-authored-by: Bryan Montz <bryan.montz@youversion.com>
jhampton pushed a commit that referenced this pull request Jul 27, 2026
## [5.4.0](5.3.0...5.4.0) (2026-07-27)

### Features

* add backoff to refresh token process ([fbed37a](fbed37a))
* adjust highlight colors when in dark mode ([1f0e6b6](1f0e6b6))
* **reader:** drive the reader to a passage via BibleReaderNavigation ([#174](#174)) ([8b6382a](8b6382a)), closes [#190](#190)
* **reader:** focus a verse in the Bible reader BL-1901 ([#200](#200)) ([21caa5a](21caa5a))

### Bug Fixes

* avoid data race while refreshing tokens ([b771cdc](b771cdc))
* language names list prefers the iOS name, but also sends Accept-Language ([193f47f](193f47f))
* split wall-of-text chapters into 10-verse chunks when necessary ([57053ff](57053ff))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants