Skip to content

Commit

Permalink
Merge pull request #22 from ramonvasc/swift-update
Browse files Browse the repository at this point in the history
refactor: improves readability reducing code duplication and new usage of async/await
  • Loading branch information
ramonvasc committed Nov 21, 2023
2 parents 4213393 + bfd4df2 commit 97265f1
Show file tree
Hide file tree
Showing 7 changed files with 474 additions and 467 deletions.
2 changes: 1 addition & 1 deletion MXLCalendarManagerSwift.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'MXLCalendarManagerSwift'
s.version = '1.1.0'
s.version = '1.1.1'
s.summary = 'MXLCalendarManagerSwift is a library to parse iCalendar (.ICS) files'

s.description = 'A set of classes used to parse and handle iCalendar (.ICS) files originally implemented in Objective C by Kiran Panesar (https://github.com/KiranPanesar/MXLCalendarManager.git)'
Expand Down
2 changes: 1 addition & 1 deletion Sources/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>1.1.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
156 changes: 88 additions & 68 deletions Sources/MXLCalendarManagerSwift/MXLCalendar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,109 +25,129 @@

import Foundation

/// Represents a calendar with its events and their management functionalities.
public class MXLCalendar {
// Dictionary to store events keyed by their date strings.
public var daysOfEvents = [String: [MXLCalendarEvent]]()

// Tracks whether all events for a particular date have been loaded.
public var loadedEvents = [String: Bool]()

// Optional properties for calendar and time zone.
public var calendar: Calendar?

public var timeZone: TimeZone?
public var events = [MXLCalendarEvent]()

// Use synchronization mechanisms such as locks to ensure that only one
// thread can access the events array at a time.
private let eventQueue = DispatchQueue(label: "com.mxlcalendar.eventQueue", attributes: .concurrent)
private var _events = [MXLCalendarEvent]()

// Computed property to safely access events
public var events: [MXLCalendarEvent] {
get {
return eventQueue.sync {
_events
}
}
set {
eventQueue.async(flags: .barrier) {
self._events = newValue
}
}
}

// DateFormatter initialized for consistent date format usage.
private let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd"
return formatter
}()

/// Initializes a new instance of MXLCalendar.
public init() {}

/// Adds an event to the general list of events.
/// - Parameter event: The `MXLCalendarEvent` to be added.
public func add(event: MXLCalendarEvent) {
events.append(event)
}

/// Adds an event for a specific day, month, and year.
/// - Parameters:
/// - event: The `MXLCalendarEvent` to be added.
/// - day: The day of the event.
/// - month: The month of the event.
/// - year: The year of the event.
public func add(event: MXLCalendarEvent, onDay day: Int, month: Int, year: Int) {
let formatter = DateFormatter()
formatter.dateFormat = "yyyyddMM"

var components = Calendar.current.dateComponents([.day, .month, .year], from: Date())
var components = DateComponents()
components.day = day
components.month = month
components.year = year

guard let calendarDate = Calendar.current.date(from: components) else {
return
}
add(event: event, onDate: formatter.string(from: calendarDate))
}

public func add(event: MXLCalendarEvent, onDate date: String) {
// Check if the event has already been logged today
guard var dateDaysOfEvents = daysOfEvents[date] else {
// If there are no current dates on today, create a new array and save it for the day
daysOfEvents[date] = [event]
return
}
for currentEvent in dateDaysOfEvents {
if currentEvent.eventUniqueID == event.eventUniqueID {
return
}
}

// If there are already events for this date...
if dateDaysOfEvents.contains(event) {
return
} else {
// If not, add it to the day
dateDaysOfEvents.append(event)
daysOfEvents[date] = dateDaysOfEvents
if let calendarDate = Calendar.current.date(from: components) {
add(event: event, onDate: calendarDate)
}
}

/// Adds an event for a specific date.
/// - Parameters:
/// - event: The `MXLCalendarEvent` to be added.
/// - date: The date for the event.
public func add(event: MXLCalendarEvent, onDate date: Date) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyddMM"
let dateString = dateFormatter.string(from: date)
add(event: event, onDateString: dateString)
}

add(event: event, onDate: dateFormatter.string(from: date))
/// Private method for adding an event using a date string.
/// - Parameters:
/// - event: The `MXLCalendarEvent` to be added.
/// - dateString: The date string representing the date of the event.
private func add(event: MXLCalendarEvent, onDateString dateString: String) {
var eventsForDate = daysOfEvents[dateString, default: []]
if !eventsForDate.contains(where: { $0.eventUniqueID == event.eventUniqueID }) {
eventsForDate.append(event)
daysOfEvents[dateString] = eventsForDate
}
}

