0

Primarily what I’m looking to do, is to pull out documents from my Cloud Firestore when the current date falls between two timestamp fields. I have included simplified code snippets below. Hopefully it makes sense, as I’m a noob.

I have a Cloud Firestore collection named ‘calendar’ with numerous documents in it.

Each document has an event ‘title’ field plus two timestamp fields ‘datebegin’ and ‘dateend’.

struct Calendar: Decodable, Identifiable {
    var title: String = ""
    var datebegin: Date = Date()
    var dateend: Date = Date()
}

I am parsing out the values from each document into event calendar instances:

class Calendars: ObservableObject {

    let db = Firestore.firestore()

  @Published var calendars = [Calendar]()

  init() {
              getDatabaseModules()
         }

 func getDatabaseModules() {
        db.collection("calendar")
            .getDocuments { snapshot, error in
            if error == nil && snapshot != nil {
                
                var calendars = [Calendar]()
                
                for event in snapshot!.documents {
                    
                    var e = Calendar()

                    e.title = event["title"] as? String ?? ""
                    e.datebegin = (event["datebegin"] as? Timestamp)?.dateValue() ?? Date()
                    e.dateend = (event["dateend"] as? Timestamp)?.dateValue() ?? Date()
                    
                    calendars.append(e)
                }
                
                DispatchQueue.main.async {
                    self.calendars = calendars
                }
            }
        }
    }

And I have been able to pull out the data in my view, so I know that I am able to access it okay:

struct HomeView: View {
    
    @EnvironmentObject var calendars: Calendars

    var body: some View {

        ForEach(0..<calendars.calendars.count, id: \.self) { events in
            Text("\(calendars.calendars[events].title)")
            Text("\(calendars.calendars[events].datebegin)")
            Text("\(calendars.calendars[events].dateend)”)
        }
    }
}
  1. Primarily what I’m looking to do, is only pull out only the calendar events when the current date (i.e. now) falls between the datebegin and dateend.

  2. And then I would subsequently sort the resulting list by day ideally (based on the Day of datebegin), so it should end up with something like this (for an example with 9 documents that meet the criteria):

Monday

  • document2.title
  • document5.title

Tuesday

  • document4.title
  • document9.title

Wednesday

  • document3.title
  • document6.title
  • document7.title

Friday

  • document1.title

Saturday

  • Document8.title

Any advice is appreciated and can provide more info as needed.

1 Answers1

0

you could try this approach, using some functions to ...pull out only the calendar events when the current date (i.e. now) falls between the datebegin and dateend... and sorting the results based on time to ...then I would subsequently sort the resulting list by day ideally (based on the Day of datebegin).... Adjust the approach to suit your desired time accuracy, for example, day, weeks ...

Note, it is not a good idea to use the name Calendar for your struct, as Swift already has a Calendar declared.

struct ContentView: View {
    @StateObject var model = Calendars()
    
    var body: some View {
        HomeView().environmentObject(model)
    }
}

struct HomeView: View {
    @EnvironmentObject var calendars: Calendars
    let now = Date()
    
    var body: some View {
        // -- here 
        List {
            ForEach(calendars.allBetween(now).sorted(by: {$0.datebegin < $1.datebegin})) { event in
                VStack {
                    Text("\(event.title)")
                    Text("\(event.datebegin)")
                    Text("\(event.dateend)")
                }
            }
        }
    }
}

struct Calendar: Decodable, Identifiable {
    let id = UUID()  // <-- here
    var title: String = ""
    var datebegin: Date = Date()
    var dateend: Date = Date()
    
    // -- here
    func isBetween(_ date: Date) -> Bool {
        datebegin.timeIntervalSince1970 < date.timeIntervalSince1970
        &&
        date.timeIntervalSince1970 < dateend.timeIntervalSince1970
    }

    // alternatively, day comparison 
    func isBetweenDay(_ date: Date) -> Bool {
        !(Foundation.Calendar.current.compare(date, to: datebegin, toGranularity: .day) == .orderedAscending || Foundation.Calendar.current.compare(date, to: dateend, toGranularity: .day) == .orderedDescending)
    }
    
}

class Calendars: ObservableObject {
    let db = Firestore.firestore()
    @Published var calendars = [Calendar]()
    
    init() {
        getDatabaseModules()
    }
    
    // -- here
    func allBetween(_ date: Date) -> [Calendar] {
        calendars.filter{ $0.isBetweenDay(date) } // <-- or $0.isBetween(date)
    }

    func getDatabaseModules() {
                db.collection("calendar")
                    .getDocuments { snapshot, error in
                        if error == nil && snapshot != nil {
                            var calendars = [Calendar]()
                            for event in snapshot!.documents {
                                var e = Calendar()
                                e.title = event["title"] as? String ?? ""
                                e.datebegin = (event["datebegin"] as? Timestamp)?.dateValue() ?? Date()
                                e.dateend = (event["dateend"] as? Timestamp)?.dateValue() ?? Date()
                                calendars.append(e)
                            }
                            DispatchQueue.main.async {
                                self.calendars = calendars
                            }
                        }
                    }
    }
}
    
  • Yes, this sorted out my primary challenge and I can see what the sorting is doing; i.e. sort by begin date. What I'm looking for though regarding the sorting is to display by DAY of the week (the day being that which is the same as whatever the datebegin is, or I can introduce another string field with that Day if necessary?). That is, all the Monday events will be together, all the Tuesday events ...etc. – user18175514 Jul 12 '22 at 17:59
  • I have also changed the model/viewmodel names from Calendar(s) to Event(s) as I see where problems may arise otherwise. – user18175514 Jul 12 '22 at 18:04
  • To group your `Events` by days, search for "group by date". Use a dictionary for the results, `[Date: [Event]]` with the day/date as the key and [Event] as the value. For example, use `calendars.reduce(...)` plus `dateComponents`. See for example: https://stackoverflow.com/questions/44242130/how-to-group-array-of-objects-by-date-in-swift – workingdog support Ukraine Jul 12 '22 at 23:44