Supplement docs
This commit is contained in:
parent
28fc91ea06
commit
ce4d5d45b8
|
@ -1,4 +1,67 @@
|
||||||
.DS_Store
|
## Build generated
|
||||||
/.build
|
build/
|
||||||
/Packages
|
DerivedData/
|
||||||
/*.xcodeproj
|
|
||||||
|
## Various settings
|
||||||
|
*.pbxuser
|
||||||
|
!default.pbxuser
|
||||||
|
*.mode1v3
|
||||||
|
!default.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
!default.mode2v3
|
||||||
|
*.perspectivev3
|
||||||
|
!default.perspectivev3
|
||||||
|
xcuserdata/
|
||||||
|
|
||||||
|
## Other
|
||||||
|
*.moved-aside
|
||||||
|
*.xccheckout
|
||||||
|
*.xcscmblueprint
|
||||||
|
|
||||||
|
## Obj-C/Swift specific
|
||||||
|
*.hmap
|
||||||
|
*.ipa
|
||||||
|
*.dSYM.zip
|
||||||
|
*.dSYM
|
||||||
|
|
||||||
|
## Playgrounds
|
||||||
|
timeline.xctimeline
|
||||||
|
playground.xcworkspace
|
||||||
|
|
||||||
|
# Swift Package Manager
|
||||||
|
#
|
||||||
|
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
||||||
|
# Packages/
|
||||||
|
# Package.pins
|
||||||
|
# Package.resolved
|
||||||
|
.build/
|
||||||
|
|
||||||
|
# CocoaPods
|
||||||
|
#
|
||||||
|
# We recommend against adding the Pods directory to your .gitignore. However
|
||||||
|
# you should judge for yourself, the pros and cons are mentioned at:
|
||||||
|
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||||
|
#
|
||||||
|
# Pods/
|
||||||
|
#
|
||||||
|
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
||||||
|
# *.xcworkspace
|
||||||
|
|
||||||
|
# Carthage
|
||||||
|
#
|
||||||
|
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||||
|
# Carthage/Checkouts
|
||||||
|
|
||||||
|
Carthage/Build
|
||||||
|
|
||||||
|
# fastlane
|
||||||
|
#
|
||||||
|
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
|
||||||
|
# screenshots whenever they are needed.
|
||||||
|
# For more information about the recommended setup visit:
|
||||||
|
# https://docs.fastlane.tools/best-practices/source-control/#source-control
|
||||||
|
|
||||||
|
fastlane/report.xml
|
||||||
|
fastlane/Preview.html
|
||||||
|
fastlane/screenshots/**/*.png
|
||||||
|
fastlane/test_output
|
|
@ -0,0 +1 @@
|
||||||
|
4.2
|
84
README.md
84
README.md
|
@ -1,22 +1,29 @@
|
||||||
# Schedule
|
# Schedule
|
||||||
|
|
||||||
Swift job scheduler.
|
⏰ A interval-based and date-based job scheduler for swift.
|
||||||
|
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- 📆 Date-based scheduling
|
||||||
|
- ⏳ Interval-based scheduling
|
||||||
|
- 📝 Mixture rules scheduling
|
||||||
|
- 🚦 Suspend, resume, cancel
|
||||||
|
- 🏷 Tag related management
|
||||||
|
- 🍻 No need to concern about runloop
|
||||||
|
- 👻 No need to concern about circular reference
|
||||||
|
- 🍭 **Sweet apis**
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Getting Start
|
|
||||||
|
|
||||||
Scheduling a job can not be simplier.
|
Scheduling a job can not be simplier.
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
func job() { }
|
func heartBeat() { }
|
||||||
Schedule.every(1.minute).do(job)
|
Schedule.every(0.5.seconds).do(heartBeat)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Interval-based Scheduling
|
### Interval-based Scheduling
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
|
@ -37,7 +44,7 @@ Schedule.from([1.second, 2.minutes, 3.hours]).do { }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Date-based Scheduling
|
### Date-based Scheduling
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
import Schedule
|
import Schedule
|
||||||
|
@ -62,7 +69,7 @@ import Schedule
|
||||||
|
|
||||||
/// concat
|
/// concat
|
||||||
let s0 = Schedule.at(birthdate)
|
let s0 = Schedule.at(birthdate)
|
||||||
let s1 = Schedule.every(.year(1))
|
let s1 = Schedule.every(1.year)
|
||||||
let birthdaySchedule = s0.concat.s1
|
let birthdaySchedule = s0.concat.s1
|
||||||
birthdaySchedule.do {
|
birthdaySchedule.do {
|
||||||
print("Happy birthday")
|
print("Happy birthday")
|
||||||
|
@ -73,32 +80,65 @@ let s3 = Schedule.every(.january(1)).at(8, 30)
|
||||||
let s4 = Schedule.every(.october(1)).at(8, 30)
|
let s4 = Schedule.every(.october(1)).at(8, 30)
|
||||||
let holiday = s3.merge(s3)
|
let holiday = s3.merge(s3)
|
||||||
holidaySchedule.do {
|
holidaySchedule.do {
|
||||||
print("Holiday~")
|
print("Happy holiday")
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
|
/// count
|
||||||
|
let s5 = Schedule.after(5.seconds).concat(Schedule.every(1.day))
|
||||||
|
let s6 = s5.count(10)
|
||||||
|
|
||||||
|
/// until
|
||||||
|
let s7 = Schedule.every(.monday).at(11, 12)
|
||||||
|
let s8 = s7.until(date)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Job management
|
### Job management
|
||||||
|
|
||||||
Normally, you don't need to care about the reference management of job. All jobs will be held by a inner instance `jobCenter`, so they won't be released.
|
In genera, you don't need to care about the reference management of job. All jobs will be held by a inner shared instance `jobCenter`, so they won't be released, unless you do that yourself.
|
||||||
|
|
||||||
If you want everything in your control:
|
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let job = Schedule.every(1.day).do { }
|
let job = Schedule.every(1.day).do { }
|
||||||
|
|
||||||
job.suspend() // will suspend job, it won't change job's schedule
|
job.suspend()
|
||||||
job.resume() // will resume the suspended job, it won't change job's schedule
|
job.resume()
|
||||||
job.cancel() // will cancel job, then job will be released after variable `job` is gone
|
job.cancel() // will release this job
|
||||||
```
|
```
|
||||||
|
|
||||||
You also can use `tag` to organize jobs, and use `queue` to define which queue the job should be dispatched to:
|
You also can use `tag` to organize jobs, and use `queue` to define which queue the job should be dispatched to:
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
Schedule.every(1.day).do(queue: myJobQueue, tag: "remind me") { }
|
let s = Schedule.every(1.day)
|
||||||
|
s.do(queue: myJobQueue, tag: "log") { }
|
||||||
|
s.do(queue: myJobQueue, tag: "log") { }
|
||||||
|
|
||||||
|
Job.suspend("log")
|
||||||
|
Job.resume("log")
|
||||||
|
Job.cancel("log")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Schedul supports all popular dependency managers.
|
||||||
|
|
||||||
|
### Cocoapods
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
pod 'Schedule'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Carthage
|
||||||
|
|
||||||
|
```swift
|
||||||
|
github "jianstm/Schedule"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Swift Package Manager
|
||||||
|
|
||||||
|
```swift
|
||||||
|
dependencies: [
|
||||||
|
.package(url: "https://github.com/jianstm/Schedule", from: "0.0.2")
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
Job.suspend("remind me") // will suspend all jobs with this tag
|
|
||||||
Job.resume("remind me") // will resume all jobs with this tag
|
|
||||||
Job.cancel("remind me") // will cancel all jobs with this tag
|
|
||||||
```
|
|
|
@ -1,6 +1,6 @@
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = "Schedule"
|
s.name = "Schedule"
|
||||||
s.version = "0.0.2"
|
s.version = "0.0.3"
|
||||||
s.summary = "Swift Job Schedule."
|
s.summary = "Swift Job Schedule."
|
||||||
s.homepage = "https://github.com/jianstm/Schedule"
|
s.homepage = "https://github.com/jianstm/Schedule"
|
||||||
s.license = { :type => "MIT", :file => "LICENSE" }
|
s.license = { :type => "MIT", :file => "LICENSE" }
|
||||||
|
@ -9,10 +9,10 @@ Pod::Spec.new do |s|
|
||||||
:tag => "#{s.version}" }
|
:tag => "#{s.version}" }
|
||||||
s.source_files = "Sources/Schedule/*.swift"
|
s.source_files = "Sources/Schedule/*.swift"
|
||||||
s.requires_arc = true
|
s.requires_arc = true
|
||||||
s.swift_version = "4.0"
|
s.swift_version = "4.2"
|
||||||
|
|
||||||
s.ios.deployment_target = "8.0"
|
s.ios.deployment_target = "8.0"
|
||||||
s.osx.deployment_target = "10.9"
|
s.osx.deployment_target = "10.10"
|
||||||
s.tvos.deployment_target = "9.0"
|
s.tvos.deployment_target = "9.0"
|
||||||
s.watchos.deployment_target = "2.0"
|
s.watchos.deployment_target = "2.0"
|
||||||
end
|
end
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>BNDL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,478 @@
|
||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 46;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXAggregateTarget section */
|
||||||
|
"Schedule::SchedulePackageTests::ProductTarget" /* SchedulePackageTests */ = {
|
||||||
|
isa = PBXAggregateTarget;
|
||||||
|
buildConfigurationList = OBJ_39 /* Build configuration list for PBXAggregateTarget "SchedulePackageTests" */;
|
||||||
|
buildPhases = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
OBJ_42 /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
name = SchedulePackageTests;
|
||||||
|
productName = SchedulePackageTests;
|
||||||
|
};
|
||||||
|
/* End PBXAggregateTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
OBJ_27 /* DateTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* DateTime.swift */; };
|
||||||
|
OBJ_28 /* Job.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* Job.swift */; };
|
||||||
|
OBJ_29 /* Schedule.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* Schedule.swift */; };
|
||||||
|
OBJ_30 /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* Util.swift */; };
|
||||||
|
OBJ_37 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; };
|
||||||
|
OBJ_48 /* DateTimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* DateTimeTests.swift */; };
|
||||||
|
OBJ_49 /* ScheduleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_16 /* ScheduleTests.swift */; };
|
||||||
|
OBJ_50 /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_17 /* Util.swift */; };
|
||||||
|
OBJ_51 /* XCTestManifests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_18 /* XCTestManifests.swift */; };
|
||||||
|
OBJ_53 /* Schedule.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "Schedule::Schedule::Product" /* Schedule.framework */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
6266004220EF028200D5E606 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = OBJ_1 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = "Schedule::Schedule";
|
||||||
|
remoteInfo = Schedule;
|
||||||
|
};
|
||||||
|
6266004320EF028200D5E606 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = OBJ_1 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = "Schedule::ScheduleTests";
|
||||||
|
remoteInfo = ScheduleTests;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
OBJ_10 /* Job.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Job.swift; sourceTree = "<group>"; };
|
||||||
|
OBJ_11 /* Schedule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Schedule.swift; sourceTree = "<group>"; };
|
||||||
|
OBJ_12 /* Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Util.swift; sourceTree = "<group>"; };
|
||||||
|
OBJ_15 /* DateTimeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeTests.swift; sourceTree = "<group>"; };
|
||||||
|
OBJ_16 /* ScheduleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleTests.swift; sourceTree = "<group>"; };
|
||||||
|
OBJ_17 /* Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Util.swift; sourceTree = "<group>"; };
|
||||||
|
OBJ_18 /* XCTestManifests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTestManifests.swift; sourceTree = "<group>"; };
|
||||||
|
OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
|
||||||
|
OBJ_9 /* DateTime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTime.swift; sourceTree = "<group>"; };
|
||||||
|
"Schedule::Schedule::Product" /* Schedule.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Schedule.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
"Schedule::ScheduleTests::Product" /* ScheduleTests.xctest */ = {isa = PBXFileReference; lastKnownFileType = file; path = ScheduleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
OBJ_31 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 0;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
OBJ_52 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 0;
|
||||||
|
files = (
|
||||||
|
OBJ_53 /* Schedule.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
OBJ_13 /* Tests */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
OBJ_14 /* ScheduleTests */,
|
||||||
|
);
|
||||||
|
name = Tests;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
|
};
|
||||||
|
OBJ_14 /* ScheduleTests */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
OBJ_15 /* DateTimeTests.swift */,
|
||||||
|
OBJ_16 /* ScheduleTests.swift */,
|
||||||
|
OBJ_17 /* Util.swift */,
|
||||||
|
OBJ_18 /* XCTestManifests.swift */,
|
||||||
|
);
|
||||||
|
name = ScheduleTests;
|
||||||
|
path = Tests/ScheduleTests;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
|
};
|
||||||
|
OBJ_19 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
"Schedule::Schedule::Product" /* Schedule.framework */,
|
||||||
|
"Schedule::ScheduleTests::Product" /* ScheduleTests.xctest */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
|
};
|
||||||
|
OBJ_5 /* */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
OBJ_6 /* Package.swift */,
|
||||||
|
OBJ_7 /* Sources */,
|
||||||
|
OBJ_13 /* Tests */,
|
||||||
|
OBJ_19 /* Products */,
|
||||||
|
);
|
||||||
|
name = "";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
OBJ_7 /* Sources */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
OBJ_8 /* Schedule */,
|
||||||
|
);
|
||||||
|
name = Sources;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
|
};
|
||||||
|
OBJ_8 /* Schedule */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
OBJ_9 /* DateTime.swift */,
|
||||||
|
OBJ_10 /* Job.swift */,
|
||||||
|
OBJ_11 /* Schedule.swift */,
|
||||||
|
OBJ_12 /* Util.swift */,
|
||||||
|
);
|
||||||
|
name = Schedule;
|
||||||
|
path = Sources/Schedule;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
"Schedule::Schedule" /* Schedule */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = OBJ_23 /* Build configuration list for PBXNativeTarget "Schedule" */;
|
||||||
|
buildPhases = (
|
||||||
|
OBJ_26 /* Sources */,
|
||||||
|
OBJ_31 /* Frameworks */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = Schedule;
|
||||||
|
productName = Schedule;
|
||||||
|
productReference = "Schedule::Schedule::Product" /* Schedule.framework */;
|
||||||
|
productType = "com.apple.product-type.framework";
|
||||||
|
};
|
||||||
|
"Schedule::ScheduleTests" /* ScheduleTests */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = OBJ_44 /* Build configuration list for PBXNativeTarget "ScheduleTests" */;
|
||||||
|
buildPhases = (
|
||||||
|
OBJ_47 /* Sources */,
|
||||||
|
OBJ_52 /* Frameworks */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
OBJ_54 /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
name = ScheduleTests;
|
||||||
|
productName = ScheduleTests;
|
||||||
|
productReference = "Schedule::ScheduleTests::Product" /* ScheduleTests.xctest */;
|
||||||
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
|
};
|
||||||
|
"Schedule::SwiftPMPackageDescription" /* SchedulePackageDescription */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = OBJ_33 /* Build configuration list for PBXNativeTarget "SchedulePackageDescription" */;
|
||||||
|
buildPhases = (
|
||||||
|
OBJ_36 /* Sources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = SchedulePackageDescription;
|
||||||
|
productName = SchedulePackageDescription;
|
||||||
|
productType = "com.apple.product-type.framework";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
OBJ_1 /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastUpgradeCheck = 9999;
|
||||||
|
};
|
||||||
|
buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "Schedule" */;
|
||||||
|
compatibilityVersion = "Xcode 3.2";
|
||||||
|
developmentRegion = English;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
);
|
||||||
|
mainGroup = OBJ_5 /* */;
|
||||||
|
productRefGroup = OBJ_19 /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
"Schedule::Schedule" /* Schedule */,
|
||||||
|
"Schedule::SwiftPMPackageDescription" /* SchedulePackageDescription */,
|
||||||
|
"Schedule::SchedulePackageTests::ProductTarget" /* SchedulePackageTests */,
|
||||||
|
"Schedule::ScheduleTests" /* ScheduleTests */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
OBJ_26 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 0;
|
||||||
|
files = (
|
||||||
|
OBJ_27 /* DateTime.swift in Sources */,
|
||||||
|
OBJ_28 /* Job.swift in Sources */,
|
||||||
|
OBJ_29 /* Schedule.swift in Sources */,
|
||||||
|
OBJ_30 /* Util.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
OBJ_36 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 0;
|
||||||
|
files = (
|
||||||
|
OBJ_37 /* Package.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
OBJ_47 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 0;
|
||||||
|
files = (
|
||||||
|
OBJ_48 /* DateTimeTests.swift in Sources */,
|
||||||
|
OBJ_49 /* ScheduleTests.swift in Sources */,
|
||||||
|
OBJ_50 /* Util.swift in Sources */,
|
||||||
|
OBJ_51 /* XCTestManifests.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
OBJ_42 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = "Schedule::ScheduleTests" /* ScheduleTests */;
|
||||||
|
targetProxy = 6266004320EF028200D5E606 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
OBJ_54 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = "Schedule::Schedule" /* Schedule */;
|
||||||
|
targetProxy = 6266004220EF028200D5E606 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
OBJ_24 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
|
||||||
|
);
|
||||||
|
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||||
|
INFOPLIST_FILE = Schedule.xcodeproj/Schedule_Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
|
||||||
|
OTHER_CFLAGS = "$(inherited)";
|
||||||
|
OTHER_LDFLAGS = "$(inherited)";
|
||||||
|
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = Schedule;
|
||||||
|
PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_VERSION = 4.0;
|
||||||
|
TARGET_NAME = Schedule;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
OBJ_25 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
|
||||||
|
);
|
||||||
|
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||||
|
INFOPLIST_FILE = Schedule.xcodeproj/Schedule_Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
|
||||||
|
OTHER_CFLAGS = "$(inherited)";
|
||||||
|
OTHER_LDFLAGS = "$(inherited)";
|
||||||
|
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = Schedule;
|
||||||
|
PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_VERSION = 4.0;
|
||||||
|
TARGET_NAME = Schedule;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
OBJ_3 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||||
|
ENABLE_NS_ASSERTIONS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
OTHER_SWIFT_FLAGS = "-DXcode";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = macosx;
|
||||||
|
SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator";
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
USE_HEADERMAP = NO;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
OBJ_34 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
LD = /usr/bin/true;
|
||||||
|
OTHER_SWIFT_FLAGS = "-swift-version 4 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk";
|
||||||
|
SWIFT_VERSION = 4.0;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
OBJ_35 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
LD = /usr/bin/true;
|
||||||
|
OTHER_SWIFT_FLAGS = "-swift-version 4 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk";
|
||||||
|
SWIFT_VERSION = 4.0;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
OBJ_4 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
COPY_PHASE_STRIP = YES;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||||
|
GCC_OPTIMIZATION_LEVEL = s;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||||
|
OTHER_SWIFT_FLAGS = "-DXcode";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = macosx;
|
||||||
|
SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator";
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
|
USE_HEADERMAP = NO;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
OBJ_40 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
OBJ_41 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
OBJ_45 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
|
||||||
|
);
|
||||||
|
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||||
|
INFOPLIST_FILE = Schedule.xcodeproj/ScheduleTests_Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks";
|
||||||
|
OTHER_CFLAGS = "$(inherited)";
|
||||||
|
OTHER_LDFLAGS = "$(inherited)";
|
||||||
|
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||||
|
SWIFT_VERSION = 4.0;
|
||||||
|
TARGET_NAME = ScheduleTests;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
OBJ_46 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
|
||||||
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
|
||||||
|
);
|
||||||
|
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||||
|
INFOPLIST_FILE = Schedule.xcodeproj/ScheduleTests_Info.plist;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks";
|
||||||
|
OTHER_CFLAGS = "$(inherited)";
|
||||||
|
OTHER_LDFLAGS = "$(inherited)";
|
||||||
|
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||||
|
SWIFT_VERSION = 4.0;
|
||||||
|
TARGET_NAME = ScheduleTests;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
OBJ_2 /* Build configuration list for PBXProject "Schedule" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
OBJ_3 /* Debug */,
|
||||||
|
OBJ_4 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
OBJ_23 /* Build configuration list for PBXNativeTarget "Schedule" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
OBJ_24 /* Debug */,
|
||||||
|
OBJ_25 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
OBJ_33 /* Build configuration list for PBXNativeTarget "SchedulePackageDescription" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
OBJ_34 /* Debug */,
|
||||||
|
OBJ_35 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
OBJ_39 /* Build configuration list for PBXAggregateTarget "SchedulePackageTests" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
OBJ_40 /* Debug */,
|
||||||
|
OBJ_41 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
OBJ_44 /* Build configuration list for PBXNativeTarget "ScheduleTests" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
OBJ_45 /* Debug */,
|
||||||
|
OBJ_46 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = OBJ_1 /* Project object */;
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,81 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "9999"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "Schedule::Schedule"
|
||||||
|
BuildableName = "Schedule.framework"
|
||||||
|
BlueprintName = "Schedule"
|
||||||
|
ReferencedContainer = "container:Schedule.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "Schedule::ScheduleTests"
|
||||||
|
BuildableName = "ScheduleTests.xctest"
|
||||||
|
BlueprintName = "ScheduleTests"
|
||||||
|
ReferencedContainer = "container:Schedule.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
</Testables>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "Schedule::Schedule"
|
||||||
|
BuildableName = "Schedule.framework"
|
||||||
|
BlueprintName = "Schedule"
|
||||||
|
ReferencedContainer = "container:Schedule.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>SchemeUserState</key>
|
||||||
|
<dict>
|
||||||
|
<key>Schedule-Package.xcscheme</key>
|
||||||
|
<dict></dict>
|
||||||
|
</dict>
|
||||||
|
<key>SuppressBuildableAutocreation</key>
|
||||||
|
<dict></dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -7,109 +7,191 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// `Interval` represents a length of time, it's contextless.
|
/// `Interval` represents a duration of time.
|
||||||
public struct Interval {
|
public struct Interval {
|
||||||
|
|
||||||
|
/// The length of this interval, measured in nanoseconds.
|
||||||
public let nanoseconds: Double
|
public let nanoseconds: Double
|
||||||
|
|
||||||
|
/// Creates an interval from the given number of nanoseconds.
|
||||||
public init(nanoseconds: Double) {
|
public init(nanoseconds: Double) {
|
||||||
self.nanoseconds = nanoseconds
|
self.nanoseconds = nanoseconds
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(seconds: Double) {
|
/// A boolean value indicating whether this interval is negative.
|
||||||
self.nanoseconds = seconds * pow(10, 9)
|
///
|
||||||
}
|
/// A interval can be negative.
|
||||||
|
///
|
||||||
public var seconds: Double {
|
/// - The interval between 6:00 and 7:00 is `1.hour`,
|
||||||
return nanoseconds / pow(10, 9)
|
/// but the interval between 7:00 and 6:00 is `-1.hour`.
|
||||||
}
|
/// In this case, `-1.hour` means **one hour ago**.
|
||||||
|
///
|
||||||
|
/// - The interval comparing `3.hour` and `1.hour` is `2.hour`,
|
||||||
|
/// but the interval comparing `1.hour` and `3.hour` is `-2.hour`.
|
||||||
|
/// In this case, `-2.hour` means **two hours shorter**
|
||||||
public var isNegative: Bool {
|
public var isNegative: Bool {
|
||||||
return nanoseconds.isLess(than: 0)
|
return nanoseconds.isLess(than: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var dispatchInterval: DispatchTimeInterval {
|
/// The magnitude of this interval.
|
||||||
if nanoseconds > Double(Int.max) { return .nanoseconds(.max) }
|
///
|
||||||
if nanoseconds < Double(Int.min) { return .nanoseconds(.min) }
|
/// It's the absolute value of the length of this interval,
|
||||||
return .nanoseconds(Int(nanoseconds))
|
/// measured in nanoseconds, but disregarding its sign.
|
||||||
|
public var magnitude: Double {
|
||||||
|
return nanoseconds.magnitude
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal var ns: Int {
|
||||||
|
if nanoseconds > Double(Int.max) { return .max }
|
||||||
|
if nanoseconds < Double(Int.min) { return .min }
|
||||||
|
return Int(nanoseconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a dispatchTimeInterval created from this interval.
|
||||||
|
///
|
||||||
|
/// The returned value will be clamped to the `DispatchTimeInterval`'s
|
||||||
|
/// usable range [`.nanoseconds(.min)....nanoseconds(.max)`].
|
||||||
|
public func asDispatchTimeInterval() -> DispatchTimeInterval {
|
||||||
|
return .nanoseconds(ns)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a boolean value indicating whether this interval is longer than the given value.
|
||||||
|
public func isLonger(than other: Interval) -> Bool {
|
||||||
|
return magnitude > other.magnitude
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a boolean value indicating whether this interval is shorter than the given value.
|
||||||
|
public func isShorter(than other: Interval) -> Bool {
|
||||||
|
return magnitude < other.magnitude
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the longest interval of the given values.
|
||||||
|
public static func longest(_ intervals: Interval...) -> Interval {
|
||||||
|
return intervals.sorted(by: { $0.magnitude > $1.magnitude })[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the shortest interval of the given values.
|
||||||
|
public static func shortest(_ intervals: Interval...) -> Interval {
|
||||||
|
return intervals.sorted(by: { $0.magnitude < $1.magnitude })[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new interval by multipling the left interval by the right number.
|
||||||
|
///
|
||||||
|
/// 1.hour * 2 == 2.hours
|
||||||
public static func *(lhs: Interval, rhs: Double) -> Interval {
|
public static func *(lhs: Interval, rhs: Double) -> Interval {
|
||||||
return Interval(nanoseconds: lhs.nanoseconds * rhs)
|
return Interval(nanoseconds: lhs.nanoseconds * rhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a new interval by adding the right interval to the left interval.
|
||||||
|
///
|
||||||
|
/// 1.hour + 1.hour == 2.hours
|
||||||
public static func +(lhs: Interval, rhs: Interval) -> Interval {
|
public static func +(lhs: Interval, rhs: Interval) -> Interval {
|
||||||
return Interval(nanoseconds: lhs.nanoseconds + rhs.nanoseconds)
|
return Interval(nanoseconds: lhs.nanoseconds + rhs.nanoseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a new instarval by subtracting the right interval from the left interval.
|
||||||
|
///
|
||||||
|
/// 2.hours - 1.hour == 1.hour
|
||||||
public static func -(lhs: Interval, rhs: Interval) -> Interval {
|
public static func -(lhs: Interval, rhs: Interval) -> Interval {
|
||||||
return Interval(nanoseconds: lhs.nanoseconds - rhs.nanoseconds)
|
return Interval(nanoseconds: lhs.nanoseconds - rhs.nanoseconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Interval {
|
||||||
|
|
||||||
|
/// Creates an interval from the given number of seconds.
|
||||||
|
public init(seconds: Double) {
|
||||||
|
self.nanoseconds = seconds * pow(10, 9)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The length of this interval, measured in seconds.
|
||||||
|
public var seconds: Double {
|
||||||
|
return nanoseconds / pow(10, 9)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The length of this interval, measured in minutes.
|
||||||
|
public var minutes: Double {
|
||||||
|
return seconds / 60
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The length of this interval, measured in hours.
|
||||||
|
public var hours: Double {
|
||||||
|
return minutes / 60
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The length of this interval, measured in days.
|
||||||
|
public var days: Double {
|
||||||
|
return hours / 24
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The length of this interval, measured in weeks.
|
||||||
|
public var weeks: Double {
|
||||||
|
return days / 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension Interval: Hashable {
|
extension Interval: Hashable {
|
||||||
|
|
||||||
|
/// The hashValue of this interval.
|
||||||
public var hashValue: Int {
|
public var hashValue: Int {
|
||||||
return nanoseconds.hashValue
|
return nanoseconds.hashValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a boolean value indicating whether the interval is equal to another interval.
|
||||||
public static func ==(lhs: Interval, rhs: Interval) -> Bool {
|
public static func ==(lhs: Interval, rhs: Interval) -> Bool {
|
||||||
return lhs.hashValue == rhs.hashValue
|
return lhs.nanoseconds == rhs.nanoseconds
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Interval: Comparable {
|
|
||||||
|
|
||||||
public static func <(lhs: Interval, rhs: Interval) -> Bool {
|
|
||||||
return lhs.nanoseconds.magnitude < rhs.nanoseconds.magnitude
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Date {
|
extension Date {
|
||||||
|
|
||||||
|
/// The interval between this date and now.
|
||||||
|
///
|
||||||
|
/// If the date is earlier than now, the interval is negative.
|
||||||
public var intervalSinceNow: Interval {
|
public var intervalSinceNow: Interval {
|
||||||
return timeIntervalSinceNow.seconds
|
return timeIntervalSinceNow.seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the interval between this date and the given date.
|
||||||
|
///
|
||||||
|
/// If the date is earlier than the given date, the interval is negative.
|
||||||
public func interval(since date: Date) -> Interval {
|
public func interval(since date: Date) -> Interval {
|
||||||
return timeIntervalSince(date).seconds
|
return timeIntervalSince(date).seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a new date by adding an interval to the date.
|
||||||
public static func +(lhs: Date, rhs: Interval) -> Date {
|
public static func +(lhs: Date, rhs: Interval) -> Date {
|
||||||
return lhs.addingTimeInterval(rhs.seconds)
|
return lhs.addingTimeInterval(rhs.seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
/// Add an interval to the date.
|
||||||
public static func +=(lhs: inout Date, rhs: Interval) -> Date {
|
public static func +=(lhs: inout Date, rhs: Interval) {
|
||||||
lhs = lhs + rhs
|
lhs = lhs + rhs
|
||||||
return lhs
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `IntervalConvertible` provides a set of intuitive apis for creating interval.
|
||||||
public protocol IntervalConvertible {
|
public protocol IntervalConvertible {
|
||||||
|
|
||||||
func asNanoseconds() -> Double
|
var nanoseconds: Interval { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Int: IntervalConvertible {
|
extension Int: IntervalConvertible {
|
||||||
|
|
||||||
public func asNanoseconds() -> Double {
|
public var nanoseconds: Interval {
|
||||||
return Double(self)
|
return Interval(nanoseconds: Double(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Double: IntervalConvertible {
|
extension Double: IntervalConvertible {
|
||||||
|
|
||||||
public func asNanoseconds() -> Double {
|
public var nanoseconds: Interval {
|
||||||
return self
|
return Interval(nanoseconds: self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension IntervalConvertible {
|
extension IntervalConvertible {
|
||||||
|
|
||||||
public var nanoseconds: Interval {
|
|
||||||
return Interval(nanoseconds: asNanoseconds())
|
|
||||||
}
|
|
||||||
|
|
||||||
public var nanosecond: Interval {
|
public var nanosecond: Interval {
|
||||||
return nanoseconds
|
return nanoseconds
|
||||||
}
|
}
|
||||||
|
@ -171,19 +253,27 @@ extension IntervalConvertible {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Weekday: Int {
|
|
||||||
case sunday = 1, monday, tuesday, wednesday, thursday, friday, saturday
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Time` represents a time without a date.
|
/// `Time` represents a time without a date.
|
||||||
|
///
|
||||||
|
/// It is a specific point in a day.
|
||||||
public struct Time {
|
public struct Time {
|
||||||
|
|
||||||
public let hour: Int
|
public let hour: Int
|
||||||
|
|
||||||
public let minute: Int
|
public let minute: Int
|
||||||
|
|
||||||
public let second: Int
|
public let second: Int
|
||||||
|
|
||||||
public let nanosecond: Int
|
public let nanosecond: Int
|
||||||
|
|
||||||
public init?(hour: Int, minute: Int, second: Int, nanosecond: Int) {
|
/// Create a date with `hour`, `minute`, `second` and `nanosecond` fields.
|
||||||
|
///
|
||||||
|
/// If parameter is illegal, then return nil.
|
||||||
|
///
|
||||||
|
/// Time(hour: 25) == nil
|
||||||
|
/// Time(hour: 1, minute: 61) == nil
|
||||||
|
public init?(hour: Int, minute: Int = 0, second: Int = 0, nanosecond: Int = 0) {
|
||||||
guard hour >= 0 && hour < 24 else { return nil }
|
guard hour >= 0 && hour < 24 else { return nil }
|
||||||
guard minute >= 0 && minute < 60 else { return nil }
|
guard minute >= 0 && minute < 60 else { return nil }
|
||||||
guard second >= 0 && second < 60 else { return nil }
|
guard second >= 0 && second < 60 else { return nil }
|
||||||
|
@ -195,17 +285,22 @@ public struct Time {
|
||||||
self.nanosecond = nanosecond
|
self.nanosecond = nanosecond
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a time with a timing string
|
||||||
|
///
|
||||||
/// For example:
|
/// For example:
|
||||||
///
|
///
|
||||||
/// "11"
|
/// Time("11") == Time(hour: 11)
|
||||||
/// "11:12"
|
/// Time("11:12") == Time(hour: 11, minute: 12)
|
||||||
/// "11:12:12"
|
/// Time("11:12:13") == Time(hour: 11, minute: 12, second: 13)
|
||||||
/// "11:12:13.123"
|
/// Time("11:12:13.123") == Time(hour: 11, minute: 12, second: 13, nanosecond: 123000000)
|
||||||
|
///
|
||||||
|
/// If timing's format is illegal, then return nil.
|
||||||
public init?(timing: String) {
|
public init?(timing: String) {
|
||||||
let args = timing.split(separator: ":")
|
let args = timing.split(separator: ":")
|
||||||
var h = 0, m = 0, s = 0, ns = 0
|
|
||||||
if args.count > 3 { return nil }
|
if args.count > 3 { return nil }
|
||||||
|
|
||||||
|
var h = 0, m = 0, s = 0, ns = 0
|
||||||
|
|
||||||
guard let _h = Int(args[0]) else { return nil }
|
guard let _h = Int(args[0]) else { return nil }
|
||||||
h = _h
|
h = _h
|
||||||
if args.count > 1 {
|
if args.count > 1 {
|
||||||
|
@ -213,22 +308,22 @@ public struct Time {
|
||||||
m = _m
|
m = _m
|
||||||
}
|
}
|
||||||
if args.count > 2 {
|
if args.count > 2 {
|
||||||
let components = args[2].split(separator: ".")
|
let values = args[2].split(separator: ".")
|
||||||
if components.count > 2 { return nil }
|
if values.count > 2 { return nil }
|
||||||
guard let _s = Int(components[0]) else { return nil }
|
guard let _s = Int(values[0]) else { return nil }
|
||||||
s = _s
|
s = _s
|
||||||
|
|
||||||
if components.count > 1 {
|
if values.count > 1 {
|
||||||
guard let _ns = Int(components[1]) else { return nil }
|
guard let _ns = Int(values[1]) else { return nil }
|
||||||
let digit = components[1].count
|
let digits = values[1].count
|
||||||
ns = Int(Double(_ns) * pow(10, Double(9 - digit)))
|
ns = Int(Double(_ns) * pow(10, Double(9 - digits)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.init(hour: h, minute: m, second: s, nanosecond: ns)
|
self.init(hour: h, minute: m, second: s, nanosecond: ns)
|
||||||
}
|
}
|
||||||
|
|
||||||
func asDateComponents() -> DateComponents {
|
internal func asDateComponents() -> DateComponents {
|
||||||
return DateComponents(hour: hour, minute: minute, second: second, nanosecond: nanosecond)
|
return DateComponents(hour: hour, minute: minute, second: second, nanosecond: nanosecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,23 +331,35 @@ public struct Time {
|
||||||
|
|
||||||
/// `Period` represents a period of time defined in terms of fields.
|
/// `Period` represents a period of time defined in terms of fields.
|
||||||
///
|
///
|
||||||
/// It's a littl different from `Interval`, for example:
|
/// It's a little different from `Interval`.
|
||||||
///
|
///
|
||||||
/// If you add a period `1.month` to the 1st January, you will get the 1st February.
|
/// For example:
|
||||||
/// If you add the same period to the 1st February, you will get the 1st March.
|
///
|
||||||
|
/// If you add a period `1.month` to the 1st January,
|
||||||
|
/// you will get the 1st February.
|
||||||
|
/// If you add the same period to the 1st February,
|
||||||
|
/// you will get the 1st March.
|
||||||
/// But the intervals(`31.days` in case 1, `28.days` or `29.days` in case 2)
|
/// But the intervals(`31.days` in case 1, `28.days` or `29.days` in case 2)
|
||||||
/// in these two cases are quite different.
|
/// in these two cases are quite different.
|
||||||
public struct Period {
|
public struct Period {
|
||||||
|
|
||||||
public let years: Int
|
public let years: Int
|
||||||
|
|
||||||
public let months: Int
|
public let months: Int
|
||||||
|
|
||||||
public let days: Int
|
public let days: Int
|
||||||
|
|
||||||
public let hours: Int
|
public let hours: Int
|
||||||
|
|
||||||
public let minutes: Int
|
public let minutes: Int
|
||||||
|
|
||||||
public let seconds: Int
|
public let seconds: Int
|
||||||
|
|
||||||
public let nanoseconds: Int
|
public let nanoseconds: Int
|
||||||
|
|
||||||
public init(years: Int = 0, months: Int = 0, days: Int = 0, hours: Int = 0, minutes: Int = 0, seconds: Int = 0, nanoseconds: Int = 0) {
|
public init(years: Int = 0, months: Int = 0, days: Int = 0,
|
||||||
|
hours: Int = 0, minutes: Int = 0, seconds: Int = 0,
|
||||||
|
nanoseconds: Int = 0) {
|
||||||
self.years = years
|
self.years = years
|
||||||
self.months = months
|
self.months = months
|
||||||
self.days = days
|
self.days = days
|
||||||
|
@ -262,72 +369,94 @@ public struct Period {
|
||||||
self.nanoseconds = nanoseconds
|
self.nanoseconds = nanoseconds
|
||||||
}
|
}
|
||||||
|
|
||||||
public func and(_ period: Period) -> Period {
|
/// Returns a new date by adding the right period to the left period.
|
||||||
let years = self.years + period.years
|
|
||||||
let months = self.months + period.months
|
|
||||||
let days = self.days + period.days
|
|
||||||
let hours = self.hours + period.hours
|
|
||||||
let minutes = self.minutes + period.minutes
|
|
||||||
let seconds = self.seconds + period.seconds
|
|
||||||
let nanoseconds = self.nanoseconds + period.nanoseconds
|
|
||||||
return Period(years: years, months: months, days: days, hours: hours, minutes: minutes, seconds: seconds, nanoseconds: nanoseconds)
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func +(lhs: Period, rhs: Period) -> Period {
|
public static func +(lhs: Period, rhs: Period) -> Period {
|
||||||
return lhs.and(rhs)
|
return Period(years: lhs.years.clampedAdding(rhs.years),
|
||||||
}
|
months: lhs.months.clampedAdding(rhs.months),
|
||||||
|
days: lhs.days.clampedAdding(rhs.days),
|
||||||
func asDateComponents() -> DateComponents {
|
hours: lhs.hours.clampedAdding(rhs.hours),
|
||||||
return DateComponents(year: years, month: months, day: days, hour: hours, minute: minutes, second: seconds, nanosecond: nanoseconds)
|
minutes: lhs.minutes.clampedAdding(rhs.minutes),
|
||||||
|
seconds: lhs.seconds.clampedAdding(rhs.seconds),
|
||||||
|
nanoseconds: lhs.nanoseconds.clampedAdding(rhs.nanoseconds))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a new date by adding the right period to the left date.
|
||||||
public static func +(lhs: Date, rhs: Period) -> Date {
|
public static func +(lhs: Date, rhs: Period) -> Date {
|
||||||
return Calendar.autoupdatingCurrent.date(byAdding: rhs.asDateComponents(), to: lhs) ?? .distantFuture
|
return Calendar.autoupdatingCurrent.date(byAdding: rhs.asDateComponents(), to: lhs) ?? .distantFuture
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
extension Period {
|
|
||||||
|
|
||||||
public static func years(_ n: Int) -> Period {
|
/// Returns a new period by adding the right interval to the left period.
|
||||||
return Period(years: n)
|
public static func +(lhs: Period, rhs: Interval) -> Period {
|
||||||
|
return Period(years: lhs.years, months: lhs.months, days: lhs.days,
|
||||||
|
hours: lhs.hours, minutes: lhs.minutes, seconds: lhs.seconds,
|
||||||
|
nanoseconds: lhs.nanoseconds.clampedAdding(rhs.ns))
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func months(_ n: Int) -> Period {
|
internal func asDateComponents() -> DateComponents {
|
||||||
return Period(months: n)
|
return DateComponents(year: years, month: months, day: days,
|
||||||
}
|
hour: hours, minute: minutes, second: seconds,
|
||||||
public static func days(_ n: Int) -> Period {
|
nanosecond: nanoseconds)
|
||||||
return Period(days: n)
|
|
||||||
}
|
|
||||||
public static func hours(_ n: Int) -> Period {
|
|
||||||
return Period(hours: n)
|
|
||||||
}
|
|
||||||
public static func minutes(_ n: Int) -> Period {
|
|
||||||
return Period(minutes: n)
|
|
||||||
}
|
|
||||||
public static func seconds(_ n: Int) -> Period {
|
|
||||||
return Period(seconds: n)
|
|
||||||
}
|
|
||||||
public static func nanoseconds(_ n: Int) -> Period {
|
|
||||||
return Period(nanoseconds: n)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Int {
|
||||||
|
|
||||||
|
/// Period by setting years to this value.
|
||||||
|
public var years: Period {
|
||||||
|
return Period(years: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Period by setting years to this value.
|
||||||
|
public var year: Period {
|
||||||
|
return years
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Period by setting months to this value.
|
||||||
|
public var months: Period {
|
||||||
|
return Period(months: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Period by setting months to this value.
|
||||||
|
public var month: Period {
|
||||||
|
return months
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// `Weekday` represents a day of the week, without a time.
|
||||||
|
public enum Weekday: Int {
|
||||||
|
case sunday = 1, monday, tuesday, wednesday, thursday, friday, saturday
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// `MonthDay` represents a day in the month, without a time.
|
||||||
public enum MonthDay {
|
public enum MonthDay {
|
||||||
|
|
||||||
case january(Int)
|
case january(Int)
|
||||||
|
|
||||||
case february(Int)
|
case february(Int)
|
||||||
|
|
||||||
case march(Int)
|
case march(Int)
|
||||||
|
|
||||||
case april(Int)
|
case april(Int)
|
||||||
|
|
||||||
case may(Int)
|
case may(Int)
|
||||||
|
|
||||||
case june(Int)
|
case june(Int)
|
||||||
|
|
||||||
case july(Int)
|
case july(Int)
|
||||||
|
|
||||||
case august(Int)
|
case august(Int)
|
||||||
|
|
||||||
case september(Int)
|
case september(Int)
|
||||||
|
|
||||||
case october(Int)
|
case october(Int)
|
||||||
|
|
||||||
case november(Int)
|
case november(Int)
|
||||||
|
|
||||||
case december(Int)
|
case december(Int)
|
||||||
|
|
||||||
func asDateComponents() -> DateComponents {
|
internal func asDateComponents() -> DateComponents {
|
||||||
switch self {
|
switch self {
|
||||||
case .january(let day): return DateComponents(month: 1, day: day)
|
case .january(let day): return DateComponents(month: 1, day: day)
|
||||||
case .february(let day): return DateComponents(month: 2, day: day)
|
case .february(let day): return DateComponents(month: 2, day: day)
|
||||||
|
|
|
@ -7,26 +7,30 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class Job {
|
/// `Job` represents an action that to be invoke.
|
||||||
|
public final class Job {
|
||||||
|
|
||||||
|
/// Last time this job was invoked at.
|
||||||
public private(set) var lastTime: Date?
|
public private(set) var lastTime: Date?
|
||||||
|
|
||||||
|
/// Next time this job will be invoked at.
|
||||||
public private(set) var nextTime: Date?
|
public private(set) var nextTime: Date?
|
||||||
|
|
||||||
private var iterator: Atomic<AnyIterator<Interval>>
|
private var iterator: AtomicBox<AnyIterator<Interval>>
|
||||||
private let onElapse: (Job) -> Void
|
private let onElapse: (Job) -> Void
|
||||||
private let timer: DispatchSourceTimer
|
private let timer: DispatchSourceTimer
|
||||||
|
|
||||||
private var suspensions = Atomic<UInt>(0)
|
private var suspensions = AtomicBox<UInt>(0)
|
||||||
|
|
||||||
init(schedule: Schedule,
|
init(schedule: Schedule,
|
||||||
queue: DispatchQueue? = nil,
|
queue: DispatchQueue? = nil,
|
||||||
tag: String? = nil,
|
tag: String? = nil,
|
||||||
onElapse: @escaping (Job) -> Void) {
|
onElapse: @escaping (Job) -> Void) {
|
||||||
self.iterator = Atomic(schedule.makeIterator())
|
self.iterator = AtomicBox(schedule.makeIterator())
|
||||||
self.onElapse = onElapse
|
self.onElapse = onElapse
|
||||||
self.timer = DispatchSource.makeTimerSource(queue: queue)
|
self.timer = DispatchSource.makeTimerSource(queue: queue)
|
||||||
|
|
||||||
let interval = self.iterator.withLock({ $0.next() })?.dispatchInterval ?? DispatchTimeInterval.never
|
let interval = self.iterator.withLock({ $0.next() })?.asDispatchTimeInterval() ?? DispatchTimeInterval.never
|
||||||
self.timer.schedule(wallDeadline: .now() + interval)
|
self.timer.schedule(wallDeadline: .now() + interval)
|
||||||
self.timer.setEventHandler { [weak self] in
|
self.timer.setEventHandler { [weak self] in
|
||||||
self?.elapse()
|
self?.elapse()
|
||||||
|
@ -49,15 +53,17 @@ public class Job {
|
||||||
|
|
||||||
nextTime = now.addingTimeInterval(i.nanoseconds / pow(10, 9))
|
nextTime = now.addingTimeInterval(i.nanoseconds / pow(10, 9))
|
||||||
onElapse(self)
|
onElapse(self)
|
||||||
timer.schedule(wallDeadline: .now() + i.dispatchInterval)
|
timer.schedule(wallDeadline: .now() + i.asDispatchTimeInterval())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reschedule this job with the schedule.
|
||||||
public func reschedule(_ schedule: Schedule) {
|
public func reschedule(_ schedule: Schedule) {
|
||||||
iterator.withLock {
|
iterator.withLock {
|
||||||
$0 = schedule.makeIterator()
|
$0 = schedule.makeIterator()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Suspend this job.
|
||||||
public func suspend() {
|
public func suspend() {
|
||||||
let canSuspend = suspensions.withLock { (n) -> Bool in
|
let canSuspend = suspensions.withLock { (n) -> Bool in
|
||||||
guard n < UInt.max else { return false }
|
guard n < UInt.max else { return false }
|
||||||
|
@ -70,6 +76,7 @@ public class Job {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resume this job.
|
||||||
public func resume() {
|
public func resume() {
|
||||||
let canResume = suspensions.withLock { (n) -> Bool in
|
let canResume = suspensions.withLock { (n) -> Bool in
|
||||||
guard n > 0 else { return false }
|
guard n > 0 else { return false }
|
||||||
|
@ -81,23 +88,27 @@ public class Job {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cancel this job.
|
||||||
public func cancel() {
|
public func cancel() {
|
||||||
timer.cancel()
|
timer.cancel()
|
||||||
JobCenter.shared.remove(self)
|
JobCenter.shared.remove(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func cancel(_ tag: String) {
|
/// Suspend all job that attach the tag.
|
||||||
JobCenter.shared.jobs(for: tag).forEach { $0.cancel() }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func suspend(_ tag: String) {
|
public static func suspend(_ tag: String) {
|
||||||
JobCenter.shared.jobs(for: tag).forEach { $0.suspend() }
|
JobCenter.shared.jobs(for: tag).forEach { $0.suspend() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resume all job that attach the tag.
|
||||||
public static func resume(_ tag: String) {
|
public static func resume(_ tag: String) {
|
||||||
JobCenter.shared.jobs(for: tag).forEach { $0.resume() }
|
JobCenter.shared.jobs(for: tag).forEach { $0.resume() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cancel all job that attach the tag.
|
||||||
|
public static func cancel(_ tag: String) {
|
||||||
|
JobCenter.shared.jobs(for: tag).forEach { $0.cancel() }
|
||||||
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
suspensions.withLock { (n) in
|
suspensions.withLock { (n) in
|
||||||
while n > 0 {
|
while n > 0 {
|
||||||
|
@ -111,10 +122,12 @@ public class Job {
|
||||||
|
|
||||||
extension Job: Hashable {
|
extension Job: Hashable {
|
||||||
|
|
||||||
|
/// The hashValue of this job.
|
||||||
public var hashValue: Int {
|
public var hashValue: Int {
|
||||||
return ObjectIdentifier(self).hashValue
|
return ObjectIdentifier(self).hashValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a boolean value indicating whether the job is equal to another job.
|
||||||
public static func ==(lhs: Job, rhs: Job) -> Bool {
|
public static func ==(lhs: Job, rhs: Job) -> Bool {
|
||||||
return lhs.hashValue == rhs.hashValue
|
return lhs.hashValue == rhs.hashValue
|
||||||
}
|
}
|
||||||
|
@ -122,6 +135,9 @@ extension Job: Hashable {
|
||||||
|
|
||||||
extension Optional: Hashable where Wrapped: Job {
|
extension Optional: Hashable where Wrapped: Job {
|
||||||
|
|
||||||
|
/// The hashValue of this job.
|
||||||
|
///
|
||||||
|
/// If job is nil, then return 0.
|
||||||
public var hashValue: Int {
|
public var hashValue: Int {
|
||||||
if case .some(let wrapped) = self {
|
if case .some(let wrapped) = self {
|
||||||
return wrapped.hashValue
|
return wrapped.hashValue
|
||||||
|
@ -129,12 +145,15 @@ extension Optional: Hashable where Wrapped: Job {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a boolean value indicating whether the job is equal to another job.
|
||||||
|
///
|
||||||
|
/// If both of these two are nil, then return true.
|
||||||
public static func ==(lhs: Optional<Job>, rhs: Optional<Job>) -> Bool {
|
public static func ==(lhs: Optional<Job>, rhs: Optional<Job>) -> Bool {
|
||||||
return lhs.hashValue == rhs.hashValue
|
return lhs.hashValue == rhs.hashValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class JobCenter {
|
private final class JobCenter {
|
||||||
|
|
||||||
static let shared = JobCenter()
|
static let shared = JobCenter()
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,13 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// `Schedule` represents a plan that gives the times
|
||||||
|
/// at which a job should be invoked.
|
||||||
|
///
|
||||||
|
/// `Schedule` is interval based.
|
||||||
|
/// When a new job is created in the `do` method, it will ask for the first
|
||||||
|
/// interval in this schedule, then set up a timer to invoke itself
|
||||||
|
/// after the interval.
|
||||||
public struct Schedule {
|
public struct Schedule {
|
||||||
|
|
||||||
private var sequence: AnySequence<Interval>
|
private var sequence: AnySequence<Interval>
|
||||||
|
@ -18,6 +25,13 @@ public struct Schedule {
|
||||||
return sequence.makeIterator()
|
return sequence.makeIterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Schedule a job with this schedule.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - queue: The dispatch queue to which the job will be submitted.
|
||||||
|
/// - tag: The tag to attach to the job.
|
||||||
|
/// - onElapse: The job to invoke when time is out.
|
||||||
|
/// - Returns: The job just created.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func `do`(queue: DispatchQueue? = nil,
|
public func `do`(queue: DispatchQueue? = nil,
|
||||||
tag: String? = nil,
|
tag: String? = nil,
|
||||||
|
@ -25,21 +39,58 @@ public struct Schedule {
|
||||||
return Job(schedule: self, queue: queue, tag: tag, onElapse: onElapse)
|
return Job(schedule: self, queue: queue, tag: tag, onElapse: onElapse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Schedule a job with this schedule.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - queue: The dispatch queue to which the job will be submitted.
|
||||||
|
/// - tag: The tag to attach to the queue
|
||||||
|
/// - onElapse: The job to invoke when time is out.
|
||||||
|
/// - Returns: The job just created.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func `do`(queue: DispatchQueue? = nil,
|
public func `do`(queue: DispatchQueue? = nil,
|
||||||
tag: String? = nil,
|
tag: String? = nil,
|
||||||
onElapse: @escaping () -> Void) -> Job {
|
onElapse: @escaping () -> Void) -> Job {
|
||||||
return self.do(queue: queue, tag: tag, onElapse: { (_) in onElapse() })
|
return self.do(queue: queue, tag: tag, onElapse: { (_) in onElapse() })
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Schedule {
|
||||||
|
|
||||||
public static func make<I>(_ makeIterator: @escaping () -> I) -> Schedule where I: IteratorProtocol, I.Element == Interval {
|
/// Creates a schedule from a `makeUnderlyingIterator()` method.
|
||||||
return Schedule(AnySequence(makeIterator))
|
///
|
||||||
|
/// The job will be invoke after each interval
|
||||||
|
/// produced by the iterator that `makeUnderlyingIterator` returns.
|
||||||
|
///
|
||||||
|
/// For example:
|
||||||
|
///
|
||||||
|
/// let schedule = Schedule.make {
|
||||||
|
/// var i = 0
|
||||||
|
/// return AnyIterator {
|
||||||
|
/// i += 1
|
||||||
|
/// return i
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// schedule.do {
|
||||||
|
/// print(Date())
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// > "2001-01-01 00:00:00"
|
||||||
|
/// > "2001-01-01 00:00:01"
|
||||||
|
/// > "2001-01-01 00:00:03"
|
||||||
|
/// > "2001-01-01 00:00:06"
|
||||||
|
/// ...
|
||||||
|
public static func make<I>(_ makeUnderlyingIterator: @escaping () -> I) -> Schedule where I: IteratorProtocol, I.Element == Interval {
|
||||||
|
return Schedule(AnySequence(makeUnderlyingIterator))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a schedule from an interval sequence.
|
||||||
|
/// The job will be invoke after each interval in the sequence.
|
||||||
public static func from<S>(_ sequence: S) -> Schedule where S: Sequence, S.Element == Interval {
|
public static func from<S>(_ sequence: S) -> Schedule where S: Sequence, S.Element == Interval {
|
||||||
return Schedule(sequence)
|
return Schedule(sequence)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a schedule from an interval array.
|
||||||
|
/// The job will be invoke after each interval in the array.
|
||||||
public static func of(_ intervals: Interval...) -> Schedule {
|
public static func of(_ intervals: Interval...) -> Schedule {
|
||||||
return Schedule(intervals)
|
return Schedule(intervals)
|
||||||
}
|
}
|
||||||
|
@ -47,9 +98,33 @@ public struct Schedule {
|
||||||
|
|
||||||
extension Schedule {
|
extension Schedule {
|
||||||
|
|
||||||
public static func make<I>(_ makeIterator: @escaping () -> I) -> Schedule where I: IteratorProtocol, I.Element == Date {
|
/// Creates a schedule from a `makeUnderlyingIterator()` method.
|
||||||
|
///
|
||||||
|
/// The job will be invoke at each date
|
||||||
|
/// produced by the iterator that `makeUnderlyingIterator` returns.
|
||||||
|
///
|
||||||
|
/// For example:
|
||||||
|
///
|
||||||
|
/// let schedule = Schedule.make {
|
||||||
|
/// return AnyIterator {
|
||||||
|
/// return Date().addingTimeInterval(3)
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// print("now:", Date())
|
||||||
|
/// schedule.do {
|
||||||
|
/// print("job", Date())
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// > "now: 2001-01-01 00:00:00"
|
||||||
|
/// > "job: 2001-01-01 00:00:03
|
||||||
|
/// ...
|
||||||
|
///
|
||||||
|
/// You should not return `Date()` in making interator
|
||||||
|
/// if you want to invoke a job immediately,
|
||||||
|
/// use `Schedule.now` then `concat` another schedule instead.
|
||||||
|
public static func make<I>(_ makeUnderlyingIterator: @escaping () -> I) -> Schedule where I: IteratorProtocol, I.Element == Date {
|
||||||
return Schedule.make { () -> AnyIterator<Interval> in
|
return Schedule.make { () -> AnyIterator<Interval> in
|
||||||
var iterator = makeIterator()
|
var iterator = makeUnderlyingIterator()
|
||||||
var previous: Date!
|
var previous: Date!
|
||||||
return AnyIterator {
|
return AnyIterator {
|
||||||
previous = previous ?? Date()
|
previous = previous ?? Date()
|
||||||
|
@ -60,14 +135,19 @@ extension Schedule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a schedule from a date sequence.
|
||||||
|
/// The job will be invoke at each date in the sequence.
|
||||||
public static func from<S>(_ sequence: S) -> Schedule where S: Sequence, S.Element == Date {
|
public static func from<S>(_ sequence: S) -> Schedule where S: Sequence, S.Element == Date {
|
||||||
return Schedule.make(sequence.makeIterator)
|
return Schedule.make(sequence.makeIterator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a schedule from a date array.
|
||||||
|
/// The job will be invoke at each date in the array.
|
||||||
public static func of(_ dates: Date...) -> Schedule {
|
public static func of(_ dates: Date...) -> Schedule {
|
||||||
return Schedule.from(dates)
|
return Schedule.from(dates)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A dates sequence corresponding to this schedule.
|
||||||
public var dates: AnySequence<Date> {
|
public var dates: AnySequence<Date> {
|
||||||
return AnySequence { () -> AnyIterator<Date> in
|
return AnySequence { () -> AnyIterator<Date> in
|
||||||
let iterator = self.makeIterator()
|
let iterator = self.makeIterator()
|
||||||
|
@ -84,23 +164,35 @@ extension Schedule {
|
||||||
|
|
||||||
extension Schedule {
|
extension Schedule {
|
||||||
|
|
||||||
|
/// A schedule with a distant past date.
|
||||||
public static var distantPast: Schedule {
|
public static var distantPast: Schedule {
|
||||||
return Schedule.of(Date.distantPast)
|
return Schedule.of(Date.distantPast)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A schedule with a distant future date.
|
||||||
public static var distantFuture: Schedule {
|
public static var distantFuture: Schedule {
|
||||||
return Schedule.of(Date.distantFuture)
|
return Schedule.of(Date.distantFuture)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A schedule with no date.
|
||||||
public static var never: Schedule {
|
public static var never: Schedule {
|
||||||
return Schedule.make {
|
return Schedule.make {
|
||||||
AnyIterator<Interval> { nil }
|
AnyIterator<Date> { nil }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Schedule {
|
extension Schedule {
|
||||||
|
|
||||||
|
/// Returns a new schedule by concatenating a schedule to this schedule.
|
||||||
|
///
|
||||||
|
/// For example:
|
||||||
|
///
|
||||||
|
/// let s0 = Schedule.of(1.second, 2.seconds, 3.seconds)
|
||||||
|
/// let s1 = Schedule.of(4.seconds, 4.seconds, 4.seconds)
|
||||||
|
/// let s2 = s0.concat(s1)
|
||||||
|
/// > s2
|
||||||
|
/// > 1.second, 2.seconds, 3.seconds, 4.seconds, 4.seconds, 4.seconds
|
||||||
public func concat(_ schedule: Schedule) -> Schedule {
|
public func concat(_ schedule: Schedule) -> Schedule {
|
||||||
return Schedule.make { () -> AnyIterator<Interval> in
|
return Schedule.make { () -> AnyIterator<Interval> in
|
||||||
let i0 = self.makeIterator()
|
let i0 = self.makeIterator()
|
||||||
|
@ -112,6 +204,15 @@ extension Schedule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a new schedule by merging a schedule to this schedule.
|
||||||
|
///
|
||||||
|
/// For example:
|
||||||
|
///
|
||||||
|
/// let s0 = Schedule.of(1.second, 3.seconds, 5.seconds)
|
||||||
|
/// let s1 = Schedule.of(2.seconds, 4.seconds, 6.seconds)
|
||||||
|
/// let s2 = s0.concat(s1)
|
||||||
|
/// > s2
|
||||||
|
/// > 1.second, 1.second, 1.second, 1.second, 1.second, 1.second
|
||||||
public func merge(_ schedule: Schedule) -> Schedule {
|
public func merge(_ schedule: Schedule) -> Schedule {
|
||||||
return Schedule.make { () -> AnyIterator<Interval> in
|
return Schedule.make { () -> AnyIterator<Interval> in
|
||||||
let i0 = self.dates.makeIterator()
|
let i0 = self.dates.makeIterator()
|
||||||
|
@ -137,6 +238,14 @@ extension Schedule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a new schedule by cutting out a specific number of this schedule.
|
||||||
|
///
|
||||||
|
/// For example:
|
||||||
|
///
|
||||||
|
/// let s0 = Schedule.every(1.second)
|
||||||
|
/// let s1 = s0.count(3)
|
||||||
|
/// > s1
|
||||||
|
/// 1.second, 1.second, 1.second
|
||||||
public func count(_ count: Int) -> Schedule {
|
public func count(_ count: Int) -> Schedule {
|
||||||
return Schedule.make { () -> AnyIterator<Interval> in
|
return Schedule.make { () -> AnyIterator<Interval> in
|
||||||
let iterator = self.makeIterator()
|
let iterator = self.makeIterator()
|
||||||
|
@ -149,48 +258,62 @@ extension Schedule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a new schedule by cutting out the part which is before the date.
|
||||||
public func until(_ date: Date) -> Schedule {
|
public func until(_ date: Date) -> Schedule {
|
||||||
return Schedule.make { () -> AnyIterator<Interval> in
|
return Schedule.make { () -> AnyIterator<Interval> in
|
||||||
let iterator = self.makeIterator()
|
let iterator = self.makeIterator()
|
||||||
var previous: Date!
|
var previous: Date!
|
||||||
return AnyIterator {
|
return AnyIterator {
|
||||||
previous = previous ?? Date()
|
previous = previous ?? Date()
|
||||||
guard let interval = iterator.next(), previous.addingTimeInterval(interval.seconds) < date else { return nil }
|
guard let interval = iterator.next(),
|
||||||
|
previous.addingTimeInterval(interval.seconds) < date else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
previous.addTimeInterval(interval.seconds)
|
previous.addTimeInterval(interval.seconds)
|
||||||
return interval
|
return interval
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a schedule that invokes the job immediately.
|
||||||
public static var now: Schedule {
|
public static var now: Schedule {
|
||||||
return Schedule.of(0.nanosecond)
|
return Schedule.of(0.nanosecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a schedule that invokes the job after the delay.
|
||||||
public static func after(_ delay: Interval) -> Schedule {
|
public static func after(_ delay: Interval) -> Schedule {
|
||||||
return Schedule.of(delay)
|
return Schedule.of(delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a schedule that invokes the job every interval.
|
||||||
public static func every(_ interval: Interval) -> Schedule {
|
public static func every(_ interval: Interval) -> Schedule {
|
||||||
return Schedule.make {
|
return Schedule.make {
|
||||||
AnyIterator { interval }
|
AnyIterator { interval }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a schedule that invokes the job at the specific date.
|
||||||
public static func at(_ date: Date) -> Schedule {
|
public static func at(_ date: Date) -> Schedule {
|
||||||
return Schedule.of(date)
|
return Schedule.of(date)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a schedule that invokes the job after the delay then repeat
|
||||||
|
/// every interval.
|
||||||
public static func after(_ delay: Interval, repeating interval: Interval) -> Schedule {
|
public static func after(_ delay: Interval, repeating interval: Interval) -> Schedule {
|
||||||
return Schedule.after(delay).concat(Schedule.every(interval))
|
return Schedule.after(delay).concat(Schedule.every(interval))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a schedule that invokes the job every period.
|
||||||
public static func every(_ period: Period) -> Schedule {
|
public static func every(_ period: Period) -> Schedule {
|
||||||
return Schedule.make { () -> AnyIterator<Interval> in
|
return Schedule.make { () -> AnyIterator<Interval> in
|
||||||
let calendar = Calendar.autoupdatingCurrent
|
let calendar = Calendar.autoupdatingCurrent
|
||||||
var previous: Date!
|
var previous: Date!
|
||||||
return AnyIterator {
|
return AnyIterator {
|
||||||
previous = previous ?? Date()
|
previous = previous ?? Date()
|
||||||
guard let next = calendar.date(byAdding: period.asDateComponents(), to: previous) else { return nil }
|
guard let next = calendar.date(byAdding: period.asDateComponents(),
|
||||||
|
to: previous) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
defer { previous = next }
|
defer { previous = next }
|
||||||
return next.interval(since: previous)
|
return next.interval(since: previous)
|
||||||
}
|
}
|
||||||
|
@ -200,11 +323,17 @@ extension Schedule {
|
||||||
|
|
||||||
extension Schedule {
|
extension Schedule {
|
||||||
|
|
||||||
|
/// `EveryDateMiddleware` represents a middleware that wraps a schedule
|
||||||
|
/// which only specify date without time.
|
||||||
|
///
|
||||||
|
/// You should call `at` method to get the time specified schedule.
|
||||||
public struct EveryDateMiddleware {
|
public struct EveryDateMiddleware {
|
||||||
|
|
||||||
fileprivate let schedule: Schedule
|
fileprivate let schedule: Schedule
|
||||||
|
|
||||||
|
/// Returns a schedule at the specific timing.
|
||||||
public func at(_ timing: Time) -> Schedule {
|
public func at(_ timing: Time) -> Schedule {
|
||||||
|
|
||||||
return Schedule.make { () -> AnyIterator<Interval> in
|
return Schedule.make { () -> AnyIterator<Interval> in
|
||||||
let iterator = self.schedule.dates.makeIterator()
|
let iterator = self.schedule.dates.makeIterator()
|
||||||
let calendar = Calendar.autoupdatingCurrent
|
let calendar = Calendar.autoupdatingCurrent
|
||||||
|
@ -212,13 +341,29 @@ extension Schedule {
|
||||||
return AnyIterator {
|
return AnyIterator {
|
||||||
previous = previous ?? Date()
|
previous = previous ?? Date()
|
||||||
guard let date = iterator.next(),
|
guard let date = iterator.next(),
|
||||||
let next = calendar.nextDate(after: date, matching: timing.asDateComponents(), matchingPolicy: .strict) else { return nil }
|
let next = calendar.nextDate(after: date,
|
||||||
|
matching: timing.asDateComponents(),
|
||||||
|
matchingPolicy: .strict) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
defer { previous = next }
|
defer { previous = next }
|
||||||
return next.interval(since: previous)
|
return next.interval(since: previous)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a schedule at the specific timing.
|
||||||
|
///
|
||||||
|
/// For example:
|
||||||
|
///
|
||||||
|
/// let s = Schedule.every(.monday).at("11:11")
|
||||||
|
///
|
||||||
|
/// Available format:
|
||||||
|
///
|
||||||
|
/// Time("11") == Time(hour: 11)
|
||||||
|
/// Time("11:12") == Time(hour: 11, minute: 12)
|
||||||
|
/// Time("11:12:13") == Time(hour: 11, minute: 12, second: 13)
|
||||||
|
/// Time("11:12:13.123") == Time(hour: 11, minute: 12, second: 13, nanosecond: 123)
|
||||||
public func at(_ timing: String) -> Schedule {
|
public func at(_ timing: String) -> Schedule {
|
||||||
guard let time = Time(timing: timing) else {
|
guard let time = Time(timing: timing) else {
|
||||||
return Schedule.never
|
return Schedule.never
|
||||||
|
@ -226,6 +371,7 @@ extension Schedule {
|
||||||
return at(time)
|
return at(time)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a schedule at the specific timing.
|
||||||
public func at(_ timing: Int...) -> Schedule {
|
public func at(_ timing: Int...) -> Schedule {
|
||||||
let hour = timing[0]
|
let hour = timing[0]
|
||||||
let minute = timing.count > 1 ? timing[1] : 0
|
let minute = timing.count > 1 ? timing[1] : 0
|
||||||
|
@ -239,6 +385,7 @@ extension Schedule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a schedule that invokes the job every specific weekday.
|
||||||
public static func every(_ weekday: Weekday) -> EveryDateMiddleware {
|
public static func every(_ weekday: Weekday) -> EveryDateMiddleware {
|
||||||
let schedule = Schedule.make { () -> AnyIterator<Interval> in
|
let schedule = Schedule.make { () -> AnyIterator<Interval> in
|
||||||
let calendar = Calendar.autoupdatingCurrent
|
let calendar = Calendar.autoupdatingCurrent
|
||||||
|
@ -257,6 +404,7 @@ extension Schedule {
|
||||||
return EveryDateMiddleware(schedule: schedule)
|
return EveryDateMiddleware(schedule: schedule)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a schedule that invokes the job every specific weekdays.
|
||||||
public static func every(_ weekdays: Weekday...) -> EveryDateMiddleware {
|
public static func every(_ weekdays: Weekday...) -> EveryDateMiddleware {
|
||||||
var schedule = every(weekdays[0]).schedule
|
var schedule = every(weekdays[0]).schedule
|
||||||
if weekdays.count > 1 {
|
if weekdays.count > 1 {
|
||||||
|
@ -267,6 +415,7 @@ extension Schedule {
|
||||||
return EveryDateMiddleware(schedule: schedule)
|
return EveryDateMiddleware(schedule: schedule)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a schedule that invokes the job every specific day in the month.
|
||||||
public static func every(_ monthDay: MonthDay) -> EveryDateMiddleware {
|
public static func every(_ monthDay: MonthDay) -> EveryDateMiddleware {
|
||||||
let schedule = Schedule.make { () -> AnyIterator<Interval> in
|
let schedule = Schedule.make { () -> AnyIterator<Interval> in
|
||||||
let calendar = Calendar.autoupdatingCurrent
|
let calendar = Calendar.autoupdatingCurrent
|
||||||
|
@ -286,6 +435,7 @@ extension Schedule {
|
||||||
return EveryDateMiddleware(schedule: schedule)
|
return EveryDateMiddleware(schedule: schedule)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a schedule that invokes the job every specific days in the months.
|
||||||
public static func every(_ mondays: MonthDay...) -> EveryDateMiddleware {
|
public static func every(_ mondays: MonthDay...) -> EveryDateMiddleware {
|
||||||
var schedule = every(mondays[0]).schedule
|
var schedule = every(mondays[0]).schedule
|
||||||
if mondays.count > 1 {
|
if mondays.count > 1 {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class Atomic<Wrapped> {
|
class AtomicBox<Wrapped> {
|
||||||
|
|
||||||
private var lock = NSLock()
|
private var lock = NSLock()
|
||||||
|
|
||||||
|
@ -41,3 +41,15 @@ extension String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Int {
|
||||||
|
|
||||||
|
func clampedAdding(_ other: Int) -> Int {
|
||||||
|
let r = addingReportingOverflow(other)
|
||||||
|
return r.overflow ? (other > 0 ? .max : .min) : r.partialValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func clampedSubtracting(_ other: Int) -> Int {
|
||||||
|
let r = subtractingReportingOverflow(other)
|
||||||
|
return r.overflow ? (other > 0 ? .max : .min) : r.partialValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,10 +12,10 @@ final class DateTimeTests: XCTestCase {
|
||||||
|
|
||||||
func testInterval2DispatchInterval() {
|
func testInterval2DispatchInterval() {
|
||||||
let i0 = 1.23.seconds
|
let i0 = 1.23.seconds
|
||||||
XCTAssertEqual(i0.dispatchInterval, DispatchTimeInterval.nanoseconds(Int(i0.nanoseconds)))
|
XCTAssertEqual(i0.asDispatchTimeInterval(), DispatchTimeInterval.nanoseconds(Int(i0.nanoseconds)))
|
||||||
|
|
||||||
let i1 = 4.56.minutes + 7.89.hours
|
let i1 = 4.56.minutes + 7.89.hours
|
||||||
XCTAssertEqual(i1.dispatchInterval, DispatchTimeInterval.nanoseconds(Int(i1.nanoseconds)))
|
XCTAssertEqual(i1.asDispatchTimeInterval(), DispatchTimeInterval.nanoseconds(Int(i1.nanoseconds)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testIntervalConvertible() {
|
func testIntervalConvertible() {
|
||||||
|
@ -42,7 +42,7 @@ final class DateTimeTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPeriodAnd() {
|
func testPeriodAnd() {
|
||||||
let dateComponents: Period = .years(1) + .months(2) + .days(3)
|
let dateComponents: Period = 1.year + 2.months + 3.days
|
||||||
XCTAssertEqual(dateComponents.years, 1)
|
XCTAssertEqual(dateComponents.years, 1)
|
||||||
XCTAssertEqual(dateComponents.months, 2)
|
XCTAssertEqual(dateComponents.months, 2)
|
||||||
XCTAssertEqual(dateComponents.days, 3)
|
XCTAssertEqual(dateComponents.days, 3)
|
||||||
|
|
|
@ -10,7 +10,6 @@ final class ScheduleTests: XCTestCase {
|
||||||
let s1 = Schedule.from(intervals)
|
let s1 = Schedule.from(intervals)
|
||||||
XCTAssert(s1.makeIterator().isEqual(to: intervals, leeway: 0.001.seconds))
|
XCTAssert(s1.makeIterator().isEqual(to: intervals, leeway: 0.001.seconds))
|
||||||
|
|
||||||
|
|
||||||
let d0 = Date() + intervals[0]
|
let d0 = Date() + intervals[0]
|
||||||
let d1 = d0 + intervals[1]
|
let d1 = d0 + intervals[1]
|
||||||
let d2 = d1 + intervals[2]
|
let d2 = d1 + intervals[2]
|
||||||
|
@ -74,17 +73,13 @@ final class ScheduleTests: XCTestCase {
|
||||||
func testMerge() {
|
func testMerge() {
|
||||||
let intervals0: [Interval] = [1.second, 2.minutes, 1.hour]
|
let intervals0: [Interval] = [1.second, 2.minutes, 1.hour]
|
||||||
let intervals1: [Interval] = [2.seconds, 1.minutes, 1.seconds]
|
let intervals1: [Interval] = [2.seconds, 1.minutes, 1.seconds]
|
||||||
|
|
||||||
let scheudle0 = Schedule.from(intervals0).merge(Schedule.from(intervals1))
|
let scheudle0 = Schedule.from(intervals0).merge(Schedule.from(intervals1))
|
||||||
|
|
||||||
let scheudle1 = Schedule.of(1.second, 1.second, 1.minutes, 1.seconds, 58.seconds, 1.hour)
|
let scheudle1 = Schedule.of(1.second, 1.second, 1.minutes, 1.seconds, 58.seconds, 1.hour)
|
||||||
XCTAssert(scheudle0.makeIterator().isEqual(to: scheudle1.makeIterator(), leeway: 0.001.seconds))
|
XCTAssert(scheudle0.makeIterator().isEqual(to: scheudle1.makeIterator(), leeway: 0.001.seconds))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEveryPeriod() {
|
func testEveryPeriod() {
|
||||||
|
let s = Schedule.every(1.year).count(10)
|
||||||
let s = Schedule.every(.years(1)).count(10)
|
|
||||||
|
|
||||||
var date = Date()
|
var date = Date()
|
||||||
for i in s.dates {
|
for i in s.dates {
|
||||||
XCTAssertEqual(i.dateComponents.year!, date.dateComponents.year! + 1)
|
XCTAssertEqual(i.dateComponents.year!, date.dateComponents.year! + 1)
|
||||||
|
|
|
@ -34,8 +34,8 @@ extension Sequence where Element == Interval {
|
||||||
var i0 = self.makeIterator()
|
var i0 = self.makeIterator()
|
||||||
var i1 = sequence.makeIterator()
|
var i1 = sequence.makeIterator()
|
||||||
while let l = i0.next(), let r = i1.next() {
|
while let l = i0.next(), let r = i1.next() {
|
||||||
let diff = Swift.max(l, r) - Swift.min(l, r)
|
let diff = Interval.longest(l, r) - Interval.shortest(l, r)
|
||||||
if diff < leeway { continue }
|
if diff.isShorter(than: leeway) { continue }
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return i0.next() == i1.next()
|
return i0.next() == i1.next()
|
||||||
|
@ -45,8 +45,8 @@ extension Sequence where Element == Interval {
|
||||||
extension Interval {
|
extension Interval {
|
||||||
|
|
||||||
func isEqual(to interval: Interval, leeway: Interval) -> Bool {
|
func isEqual(to interval: Interval, leeway: Interval) -> Bool {
|
||||||
let diff = Swift.max(self, interval) - Swift.min(self, interval)
|
let diff = Interval.longest(self, interval) - Interval.shortest(self, interval)
|
||||||
return diff < leeway
|
return diff.isShorter(than: leeway)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue