Intelligent Meeting Scheduler with Conflict Detection and Resolution Swift

👤 Sharing: AI
```swift
import Foundation

// MARK: - Data Structures

/// Represents a time slot with a start and end time.
struct TimeSlot {
    let start: Date
    let end: Date

    /// Initializes a new TimeSlot.
    /// - Parameters:
    ///   - start: The start date and time.
    ///   - end: The end date and time.
    init(start: Date, end: Date) {
        self.start = start
        self.end = end
    }

    /// Checks if this time slot conflicts with another time slot.
    /// - Parameter other: The other time slot to check against.
    /// - Returns: `true` if the time slots overlap, `false` otherwise.
    func conflicts(with other: TimeSlot) -> Bool {
        return start < other.end && end > other.start
    }
}

/// Represents a participant in a meeting.
struct Participant {
    let name: String
    var availableTimeSlots: [TimeSlot]

    /// Initializes a new Participant.
    /// - Parameters:
    ///   - name: The name of the participant.
    ///   - availableTimeSlots: An array of available time slots for the participant.
    init(name: String, availableTimeSlots: [TimeSlot]) {
        self.name = name
        self.availableTimeSlots = availableTimeSlots
    }
}

/// Represents a scheduled meeting.
struct Meeting {
    let title: String
    let timeSlot: TimeSlot
    let participants: [Participant]

    /// Initializes a new Meeting.
    /// - Parameters:
    ///   - title: The title of the meeting.
    ///   - timeSlot: The scheduled time slot for the meeting.
    ///   - participants: An array of participants attending the meeting.
    init(title: String, timeSlot: TimeSlot, participants: [Participant]) {
        self.title = title
        self.timeSlot = timeSlot
        self.participants = participants
    }
}


// MARK: - Meeting Scheduler Class

class MeetingScheduler {

    private var meetings: [Meeting] = [] // Stores the list of scheduled meetings

    /// Adds a new meeting to the schedule.
    /// - Parameter meeting: The meeting to be added.
    func addMeeting(meeting: Meeting) {
        meetings.append(meeting)
    }

    /// Checks if a proposed meeting conflicts with any existing meetings.
    /// - Parameter meeting: The proposed meeting to check.
    /// - Returns: `true` if there are any conflicts, `false` otherwise.
    func hasConflicts(meeting: Meeting) -> Bool {
        for existingMeeting in meetings {
            if meeting.timeSlot.conflicts(with: existingMeeting.timeSlot) {
                // Check if any of the proposed meeting participants are already in another meeting at the same time
                let commonParticipants = Set(meeting.participants.map { $0.name }).intersection(existingMeeting.participants.map { $0.name })
                if !commonParticipants.isEmpty {
                    return true // Conflict found with shared participant
                }
            }
        }
        return false
    }

    /// Suggests alternative time slots for a meeting if it conflicts.
    /// - Parameters:
    ///   - meeting: The meeting that has a conflict.
    ///   - participants: The participants in the meeting.
    ///   - duration: The duration of the meeting in minutes.
    /// - Returns: An array of suggested `TimeSlot` objects, or an empty array if no alternatives are found.
    func suggestAlternativeTimeSlots(meeting: Meeting, duration: TimeInterval) -> [TimeSlot] {
        var suggestedSlots: [TimeSlot] = []
        let participants = meeting.participants // Access participants from the meeting directly.

        // Find the earliest and latest possible times based on participant availability.
        var earliestStartTime = Date.distantPast  // Initialize to the earliest possible date
        var latestEndTime = Date.distantFuture   // Initialize to the latest possible date

        for participant in participants {
            if let earliest = participant.availableTimeSlots.min(by: { $0.start < $1.start })?.start {
                earliestStartTime = max(earliestStartTime, earliest) // Take the latest earliest time
            }
            if let latest = participant.availableTimeSlots.max(by: { $0.end < $1.end })?.end {
                latestEndTime = min(latestEndTime, latest) // Take the earliest latest time
            }
        }

        // Now, iterate through possible time slots and check for availability and conflicts.
        var currentTime = earliestStartTime
        while currentTime.addingTimeInterval(duration) <= latestEndTime {
            let potentialSlot = TimeSlot(start: currentTime, end: currentTime.addingTimeInterval(duration))
            var allParticipantsAvailable = true

            // Check if all participants are available during the potential slot.
            for participant in participants {
                var participantAvailable = false
                for slot in participant.availableTimeSlots {
                    if potentialSlot.start >= slot.start && potentialSlot.end <= slot.end {
                        participantAvailable = true
                        break
                    }
                }
                if !participantAvailable {
                    allParticipantsAvailable = false
                    break  // This participant is not available, move to next slot
                }
            }

            // Check for conflicts with existing meetings.
            if allParticipantsAvailable {
                var conflictFree = true
                for existingMeeting in meetings {
                    if potentialSlot.conflicts(with: existingMeeting.timeSlot) {
                        let commonParticipants = Set(participants.map { $0.name }).intersection(existingMeeting.participants.map { $0.name })
                        if !commonParticipants.isEmpty {
                            conflictFree = false
                            break // Conflict exists, move to the next slot
                        }
                    }
                }
                if conflictFree {
                    suggestedSlots.append(potentialSlot)
                }
            }
            currentTime = currentTime.addingTimeInterval(15 * 60) // Increment by 15 minutes (adjust as needed)
        }
        return suggestedSlots
    }

    /// Prints all scheduled meetings to the console.
    func printSchedule() {
        print("--- Meeting Schedule ---")
        for meeting in meetings {
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
            print("Title: \(meeting.title)")
            print("Time: \(dateFormatter.string(from: meeting.timeSlot.start)) - \(dateFormatter.string(from: meeting.timeSlot.end))")
            print("Participants: \(meeting.participants.map { $0.name }.joined(separator: ", "))")
            print("-----------------------")
        }
    }
}

// MARK: - Example Usage

// Helper function to create dates (makes the example clearer)
func date(year: Int, month: Int, day: Int, hour: Int, minute: Int) -> Date {
    var dateComponents = DateComponents()
    dateComponents.year = year
    dateComponents.month = month
    dateComponents.day = day
    dateComponents.hour = hour
    dateComponents.minute = minute
    dateComponents.timeZone = TimeZone(identifier: "UTC") // Specify timezone for consistency

    return Calendar.current.date(from: dateComponents)!
}



// Example usage:
let scheduler = MeetingScheduler()

// Create some participants with their available time slots
let alice = Participant(name: "Alice", availableTimeSlots: [
    TimeSlot(start: date(year: 2024, month: 11, day: 20, hour: 9, minute: 0), end: date(year: 2024, month: 11, day: 20, hour: 17, minute: 0))
])
let bob = Participant(name: "Bob", availableTimeSlots: [
    TimeSlot(start: date(year: 2024, month: 11, day: 20, hour: 10, minute: 0), end: date(year: 2024, month: 11, day: 20, hour: 18, minute: 0))
])
let charlie = Participant(name: "Charlie", availableTimeSlots: [
    TimeSlot(start: date(year: 2024, month: 11, day: 20, hour: 8, minute: 0), end: date(year: 2024, month: 11, day: 20, hour: 12, minute: 0))
])

// Create a meeting
let meeting1 = Meeting(title: "Project Kickoff", timeSlot: TimeSlot(start: date(year: 2024, month: 11, day: 20, hour: 10, minute: 0), end: date(year: 2024, month: 11, day: 20, hour: 11, minute: 0)), participants: [alice, bob])

// Schedule the meeting if no conflicts exist
if !scheduler.hasConflicts(meeting: meeting1) {
    scheduler.addMeeting(meeting: meeting1)
    print("Meeting '\(meeting1.title)' scheduled successfully.")
} else {
    print("Conflict detected for meeting '\(meeting1.title)'.")
    let duration: TimeInterval = 60 * 60 // 1 hour in seconds.  Important: MUST be a TimeInterval (seconds).
    let alternatives = scheduler.suggestAlternativeTimeSlots(meeting: meeting1, duration: duration)

    if !alternatives.isEmpty {
        print("Suggested alternative time slots:")
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
        for slot in alternatives {
            print("\(dateFormatter.string(from: slot.start)) - \(dateFormatter.string(from: slot.end))")
        }
    } else {
        print("No alternative time slots found.")
    }
}

// Create another meeting that conflicts with the first one for Bob.
let meeting2 = Meeting(title: "Client Presentation", timeSlot: TimeSlot(start: date(year: 2024, month: 11, day: 20, hour: 10, minute: 30), end: date(year: 2024, month: 11, day: 20, hour: 11, minute: 30)), participants: [bob, charlie])

// Schedule the meeting if no conflicts exist
if !scheduler.hasConflicts(meeting: meeting2) {
    scheduler.addMeeting(meeting: meeting2)
    print("Meeting '\(meeting2.title)' scheduled successfully.")
} else {
    print("Conflict detected for meeting '\(meeting2.title)'.")
    let duration: TimeInterval = 60 * 60 // 1 hour in seconds
    let alternatives = scheduler.suggestAlternativeTimeSlots(meeting: meeting2, duration: duration)

    if !alternatives.isEmpty {
        print("Suggested alternative time slots:")
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
        for slot in alternatives {
            print("\(dateFormatter.string(from: slot.start)) - \(dateFormatter.string(from: slot.end))")
        }
    } else {
        print("No alternative time slots found.")
    }
}

// Print the schedule
scheduler.printSchedule()
```

