Search code examples
swiftswiftuicalendar

Populating a SwiftUI Picker with Calendars


I am trying to populate a SwiftUI picker with a list of calendars.

As far as I can tell, the process should be something like this:

  • Declare an array of calendars:
  • Populate the array in the view’s onAppear method
  • Populate the picker from the array.

I can’t get anything to work without some fundamental error. My code goes something like this:

Create the variable at the top level:

let eventStore : EKEventStore = EKEventStore()
let sources = eventStore.sources

var calendars: [EKCalendar] = []

In the .onAppear() method populate the variable:

for source in sources {
    calendars = calendars + source.calendars(for: .event).sorted{ $0.title < $1.title }
}

Populate the picker:

struct ContentView: View {
    @Binding var calendar: EKCalendar!
    
    var body: some View {
        VStack {
            Picker("Select a calendar", selection: $calendar) {
                
//              ForEach(calendars) { calendar in
//                  Text("hahaha")
//                  Text(calendar.title)
//              }
            }
            .pickerStyle(.menu)
        }
    }
}

The commented out code above doesn’t work. How can I populate the picker?


Solution

  • Try this example approach, having the selection calendar and the calendars in your ContentView, at least as a test. Note the importance of having a .tag(Optional(calendar)) in the Picker.

    Of course you will need to have the Calendar enabled in your App Sandbox settings, and have some calendars in your EKEventStore, otherwise you will see only "no selection".

    struct ContentView: View {
        @State private var calendar: EKCalendar? // <--- here
        @State private var calendars: [EKCalendar] = [] // <--- here
    
       let eventStore : EKEventStore = EKEventStore()
        
        var body: some View {
            VStack {
                Picker("Select a calendar", selection: $calendar) {
                    Text("no selection").tag(Optional<EKCalendar>.none)
                    ForEach(calendars, id: \.self) { calendar in
                        Text(calendar.title)
                            .tag(Optional(calendar)) // <--- here
                    }
                }
                .pickerStyle(.menu)
            }
            .onAppear {
                // --- here
                for source in eventStore.sources {
                    calendars = calendars + source.calendars(for: .event).sorted{ $0.title < $1.title }
                }
                print("----> calendars: \(calendars.count)") // for testing info
            }
        }
    }
    

    Note, you will also need to grant (request from the user) permission to access the various Calendars.