Wesley de Groot's Blog
Fixing slow scrolling in Calendo

Back

In this post, I'll go through the steps which i took to fix a bug with slow scrolling in Calendo.
Interested in the development of Calendo? Read my previous post about Building Calendo or search on the Tag: Calendo.

What was the problem?

When you scrolled the calendar in Calendo, the scrolling was slow and laggy.
This was a problem because it made the app feel unresponsive and slow.

In this post, I explain how I fixed this problem and which steps I have taken to fix this.

Debugging... (pre-release)

first suspect: The ScrollView

The ScrollView contains a list with appointments (-1 month to +1 month) from the currently selected month.
The cells where build like this:

┌──────────────────────────────────┐
│ Date string              ┌─────┐ │
│ Appointment title        │ MAP │ │
│ Appointment Address      └─────┘ │
└──────────────────────────────────┘

I was thinking that the MapView was causing the issue, because it was loading the map every time the cell was shown.

second suspect: The CalendarView

Is the CalendarView causing the issue?
The CalendarView is pretty easy, it's just a grid with the days of the month, and a small circle that indicates the amount of appointments on that day.
This can't be the issue.

Partial solution (pre-release)

I ended up removing the MapView, to increase the speed on older devices, it did not had the magical effect that I was hoping for, but at that moment I was thinking, every little bit helps.

The New Appointment cell looked like this:

┌──────────────────────────────────┐
│ Date string                      │
│ Appointment title                │
│ Appointment Address              │
└──────────────────────────────────┘

Debugging...

Or actually thinking, because I had no points to investigate, or maybe a better explanation, I had no idea where to start.

  • Too much appointments?
    Could the issue be that there are too many appointments in the calendar?
  • Is the day indicator causing the issue?
    The day indicator is a small circle that shows the current day in the calendar. Could this be causing the issue?
    If the day indicator is hidden, scrolling is snappy and fast, this is what made me think that the day indicator was causing the issue.

Problem avoiding

  • Rebuilt CalendarView
    This improved the performance a little bit, but not enough to make it feel snappy.

  • Added a setting to disable certain calendars.
    This made loading faster if you had a lot of calendars, but it did not solve the issue.

  • Added a setting to disable the day indicator.
    This made the scrolling snappy and fast, but it was not a solution, because the day indicator is a key feature in the calendar.

Bonus find

Sometimes EKEventStore was complaining that i was running to much connections to the calendar.
After some investigation, I found out that I was constantly re-initializing the EKEventStore (when Mocking the calendar), I fixed this to make a global variable to store the EKEventStore in memory.

This was as simple as:

/// Global event store
public let globalEventStore = EKEventStore()

More Debugging...

first suspect: The ScrollView

I tried to fire notifications instead of updating the @State/@EnvironmentObject in the ScrollView cells, this did not solve the issue.

second suspect: The CalendarView

I was too focused on the day indicator (I did not see the real issue)

third suspect: The CalendarView day indicator

I was thinking that the day indicator was causing the issue, because when I disabled the day indicator, the scrolling was fast and snappy.
Below the day(number) in the month, there is a small circle that indicates the amount of appointments on that day.

For debugging purposes, i was thinking maybe the issue is that I look up the appointments on the main thread, so i added this little piece of code to the CalendarView's cell:

extension Date {
    var appointments: [Color] {
        let eventStore = globalEventStore
        var events: [Color] = []

// DEBUG CODE START
#if targetEnvironment(simulator) 
        for _ in 0...Int.random(in: 0...3) {
            events.append(
                Color.init(cgColor: Color.random.cgColor!)
            )
        }
        return events
#endif
// DEBUG CODE END

        /// Some code to get the appointments
        return events
    }
}

Something strange happend, every time I scrolled the calendar, the colors where changing, this was not what I was expecting, because the appointments should be the same every time I scroll the calendar.

Solution

var appointmentStore: [Date: [Color]] = [:]

extension Date {
    var appointments: [Color] {
        if let appointments = appointmentStore[self] {
            return appointments
        }

        let eventStore = globalEventStore
        var events: [Color] = []

        /// Some code to get the appointments
        appointmentStore[self] = events
        return events
    }
}

Conclusion

The solution was to cache the appointments for each day in the month, this made the scrolling fast and snappy.
I learned that you should not focus on one thing, but look at the bigger picture, and try to find the root cause of the issue.
I stared myself blind on the day indicator, but the real issue was the appointments that where loaded every time the cell was shown.

I hope this post helps you to find the root cause of your issue, and that you can fix it as well.

Download Calendo

You can download Calendo from the AppStore!.

Read more

Share


Share Bluesky Mastodon Twitter LinkedIn Facebook
x-twitter mastodon github linkedin discord threads instagram whatsapp bluesky square-rss sitemap