Swift Charts: How to prevent scroll position jump when loading more data dynamically

I'm implementing infinite scrolling with Swift Charts where additional historical data loads when scrolling near the beginning of the dataset. However, when new data is loaded, the chart's scroll position jumps unexpectedly.

Current behavior:

  • Initially loads 10 data points, displaying the latest 5
  • When scrolling backwards with only 3 points remaining off-screen, triggers loading of 10 more historical points
  • After loading, the scroll position jumps to the 3rd position of the new dataset instead of maintaining the current view

Expected behavior:

  • Scroll position should remain stable when new data is loaded
  • User's current view should not change during data loading

Here's my implementation logic using some mock data:

import SwiftUI
import Charts

struct DataPoint: Identifiable {
  let id = UUID()
  let date: Date
  let value: Double
}

class ChartViewModel: ObservableObject {
  @Published var dataPoints: [DataPoint] = []
  private var isLoading = false
  
  init() {
    loadMoreData()
  }
  
  func loadMoreData() {
    guard !isLoading else { return }
    
    isLoading = true
    
    let newData = self.generateDataPoints(
      endDate: self.dataPoints.first?.date ?? Date(),
      count: 10
    )
    
    self.dataPoints.insert(contentsOf: newData, at: 0)
    self.isLoading = false
    
    print("\(dataPoints.count) data points.")
  }
  
  private func generateDataPoints(endDate: Date, count: Int) -> [DataPoint] {
    var points: [DataPoint] = []
    let calendar = Calendar.current
    
    for i in 0..<count {
      let date = calendar.date(
        byAdding: .day,
        value: -i,
        to: endDate
      ) ?? endDate
      
      let value = Double.random(in: 0...100)
      points.append(DataPoint(date: date, value: value))
    }
    
    return points.sorted { $0.date < $1.date }
  }
}

struct ScrollableChart: View {
  @StateObject private var viewModel = ChartViewModel()
  @State private var scrollPosition: Date
  
  @State private var scrollDebounceTask: Task<Void, Never>?
  
  init() {
    self.scrollPosition = .now.addingTimeInterval(-4*24*3600)
  }
  
  var body: some View {
    Chart(viewModel.dataPoints) { point in
      BarMark(
        x: .value("Time", point.date, unit: .day),
        y: .value("Value", point.value)
      )
    }
    .chartScrollableAxes(.horizontal)
    .chartXVisibleDomain(length: 5 * 24 * 3600)
    .chartScrollPosition(x: $scrollPosition)
    .chartXScale(domain: .automatic(includesZero: false))
    .frame(height: 300)
    .onChange(of: scrollPosition) { oldPosition, newPosition in
      scrollDebounceTask?.cancel()
      
      scrollDebounceTask = Task {
        try? await Task.sleep(for: .milliseconds(300))
        
        if !Task.isCancelled {
          checkAndLoadMoreData(currentPosition: newPosition)
        }
      }
    }
  }
  
  private func checkAndLoadMoreData(currentPosition: Date?) {
    guard let currentPosition,
          let earliestDataPoint = viewModel.dataPoints.first?.date else {
      return
    }
    
    let timeInterval = currentPosition.timeIntervalSince(earliestDataPoint)
    
    if timeInterval <= 3 * 24 * 3600 {
      viewModel.loadMoreData()
    }
  }
}

I attempted to compensate for this jump by adding: scrollPosition = scrollPosition.addingTimeInterval(10 * 24 * 3600) after viewModel.loadMoreData(). However, this caused the chart to jump in the opposite direction by 10 days, rather than maintaining the current position.

What's the problem with my code and how to fix it?

Answered by DTS Engineer in 826609022

Our engineering teams need to investigate this issue, as resolution may involve changes to Apple's software. Please file a bug report, include a small Xcode project and some directions that can be used to reproduce the problem, and post the FB number here once you do. If you post the bug number here I'll check the status next time I do a sweep of forums posts where I've suggested bug reports.

Bug Reporting: How and Why? has tips on creating your bug report.

Our engineering teams need to investigate this issue, as resolution may involve changes to Apple's software. Please file a bug report, include a small Xcode project and some directions that can be used to reproduce the problem, and post the FB number here once you do. If you post the bug number here I'll check the status next time I do a sweep of forums posts where I've suggested bug reports.

Bug Reporting: How and Why? has tips on creating your bug report.

Hi, I've submitted a bug report as requested with the number FB16624502. The report includes a sample project. Thank you for escalating this to the engineering team.

Hi there. Thanks for filing a bug report. I have forwarded your bug report to engineering and discussed it with them. They have requested a movie demonstrating the problem as well as platform and version information.

And thanks for providing a sample. I tried that on iOS 18.3.1 but I guess you could call what I am seeing a jumping behavior. Looking at your code it appears you are using the scrolling position to decide when to load new chart data. Right now, I can't tell I it's the data being loaded that causes the jump or if it's the scrolling hitting the end of the loaded chart data and stopping before the new chart data is loaded that's causing the appearance of a jump. But, it's a great sample and I think it demonstrates your concern. I'll circle back and let you know anything else I'm able to find out.

Hi there,

I've updated bug report with demonstration videos and an updated Xcode project.

Environment details:

  • Xcode: Version 16.2 (16C5032a)
  • macOS: 15.3 (24D60) on Apple M2 Pro
  • Test device: iPhone 13 Pro running iOS 18.3.1 (22D72)

Please let me know if you need any additional information to help investigate this issue.

Thanks for the details. I added them to your bug report. I have discussed your bug with engineering and verified it has been filed in the right place. That's all I can tell you for now.

Please keep your bug up to date by adding the results of testing with any new system software updates and beta releases. You can get beta releases for testing through your developer account on developer.apple.com. Please see https://developer.apple.com/download/.

Swift Charts: How to prevent scroll position jump when loading more data dynamically
 
 
Q