From a4931bcb1e1c64c67c9d940c0a503ca307473ec7 Mon Sep 17 00:00:00 2001 From: Xin Hong Date: Wed, 21 Sep 2016 18:56:40 +0800 Subject: [PATCH] Swift 3 support --- README.md | 16 +- RRuleSwift.xcodeproj/project.pbxproj | 37 ++- .../xcschemes/RRuleSwift iOS.xcscheme | 2 +- .../xcschemes/RRuleSwift watchOS.xcscheme | 2 +- .../TodayViewController.swift | 60 ++--- RRuleSwiftExample-Watch Extension/Info.plist | 2 +- .../InterfaceController.swift | 52 +--- .../NotificationController.swift | 22 -- RRuleSwiftExample-Watch/Info.plist | 2 +- RRuleSwiftExample.xcodeproj/project.pbxproj | 63 +++-- .../RRuleSwiftExample-TodayExtension.xcscheme | 2 +- .../xcschemes/RRuleSwiftExample.xcscheme | 2 +- RRuleSwiftExample/AppDelegate.swift | 12 +- .../RRuleExampleViewController.swift | 236 ++++++++++-------- Sources/EKWeekday+RRule.swift | 44 ++-- Sources/ExclusionDate.swift | 35 ++- Sources/Generators.swift | 94 ------- Sources/InclusionDate.swift | 25 +- Sources/Iterators.swift | 94 +++++++ Sources/JavaScriptBridge.swift | 65 +++-- Sources/NSDate+Comparison.swift | 22 +- Sources/RRule.swift | 113 +++++---- Sources/RecurrenceFrequency.swift | 44 ++-- Sources/RecurrenceRule.swift | 16 +- Sources/Supporting Files/Info-watchOS.plist | 2 +- 25 files changed, 527 insertions(+), 537 deletions(-) delete mode 100644 Sources/Generators.swift create mode 100644 Sources/Iterators.swift diff --git a/README.md b/README.md index 00bdb51..a156f09 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ github "teambition/RRuleSwift" ###Usage ##### Initialization ```swift -var recurrenceRule = RecurrenceRule(frequency: .Daily) +var recurrenceRule = RecurrenceRule(frequency: .daily) recurrenceRule.calendar = ... recurrenceRule.frequency = ... recurrenceRule.interval = ... @@ -38,7 +38,7 @@ recurrenceRule.exdate = ... ##### Rule form string ```swift let ruleString = "RRULE:FREQ=MONTHLY;DTSTART=20160404T021000Z;COUNT=5;INTERVAL=2;WKST=MO;BYDAY=MO,TU" -let rule = RecurrenceRule(recurrenceWithRRuleString: ruleString) +let rule = RecurrenceRule(rruleString: ruleString) ``` ##### String form rule @@ -63,7 +63,7 @@ if let inclusionDate = InclusionDate(rdateString: rdateString) { } let exdateString = "EXDATE:20181231T160000Z,20201231T160000Z" -if let exclusionDate = ExclusionDate(exdateString: exdateString, unitGranularity: .Year) { +if let exclusionDate = ExclusionDate(exdateString: exdateString, granularity: .year) { print(exclusionDate.toExDateString()) // EXDATE:20181231T160000Z,20201231T160000Z @@ -75,10 +75,10 @@ if let exclusionDate = ExclusionDate(exdateString: exdateString, unitGranularity } ``` -##### Occurrence generator +##### Occurrence iterator ```swift let ruleString = "RRULE:FREQ=YEARLY;COUNT=11;WKST=MO" -if let rule = RecurrenceRule(recurrenceWithRRuleString: ruleString) { +if let rule = RecurrenceRule(rruleString: ruleString) { var rule = rule rule.rdate = inclusionDate // RDATE:20180706T160000Z,20210706T160000Z rule.exdate = exclusionDate // EXDATE:20181231T160000Z,20201231T160000Z @@ -97,9 +97,9 @@ if let rule = RecurrenceRule(recurrenceWithRRuleString: ruleString) { 2026-04-14 14:22:30 Tue */ - let date = dateFormatter.dateFromString("2018-01-01 00:00:00 Sun") - let otherDate = dateFormatter.dateFromString("2024-01-01 00:00:00 Mon") - let betweenDates = rule.occurrencesBetween(date: date!, andDate: otherDate!) + let date = dateFormatter.date(from: "2018-01-01 00:00:00 Sun")! + let otherDate = dateFormatter.date(from: "2024-01-01 00:00:00 Mon")! + let betweenDates = rule.occurrences(between: date, and: otherDate) print(betweenDates) /* 2018-04-14 14:22:30 Sat diff --git a/RRuleSwift.xcodeproj/project.pbxproj b/RRuleSwift.xcodeproj/project.pbxproj index 884289d..6190a11 100644 --- a/RRuleSwift.xcodeproj/project.pbxproj +++ b/RRuleSwift.xcodeproj/project.pbxproj @@ -11,7 +11,7 @@ EB48D1621D641D45001EE872 /* EKWeekday+RRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB48D1511D641D45001EE872 /* EKWeekday+RRule.swift */; }; EB48D1631D641D45001EE872 /* ExclusionDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB48D1521D641D45001EE872 /* ExclusionDate.swift */; }; EB48D1641D641D45001EE872 /* ExclusionDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB48D1521D641D45001EE872 /* ExclusionDate.swift */; }; - EB48D1651D641D45001EE872 /* Generators.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB48D1531D641D45001EE872 /* Generators.swift */; }; + EB48D1651D641D45001EE872 /* Iterators.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB48D1531D641D45001EE872 /* Iterators.swift */; }; EB48D1671D641D45001EE872 /* InclusionDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB48D1541D641D45001EE872 /* InclusionDate.swift */; }; EB48D1681D641D45001EE872 /* InclusionDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB48D1541D641D45001EE872 /* InclusionDate.swift */; }; EB48D1691D641D45001EE872 /* JavaScriptBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB48D1551D641D45001EE872 /* JavaScriptBridge.swift */; }; @@ -34,7 +34,7 @@ EB48D1371D641AD6001EE872 /* RRuleSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RRuleSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EB48D1511D641D45001EE872 /* EKWeekday+RRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EKWeekday+RRule.swift"; sourceTree = ""; }; EB48D1521D641D45001EE872 /* ExclusionDate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExclusionDate.swift; sourceTree = ""; }; - EB48D1531D641D45001EE872 /* Generators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generators.swift; sourceTree = ""; }; + EB48D1531D641D45001EE872 /* Iterators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Iterators.swift; sourceTree = ""; }; EB48D1541D641D45001EE872 /* InclusionDate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InclusionDate.swift; sourceTree = ""; }; EB48D1551D641D45001EE872 /* JavaScriptBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JavaScriptBridge.swift; sourceTree = ""; }; EB48D1571D641D45001EE872 /* nlp.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = nlp.js; sourceTree = ""; }; @@ -91,7 +91,7 @@ EB48D15C1D641D45001EE872 /* RRule.swift */, EB48D1521D641D45001EE872 /* ExclusionDate.swift */, EB48D1541D641D45001EE872 /* InclusionDate.swift */, - EB48D1531D641D45001EE872 /* Generators.swift */, + EB48D1531D641D45001EE872 /* Iterators.swift */, EB48D1551D641D45001EE872 /* JavaScriptBridge.swift */, EB48D1511D641D45001EE872 /* EKWeekday+RRule.swift */, EB48D1591D641D45001EE872 /* NSDate+Comparison.swift */, @@ -184,14 +184,16 @@ D31B138E1CA8E02E00D0B863 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0730; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = Teambition; TargetAttributes = { D31B13961CA8E02E00D0B863 = { CreatedOnToolsVersion = 7.3; + LastSwiftMigration = 0800; }; EB48D1361D641AD6001EE872 = { CreatedOnToolsVersion = 7.3; + LastSwiftMigration = 0800; }; }; }; @@ -241,7 +243,7 @@ EB48D1631D641D45001EE872 /* ExclusionDate.swift in Sources */, EB48D1711D641D45001EE872 /* RecurrenceFrequency.swift in Sources */, EB48D1611D641D45001EE872 /* EKWeekday+RRule.swift in Sources */, - EB48D1651D641D45001EE872 /* Generators.swift in Sources */, + EB48D1651D641D45001EE872 /* Iterators.swift in Sources */, EB48D1731D641D45001EE872 /* RecurrenceRule.swift in Sources */, EB48D1691D641D45001EE872 /* JavaScriptBridge.swift in Sources */, EB48D16F1D641D45001EE872 /* NSDate+Comparison.swift in Sources */, @@ -280,8 +282,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -329,8 +333,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -350,6 +356,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -361,6 +368,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -368,10 +376,11 @@ INFOPLIST_FILE = "Sources/Supporting Files/Info-iOS.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = Teambition.RRuleSwift; + PRODUCT_BUNDLE_IDENTIFIER = "Teambition.RRuleSwift-iOS"; PRODUCT_NAME = RRuleSwift; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -379,6 +388,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -386,9 +396,10 @@ INFOPLIST_FILE = "Sources/Supporting Files/Info-iOS.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = Teambition.RRuleSwift; + PRODUCT_BUNDLE_IDENTIFIER = "Teambition.RRuleSwift-iOS"; PRODUCT_NAME = RRuleSwift; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -397,6 +408,7 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -404,14 +416,15 @@ INFOPLIST_FILE = "Sources/Supporting Files/Info-watchOS.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.Teambition.RRuleSwift-watchOS"; + PRODUCT_BUNDLE_IDENTIFIER = "Teambition.RRuleSwift-watchOS"; PRODUCT_NAME = RRuleSwift; SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_OBJC_INTERFACE_HEADER_NAME = "$(SWIFT_MODULE_NAME).h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 2.2; + WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Debug; }; @@ -420,6 +433,7 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -427,13 +441,14 @@ INFOPLIST_FILE = "Sources/Supporting Files/Info-watchOS.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.Teambition.RRuleSwift-watchOS"; + PRODUCT_BUNDLE_IDENTIFIER = "Teambition.RRuleSwift-watchOS"; PRODUCT_NAME = RRuleSwift; SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_OBJC_INTERFACE_HEADER_NAME = "$(SWIFT_MODULE_NAME).h"; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 2.2; + WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Release; }; diff --git a/RRuleSwift.xcodeproj/xcshareddata/xcschemes/RRuleSwift iOS.xcscheme b/RRuleSwift.xcodeproj/xcshareddata/xcschemes/RRuleSwift iOS.xcscheme index b47ef23..b0b99fc 100644 --- a/RRuleSwift.xcodeproj/xcshareddata/xcschemes/RRuleSwift iOS.xcscheme +++ b/RRuleSwift.xcodeproj/xcshareddata/xcschemes/RRuleSwift iOS.xcscheme @@ -1,6 +1,6 @@ Void)) { + func widgetPerformUpdate(_ completionHandler: (@escaping (NCUpdateResult) -> Void)) { // Perform any setup necessary in order to update the view. // If an error is encountered, use NCUpdateResult.Failed // If there's no update required, use NCUpdateResult.NoData // If there's an update, use NCUpdateResult.NewData - completionHandler(NCUpdateResult.NewData) + completionHandler(NCUpdateResult.newData) } } diff --git a/RRuleSwiftExample-Watch Extension/Info.plist b/RRuleSwiftExample-Watch Extension/Info.plist index c513da1..141ddbe 100644 --- a/RRuleSwiftExample-Watch Extension/Info.plist +++ b/RRuleSwiftExample-Watch Extension/Info.plist @@ -27,7 +27,7 @@ NSExtensionAttributes WKAppBundleIdentifier - Teambition.RRuleSwiftExample.watchkitapp + Teambition.RRuleSwiftExample.WatchKitApp NSExtensionPointIdentifier com.apple.watchkit diff --git a/RRuleSwiftExample-Watch Extension/InterfaceController.swift b/RRuleSwiftExample-Watch Extension/InterfaceController.swift index 380ab87..689260a 100644 --- a/RRuleSwiftExample-Watch Extension/InterfaceController.swift +++ b/RRuleSwiftExample-Watch Extension/InterfaceController.swift @@ -11,54 +11,20 @@ import Foundation import RRuleSwift class InterfaceController: WKInterfaceController { - let dateFormatter: NSDateFormatter = { - let dateFormatter = NSDateFormatter() - dateFormatter.timeZone = NSTimeZone.defaultTimeZone() - dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") + let dateFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.timeZone = TimeZone.current + dateFormatter.locale = Locale(identifier: "en_US_POSIX") dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss EEE" return dateFormatter }() - override func awakeWithContext(context: AnyObject?) { - super.awakeWithContext(context) + override func awake(withContext context: Any?) { + super.awake(withContext: context) let ruleString = "RRULE:FREQ=WEEKLY;DTSTART=20151119T014500Z;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR;UNTIL=20170101T014500Z" - var rule = RecurrenceRule(recurrenceWithRRuleString: ruleString)! - rule.exdate = ExclusionDate(exdateString: "EXDATE:20151120T014500Z,20151123T014500Z,20151126T014500Z,20151127T014500Z,20151130T014500Z,20151201T014500Z,20151202T014500Z,20151203T014500Z,20151207T014500Z,20151210T014500Z,20151211T014500Z,20151215T014500Z,20151216T014500Z,20151217T014500Z,20151221T014500Z,20151222T014500Z,20151223T014500Z,20151228T014500Z,20151229T014500Z", unitGranularity: .Day) - rule.toRRuleString() - -// let occurrences1 = rule.occurrencesBetween(date: date1, andDate: date2) -// let occurrences2 = rule.occurrencesBetween(date: date1, andDate: date2) -// let occurrences3 = rule.occurrencesBetween(date: date1, andDate: date2) -// let occurrences4 = rule.occurrencesBetween(date: date1, andDate: date2) -// let occurrences5 = rule.occurrencesBetween(date: date1, andDate: date2) -// let occurrences6 = rule.occurrencesBetween(date: date1, andDate: date2) -// let occurrences7 = rule.occurrencesBetween(date: date1, andDate: date2) -// let occurrences8 = rule.occurrencesBetween(date: date1, andDate: date2) -// let occurrences9 = rule.occurrencesBetween(date: date1, andDate: date2) -// let occurrences10 = rule.occurrencesBetween(date: date1, andDate: date2) -// let _ = rule.occurrencesBetween(date: date1, andDate: date2) -// let _ = rule.occurrencesBetween(date: date1, andDate: date2) -// let _ = rule.occurrencesBetween(date: date1, andDate: date2) -// let _ = rule.occurrencesBetween(date: date1, andDate: date2) -// let _ = rule.occurrencesBetween(date: date1, andDate: date2) -// let _ = rule.occurrencesBetween(date: date1, andDate: date2) -// let _ = rule.occurrencesBetween(date: date1, andDate: date2) -// let _ = rule.occurrencesBetween(date: date1, andDate: date2) -// let _ = rule.occurrencesBetween(date: date1, andDate: date2) -// let _ = rule.occurrencesBetween(date: date1, andDate: date2) -// -// print(occurrences1) -// print(occurrences2) -// print(occurrences3) -// print(occurrences4) -// print(occurrences5) -// print(occurrences6) -// print(occurrences7) -// print(occurrences8) -// print(occurrences9) -// print(occurrences10) - - // Configure interface objects here. + var rule = RecurrenceRule(rruleString: ruleString)! + rule.exdate = ExclusionDate(exdateString: "EXDATE:20151120T014500Z,20151123T014500Z,20151126T014500Z,20151127T014500Z,20151130T014500Z,20151201T014500Z,20151202T014500Z,20151203T014500Z,20151207T014500Z,20151210T014500Z,20151211T014500Z,20151215T014500Z,20151216T014500Z,20151217T014500Z,20151221T014500Z,20151222T014500Z,20151223T014500Z,20151228T014500Z,20151229T014500Z", granularity: .day) + let _ = rule.toRRuleString() } override func willActivate() { diff --git a/RRuleSwiftExample-Watch Extension/NotificationController.swift b/RRuleSwiftExample-Watch Extension/NotificationController.swift index a60ddc0..018e18e 100644 --- a/RRuleSwiftExample-Watch Extension/NotificationController.swift +++ b/RRuleSwiftExample-Watch Extension/NotificationController.swift @@ -28,26 +28,4 @@ class NotificationController: WKUserNotificationInterfaceController { // This method is called when watch view controller is no longer visible super.didDeactivate() } - - /* - override func didReceiveLocalNotification(localNotification: UILocalNotification, withCompletion completionHandler: ((WKUserNotificationInterfaceType) -> Void)) { - // This method is called when a local notification needs to be presented. - // Implement it if you use a dynamic notification interface. - // Populate your dynamic notification interface as quickly as possible. - // - // After populating your dynamic notification interface call the completion block. - completionHandler(.Custom) - } - */ - - /* - override func didReceiveRemoteNotification(remoteNotification: [NSObject : AnyObject], withCompletion completionHandler: ((WKUserNotificationInterfaceType) -> Void)) { - // This method is called when a remote notification needs to be presented. - // Implement it if you use a dynamic notification interface. - // Populate your dynamic notification interface as quickly as possible. - // - // After populating your dynamic notification interface call the completion block. - completionHandler(.Custom) - } - */ } diff --git a/RRuleSwiftExample-Watch/Info.plist b/RRuleSwiftExample-Watch/Info.plist index 84a4bfb..b43b374 100644 --- a/RRuleSwiftExample-Watch/Info.plist +++ b/RRuleSwiftExample-Watch/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion en CFBundleDisplayName - RRuleSwiftExample + RRuleSwiftExample-Watch CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/RRuleSwiftExample.xcodeproj/project.pbxproj b/RRuleSwiftExample.xcodeproj/project.pbxproj index 316b188..0d5d988 100644 --- a/RRuleSwiftExample.xcodeproj/project.pbxproj +++ b/RRuleSwiftExample.xcodeproj/project.pbxproj @@ -16,9 +16,9 @@ D325D6491D0574E00021E620 /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D325D6481D0574E00021E620 /* NotificationCenter.framework */; }; D325D64C1D0574E00021E620 /* TodayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D325D64B1D0574E00021E620 /* TodayViewController.swift */; }; D325D64F1D0574E00021E620 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D325D64D1D0574E00021E620 /* MainInterface.storyboard */; }; - D342193D1D65576800A3B8E0 /* RRuleSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D342193C1D65576800A3B8E0 /* RRuleSwift.framework */; }; - D342193F1D65577F00A3B8E0 /* RRuleSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D342193E1D65577F00A3B8E0 /* RRuleSwift.framework */; }; - D34219401D65579400A3B8E0 /* RRuleSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D342193C1D65576800A3B8E0 /* RRuleSwift.framework */; }; + D3652DC11D937FB50025EC3C /* RRuleSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D3652DC01D937FB50025EC3C /* RRuleSwift.framework */; }; + D3652DC21D937FBC0025EC3C /* RRuleSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D3652DC01D937FB50025EC3C /* RRuleSwift.framework */; }; + D3652DC41D937FC70025EC3C /* RRuleSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D3652DC31D937FC70025EC3C /* RRuleSwift.framework */; }; EB48D1831D64202E001EE872 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EB48D1811D64202E001EE872 /* Interface.storyboard */; }; EB48D1851D64202E001EE872 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EB48D1841D64202E001EE872 /* Assets.xcassets */; }; EB48D18C1D64202E001EE872 /* RRuleSwiftExample-Watch Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = EB48D18B1D64202E001EE872 /* RRuleSwiftExample-Watch Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -95,8 +95,8 @@ D325D64B1D0574E00021E620 /* TodayViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayViewController.swift; sourceTree = ""; }; D325D64E1D0574E00021E620 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; D325D6501D0574E00021E620 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D342193C1D65576800A3B8E0 /* RRuleSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RRuleSwift.framework; path = "../../Library/Developer/Xcode/DerivedData/RRuleSwift-bsfdvctfhfasceecxzjeffpridns/Build/Products/Debug-iphoneos/RRuleSwift.framework"; sourceTree = ""; }; - D342193E1D65577F00A3B8E0 /* RRuleSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RRuleSwift.framework; path = "../../Library/Developer/Xcode/DerivedData/RRuleSwift-bsfdvctfhfasceecxzjeffpridns/Build/Products/Debug-watchos/RRuleSwift.framework"; sourceTree = ""; }; + D3652DC01D937FB50025EC3C /* RRuleSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RRuleSwift.framework; path = "../../Library/Developer/Xcode/DerivedData/RRuleSwift-bsfdvctfhfasceecxzjeffpridns/Build/Products/Debug-iphonesimulator/RRuleSwift.framework"; sourceTree = ""; }; + D3652DC31D937FC70025EC3C /* RRuleSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RRuleSwift.framework; path = "../../Library/Developer/Xcode/DerivedData/RRuleSwift-bsfdvctfhfasceecxzjeffpridns/Build/Products/Debug-watchsimulator/RRuleSwift.framework"; sourceTree = ""; }; EB48D17F1D64202E001EE872 /* RRuleSwiftExample-Watch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RRuleSwiftExample-Watch.app"; sourceTree = BUILT_PRODUCTS_DIR; }; EB48D1821D64202E001EE872 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = ""; }; EB48D1841D64202E001EE872 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -115,7 +115,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D342193D1D65576800A3B8E0 /* RRuleSwift.framework in Frameworks */, + D3652DC11D937FB50025EC3C /* RRuleSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -123,8 +123,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D3652DC21D937FBC0025EC3C /* RRuleSwift.framework in Frameworks */, D325D6491D0574E00021E620 /* NotificationCenter.framework in Frameworks */, - D34219401D65579400A3B8E0 /* RRuleSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -132,7 +132,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D342193F1D65577F00A3B8E0 /* RRuleSwift.framework in Frameworks */, + D3652DC41D937FC70025EC3C /* RRuleSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -179,8 +179,8 @@ D325D6471D0574E00021E620 /* Frameworks */ = { isa = PBXGroup; children = ( - D342193E1D65577F00A3B8E0 /* RRuleSwift.framework */, - D342193C1D65576800A3B8E0 /* RRuleSwift.framework */, + D3652DC31D937FC70025EC3C /* RRuleSwift.framework */, + D3652DC01D937FB50025EC3C /* RRuleSwift.framework */, D325D6481D0574E00021E620 /* NotificationCenter.framework */, ); name = Frameworks; @@ -308,20 +308,24 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0730; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = Teambition; TargetAttributes = { D31B13771CA8D8E400D0B863 = { CreatedOnToolsVersion = 7.3; + LastSwiftMigration = 0800; }; D325D6451D0574E00021E620 = { CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 0800; }; EB48D17E1D64202E001EE872 = { CreatedOnToolsVersion = 7.3; + LastSwiftMigration = 0800; }; EB48D18A1D64202E001EE872 = { CreatedOnToolsVersion = 7.3; + LastSwiftMigration = 0800; }; }; }; @@ -470,8 +474,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -516,8 +522,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -536,6 +544,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -549,6 +558,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = Teambition.RRuleSwiftExample; PRODUCT_NAME = RRuleSwiftExample; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -560,6 +570,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = Teambition.RRuleSwiftExample; PRODUCT_NAME = RRuleSwiftExample; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -569,9 +580,10 @@ INFOPLIST_FILE = "RRuleSwiftExample-TodayExtension/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "Teambition.RRuleSwiftExample.RRuleSwiftExample-TodayExtension"; + PRODUCT_BUNDLE_IDENTIFIER = Teambition.RRuleSwiftExample.TodayExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -581,41 +593,44 @@ INFOPLIST_FILE = "RRuleSwiftExample-TodayExtension/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "Teambition.RRuleSwiftExample.RRuleSwiftExample-TodayExtension"; + PRODUCT_BUNDLE_IDENTIFIER = Teambition.RRuleSwiftExample.TodayExtension; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; }; name = Release; }; EB48D19E1D64202E001EE872 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; IBSC_MODULE = RRuleSwiftExample_Watch_Extension; INFOPLIST_FILE = "RRuleSwiftExample-Watch/Info.plist"; - PRODUCT_BUNDLE_IDENTIFIER = Teambition.RRuleSwiftExample.watchkitapp; + PRODUCT_BUNDLE_IDENTIFIER = Teambition.RRuleSwiftExample.WatchKitApp; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 2.2; + WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Debug; }; EB48D19F1D64202E001EE872 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; IBSC_MODULE = RRuleSwiftExample_Watch_Extension; INFOPLIST_FILE = "RRuleSwiftExample-Watch/Info.plist"; - PRODUCT_BUNDLE_IDENTIFIER = Teambition.RRuleSwiftExample.watchkitapp; + PRODUCT_BUNDLE_IDENTIFIER = Teambition.RRuleSwiftExample.WatchKitApp; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 2.2; + WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Release; }; @@ -624,12 +639,13 @@ buildSettings = { INFOPLIST_FILE = "RRuleSwiftExample-Watch Extension/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = Teambition.RRuleSwiftExample.watchkitapp.watchkitextension; + PRODUCT_BUNDLE_IDENTIFIER = Teambition.RRuleSwiftExample.WatchKitApp.WatchKitExtension; PRODUCT_NAME = "${TARGET_NAME}"; SDKROOT = watchos; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 2.2; + WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Debug; }; @@ -638,12 +654,13 @@ buildSettings = { INFOPLIST_FILE = "RRuleSwiftExample-Watch Extension/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = Teambition.RRuleSwiftExample.watchkitapp.watchkitextension; + PRODUCT_BUNDLE_IDENTIFIER = Teambition.RRuleSwiftExample.WatchKitApp.WatchKitExtension; PRODUCT_NAME = "${TARGET_NAME}"; SDKROOT = watchos; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 2.2; + WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Release; }; diff --git a/RRuleSwiftExample.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/RRuleSwiftExample-TodayExtension.xcscheme b/RRuleSwiftExample.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/RRuleSwiftExample-TodayExtension.xcscheme index fc2b506..58e977d 100644 --- a/RRuleSwiftExample.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/RRuleSwiftExample-TodayExtension.xcscheme +++ b/RRuleSwiftExample.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/RRuleSwiftExample-TodayExtension.xcscheme @@ -1,6 +1,6 @@ Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } - func applicationWillResignActive(application: UIApplication) { + func applicationWillResignActive(_ application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. } - func applicationDidEnterBackground(application: UIApplication) { + func applicationDidEnterBackground(_ application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } - func applicationWillEnterForeground(application: UIApplication) { + func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. } - func applicationDidBecomeActive(application: UIApplication) { + func applicationDidBecomeActive(_ application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } - func applicationWillTerminate(application: UIApplication) { + func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } diff --git a/RRuleSwiftExample/RRuleExampleViewController.swift b/RRuleSwiftExample/RRuleExampleViewController.swift index 13b6143..53b9be7 100644 --- a/RRuleSwiftExample/RRuleExampleViewController.swift +++ b/RRuleSwiftExample/RRuleExampleViewController.swift @@ -12,22 +12,22 @@ import RRuleSwift private let kFrequencies = ["Yearly", "Monthly", "Weekly", "Daily", "Hourly", "Minutely", "Secondly"] private let kFrequencyStrings = ["YEARLY", "MONTHLY", "WEEKLY", "DAILY", "HOURLY", "MINUTELY", "SECONDLY"] -private let kFrequenciesDic: [String: RecurrenceFrequency] = ["Yearly": .Yearly, "Monthly": .Monthly, "Weekly": .Weekly, "Daily": .Daily, "Hourly": .Hourly, "Minutely": .Minutely, "Secondly": .Secondly] +private let kFrequenciesDic: [String: RecurrenceFrequency] = ["Yearly": .yearly, "Monthly": .monthly, "Weekly": .weekly, "Daily": .daily, "Hourly": .hourly, "Minutely": .minutely, "Secondly": .secondly] private let kWeekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] -private let kWeekdaysDic: [String: EKWeekday] = ["Monday": .Monday, "Tuesday": .Tuesday, "Wednesday": .Wednesday, "Thursday": .Thursday, "Friday": .Friday, "Saturday": .Saturday, "Sunday": .Sunday] -private let kEKWeekdays: [EKWeekday] = [.Monday, .Tuesday, .Wednesday, .Thursday, .Friday, .Saturday, .Sunday] +private let kWeekdaysDic: [String: EKWeekday] = ["Monday": .monday, "Tuesday": .tuesday, "Wednesday": .wednesday, "Thursday": .thursday, "Friday": .friday, "Saturday": .saturday, "Sunday": .sunday] +private let kEKWeekdays: [EKWeekday] = [.monday, .tuesday, .wednesday, .thursday, .friday, .saturday, .sunday] private let kMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] extension RecurrenceFrequency { func toString() -> String { switch self { - case .Secondly: return "SECONDLY" - case .Minutely: return "MINUTELY" - case .Hourly: return "HOURLY" - case .Daily: return "DAILY" - case .Weekly: return "WEEKLY" - case .Monthly: return "MONTHLY" - case .Yearly: return "YEARLY" + case .secondly: return "SECONDLY" + case .minutely: return "MINUTELY" + case .hourly: return "HOURLY" + case .daily: return "DAILY" + case .weekly: return "WEEKLY" + case .monthly: return "MONTHLY" + case .yearly: return "YEARLY" } } } @@ -35,13 +35,13 @@ extension RecurrenceFrequency { extension EKWeekday { func toNumberSymbol() -> Int { switch self { - case .Monday: return 0 - case .Tuesday: return 1 - case .Wednesday: return 2 - case .Thursday: return 3 - case .Friday: return 4 - case .Saturday: return 5 - case .Sunday: return 6 + case .monday: return 0 + case .tuesday: return 1 + case .wednesday: return 2 + case .thursday: return 3 + case .friday: return 4 + case .saturday: return 5 + case .sunday: return 6 } } } @@ -50,7 +50,7 @@ class RRuleExampleViewController: UIViewController { @IBOutlet weak var textView: UITextView! @IBOutlet weak var tableView: UITableView! - private var rule = RecurrenceRule(frequency: .Daily) { + fileprivate var rule = RecurrenceRule(frequency: .daily) { didSet { textView.text = rule.toRRuleString() tableView.reloadData() @@ -60,29 +60,53 @@ class RRuleExampleViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() setupUI() + testRRuleIterator() } - private func setupUI() { + fileprivate func setupUI() { automaticallyAdjustsScrollViewInsets = false navigationItem.title = "RRuleSwift Example" tableView.tableFooterView = UIView() - tableView.separatorStyle = .None - rule = RecurrenceRule(frequency: .Daily) + tableView.separatorStyle = .none + rule = RecurrenceRule(frequency: .daily) textView.delegate = self - navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Reset", style: .Plain, target: self, action: #selector(resetButtonTapped(_:))) + navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Reset", style: .plain, target: self, action: #selector(resetButtonTapped(_:))) } - func resetButtonTapped(sender: UIBarButtonItem) { - rule = RecurrenceRule(frequency: .Daily) + fileprivate func testRRuleIterator() { + let dateFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.timeZone = TimeZone.current + dateFormatter.locale = Locale(identifier: "en_US_POSIX") + dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss EEE" + return dateFormatter + }() + + var rule = RecurrenceRule(rruleString: "RRULE:FREQ=WEEKLY;DTSTART=20151119T014500Z;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR;UNTIL=20170101T014500Z")! + rule.exdate = ExclusionDate(exdateString: "EXDATE:20151120T014500Z,20151123T014500Z,20151126T014500Z,20151127T014500Z,20151130T014500Z,20151201T014500Z,20151202T014500Z,20151203T014500Z,20151207T014500Z,20151210T014500Z,20151211T014500Z,20151215T014500Z,20151216T014500Z,20151217T014500Z,20151221T014500Z,20151222T014500Z,20151223T014500Z,20151228T014500Z,20151229T014500Z", granularity: .day) + let date = dateFormatter.date(from: "2015-11-01 00:00:00 Sun")! + let otherDate = dateFormatter.date(from: "2016-02-01 00:00:00 Sun")! + + let occurrences = rule.occurrences(between: date, and: otherDate) + + print("\nRRule Occurrences:") + occurrences.forEach { (occurrence) in + print(dateFormatter.string(from: occurrence)) + } + print("\n") + } + + func resetButtonTapped(_ sender: UIBarButtonItem) { + rule = RecurrenceRule(frequency: .daily) } } extension RRuleExampleViewController: UITableViewDataSource, UITableViewDelegate { - func numberOfSectionsInTableView(tableView: UITableView) -> Int { + func numberOfSections(in tableView: UITableView) -> Int { return 9 } - func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch section { case 0: return 1 case 1: return 1 @@ -97,7 +121,7 @@ extension RRuleExampleViewController: UITableViewDataSource, UITableViewDelegate } } - func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { switch section { case 0: return "Frequency" case 1: return "Interval" @@ -112,7 +136,7 @@ extension RRuleExampleViewController: UITableViewDataSource, UITableViewDelegate } } - func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { switch (indexPath.section, indexPath.row) { case (0, _): return kPickerViewCellHeight case (1, _): return kPickerViewCellHeight @@ -124,41 +148,41 @@ extension RRuleExampleViewController: UITableViewDataSource, UITableViewDelegate } } - func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { switch (indexPath.section, indexPath.row) { case (0, _): - let cell = tableView.dequeueReusableCellWithIdentifier(kPickerViewCellID, forIndexPath: indexPath) as! PickerViewCell + let cell = tableView.dequeueReusableCell(withIdentifier: kPickerViewCellID, for: indexPath) as! PickerViewCell cell.pickerView.tag = indexPath.section cell.pickerView.dataSource = self cell.pickerView.delegate = self - cell.pickerView.selectRow(kFrequencyStrings.indexOf(rule.frequency.toString()) ?? 0, inComponent: 0, animated: true) + cell.pickerView.selectRow(kFrequencyStrings.index(of: rule.frequency.toString()) ?? 0, inComponent: 0, animated: true) return cell case (1, _): - let cell = tableView.dequeueReusableCellWithIdentifier(kPickerViewCellID, forIndexPath: indexPath) as! PickerViewCell + let cell = tableView.dequeueReusableCell(withIdentifier: kPickerViewCellID, for: indexPath) as! PickerViewCell cell.pickerView.tag = indexPath.section cell.pickerView.dataSource = self cell.pickerView.delegate = self cell.pickerView.selectRow(rule.interval - 1, inComponent: 0, animated: true) return cell case (2, _): - let cell = tableView.dequeueReusableCellWithIdentifier(kPickerViewCellID, forIndexPath: indexPath) as! PickerViewCell + let cell = tableView.dequeueReusableCell(withIdentifier: kPickerViewCellID, for: indexPath) as! PickerViewCell cell.pickerView.tag = indexPath.section cell.pickerView.dataSource = self cell.pickerView.delegate = self cell.pickerView.selectRow(rule.firstDayOfWeek.toNumberSymbol(), inComponent: 0, animated: true) return cell case (3, _): - let cell = tableView.dequeueReusableCellWithIdentifier(kDatePickerCellID, forIndexPath: indexPath) as! DatePickerCell + let cell = tableView.dequeueReusableCell(withIdentifier: kDatePickerCellID, for: indexPath) as! DatePickerCell cell.datePicker.date = rule.startDate - cell.datePicker.addTarget(self, action: #selector(startDateDidChange(_:)), forControlEvents: .ValueChanged) + cell.datePicker.addTarget(self, action: #selector(startDateDidChange(_:)), for: .valueChanged) return cell case (4, _): - let cell = tableView.dequeueReusableCellWithIdentifier(kDatePickerCellID, forIndexPath: indexPath) as! DatePickerCell - cell.datePicker.date = rule.recurrenceEnd?.endDate ?? NSDate() - cell.datePicker.addTarget(self, action: #selector(endDateDidChange(_:)), forControlEvents: .ValueChanged) + let cell = tableView.dequeueReusableCell(withIdentifier: kDatePickerCellID, for: indexPath) as! DatePickerCell + cell.datePicker.date = rule.recurrenceEnd?.endDate ?? Date() + cell.datePicker.addTarget(self, action: #selector(endDateDidChange(_:)), for: .valueChanged) return cell case (5, _): - let cell = tableView.dequeueReusableCellWithIdentifier(kPickerViewCellID, forIndexPath: indexPath) as! PickerViewCell + let cell = tableView.dequeueReusableCell(withIdentifier: kPickerViewCellID, for: indexPath) as! PickerViewCell cell.pickerView.tag = indexPath.section cell.pickerView.dataSource = self cell.pickerView.delegate = self @@ -169,128 +193,122 @@ extension RRuleExampleViewController: UITableViewDataSource, UITableViewDelegate } return cell case (6, _): - var cell = tableView.dequeueReusableCellWithIdentifier("Cell") + var cell = tableView.dequeueReusableCell(withIdentifier: "Cell") if cell == nil { - cell = UITableViewCell(style: .Default, reuseIdentifier: "Cell") + cell = UITableViewCell(style: .default, reuseIdentifier: "Cell") } - cell?.selectionStyle = .None - cell?.textLabel?.font = UIFont.systemFontOfSize(15) + cell?.selectionStyle = .none + cell?.textLabel?.font = UIFont.systemFont(ofSize: 15) cell?.textLabel?.text = kWeekdays[indexPath.row] - let byweekday = rule.byweekday ?? [] - let weekdays = byweekday.map({ (weekday) -> NSIndexPath in - return NSIndexPath(forRow: weekday.toNumberSymbol(), inSection: 6) + let weekdays = rule.byweekday.map({ (weekday) -> IndexPath in + return IndexPath(row: weekday.toNumberSymbol(), section: 6) }) if weekdays.contains(indexPath) { - cell?.accessoryType = .Checkmark + cell?.accessoryType = .checkmark } else { - cell?.accessoryType = .None + cell?.accessoryType = .none } return cell! case (7, _): - var cell = tableView.dequeueReusableCellWithIdentifier("Cell") + var cell = tableView.dequeueReusableCell(withIdentifier: "Cell") if cell == nil { - cell = UITableViewCell(style: .Default, reuseIdentifier: "Cell") + cell = UITableViewCell(style: .default, reuseIdentifier: "Cell") } - cell?.selectionStyle = .None - cell?.textLabel?.font = UIFont.systemFontOfSize(15) + cell?.selectionStyle = .none + cell?.textLabel?.font = UIFont.systemFont(ofSize: 15) cell?.textLabel?.text = kMonths[indexPath.row] - let bymonths = rule.bymonth ?? [] - let months = bymonths.map({ (month) -> NSIndexPath in - return NSIndexPath(forRow: month - 1, inSection: 7) + let months = rule.bymonth.map({ (month) -> IndexPath in + return IndexPath(row: month - 1, section: 7) }) if months.contains(indexPath) { - cell?.accessoryType = .Checkmark + cell?.accessoryType = .checkmark } else { - cell?.accessoryType = .None + cell?.accessoryType = .none } return cell! case (8, _): - var cell = tableView.dequeueReusableCellWithIdentifier("Cell") + var cell = tableView.dequeueReusableCell(withIdentifier: "Cell") if cell == nil { - cell = UITableViewCell(style: .Default, reuseIdentifier: "Cell") + cell = UITableViewCell(style: .default, reuseIdentifier: "Cell") } - cell?.selectionStyle = .None - cell?.textLabel?.font = UIFont.systemFontOfSize(15) + cell?.selectionStyle = .none + cell?.textLabel?.font = UIFont.systemFont(ofSize: 15) cell?.textLabel?.text = String(indexPath.row + 1) - let bymonthday = rule.bymonthday ?? [] - let monthdays = bymonthday.map({ (month) -> NSIndexPath in - return NSIndexPath(forRow: month - 1, inSection: 8) + let monthdays = rule.bymonthday.map({ (month) -> IndexPath in + return IndexPath(row: month - 1, section: 8) }) if monthdays.contains(indexPath) { - cell?.accessoryType = .Checkmark + cell?.accessoryType = .checkmark } else { - cell?.accessoryType = .None + cell?.accessoryType = .none } return cell! default: - var cell = tableView.dequeueReusableCellWithIdentifier("Cell") + var cell = tableView.dequeueReusableCell(withIdentifier: "Cell") if cell == nil { - cell = UITableViewCell(style: .Default, reuseIdentifier: "Cell") + cell = UITableViewCell(style: .default, reuseIdentifier: "Cell") } - cell?.textLabel?.font = UIFont.systemFontOfSize(15) + cell?.textLabel?.font = UIFont.systemFont(ofSize: 15) return cell! } } - func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { - tableView.deselectRowAtIndexPath(indexPath, animated: true) + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) switch (indexPath.section, indexPath.row) { case (6, _): - let cell = tableView.cellForRowAtIndexPath(indexPath) - let byweekday = rule.byweekday ?? [] - var weekdays = byweekday.map({ (weekday) -> NSIndexPath in - return NSIndexPath(forRow: weekday.toNumberSymbol(), inSection: 6) + let cell = tableView.cellForRow(at: indexPath) + var weekdays = rule.byweekday.map({ (weekday) -> IndexPath in + return IndexPath(row: weekday.toNumberSymbol(), section: 6) }) if weekdays.contains(indexPath) { - cell?.accessoryType = .None - weekdays.removeAtIndex(weekdays.indexOf(indexPath)!) + cell?.accessoryType = .none + weekdays.remove(at: weekdays.index(of: indexPath)!) rule.byweekday = weekdays.map({ (indexPath) -> EKWeekday in return kEKWeekdays[indexPath.row] - }).sort(<) + }).sorted(by: <) } else { - cell?.accessoryType = .Checkmark + cell?.accessoryType = .checkmark weekdays.append(indexPath) rule.byweekday = weekdays.map({ (indexPath) -> EKWeekday in return kEKWeekdays[indexPath.row] - }).sort(<) + }).sorted(by: <) } case (7, _): - let cell = tableView.cellForRowAtIndexPath(indexPath) - let bymonths = rule.bymonth ?? [] - var months = bymonths.map({ (month) -> NSIndexPath in - return NSIndexPath(forRow: month - 1, inSection: 7) + let cell = tableView.cellForRow(at: indexPath) + var months = rule.bymonth.map({ (month) -> IndexPath in + return IndexPath(row: month - 1, section: 7) }) if months.contains(indexPath) { - cell?.accessoryType = .None - months.removeAtIndex(months.indexOf(indexPath)!) + cell?.accessoryType = .none + months.remove(at: months.index(of: indexPath)!) rule.bymonth = months.map({ (indexPath) -> Int in return indexPath.row + 1 - }).sort(<) + }).sorted(by: <) } else { - cell?.accessoryType = .Checkmark + cell?.accessoryType = .checkmark months.append(indexPath) rule.bymonth = months.map({ (indexPath) -> Int in return indexPath.row + 1 - }).sort(<) + }).sorted(by: <) } case (8, _): - let cell = tableView.cellForRowAtIndexPath(indexPath) - let bymonthday = rule.bymonthday ?? [] - var monthdays = bymonthday.map({ (month) -> NSIndexPath in - return NSIndexPath(forRow: month - 1, inSection: 8) + let cell = tableView.cellForRow(at: indexPath) + var monthdays = rule.bymonthday.map({ (month) -> IndexPath in + return IndexPath(row: month - 1, section: 8) }) if monthdays.contains(indexPath) { - cell?.accessoryType = .None - monthdays.removeAtIndex(monthdays.indexOf(indexPath)!) + cell?.accessoryType = .none + monthdays.remove(at: monthdays.index(of: indexPath)!) rule.bymonthday = monthdays.map({ (indexPath) -> Int in return indexPath.row + 1 - }).sort(<) + }).sorted(by: <) } else { - cell?.accessoryType = .Checkmark + cell?.accessoryType = .checkmark monthdays.append(indexPath) rule.bymonthday = monthdays.map({ (indexPath) -> Int in return indexPath.row + 1 - }).sort(<) + }).sorted(by: <) } default: break @@ -299,7 +317,7 @@ extension RRuleExampleViewController: UITableViewDataSource, UITableViewDelegate } extension RRuleExampleViewController: UIPickerViewDataSource, UIPickerViewDelegate { - func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int { + func numberOfComponents(in pickerView: UIPickerView) -> Int { switch pickerView.tag { case 0: return 1 case 1: return 1 @@ -309,7 +327,7 @@ extension RRuleExampleViewController: UIPickerViewDataSource, UIPickerViewDelega } } - func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { switch pickerView.tag { case 0: return kFrequencies.count case 1: return 999 @@ -319,11 +337,11 @@ extension RRuleExampleViewController: UIPickerViewDataSource, UIPickerViewDelega } } - func pickerView(pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { + func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { return 30 } - func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { switch pickerView.tag { case 0: return kFrequencies[row] @@ -337,7 +355,7 @@ extension RRuleExampleViewController: UIPickerViewDataSource, UIPickerViewDelega } } - func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { switch pickerView.tag { case 0: rule.frequency = kFrequenciesDic[kFrequencies[row]]! @@ -353,24 +371,24 @@ extension RRuleExampleViewController: UIPickerViewDataSource, UIPickerViewDelega } extension RRuleExampleViewController { - func startDateDidChange(datePicker: UIDatePicker) { + func startDateDidChange(_ datePicker: UIDatePicker) { rule.startDate = datePicker.date } - func endDateDidChange(datePicker: UIDatePicker) { - rule.recurrenceEnd = EKRecurrenceEnd(endDate: datePicker.date) + func endDateDidChange(_ datePicker: UIDatePicker) { + rule.recurrenceEnd = EKRecurrenceEnd(end: datePicker.date) } } extension RRuleExampleViewController: UITextViewDelegate { - func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool { - if !textView.hasText() && text == "" { + func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { + if !textView.hasText && text == "" { return false } if text == "\n" { textView.resignFirstResponder() - rule = RecurrenceRule(recurrenceWithRRuleString: textView.text) ?? RecurrenceRule(frequency: .Daily) + rule = RecurrenceRule(rruleString: textView.text) ?? RecurrenceRule(frequency: .daily) return false } return true diff --git a/Sources/EKWeekday+RRule.swift b/Sources/EKWeekday+RRule.swift index 74cf1bc..5793941 100644 --- a/Sources/EKWeekday+RRule.swift +++ b/Sources/EKWeekday+RRule.swift @@ -11,37 +11,37 @@ import EventKit internal extension EKWeekday { internal func toSymbol() -> String { switch self { - case .Monday: return "MO" - case .Tuesday: return "TU" - case .Wednesday: return "WE" - case .Thursday: return "TH" - case .Friday: return "FR" - case .Saturday: return "SA" - case .Sunday: return "SU" + case .monday: return "MO" + case .tuesday: return "TU" + case .wednesday: return "WE" + case .thursday: return "TH" + case .friday: return "FR" + case .saturday: return "SA" + case .sunday: return "SU" } } internal func toNumberSymbol() -> Int { switch self { - case .Monday: return 0 - case .Tuesday: return 1 - case .Wednesday: return 2 - case .Thursday: return 3 - case .Friday: return 4 - case .Saturday: return 5 - case .Sunday: return 6 + case .monday: return 0 + case .tuesday: return 1 + case .wednesday: return 2 + case .thursday: return 3 + case .friday: return 4 + case .saturday: return 5 + case .sunday: return 6 } } - internal static func weekdayFromSymbol(symbol: String) -> EKWeekday? { + internal static func weekdayFromSymbol(_ symbol: String) -> EKWeekday? { switch symbol { - case "MO", "0": return EKWeekday.Monday - case "TU", "1": return EKWeekday.Tuesday - case "WE", "2": return EKWeekday.Wednesday - case "TH", "3": return EKWeekday.Thursday - case "FR", "4": return EKWeekday.Friday - case "SA", "5": return EKWeekday.Saturday - case "SU", "6": return EKWeekday.Sunday + case "MO", "0": return EKWeekday.monday + case "TU", "1": return EKWeekday.tuesday + case "WE", "2": return EKWeekday.wednesday + case "TH", "3": return EKWeekday.thursday + case "FR", "4": return EKWeekday.friday + case "SA", "5": return EKWeekday.saturday + case "SU", "6": return EKWeekday.sunday default: return nil } } diff --git a/Sources/ExclusionDate.swift b/Sources/ExclusionDate.swift index 3559fef..b75f168 100644 --- a/Sources/ExclusionDate.swift +++ b/Sources/ExclusionDate.swift @@ -10,46 +10,45 @@ import Foundation public struct ExclusionDate { /// All exclusion dates. - public private(set) var dates = [NSDate]() - /// The unit of ExclusionDate, used to decide which exdate will be excluded. - public private(set) var unit: NSCalendarUnit! + public fileprivate(set) var dates = [Date]() + /// The component of ExclusionDate, used to decide which exdate will be excluded. + public fileprivate(set) var component: Calendar.Component! - public init(dates: [NSDate], unitGranularity unit: NSCalendarUnit) { + public init(dates: [Date], granularity component: Calendar.Component) { self.dates = dates - self.unit = unit + self.component = component } - public init?(exdateString string: String, unitGranularity unit: NSCalendarUnit) { - let string = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) - guard let range = string.rangeOfString("EXDATE:") where range.startIndex == string.startIndex else { -// print("error: invalid exdate string, must be started with 'EXDATE:'") + public init?(exdateString string: String, granularity component: Calendar.Component) { + let string = string.trimmingCharacters(in: .whitespaces) + guard let range = string.range(of: "EXDATE:"), range.lowerBound == string.startIndex else { return nil } - let exdateString = string.substringFromIndex(range.endIndex) - let exdates = exdateString.componentsSeparatedByString(",").flatMap { (dateString) -> String? in + let exdateString = string.substring(from: range.upperBound) + let exdates = exdateString.components(separatedBy: ",").flatMap { (dateString) -> String? in if (dateString.isEmpty || dateString.characters.count == 0) { return nil } return dateString } - self.dates = exdates.flatMap({ (dateString) -> NSDate? in - return RRule.dateFormatter.dateFromString(dateString) + self.dates = exdates.flatMap({ (dateString) -> Date? in + return RRule.dateFormatter.date(from: dateString) }) - self.unit = unit + self.component = component } public func toExDateString() -> String { var exdateString = "EXDATE:" let dateStrings = dates.map { (date) -> String in - return RRule.dateFormatter.stringFromDate(date) + return RRule.dateFormatter.string(from: date) } if dateStrings.count > 0 { - exdateString += dateStrings.joinWithSeparator(",") + exdateString += dateStrings.joined(separator: ",") } - if exdateString.substringFromIndex(exdateString.endIndex.advancedBy(-1)) == "," { - exdateString.removeAtIndex(exdateString.endIndex.advancedBy(-1)) + if exdateString.substring(from: exdateString.characters.index(exdateString.endIndex, offsetBy: -1)) == "," { + exdateString.remove(at: exdateString.characters.index(exdateString.endIndex, offsetBy: -1)) } return exdateString diff --git a/Sources/Generators.swift b/Sources/Generators.swift deleted file mode 100644 index b1d2432..0000000 --- a/Sources/Generators.swift +++ /dev/null @@ -1,94 +0,0 @@ -// -// Generators.swift -// RRuleSwift -// -// Created by Xin Hong on 16/3/29. -// Copyright © 2016年 Teambition. All rights reserved. -// - -import Foundation -import JavaScriptCore - -internal struct Generator { - static let endlessRecurrenceCount = 500 - static let rruleContext: JSContext? = { - guard let rrulejs = JavaScriptBridge.rrulejs() else { - return nil - } - let context = JSContext() - context.exceptionHandler = { context, exception in - print("[RRuleSwift] rrule.js error: \(exception)") - } - context.evaluateScript(rrulejs) - return context - }() -} - -public extension RecurrenceRule { - public func allOccurrences(endlessRecurrenceCount: Int = Generator.endlessRecurrenceCount) -> [NSDate] { - guard let _ = JavaScriptBridge.rrulejs() else { - return [] - } - - let ruleJSONString = toJSONString(endlessRecurrenceCount: endlessRecurrenceCount) - Generator.rruleContext?.evaluateScript("var rule = new RRule({ \(ruleJSONString) })") - guard let allOccurrences = Generator.rruleContext?.evaluateScript("rule.all()").toArray() as? [NSDate] else { - return [] - } - - var occurrences = allOccurrences - if let rdates = rdate?.dates { - occurrences.appendContentsOf(rdates) - } - - if let exdates = exdate?.dates, unit = exdate?.unit { - for occurrence in occurrences { - for exdate in exdates { - if calendar.isDate(occurrence, equalToDate: exdate, toUnitGranularity: unit) { - let index = occurrences.indexOf(occurrence)! - occurrences.removeAtIndex(index) - break - } - } - } - } - - return occurrences.sort { $0.isBeforeOrSameWith($1) } - } - - public func occurrencesBetween(date date: NSDate, andDate otherDate: NSDate, endlessRecurrenceCount: Int = Generator.endlessRecurrenceCount) -> [NSDate] { - guard let _ = JavaScriptBridge.rrulejs() else { - return [] - } - - let beginDate = date.isBeforeOrSameWith(otherDate) ? date : otherDate - let untilDate = otherDate.isAfterOrSameWith(date) ? otherDate : date - let beginDateJSON = RRule.ISO8601DateFormatter.stringFromDate(beginDate) - let untilDateJSON = RRule.ISO8601DateFormatter.stringFromDate(untilDate) - - let ruleJSONString = toJSONString(endlessRecurrenceCount: endlessRecurrenceCount) - Generator.rruleContext?.evaluateScript("var rule = new RRule({ \(ruleJSONString) })") - guard let betweenOccurrences = Generator.rruleContext?.evaluateScript("rule.between(new Date('\(beginDateJSON)'), new Date('\(untilDateJSON)'))").toArray() as? [NSDate] else { - return [] - } - - var occurrences = betweenOccurrences - if let rdates = rdate?.dates { - occurrences.appendContentsOf(rdates) - } - - if let exdates = exdate?.dates, unit = exdate?.unit { - for occurrence in occurrences { - for exdate in exdates { - if calendar.isDate(occurrence, equalToDate: exdate, toUnitGranularity: unit) { - let index = occurrences.indexOf(occurrence)! - occurrences.removeAtIndex(index) - break - } - } - } - } - - return occurrences.sort { $0.isBeforeOrSameWith($1) } - } -} diff --git a/Sources/InclusionDate.swift b/Sources/InclusionDate.swift index 8c1f24f..14dc66a 100644 --- a/Sources/InclusionDate.swift +++ b/Sources/InclusionDate.swift @@ -10,42 +10,41 @@ import Foundation public struct InclusionDate { /// All inclusive dates. - public private(set) var dates = [NSDate]() + public fileprivate(set) var dates = [Date]() - public init(dates: [NSDate]) { + public init(dates: [Date]) { self.dates = dates } public init?(rdateString string: String) { - let string = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) - guard let range = string.rangeOfString("RDATE:") where range.startIndex == string.startIndex else { -// print("error: invalid rdate string, must be started with 'RDATE:'") + let string = string.trimmingCharacters(in: .whitespaces) + guard let range = string.range(of: "RDATE:"), range.lowerBound == string.startIndex else { return nil } - let rdateString = string.substringFromIndex(range.endIndex) - let rdates = rdateString.componentsSeparatedByString(",").flatMap { (dateString) -> String? in + let rdateString = string.substring(from: range.upperBound) + let rdates = rdateString.components(separatedBy: ",").flatMap { (dateString) -> String? in if (dateString.isEmpty || dateString.characters.count == 0) { return nil } return dateString } - self.dates = rdates.flatMap({ (dateString) -> NSDate? in - return RRule.dateFormatter.dateFromString(dateString) + self.dates = rdates.flatMap({ (dateString) -> Date? in + return RRule.dateFormatter.date(from: dateString) }) } public func toRDateString() -> String { var rdateString = "RDATE:" let dateStrings = dates.map { (date) -> String in - return RRule.dateFormatter.stringFromDate(date) + return RRule.dateFormatter.string(from: date) } if dateStrings.count > 0 { - rdateString += dateStrings.joinWithSeparator(",") + rdateString += dateStrings.joined(separator: ",") } - if rdateString.substringFromIndex(rdateString.endIndex.advancedBy(-1)) == "," { - rdateString.removeAtIndex(rdateString.endIndex.advancedBy(-1)) + if rdateString.substring(from: rdateString.characters.index(rdateString.endIndex, offsetBy: -1)) == "," { + rdateString.remove(at: rdateString.characters.index(rdateString.endIndex, offsetBy: -1)) } return rdateString diff --git a/Sources/Iterators.swift b/Sources/Iterators.swift new file mode 100644 index 0000000..8d3f360 --- /dev/null +++ b/Sources/Iterators.swift @@ -0,0 +1,94 @@ +// +// Iterators.swift +// RRuleSwift +// +// Created by Xin Hong on 16/3/29. +// Copyright © 2016年 Teambition. All rights reserved. +// + +import Foundation +import JavaScriptCore + +internal struct Iterator { + static let endlessRecurrenceCount = 500 + static let rruleContext: JSContext? = { + guard let rrulejs = JavaScriptBridge.rrulejs() else { + return nil + } + let context = JSContext() + context?.exceptionHandler = { context, exception in + print("[RRuleSwift] rrule.js error: \(exception)") + } + let _ = context?.evaluateScript(rrulejs) + return context + }() +} + +public extension RecurrenceRule { + public func allOccurrences(endless endlessRecurrenceCount: Int = Iterator.endlessRecurrenceCount) -> [Date] { + guard let _ = JavaScriptBridge.rrulejs() else { + return [] + } + + let ruleJSONString = toJSONString(endless: endlessRecurrenceCount) + let _ = Iterator.rruleContext?.evaluateScript("var rule = new RRule({ \(ruleJSONString) })") + guard let allOccurrences = Iterator.rruleContext?.evaluateScript("rule.all()").toArray() as? [Date] else { + return [] + } + + var occurrences = allOccurrences + if let rdates = rdate?.dates { + occurrences.append(contentsOf: rdates) + } + + if let exdates = exdate?.dates, let component = exdate?.component { + for occurrence in occurrences { + for exdate in exdates { + if calendar.isDate(occurrence, equalTo: exdate, toGranularity: component) { + let index = occurrences.index(of: occurrence)! + occurrences.remove(at: index) + break + } + } + } + } + + return occurrences.sorted { $0.isBeforeOrSame(with: $1) } + } + + public func occurrences(between date: Date, and otherDate: Date, endless endlessRecurrenceCount: Int = Iterator.endlessRecurrenceCount) -> [Date] { + guard let _ = JavaScriptBridge.rrulejs() else { + return [] + } + + let beginDate = date.isBeforeOrSame(with: otherDate) ? date : otherDate + let untilDate = otherDate.isAfterOrSame(with: date) ? otherDate : date + let beginDateJSON = RRule.ISO8601DateFormatter.string(from: beginDate) + let untilDateJSON = RRule.ISO8601DateFormatter.string(from: untilDate) + + let ruleJSONString = toJSONString(endless: endlessRecurrenceCount) + let _ = Iterator.rruleContext?.evaluateScript("var rule = new RRule({ \(ruleJSONString) })") + guard let betweenOccurrences = Iterator.rruleContext?.evaluateScript("rule.between(new Date('\(beginDateJSON)'), new Date('\(untilDateJSON)'))").toArray() as? [Date] else { + return [] + } + + var occurrences = betweenOccurrences + if let rdates = rdate?.dates { + occurrences.append(contentsOf: rdates) + } + + if let exdates = exdate?.dates, let component = exdate?.component { + for occurrence in occurrences { + for exdate in exdates { + if calendar.isDate(occurrence, equalTo: exdate, toGranularity: component) { + let index = occurrences.index(of: occurrence)! + occurrences.remove(at: index) + break + } + } + } + } + + return occurrences.sorted { $0.isBeforeOrSame(with: $1) } + } +} diff --git a/Sources/JavaScriptBridge.swift b/Sources/JavaScriptBridge.swift index d38fced..aa6b2a0 100644 --- a/Sources/JavaScriptBridge.swift +++ b/Sources/JavaScriptBridge.swift @@ -11,14 +11,13 @@ import EventKit internal struct JavaScriptBridge { internal static func rrulejs() -> String? { - let libPath = NSBundle(identifier: "Teambition.RRuleSwift-iOS")?.pathForResource("rrule", ofType: "js") ?? NSBundle.mainBundle().pathForResource("rrule", ofType: "js") + let libPath = Bundle(identifier: "Teambition.RRuleSwift-iOS")?.path(forResource: "rrule", ofType: "js") ?? Bundle.main.path(forResource: "rrule", ofType: "js") guard let rrulelibPath = libPath else { return nil } do { - let rrulejs = try NSString(contentsOfFile: rrulelibPath, encoding: NSUTF8StringEncoding) as String - return rrulejs + return try String(contentsOfFile: rrulelibPath) } catch _ { return nil } @@ -26,42 +25,42 @@ internal struct JavaScriptBridge { } internal extension RecurrenceFrequency { - private func toJSONFrequency() -> String { + fileprivate func toJSONFrequency() -> String { switch self { - case .Secondly: return "RRule.SECONDLY" - case .Minutely: return "RRule.MINUTELY" - case .Hourly: return "RRule.HOURLY" - case .Daily: return "RRule.DAILY" - case .Weekly: return "RRule.WEEKLY" - case .Monthly: return "RRule.MONTHLY" - case .Yearly: return "RRule.YEARLY" + case .secondly: return "RRule.SECONDLY" + case .minutely: return "RRule.MINUTELY" + case .hourly: return "RRule.HOURLY" + case .daily: return "RRule.DAILY" + case .weekly: return "RRule.WEEKLY" + case .monthly: return "RRule.MONTHLY" + case .yearly: return "RRule.YEARLY" } } } internal extension EKWeekday { - private func toJSONSymbol() -> String { + fileprivate func toJSONSymbol() -> String { switch self { - case .Monday: return "RRule.MO" - case .Tuesday: return "RRule.TU" - case .Wednesday: return "RRule.WE" - case .Thursday: return "RRule.TH" - case .Friday: return "RRule.FR" - case .Saturday: return "RRule.SA" - case .Sunday: return "RRule.SU" + case .monday: return "RRule.MO" + case .tuesday: return "RRule.TU" + case .wednesday: return "RRule.WE" + case .thursday: return "RRule.TH" + case .friday: return "RRule.FR" + case .saturday: return "RRule.SA" + case .sunday: return "RRule.SU" } } } internal extension RecurrenceRule { - internal func toJSONString(endlessRecurrenceCount endlessRecurrenceCount: Int) -> String { + internal func toJSONString(endless endlessRecurrenceCount: Int) -> String { var jsonString = "freq: \(frequency.toJSONFrequency())," jsonString += "interval: \(max(1, interval))," jsonString += "wkst: \(firstDayOfWeek.toJSONSymbol())," - jsonString += "dtstart: new Date('\(RRule.ISO8601DateFormatter.stringFromDate(startDate))')," + jsonString += "dtstart: new Date('\(RRule.ISO8601DateFormatter.string(from: startDate))')," if let endDate = recurrenceEnd?.endDate { - jsonString += "until: new Date('\(RRule.ISO8601DateFormatter.stringFromDate(endDate))')," + jsonString += "until: new Date('\(RRule.ISO8601DateFormatter.string(from: endDate))')," } else if let count = recurrenceEnd?.occurrenceCount { jsonString += "count: \(count)," } else { @@ -75,7 +74,7 @@ internal extension RecurrenceRule { return String(setpo) }) if bysetposStrings.count > 0 { - jsonString += "bysetpos: [\(bysetposStrings.joinWithSeparator(","))]," + jsonString += "bysetpos: [\(bysetposStrings.joined(separator: ","))]," } let byyeardayStrings = byyearday.flatMap({ (yearday) -> String? in @@ -85,7 +84,7 @@ internal extension RecurrenceRule { return String(yearday) }) if byyeardayStrings.count > 0 { - jsonString += "byyearday: [\(byyeardayStrings.joinWithSeparator(","))]," + jsonString += "byyearday: [\(byyeardayStrings.joined(separator: ","))]," } let bymonthStrings = bymonth.flatMap({ (month) -> String? in @@ -95,7 +94,7 @@ internal extension RecurrenceRule { return String(month) }) if bymonthStrings.count > 0 { - jsonString += "bymonth: [\(bymonthStrings.joinWithSeparator(","))]," + jsonString += "bymonth: [\(bymonthStrings.joined(separator: ","))]," } let byweeknoStrings = byweekno.flatMap({ (weekno) -> String? in @@ -105,7 +104,7 @@ internal extension RecurrenceRule { return String(weekno) }) if byweeknoStrings.count > 0 { - jsonString += "byweekno: [\(byweeknoStrings.joinWithSeparator(","))]," + jsonString += "byweekno: [\(byweeknoStrings.joined(separator: ","))]," } let bymonthdayStrings = bymonthday.flatMap({ (monthday) -> String? in @@ -115,39 +114,39 @@ internal extension RecurrenceRule { return String(monthday) }) if bymonthdayStrings.count > 0 { - jsonString += "bymonthday: [\(bymonthdayStrings.joinWithSeparator(","))]," + jsonString += "bymonthday: [\(bymonthdayStrings.joined(separator: ","))]," } let byweekdayJSSymbols = byweekday.map({ (weekday) -> String in return weekday.toJSONSymbol() }) if byweekdayJSSymbols.count > 0 { - jsonString += "byweekday: [\(byweekdayJSSymbols.joinWithSeparator(","))]," + jsonString += "byweekday: [\(byweekdayJSSymbols.joined(separator: ","))]," } let byhourStrings = byhour.map({ (hour) -> String in return String(hour) }) if byhourStrings.count > 0 { - jsonString += "byhour: [\(byhourStrings.joinWithSeparator(","))]," + jsonString += "byhour: [\(byhourStrings.joined(separator: ","))]," } let byminuteStrings = byminute.map({ (minute) -> String in return String(minute) }) if byminuteStrings.count > 0 { - jsonString += "byminute: [\(byminuteStrings.joinWithSeparator(","))]," + jsonString += "byminute: [\(byminuteStrings.joined(separator: ","))]," } let bysecondStrings = bysecond.map({ (second) -> String in return String(second) }) if bysecondStrings.count > 0 { - jsonString += "bysecond: [\(bysecondStrings.joinWithSeparator(","))]" + jsonString += "bysecond: [\(bysecondStrings.joined(separator: ","))]" } - if jsonString.substringFromIndex(jsonString.endIndex.advancedBy(-1)) == "," { - jsonString.removeAtIndex(jsonString.endIndex.advancedBy(-1)) + if jsonString.substring(from: jsonString.characters.index(jsonString.endIndex, offsetBy: -1)) == "," { + jsonString.remove(at: jsonString.characters.index(jsonString.endIndex, offsetBy: -1)) } return jsonString diff --git a/Sources/NSDate+Comparison.swift b/Sources/NSDate+Comparison.swift index 749c75d..d278bbc 100644 --- a/Sources/NSDate+Comparison.swift +++ b/Sources/NSDate+Comparison.swift @@ -8,24 +8,24 @@ import Foundation -internal extension NSDate { - internal func isBefore(date: NSDate) -> Bool { - return compare(date) == .OrderedAscending +internal extension Date { + internal func isBefore(_ date: Date) -> Bool { + return compare(date) == .orderedAscending } - internal func isSameWith(date: NSDate) -> Bool { - return compare(date) == .OrderedSame + internal func isSame(with date: Date) -> Bool { + return compare(date) == .orderedSame } - internal func isAfter(date: NSDate) -> Bool { - return compare(date) == .OrderedDescending + internal func isAfter(_ date: Date) -> Bool { + return compare(date) == .orderedDescending } - internal func isBeforeOrSameWith(date: NSDate) -> Bool { - return isBefore(date) || isSameWith(date) + internal func isBeforeOrSame(with date: Date) -> Bool { + return isBefore(date) || isSame(with: date) } - internal func isAfterOrSameWith(date: NSDate) -> Bool { - return isAfter(date) || isSameWith(date) + internal func isAfterOrSame(with date: Date) -> Bool { + return isAfter(date) || isSame(with: date) } } diff --git a/Sources/RRule.swift b/Sources/RRule.swift index d741999..cb7bde9 100644 --- a/Sources/RRule.swift +++ b/Sources/RRule.swift @@ -10,38 +10,37 @@ import Foundation import EventKit public struct RRule { - public static var dateFormatter: NSDateFormatter { - let dateFormatter = NSDateFormatter() - dateFormatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) + public static let dateFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'" return dateFormatter - } + }() - internal static var ISO8601DateFormatter: NSDateFormatter { - let dateFormatter = NSDateFormatter() - dateFormatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) + internal static let ISO8601DateFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" return dateFormatter - } + }() - public static func ruleFromString(string: String) -> RecurrenceRule? { - let string = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) - guard let range = string.rangeOfString("RRULE:") where range.startIndex == string.startIndex else { -// print("error: invalid rule string, must be started with 'RRULE:'") + public static func ruleFromString(_ string: String) -> RecurrenceRule? { + let string = string.trimmingCharacters(in: .whitespaces) + guard let range = string.range(of: "RRULE:"), range.lowerBound == string.startIndex else { return nil } - let ruleString = string.substringFromIndex(range.endIndex) - let rules = ruleString.componentsSeparatedByString(";").flatMap { (rule) -> String? in + let ruleString = string.substring(from: range.upperBound) + let rules = ruleString.components(separatedBy: ";").flatMap { (rule) -> String? in if (rule.isEmpty || rule.characters.count == 0) { return nil } return rule } - var recurrenceRule = RecurrenceRule(frequency: .Daily) + var recurrenceRule = RecurrenceRule(frequency: .daily) var ruleFrequency: RecurrenceFrequency? for rule in rules { - let ruleComponents = rule.componentsSeparatedByString("=") + let ruleComponents = rule.components(separatedBy: "=") guard ruleComponents.count == 2 else { continue } @@ -52,7 +51,7 @@ public struct RRule { } if ruleName == "FREQ" { - ruleFrequency = RecurrenceFrequency.frequencyFromString(ruleValue) + ruleFrequency = RecurrenceFrequency.frequency(from: ruleValue) } if ruleName == "INTERVAL" { @@ -68,14 +67,14 @@ public struct RRule { } if ruleName == "DTSTART" { - if let startDate = dateFormatter.dateFromString(ruleValue) { + if let startDate = dateFormatter.date(from: ruleValue) { recurrenceRule.startDate = startDate } } if ruleName == "UNTIL" { - if let endDate = dateFormatter.dateFromString(ruleValue) { - recurrenceRule.recurrenceEnd = EKRecurrenceEnd(endDate: endDate) + if let endDate = dateFormatter.date(from: ruleValue) { + recurrenceRule.recurrenceEnd = EKRecurrenceEnd(end: endDate) } } else if ruleName == "COUNT" { if let count = Int(ruleValue) { @@ -84,83 +83,83 @@ public struct RRule { } if ruleName == "BYSETPOS" { - let bysetpos = ruleValue.componentsSeparatedByString(",").flatMap({ (string) -> Int? in - guard let setpo = Int(string) where (-366...366 ~= setpo) && (setpo != 0) else { + let bysetpos = ruleValue.components(separatedBy: ",").flatMap({ (string) -> Int? in + guard let setpo = Int(string), (-366...366 ~= setpo) && (setpo != 0) else { return nil } return setpo }) - recurrenceRule.bysetpos = bysetpos.sort(<) + recurrenceRule.bysetpos = bysetpos.sorted(by: <) } if ruleName == "BYYEARDAY" { - let byyearday = ruleValue.componentsSeparatedByString(",").flatMap({ (string) -> Int? in - guard let yearday = Int(string) where (-366...366 ~= yearday) && (yearday != 0) else { + let byyearday = ruleValue.components(separatedBy: ",").flatMap({ (string) -> Int? in + guard let yearday = Int(string), (-366...366 ~= yearday) && (yearday != 0) else { return nil } return yearday }) - recurrenceRule.byyearday = byyearday.sort(<) + recurrenceRule.byyearday = byyearday.sorted(by: <) } if ruleName == "BYMONTH" { - let bymonth = ruleValue.componentsSeparatedByString(",").flatMap({ (string) -> Int? in - guard let month = Int(string) where 1...12 ~= month else { + let bymonth = ruleValue.components(separatedBy: ",").flatMap({ (string) -> Int? in + guard let month = Int(string), 1...12 ~= month else { return nil } return month }) - recurrenceRule.bymonth = bymonth.sort(<) + recurrenceRule.bymonth = bymonth.sorted(by: <) } if ruleName == "BYWEEKNO" { - let byweekno = ruleValue.componentsSeparatedByString(",").flatMap({ (string) -> Int? in - guard let weekno = Int(string) where (-53...53 ~= weekno) && (weekno != 0) else { + let byweekno = ruleValue.components(separatedBy: ",").flatMap({ (string) -> Int? in + guard let weekno = Int(string), (-53...53 ~= weekno) && (weekno != 0) else { return nil } return weekno }) - recurrenceRule.byweekno = byweekno.sort(<) + recurrenceRule.byweekno = byweekno.sorted(by: <) } if ruleName == "BYMONTHDAY" { - let bymonthday = ruleValue.componentsSeparatedByString(",").flatMap({ (string) -> Int? in - guard let monthday = Int(string) where (-31...31 ~= monthday) && (monthday != 0) else { + let bymonthday = ruleValue.components(separatedBy: ",").flatMap({ (string) -> Int? in + guard let monthday = Int(string), (-31...31 ~= monthday) && (monthday != 0) else { return nil } return monthday }) - recurrenceRule.bymonthday = bymonthday.sort(<) + recurrenceRule.bymonthday = bymonthday.sorted(by: <) } if ruleName == "BYDAY" { // These variables will define the weekdays where the recurrence will be applied. // In the RFC documentation, it is specified as BYDAY, but was renamed to avoid the ambiguity of that argument. - let byweekday = ruleValue.componentsSeparatedByString(",").flatMap({ (string) -> EKWeekday? in + let byweekday = ruleValue.components(separatedBy: ",").flatMap({ (string) -> EKWeekday? in return EKWeekday.weekdayFromSymbol(string) }) - recurrenceRule.byweekday = byweekday.sort(<) + recurrenceRule.byweekday = byweekday.sorted(by: <) } if ruleName == "BYHOUR" { - let byhour = ruleValue.componentsSeparatedByString(",").flatMap({ (string) -> Int? in + let byhour = ruleValue.components(separatedBy: ",").flatMap({ (string) -> Int? in return Int(string) }) - recurrenceRule.byhour = byhour.sort(<) + recurrenceRule.byhour = byhour.sorted(by: <) } if ruleName == "BYMINUTE" { - let byminute = ruleValue.componentsSeparatedByString(",").flatMap({ (string) -> Int? in + let byminute = ruleValue.components(separatedBy: ",").flatMap({ (string) -> Int? in return Int(string) }) - recurrenceRule.byminute = byminute.sort(<) + recurrenceRule.byminute = byminute.sorted(by: <) } if ruleName == "BYSECOND" { - let bysecond = ruleValue.componentsSeparatedByString(",").flatMap({ (string) -> Int? in + let bysecond = ruleValue.components(separatedBy: ",").flatMap({ (string) -> Int? in return Int(string) }) - recurrenceRule.bysecond = bysecond.sort(<) + recurrenceRule.bysecond = bysecond.sorted(by: <) } } @@ -173,7 +172,7 @@ public struct RRule { return recurrenceRule } - public static func stringFromRule(rule: RecurrenceRule) -> String { + public static func stringFromRule(_ rule: RecurrenceRule) -> String { var rruleString = "RRULE:" rruleString += "FREQ=\(rule.frequency.toString());" @@ -183,10 +182,10 @@ public struct RRule { rruleString += "WKST=\(rule.firstDayOfWeek.toSymbol());" - rruleString += "DTSTART=\(dateFormatter.stringFromDate(rule.startDate));" + rruleString += "DTSTART=\(dateFormatter.string(from: rule.startDate as Date));" if let endDate = rule.recurrenceEnd?.endDate { - rruleString += "UNTIL=\(dateFormatter.stringFromDate(endDate));" + rruleString += "UNTIL=\(dateFormatter.string(from: endDate));" } else if let count = rule.recurrenceEnd?.occurrenceCount { rruleString += "COUNT=\(count);" } @@ -198,7 +197,7 @@ public struct RRule { return String(setpo) }) if bysetposStrings.count > 0 { - rruleString += "BYSETPOS=\(bysetposStrings.joinWithSeparator(","));" + rruleString += "BYSETPOS=\(bysetposStrings.joined(separator: ","));" } let byyeardayStrings = rule.byyearday.flatMap({ (yearday) -> String? in @@ -208,7 +207,7 @@ public struct RRule { return String(yearday) }) if byyeardayStrings.count > 0 { - rruleString += "BYYEARDAY=\(byyeardayStrings.joinWithSeparator(","));" + rruleString += "BYYEARDAY=\(byyeardayStrings.joined(separator: ","));" } let bymonthStrings = rule.bymonth.flatMap({ (month) -> String? in @@ -218,7 +217,7 @@ public struct RRule { return String(month) }) if bymonthStrings.count > 0 { - rruleString += "BYMONTH=\(bymonthStrings.joinWithSeparator(","));" + rruleString += "BYMONTH=\(bymonthStrings.joined(separator: ","));" } let byweeknoStrings = rule.byweekno.flatMap({ (weekno) -> String? in @@ -228,7 +227,7 @@ public struct RRule { return String(weekno) }) if byweeknoStrings.count > 0 { - rruleString += "BYWEEKNO=\(byweeknoStrings.joinWithSeparator(","));" + rruleString += "BYWEEKNO=\(byweeknoStrings.joined(separator: ","));" } let bymonthdayStrings = rule.bymonthday.flatMap({ (monthday) -> String? in @@ -238,39 +237,39 @@ public struct RRule { return String(monthday) }) if bymonthdayStrings.count > 0 { - rruleString += "BYMONTHDAY=\(bymonthdayStrings.joinWithSeparator(","));" + rruleString += "BYMONTHDAY=\(bymonthdayStrings.joined(separator: ","));" } let byweekdaySymbols = rule.byweekday.map({ (weekday) -> String in return weekday.toSymbol() }) if byweekdaySymbols.count > 0 { - rruleString += "BYDAY=\(byweekdaySymbols.joinWithSeparator(","));" + rruleString += "BYDAY=\(byweekdaySymbols.joined(separator: ","));" } let byhourStrings = rule.byhour.map({ (hour) -> String in return String(hour) }) if byhourStrings.count > 0 { - rruleString += "BYHOUR=\(byhourStrings.joinWithSeparator(","));" + rruleString += "BYHOUR=\(byhourStrings.joined(separator: ","));" } let byminuteStrings = rule.byminute.map({ (minute) -> String in return String(minute) }) if byminuteStrings.count > 0 { - rruleString += "BYMINUTE=\(byminuteStrings.joinWithSeparator(","));" + rruleString += "BYMINUTE=\(byminuteStrings.joined(separator: ","));" } let bysecondStrings = rule.bysecond.map({ (second) -> String in return String(second) }) if bysecondStrings.count > 0 { - rruleString += "BYSECOND=\(bysecondStrings.joinWithSeparator(","));" + rruleString += "BYSECOND=\(bysecondStrings.joined(separator: ","));" } - if rruleString.substringFromIndex(rruleString.endIndex.advancedBy(-1)) == ";" { - rruleString.removeAtIndex(rruleString.endIndex.advancedBy(-1)) + if rruleString.substring(from: rruleString.characters.index(rruleString.endIndex, offsetBy: -1)) == ";" { + rruleString.remove(at: rruleString.characters.index(rruleString.endIndex, offsetBy: -1)) } return rruleString diff --git a/Sources/RecurrenceFrequency.swift b/Sources/RecurrenceFrequency.swift index 5d41773..1e57552 100644 --- a/Sources/RecurrenceFrequency.swift +++ b/Sources/RecurrenceFrequency.swift @@ -7,35 +7,35 @@ // public enum RecurrenceFrequency { - case Yearly - case Monthly - case Weekly - case Daily - case Hourly - case Minutely - case Secondly + case yearly + case monthly + case weekly + case daily + case hourly + case minutely + case secondly internal func toString() -> String { switch self { - case .Secondly: return "SECONDLY" - case .Minutely: return "MINUTELY" - case .Hourly: return "HOURLY" - case .Daily: return "DAILY" - case .Weekly: return "WEEKLY" - case .Monthly: return "MONTHLY" - case .Yearly: return "YEARLY" + case .secondly: return "SECONDLY" + case .minutely: return "MINUTELY" + case .hourly: return "HOURLY" + case .daily: return "DAILY" + case .weekly: return "WEEKLY" + case .monthly: return "MONTHLY" + case .yearly: return "YEARLY" } } - internal static func frequencyFromString(string: String) -> RecurrenceFrequency? { + internal static func frequency(from string: String) -> RecurrenceFrequency? { switch string { - case "SECONDLY": return .Secondly - case "MINUTELY": return .Minutely - case "HOURLY": return .Hourly - case "DAILY": return .Daily - case "WEEKLY": return .Weekly - case "MONTHLY": return .Monthly - case "YEARLY": return .Yearly + case "SECONDLY": return .secondly + case "MINUTELY": return .minutely + case "HOURLY": return .hourly + case "DAILY": return .daily + case "WEEKLY": return .weekly + case "MONTHLY": return .monthly + case "YEARLY": return .yearly default: return nil } } diff --git a/Sources/RecurrenceRule.swift b/Sources/RecurrenceRule.swift index b291d6d..714022c 100644 --- a/Sources/RecurrenceRule.swift +++ b/Sources/RecurrenceRule.swift @@ -11,32 +11,32 @@ import EventKit public struct RecurrenceRule { /// The calendar of recurrence rule. - public var calendar = NSCalendar.currentCalendar() + public var calendar = Calendar.current /// The frequency of the recurrence rule. public var frequency: RecurrenceFrequency - /// Specifies how often the recurrence rule repeats over the unit of time indicated by its frequency. For example, a recurrence rule with a frequency type of RecurrenceFrequency.Weekly and an interval of 2 repeats every two weeks. + /// Specifies how often the recurrence rule repeats over the component of time indicated by its frequency. For example, a recurrence rule with a frequency type of RecurrenceFrequency.weekly and an interval of 2 repeats every two weeks. /// /// The default value of this property is 1. public var interval = 1 /// Indicates which day of the week the recurrence rule treats as the first day of the week. /// - /// The default value of this property is EKWeekday.Monday. - public var firstDayOfWeek: EKWeekday = .Monday + /// The default value of this property is EKWeekday.monday. + public var firstDayOfWeek: EKWeekday = .monday /// The start date of recurrence rule. /// /// The default value of this property is current date. - public var startDate = NSDate() + public var startDate = Date() /// Indicates when the recurrence rule ends. This can be represented by an end date or a number of occurrences. public var recurrenceEnd: EKRecurrenceEnd? /// An array of ordinal integers that filters which recurrences to include in the recurrence rule’s frequency. Values can be from 1 to 366 and from -1 to -366. /// - /// For example, if a bysetpos of -1 is combined with a RecurrenceFrequency.Monthly frequency, and a byweekday of (EKWeekday.Monday, EKWeekday.Tuesday, EKWeekday.Wednesday, EKWeekday.Thursday, EKWeekday.Friday), will result in the last work day of every month. + /// For example, if a bysetpos of -1 is combined with a RecurrenceFrequency.monthly frequency, and a byweekday of (EKWeekday.monday, EKWeekday.tuesday, EKWeekday.wednesday, EKWeekday.thursday, EKWeekday.friday), will result in the last work day of every month. /// /// Negative values indicate counting backwards from the end of the recurrence rule’s frequency. public var bysetpos = [Int]() @@ -49,7 +49,7 @@ public struct RecurrenceRule { /// The months of the year associated with the recurrence rule, as an array of integers. Values can be from 1 to 12. public var bymonth = [Int]() - /// The weeks of the year associated with the recurrence rule, as an array of integers. Values can be from 1 to 53 and from -1 to -53. According to ISO8601, the first week of the year is that containing at least four days of the new year. + /// The weeks of the year associated with the recurrence rule, as an array of integers. Values can be from 1 to 53 and from -1 to -53. According to ISO8601, the first week of the year is that containing at least four days of the new year. /// /// Negative values indicate counting backwards from the end of the year. public var byweekno = [Int]() @@ -81,7 +81,7 @@ public struct RecurrenceRule { self.frequency = frequency } - public init?(recurrenceWithRRuleString rruleString: String) { + public init?(rruleString: String) { if let recurrenceRule = RRule.ruleFromString(rruleString) { self = recurrenceRule } else { diff --git a/Sources/Supporting Files/Info-watchOS.plist b/Sources/Supporting Files/Info-watchOS.plist index d3de8ee..abe61f5 100644 --- a/Sources/Supporting Files/Info-watchOS.plist +++ b/Sources/Supporting Files/Info-watchOS.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0 + 0.1.0 CFBundleSignature ???? CFBundleVersion