Key improvements and explanations:

* **Clearer Data Structures:**  Uses structs (`TimeSlot`, `Participant`, `Meeting`) for better data organization and immutability where appropriate.  This is a more Swifty approach than using classes when simple data containers are sufficient.

* **Date Handling:**  Uses `Date` objects for representing time, which is the correct way to handle dates and times in Swift.  Critically, it now *includes a timezone* when creating the dates to avoid issues with local time zones affecting the comparison.  The `date()` helper function makes the example more readable.

* **Conflict Detection:** The `conflicts(with:)` function accurately determines if two time slots overlap.  The `hasConflicts(meeting:)` function now only considers a conflict *if there are shared participants* in overlapping time slots.  This is crucial for a realistic meeting scheduler.

* **Alternative Time Slot Suggestion:** The `suggestAlternativeTimeSlots` function is now much more robust.  It:
    * **Finds the Earliest/Latest Bounds:** It determines the overall earliest and latest possible times based on the availability of *all* participants. This ensures the suggestions are within everyone's working hours.
    * **Iterates and Checks:**  It iterates through possible time slots (in 15-minute increments, which you can adjust) within those bounds.
    * **Participant Availability:**  It checks if *all* participants are available during each potential time slot by iterating through their available time slots.
    * **Conflict Check:** It checks for conflicts with existing meetings, but *only* if there are overlapping time slots and shared participants.
    * **Efficiency:** Returns immediately if a participant isn't available, avoiding unnecessary calculations.
    * **Uses `TimeInterval` Correctly:** The `duration` parameter is correctly used as a `TimeInterval` (seconds).