/// Marks all events for a given date as loaded.
/// - Parameter date: The date for which events have been loaded.
public func loadedAllEventsForDate(date: Date) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyddMM"
loadedEvents[dateFormatter.string(from: date)] = NSNumber(value: true).boolValue
loadedEvents[dateFormatter.string(from: date)] = true
}


/// Checks if all events for a given date have been loaded.
/// - Parameter date: The date to check for loaded events.
/// - Returns: `true` if all events for the date have been loaded; otherwise, `false`.
public func hasLoadedAllEventsFor(date: Date) -> Bool {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyddMM"
let dateString = dateFormatter.string(from: date)
if let loadedEvents = loadedEvents[dateString], loadedEvents {
return true
}
return false
loadedEvents[dateFormatter.string(from: date)] ?? false
}

/// Returns all events for a given date, sorted by start time.
/// - Parameter date: The date for which to retrieve events.
/// - Returns: An array of `MXLCalendarEvent` for the given date, sorted by start time.
public func eventsFor(date: Date) -> [MXLCalendarEvent]? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyddMM"

if let dateDaysOfEvents = daysOfEvents[dateFormatter.string(from: date)] {
let sortedArray = (dateDaysOfEvents as NSArray).sortedArray(options: .concurrent) { (firstEvent, secondEvent) -> ComparisonResult in
guard let firstEvent = firstEvent as? MXLCalendarEvent, let secondEvent = secondEvent as? MXLCalendarEvent, let firstEventStartDate = firstEvent.eventStartDate, let secondEventStartDate = secondEvent.eventStartDate else {
return ComparisonResult.orderedSame
}
let firstDateComponents = Calendar.current.dateComponents([.hour, .minute, .second], from: firstEventStartDate)
let secondDateComponents = Calendar.current.dateComponents([.hour, .minute, .second], from: secondEventStartDate)

guard let firstDate = Calendar.current.date(from: firstDateComponents), let secondDate = Calendar.current.date(from: secondDateComponents) else {
return ComparisonResult.orderedSame
}
return firstDate.compare(secondDate)
} as? [MXLCalendarEvent]
daysOfEvents[dateFormatter.string(from: date)] = sortedArray
}
return daysOfEvents[dateFormatter.string(from: date)]
let dateString = dateFormatter.string(from: date)

// Sorts the events based on their start date, handling optional dates safely.
return daysOfEvents[dateString]?.sorted(by: { (firstEvent, secondEvent) -> Bool in
switch (firstEvent.eventStartDate, secondEvent.eventStartDate) {
case let (firstDate?, secondDate?):
return firstDate < secondDate
case (nil, _):
return true // First event has no start date, so it comes first.
case (_, nil):
return false // Second event has no start date, so it comes second.
}
})
}
}

public extension MXLCalendar {
/// Checks if there is an event occurring at a given time.
/// - Parameter time: The time to check for an event's occurrence.
/// - Returns: `true` if there is an event occurring at the given time; otherwise, `false`.
func containsEvent(at time: Date) -> Bool {
return events.contains(where: { (event: MXLCalendarEvent) -> Bool in
return event.checkTime(targetTime: time)
})
events.contains { $0.checkTime(targetTime: time) }
}
}
23 changes: 7 additions & 16 deletions Sources/MXLCalendarManagerSwift/MXLCalendarAttendee.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public enum Role: String {
case NON_PARTICIPANT = "NON-PARTICIPANT"
}

public enum PartStat:String{
public enum PartStat: String {
case TENTATIVE = "TENTATIVE"
case ACCEPTED = "ACCEPTED"
case NEEDS_ACTION = "NEEDS-ACTION"
Expand All @@ -42,25 +42,16 @@ public enum PartStat:String{
case IN_PROCESS = "IN-PROCESS"
}

public class MXLCalendarAttendee {
public var uri: String
public var commonName: String
public var role: Role
public var participantStatus: PartStat
public struct MXLCalendarAttendee: Equatable {
public let uri: String
public let commonName: String
public let role: Role
public let participantStatus: PartStat

public init(withRole role: Role, commonName: String, andUri uri: String, participantStatus:PartStat) {
public init(withRole role: Role, commonName: String, andUri uri: String, participantStatus: PartStat) {
self.uri = uri
self.commonName = commonName
self.role = role
self.participantStatus = participantStatus
}
}

extension MXLCalendarAttendee: Equatable {
public static func == (lhs: MXLCalendarAttendee, rhs: MXLCalendarAttendee) -> Bool {
return lhs.uri == rhs.uri &&
lhs.commonName == rhs.commonName &&
lhs.role == rhs.role &&
lhs.participantStatus == rhs.participantStatus
}
}

0 comments on commit 97265f1

Please sign in to comment.