Skip to content

Commit

Permalink
Merge pull request #21 from ramonvasc/description-tag
Browse files Browse the repository at this point in the history
feat: parse description with parameters on calendar manager
  • Loading branch information
ramonvasc committed Nov 10, 2023
2 parents c5ba671 + 22784fc commit 4213393
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 1 deletion.
43 changes: 42 additions & 1 deletion Sources/MXLCalendarManagerSwift/MXLCalendarManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,32 @@

import Foundation

/// `MXLCalendarManager` is a class responsible for parsing iCalendar (.ics) files.
public class MXLCalendarManager {

/// Initializes a new instance of the calendar manager.
public init() {}


/// Asynchronously scans and parses an iCalendar file from a remote URL.
/// - Parameters:
/// - fileURL: The URL of the remote .ics file.
/// - localeIdentifier: Locale identifier used for date parsing (default is "en_US_POSIX").
/// - callback: The completion handler to call with the parsed calendar or an error.
public func scanICSFileAtRemoteURL(fileURL: URL, localeIdentifier: String = "en_US_POSIX", withCompletionHandler callback: @escaping (MXLCalendar?, Error?) -> Void) {

var fileData = Data()
DispatchQueue.global(qos: .default).async {
do {
// Attempt to download the file data
fileData = try Data(contentsOf: fileURL)
} catch (let downloadError) {
// Handle errors during download
callback(nil, downloadError)
return
}

DispatchQueue.main.async {
// Convert the file data to a string and parse it
guard let fileString = String(data: fileData, encoding: .utf8) else {
return
}
Expand All @@ -49,18 +59,29 @@ public class MXLCalendarManager {
}
}

/// Scans and parses an iCalendar file from a local file path.
/// - Parameters:
/// - filePath: The local file path of the .ics file.
/// - localeIdentifier: Locale identifier used for date parsing (default is "en_US_POSIX").
/// - callback: The completion handler to call with the parsed calendar or an error.
public func scanICSFileatLocalPath(filePath: String, localeIdentifier: String = "en_US_POSIX", withCompletionHandler callback: @escaping (MXLCalendar?, Error?) -> Void) {
var calendarFile = String()
do {
// Attempt to read the file content as a string
calendarFile = try String(contentsOfFile: filePath, encoding: .utf8)
} catch (let fileError) {
// Handle file read errors
callback(nil, fileError)
return
}

// Parse the calendar string
parse(icsString: calendarFile, localeIdentifier: localeIdentifier, withCompletionHandler: callback)
}

/// Creates an `MXLCalendarAttendee` object from a given attendee string.
/// - Parameter string: The attendee string in the .ics file format.
/// - Returns: An `MXLCalendarAttendee` object if parsing is successful, otherwise nil.
func createAttendee(string: String) -> MXLCalendarAttendee? {
var eventScanner = Scanner(string: string)
var uri = String()
Expand Down Expand Up @@ -124,7 +145,17 @@ public class MXLCalendarManager {
return MXLCalendarAttendee(withRole: roleEnum, commonName: comomName, andUri: uri, participantStatus: partStatEnum)
}

/// Parses an iCalendar formatted string and constructs a `MXLCalendar` object.
/// - Parameters:
/// - icsString: The iCalendar formatted string.
/// - localeIdentifier: Locale identifier used for date parsing.
/// - callback: The completion handler to call with the parsed calendar or an error.
public func parse(icsString: String, localeIdentifier: String = "en_US_POSIX", withCompletionHandler callback: @escaping (MXLCalendar?, Error?) -> Void) {
// Regular expression setup to remove new lines
// Splitting the ics string into events and parsing each event
// For each event, extract various properties like start time, end time, attendees, etc.
//
// Once all events are parsed, the callback is called with the constructed `MXLCalendar`.
var regex = NSRegularExpression()
do {
regex = try NSRegularExpression(pattern: "\n +", options: .caseInsensitive)
Expand Down Expand Up @@ -276,6 +307,16 @@ public class MXLCalendarManager {
_ = eventScanner.scanUpToString("DESCRIPTION:")
descriptionString = eventScanner.scanUpToString("\n") ?? ""
descriptionString = descriptionString.replacingOccurrences(of: "DESCRIPTION:", with: "").replacingOccurrences(of: "\r", with: "")

if descriptionString.isEmpty {
eventScanner = Scanner(string: event)
eventScanner.charactersToBeSkipped = nil
_ = eventScanner.scanUpToString("DESCRIPTION;")
_ = eventScanner.scanUpToString(":")
_ = eventScanner.scanString(":")
descriptionString = eventScanner.scanUpToString("\n") ?? ""
descriptionString = descriptionString.replacingOccurrences(of: "DESCRIPTION;", with: "").replacingOccurrences(of: "\r", with: "")
}

// Extract last modified datetime
eventScanner = Scanner(string: event)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,26 @@ END:VEVENT
testHelper(trueOccurrences: [firstOccurrence, lastOccurrence],
falseOccurrences: [afterLastOccurrence])
}

func testDescriptionTagWithParameters() {
let eventString = """
BEGIN:VEVENT
UID:2018101539720180906T132000
SUMMARY:15397 - 19th Century in Europe and Asia: Revolutions, Empires and Nations (the)
CATEGORIES:Cours magistral
DESCRIPTION;ENCODING=QUOTED-PRINTABLE:Enseignant(s) :\n - SCHEMPER Lukas :\n Enseignement :\n - AHIS 12A00 - 19th Century in Europe and Asia: Revolutions, Empires and Nations (the) - 201810
DTSTART;TZID=Europe/Paris:20180906T132000
DTEND;TZID=Europe/Paris:20180906T152000
LOCATION:Salle : GRAND AMPH - 77 rue Bellot - 76600 Le Havre
END:VEVENT
"""
parseCalendarWithEvent(eventString: eventString)

let expectedResult = "Enseignant(s) :- SCHEMPER Lukas :Enseignement :- AHIS 12A00 - 19th Century in Europe and Asia: Revolutions, Empires and Nations (the) - 201810"
let eventDescription = parsedCalendar.events.first?.eventDescription
XCTAssertNotNil(eventDescription)
XCTAssertEqual(eventDescription, expectedResult)
}

// MARK - Helpers

Expand Down

0 comments on commit 4213393

Please sign in to comment.