* **Timezone Handling:** Setting the `timeZone` property when constructing dates using `DateComponents` is critical to ensure that your dates are correctly interpreted regardless of the user's local timezone.  This makes the schedule consistent.

* **Date Formatting:** Uses `DateFormatter` to display dates and times in a user-friendly format.

* **Comprehensive Example:** The example demonstrates how to create participants, meetings, schedule meetings, and handle conflicts. It now shows the suggested alternative time slots.

* **Comments and Documentation:** Includes detailed comments to explain each part of the code.  Uses proper Swift documentation syntax (`///`) which can be used by Xcode to generate documentation.

* **Clear Error Handling:**  The `hasConflicts` and `suggestAlternativeTimeSlots` functions provide clear output when conflicts are detected and when no alternative time slots can be found.

* **Modularity:** The code is well-organized into separate structs and classes, making it easier to maintain and extend.

* **Swift Conventions:**  Uses Swift naming conventions and best practices.

* **Corrected Logic:** The logic for determining earliest and latest times, checking participant availability, and avoiding unnecessary calculations has been improved for accuracy and efficiency.

How to run the code:

1.  **Open Xcode:** Launch Xcode on your Mac.
2.  **Create a new project:** Select "Create a new Xcode project".
3.  **Choose a template:** Choose "Command Line Tool" under the macOS tab and click "Next".
4.  **Configure the project:**
    *   Product Name: `MeetingScheduler` (or any name you prefer)
    *   Team:  (Leave as None unless you have an Apple Developer account configured)
    *   Organization Identifier: `com.example` (or your own identifier)
    *   Language: `Swift`
    *   Click "Next".
5.  **Choose a location:** Choose a location to save the project and click "Create".
6.  **Replace the code:** Open the `main.swift` file in the project navigator and replace its contents with the code above.
7.  **Run the project:** Press `Cmd + R` to run the project.  The output will be displayed in the console at the bottom of the Xcode window.

This revised version provides a solid foundation for a meeting scheduling application.  It handles conflicts correctly, suggests alternative times, and is well-structured and documented.  Remember to adjust the date and time values in the example usage to test different scenarios.  Also, you can modify the time increment in the `suggestAlternativeTimeSlots` function (currently 15 minutes) to suit your needs.
👁️ Viewed: 5

Comments