The bug you're seeing is a classic race condition. Here's the sequence of events:
In updateUIView, your code detects that the book string has changed.
You set the new text with uiView.text = book.
Setting the text on a UITextView triggers a complex, asynchronous layout and rendering process. The view needs to calculate the size of the new text, figure out line breaks, etc. This does not happen instantly.
Your code then immediately tries to restore the offset using uiView.setContentOffset(...).
The problem: At this exact moment, uiView.contentSize has not yet been updated to reflect the full height of the new text. It might still have the old size, or a zero size, or some intermediate value.
When you scroll far down, your savedY is a large number (e.g., 20,000). But the maxYOffset you calculate is based on the incorrect, smaller contentSize (e.g., 500). Your clamping logic min(savedY, maxYOffset) then incorrectly clamps the offset to 500. A moment later, UITextView finishes its layout, the contentSize.height jumps to its correct final value (e.g., 50,000), but you've already scrolled to the wrong position.