From 7c092e51847e117c6a5dfde961fac87771d3de96 Mon Sep 17 00:00:00 2001 From: Parthasarathy Gudivada Date: Wed, 31 Jan 2018 16:14:42 -0500 Subject: [PATCH] Testing escaped characters for RFS5545 version of iCAL. This pr is for discussion purpose only. --- .../Extensions/EscapeStringConstants.swift | 34 ++++++++++ Sources/Extensions/String.swift | 17 +++++ Sources/Protocols/ICALEscape.swift | 26 ++++++++ Sources/RFS5545Escape.swift | 33 ++++++++++ Sources/ReplaceString.swift | 18 +++++ Tests/EscapedTests.swift | 66 +++++++++++++++++++ Tests/escapedExample.ics | 25 +++++++ Tests/iCalKitTests.swift | 1 + iCalKit.xcodeproj/project.pbxproj | 64 ++++++++++++++++-- 9 files changed, 280 insertions(+), 4 deletions(-) create mode 100644 Sources/Extensions/EscapeStringConstants.swift create mode 100644 Sources/Protocols/ICALEscape.swift create mode 100644 Sources/RFS5545Escape.swift create mode 100644 Sources/ReplaceString.swift create mode 100644 Tests/EscapedTests.swift create mode 100644 Tests/escapedExample.ics diff --git a/Sources/Extensions/EscapeStringConstants.swift b/Sources/Extensions/EscapeStringConstants.swift new file mode 100644 index 0000000..ad062a7 --- /dev/null +++ b/Sources/Extensions/EscapeStringConstants.swift @@ -0,0 +1,34 @@ +// +// EscapeStringConstants.swift +// iCalKit +// +// Created by Parthasarathy Gudivada on 1/31/18. +// Copyright © 2018 iCalKit. All rights reserved. +// + +import Foundation + + +extension String { + + static let escapedBackslash = "\\\\" + static let unEscapedBackslash = "\\" + + static let escapedComma = "\\," + static let unEscapedComma = "," + + static let escapedSemicolon = "\\;" + static let unEscapedSemicolon = ";" + + static let escapedNewline = "\\n" + static let unEscapedNewline = "\n" +} + + + + + + + + + diff --git a/Sources/Extensions/String.swift b/Sources/Extensions/String.swift index 12afe24..0a32bb4 100644 --- a/Sources/Extensions/String.swift +++ b/Sources/Extensions/String.swift @@ -18,3 +18,20 @@ extension String { return iCal.dateFormatter.date(from: self) } } + +extension String { + + func replacingEscapeOccurrences(_ using: [SubstituteEscapeString]) -> String { + + var updatedString = self + + using.forEach { + updatedString = updatedString.replacingOccurrences(of: $0.encoded, + with: $0.decoded, + options: .caseInsensitive) + } + + return updatedString + } +} + diff --git a/Sources/Protocols/ICALEscape.swift b/Sources/Protocols/ICALEscape.swift new file mode 100644 index 0000000..d0da777 --- /dev/null +++ b/Sources/Protocols/ICALEscape.swift @@ -0,0 +1,26 @@ +// +// ICALEscape.swift +// iCalKit +// +// Created by Parthasarathy Gudivada on 1/31/18. +// Copyright © 2018 iCalKit. All rights reserved. +// + +import Foundation + +protocol ICALEscape { + + var escapeUnescapeArray: [SubstituteEscapeString] { + get + } + + func unescapedString(_ for: String) -> String +} + +extension ICALEscape { + + func unescapedString(_ forTheInput: String) -> String { + return forTheInput.replacingEscapeOccurrences(escapeUnescapeArray) + } +} + diff --git a/Sources/RFS5545Escape.swift b/Sources/RFS5545Escape.swift new file mode 100644 index 0000000..d53cf5a --- /dev/null +++ b/Sources/RFS5545Escape.swift @@ -0,0 +1,33 @@ +// +// RFS5545Escape.swift +// iCalKit +// +// Created by Parthasarathy Gudivada on 1/31/18. +// Copyright © 2018 iCalKit. All rights reserved. +// + +import Foundation + + + + +struct RFS5545Escape: ICALEscape { + + let replaceComma = SubstituteEscapeString(encoded: .escapedComma, + decoded: .unEscapedComma) + let replaceSemicolon = SubstituteEscapeString(encoded: .escapedSemicolon, + decoded: .unEscapedSemicolon) + let replaceNewline = SubstituteEscapeString(encoded: .escapedNewline, + decoded: .unEscapedNewline) +// let replaceBackslash = SubstituteEscapeString(encoded: .escapedBackslash, +// decoded: .unEscapedBackslash) + + var escapeUnescapeArray: [SubstituteEscapeString] { + + return [replaceComma, + replaceNewline, + replaceSemicolon] + } + +} + diff --git a/Sources/ReplaceString.swift b/Sources/ReplaceString.swift new file mode 100644 index 0000000..476b508 --- /dev/null +++ b/Sources/ReplaceString.swift @@ -0,0 +1,18 @@ +// +// ReplaceString.swift +// iCalKit +// +// Created by Parthasarathy Gudivada on 1/31/18. +// Copyright © 2018 iCalKit. All rights reserved. +// + +import Foundation + +/** + To represent the character that gets encoded and the corresponding decoded character used in the iCAL standards. + eg., Comma (",") gets encoded as ( "\," with the backslash ) for the data interchange. + */ +struct SubstituteEscapeString { + let encoded: String + let decoded: String +} diff --git a/Tests/EscapedTests.swift b/Tests/EscapedTests.swift new file mode 100644 index 0000000..6f6929a --- /dev/null +++ b/Tests/EscapedTests.swift @@ -0,0 +1,66 @@ +// +// EscapedTests.swift +// iCalKit-iOS Tests +// +// Created by Parthasarathy Gudivada on 1/31/18. +// Copyright © 2018 iCalKit. All rights reserved. +// + +import XCTest +@testable import iCalKit + +class EscapedTests: XCTestCase { + + var exampleCals: [iCalKit.Calendar] = [] + + override func setUp() { + super.setUp() + + let bundle = Bundle(for: type(of: self)) + + guard let url = bundle.url(forResource: "escapedExample", withExtension: "ics") else { + XCTAssert(false, "no test escaped ics file") + return + } + + do { + let encodedCalString = try String(contentsOf: url, encoding: .utf8) + let unescapedString = RFS5545Escape().unescapedString(encodedCalString) + exampleCals = iCal.load(string: unescapedString) + } catch { + print(error.localizedDescription) + } + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testLoadLocalFile() { + XCTAssertTrue(exampleCals.count > 0, "we should have a calendar entry") + } + + func testValidateEscpaedSummary() { + + guard let cal = exampleCals.first else { + XCTAssert(false, "No Calendar found") + return + } + + XCTAssertTrue(cal.subComponents.count == 2, "should have two events") + + guard let firstEvent = cal.subComponents[0] as? Event else { + XCTAssert(false, "There should be an event") + return + } + + let summary = "Bastille , Day ; Party " + + XCTAssertTrue(summary == firstEvent.summary!, "summaries should be equal") + } + + + + +} diff --git a/Tests/escapedExample.ics b/Tests/escapedExample.ics new file mode 100644 index 0000000..75d1b5e --- /dev/null +++ b/Tests/escapedExample.ics @@ -0,0 +1,25 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//hacksw/handcal//NONSGML v1.0//EN +BEGIN:VEVENT +UID:uid1@example.com +DTSTAMP:19970714T170000Z +ORGANIZER;CN=John Doe:MAILTO:john.doe@example.com +DTSTART:19970714T170000Z +DTEND:19970715T035959Z +BEGIN:VALARM +TRIGGER:-PT1440M +ACTION:DISPLAY +DESCRIPTION:Reminder +END:VALARM +SUMMARY:Bastille \, Day \; Party +END:VEVENT +BEGIN:VEVENT +UID:uid2@example.com +DTSTAMP:19980714T170000Z +ORGANIZER;CN=Jim Doe:MAILTO:jim.doe@example.com +DTSTART:19980714T170000Z +DTEND:19980715T035959Z +SUMMARY:Something completely different +END:VEVENT +END:VCALENDAR diff --git a/Tests/iCalKitTests.swift b/Tests/iCalKitTests.swift index c5eab63..f18c6b8 100644 --- a/Tests/iCalKitTests.swift +++ b/Tests/iCalKitTests.swift @@ -12,6 +12,7 @@ import XCTest class iCalTests: XCTestCase { static var allTests = [ ("testLoadLocalFile", testLoadLocalFile), + ("testEventData", testEventData), ("testQuickstart", testQuickstart), ("testQuickstartFromUrl", testQuickstartFromUrl), diff --git a/iCalKit.xcodeproj/project.pbxproj b/iCalKit.xcodeproj/project.pbxproj index 8f8ea8f..4bea315 100644 --- a/iCalKit.xcodeproj/project.pbxproj +++ b/iCalKit.xcodeproj/project.pbxproj @@ -54,6 +54,28 @@ 41A675901F5D7EB800E8EC55 /* example.ics in Resources */ = {isa = PBXBuildFile; fileRef = 41CE90051F5D572D00C50155 /* example.ics */; }; 41A675911F5D7EB800E8EC55 /* example.ics in Resources */ = {isa = PBXBuildFile; fileRef = 41CE90051F5D572D00C50155 /* example.ics */; }; 52D6D9871BEFF229002C0205 /* iCalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* iCalKit.framework */; }; + AC324030202238FE003965D7 /* EscapedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC32402F202238FE003965D7 /* EscapedTests.swift */; }; + AC5E1B1F20225C7D00278318 /* escapedExample.ics in Resources */ = {isa = PBXBuildFile; fileRef = AC5E1B1D20225C5B00278318 /* escapedExample.ics */; }; + AC5E1B2120225C8000278318 /* escapedExample.ics in Resources */ = {isa = PBXBuildFile; fileRef = AC5E1B1D20225C5B00278318 /* escapedExample.ics */; }; + AC5E1B2220225C8100278318 /* escapedExample.ics in Resources */ = {isa = PBXBuildFile; fileRef = AC5E1B1D20225C5B00278318 /* escapedExample.ics */; }; + AC5E1B292022645E00278318 /* EscapedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC32402F202238FE003965D7 /* EscapedTests.swift */; }; + AC5E1B2A2022645F00278318 /* EscapedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC32402F202238FE003965D7 /* EscapedTests.swift */; }; + ACAA8FC320223110002C0A88 /* EscapeStringConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FC220223110002C0A88 /* EscapeStringConstants.swift */; }; + ACAA8FC420223110002C0A88 /* EscapeStringConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FC220223110002C0A88 /* EscapeStringConstants.swift */; }; + ACAA8FC520223110002C0A88 /* EscapeStringConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FC220223110002C0A88 /* EscapeStringConstants.swift */; }; + ACAA8FC620223111002C0A88 /* EscapeStringConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FC220223110002C0A88 /* EscapeStringConstants.swift */; }; + ACAA8FC8202233FE002C0A88 /* ICALEscape.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FC7202233FE002C0A88 /* ICALEscape.swift */; }; + ACAA8FC9202233FE002C0A88 /* ICALEscape.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FC7202233FE002C0A88 /* ICALEscape.swift */; }; + ACAA8FCA202233FE002C0A88 /* ICALEscape.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FC7202233FE002C0A88 /* ICALEscape.swift */; }; + ACAA8FCB202233FE002C0A88 /* ICALEscape.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FC7202233FE002C0A88 /* ICALEscape.swift */; }; + ACAA8FCD2022348C002C0A88 /* RFS5545Escape.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FCC2022348C002C0A88 /* RFS5545Escape.swift */; }; + ACAA8FCE2022348C002C0A88 /* RFS5545Escape.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FCC2022348C002C0A88 /* RFS5545Escape.swift */; }; + ACAA8FCF2022348C002C0A88 /* RFS5545Escape.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FCC2022348C002C0A88 /* RFS5545Escape.swift */; }; + ACAA8FD02022348C002C0A88 /* RFS5545Escape.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FCC2022348C002C0A88 /* RFS5545Escape.swift */; }; + ACAA8FD2202234C9002C0A88 /* ReplaceString.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FD1202234C8002C0A88 /* ReplaceString.swift */; }; + ACAA8FD3202234C9002C0A88 /* ReplaceString.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FD1202234C8002C0A88 /* ReplaceString.swift */; }; + ACAA8FD4202234C9002C0A88 /* ReplaceString.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FD1202234C8002C0A88 /* ReplaceString.swift */; }; + ACAA8FD5202234C9002C0A88 /* ReplaceString.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACAA8FD1202234C8002C0A88 /* ReplaceString.swift */; }; DD7502881C68FEDE006590AF /* iCalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6DA0F1BF000BD002C0205 /* iCalKit.framework */; }; DD7502921C690C7A006590AF /* iCalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D9F01BEFFFBE002C0205 /* iCalKit.framework */; }; /* End PBXBuildFile section */ @@ -101,6 +123,12 @@ 52D6D9E21BEFFF6E002C0205 /* iCalKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = iCalKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 52D6D9F01BEFFFBE002C0205 /* iCalKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = iCalKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 52D6DA0F1BF000BD002C0205 /* iCalKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = iCalKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + AC32402F202238FE003965D7 /* EscapedTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EscapedTests.swift; sourceTree = ""; }; + AC5E1B1D20225C5B00278318 /* escapedExample.ics */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = escapedExample.ics; sourceTree = ""; }; + ACAA8FC220223110002C0A88 /* EscapeStringConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EscapeStringConstants.swift; sourceTree = ""; }; + ACAA8FC7202233FE002C0A88 /* ICALEscape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ICALEscape.swift; sourceTree = ""; }; + ACAA8FCC2022348C002C0A88 /* RFS5545Escape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RFS5545Escape.swift; sourceTree = ""; }; + ACAA8FD1202234C8002C0A88 /* ReplaceString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplaceString.swift; sourceTree = ""; }; AD2FAA261CD0B6D800659CF4 /* iCalKit.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = iCalKit.plist; sourceTree = ""; }; AD2FAA281CD0B6E100659CF4 /* iCalKitTests.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = iCalKitTests.plist; sourceTree = ""; }; DD75027A1C68FCFC006590AF /* iCalKit-macOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "iCalKit-macOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -166,8 +194,10 @@ 41CE90041F5D572D00C50155 /* Tests */ = { isa = PBXGroup; children = ( + AC5E1B1D20225C5B00278318 /* escapedExample.ics */, 41CE90051F5D572D00C50155 /* example.ics */, 41CE90061F5D572D00C50155 /* iCalKitTests.swift */, + AC32402F202238FE003965D7 /* EscapedTests.swift */, ); path = Tests; sourceTree = ""; @@ -175,14 +205,16 @@ 41CE90081F5D576800C50155 /* Sources */ = { isa = PBXGroup; children = ( - 41CE900F1F5D576800C50155 /* iCal.swift */, 41CE90091F5D576800C50155 /* Alarm.swift */, 41CE900A1F5D576800C50155 /* Calendar.swift */, + 41CE90151F5D579C00C50155 /* Enums */, 41CE900D1F5D576800C50155 /* Event.swift */, + 41CE90141F5D579000C50155 /* Extensions */, + 41CE900F1F5D576800C50155 /* iCal.swift */, 41CE90111F5D576800C50155 /* Parser.swift */, 41CE90131F5D578000C50155 /* Protocols */, - 41CE90141F5D579000C50155 /* Extensions */, - 41CE90151F5D579C00C50155 /* Enums */, + ACAA8FCC2022348C002C0A88 /* RFS5545Escape.swift */, + ACAA8FD1202234C8002C0A88 /* ReplaceString.swift */, ); path = Sources; sourceTree = ""; @@ -190,8 +222,9 @@ 41CE90131F5D578000C50155 /* Protocols */ = { isa = PBXGroup; children = ( - 41CE90101F5D576800C50155 /* IcsElement.swift */, 41CE900B1F5D576800C50155 /* CalendarComponent.swift */, + ACAA8FC7202233FE002C0A88 /* ICALEscape.swift */, + 41CE90101F5D576800C50155 /* IcsElement.swift */, ); path = Protocols; sourceTree = ""; @@ -201,6 +234,7 @@ children = ( 41CE90121F5D576800C50155 /* String.swift */, 41CE900C1F5D576800C50155 /* Date.swift */, + ACAA8FC220223110002C0A88 /* EscapeStringConstants.swift */, ); path = Extensions; sourceTree = ""; @@ -498,6 +532,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + AC5E1B1F20225C7D00278318 /* escapedExample.ics in Resources */, 41A675911F5D7EB800E8EC55 /* example.ics in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -527,6 +562,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + AC5E1B2120225C8000278318 /* escapedExample.ics in Resources */, 41A675901F5D7EB800E8EC55 /* example.ics in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -535,6 +571,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + AC5E1B2220225C8100278318 /* escapedExample.ics in Resources */, 41A6758F1F5D7EB800E8EC55 /* example.ics in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -546,11 +583,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + ACAA8FCD2022348C002C0A88 /* RFS5545Escape.swift in Sources */, + ACAA8FD2202234C9002C0A88 /* ReplaceString.swift in Sources */, 41A675881F5D7C8500E8EC55 /* Event.swift in Sources */, 41A6758E1F5D7C8500E8EC55 /* iCalError.swift in Sources */, 41A675851F5D7C8500E8EC55 /* iCal.swift in Sources */, 41A6758A1F5D7C8500E8EC55 /* IcsElement.swift in Sources */, 41A675891F5D7C8500E8EC55 /* Parser.swift in Sources */, + ACAA8FC8202233FE002C0A88 /* ICALEscape.swift in Sources */, + ACAA8FC320223110002C0A88 /* EscapeStringConstants.swift in Sources */, 41A675871F5D7C8500E8EC55 /* Calendar.swift in Sources */, 41A6758B1F5D7C8500E8EC55 /* CalendarComponent.swift in Sources */, 41A6758C1F5D7C8500E8EC55 /* String.swift in Sources */, @@ -564,6 +605,7 @@ buildActionMask = 2147483647; files = ( 41A675661F5D7C7B00E8EC55 /* iCalKitTests.swift in Sources */, + AC324030202238FE003965D7 /* EscapedTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -571,11 +613,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + ACAA8FCF2022348C002C0A88 /* RFS5545Escape.swift in Sources */, + ACAA8FD4202234C9002C0A88 /* ReplaceString.swift in Sources */, 41A675741F5D7C8400E8EC55 /* Event.swift in Sources */, 41A6757A1F5D7C8400E8EC55 /* iCalError.swift in Sources */, 41A675711F5D7C8400E8EC55 /* iCal.swift in Sources */, 41A675761F5D7C8400E8EC55 /* IcsElement.swift in Sources */, 41A675751F5D7C8400E8EC55 /* Parser.swift in Sources */, + ACAA8FCA202233FE002C0A88 /* ICALEscape.swift in Sources */, + ACAA8FC520223110002C0A88 /* EscapeStringConstants.swift in Sources */, 41A675731F5D7C8400E8EC55 /* Calendar.swift in Sources */, 41A675771F5D7C8400E8EC55 /* CalendarComponent.swift in Sources */, 41A675781F5D7C8400E8EC55 /* String.swift in Sources */, @@ -588,11 +634,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + ACAA8FD02022348C002C0A88 /* RFS5545Escape.swift in Sources */, + ACAA8FD5202234C9002C0A88 /* ReplaceString.swift in Sources */, 41A6756A1F5D7C8400E8EC55 /* Event.swift in Sources */, 41A675701F5D7C8400E8EC55 /* iCalError.swift in Sources */, 41A675671F5D7C8400E8EC55 /* iCal.swift in Sources */, 41A6756C1F5D7C8400E8EC55 /* IcsElement.swift in Sources */, 41A6756B1F5D7C8400E8EC55 /* Parser.swift in Sources */, + ACAA8FCB202233FE002C0A88 /* ICALEscape.swift in Sources */, + ACAA8FC620223111002C0A88 /* EscapeStringConstants.swift in Sources */, 41A675691F5D7C8400E8EC55 /* Calendar.swift in Sources */, 41A6756D1F5D7C8400E8EC55 /* CalendarComponent.swift in Sources */, 41A6756E1F5D7C8400E8EC55 /* String.swift in Sources */, @@ -605,11 +655,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + ACAA8FCE2022348C002C0A88 /* RFS5545Escape.swift in Sources */, + ACAA8FD3202234C9002C0A88 /* ReplaceString.swift in Sources */, 41A6757E1F5D7C8500E8EC55 /* Event.swift in Sources */, 41A675841F5D7C8500E8EC55 /* iCalError.swift in Sources */, 41A6757B1F5D7C8400E8EC55 /* iCal.swift in Sources */, 41A675801F5D7C8500E8EC55 /* IcsElement.swift in Sources */, 41A6757F1F5D7C8500E8EC55 /* Parser.swift in Sources */, + ACAA8FC9202233FE002C0A88 /* ICALEscape.swift in Sources */, + ACAA8FC420223110002C0A88 /* EscapeStringConstants.swift in Sources */, 41A6757D1F5D7C8500E8EC55 /* Calendar.swift in Sources */, 41A675811F5D7C8500E8EC55 /* CalendarComponent.swift in Sources */, 41A675821F5D7C8500E8EC55 /* String.swift in Sources */, @@ -623,6 +677,7 @@ buildActionMask = 2147483647; files = ( 41A675651F5D7C7B00E8EC55 /* iCalKitTests.swift in Sources */, + AC5E1B292022645E00278318 /* EscapedTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -631,6 +686,7 @@ buildActionMask = 2147483647; files = ( 41A675641F5D7C7A00E8EC55 /* iCalKitTests.swift in Sources */, + AC5E1B2A2022645F00278318 /* EscapedTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };