Schedule timing task in Swift using a fluent API. (A friendly alternative to Timer)

Last update: Aug 6, 2022

Schedule(简体中文)

Schedule is a timing tasks scheduler written in Swift. It allows you run timing tasks with elegant and intuitive syntax.

Features

  • Elegant and intuitive API
  • Rich preset rules
  • Powerful management mechanism
  • Detailed execution history
  • Thread safe
  • Complete documentation
  • ~100%+ test coverage

Why You Should Use Schedule

Features Timer DispatchSourceTimer Schedule
Interval-based Schedule
📆  Date-based Schedule
🌈 Combined Plan Schedule
🗣️ Natural Language Parse
🏷 Batch Task Management
📝 Execution Record
🎡 Plan Reset
🚦 Suspend, Resume, Cancel
🍰 Child-action

Usage

Overview

Scheduling a task has never been so elegant and intuitive, all you have to do is:

// 1. define your plan:
let plan = Plan.after(3.seconds)

// 2. do your task:
let task = plan.do {
    print("3 seconds passed!")
}

Rules

Interval-based Schedule

The running mechanism of Schedule is based on Plan, and Plan is actually a sequence of Interval.

Schedule makes Plan definitions more elegant and intuitive by extending Int and Double. Also, because Interval is a built-in type of Schedule, you don't have to worry about it being polluting your namespace.

let t1 = Plan.every(1.second).do { }

let t2 = Plan.after(1.hour, repeating: 1.minute).do { }

let t3 = Plan.of(1.second, 2.minutes, 3.hours).do { }

Date-based Schedule

Configuring date-based Plan is the same, with the expressive Swift syntax, Schedule makes your code look like a fluent conversation.

let t1 = Plan.at(date).do { }

let t2 = Plan.every(.monday, .tuesday).at("9:00:00").do { }

let t3 = Plan.every(.september(30)).at(10, 30).do { }

let t4 = Plan.every("one month and ten days").do { }

let t5 = Plan.of(date0, date1, date2).do { }

Natural Language Parse

In addition, Schedule also supports simple natural language parsing.

let t1 = Plan.every("one hour and ten minutes").do { }

let t2 = Plan.every("1 hour, 5 minutes and 10 seconds").do { }

let t3 = Plan.every(.friday).at("9:00 pm").do { }

Period.registerQuantifier("many", for: 100 * 1000)
let t4 = Plan.every("many days").do { }

Combined Plan Schedule

Schedule provides several basic collection operators, which means you can use them to customize your own powerful plans.

/// Concat
let p0 = Plan.at(birthdate)
let p1 = Plan.every(1.year)
let birthday = p0.concat.p1
let t1 = birthday.do { 
    print("Happy birthday")
}

/// Merge
let p3 = Plan.every(.january(1)).at("8:00")
let p4 = Plan.every(.october(1)).at("9:00 AM")
let holiday = p3.merge(p4)
let t2 = holiday.do {
    print("Happy holiday")
}

/// First
let p5 = Plan.after(5.seconds).concat(Schedule.every(1.day))
let p6 = s5.first(10)

/// Until
let p7 = P.every(.monday).at(11, 12)
let p8 = p7.until(date)

Management

DispatchQueue

When calling plan.do to dispatch a timing task, you can use queue to specify which DispatchQueue the task will be dispatched to when the time is up. This operation does not rely on RunLoop like Timer, so you can call it on any thread.

Plan.every(1.second).do(queue: .global()) {
    print("On a globle queue")
}

RunLoop

If queue is not specified, Schedule will use RunLoop to dispatch the task, at which point the task will execute on the current thread. Please note, like Timer, which is also based on RunLoop, you need to ensure that the current thread has an available RunLoop. By default, the task will be added to .common mode, you can specify another mode when creating the task.

let task = Plan.every(1.second).do(mode: .default) {
    print("on default mode...")
}

Timeline

You can observe the execution record of the task in real time using the following properties.

task.creationDate

task.executionHistory

task.firstExecutionDate
task.lastExecutionDate

task.estimatedNextExecutionDate

TaskCenter & Tag

Tasks are automatically added to TaskCenter.default by default,you can organize them using tags and task center.

let plan = Plan.every(1.day)
let task0 = plan.do(queue: myTaskQueue) { }
let task1 = plan.do(queue: myTaskQueue) { }

TaskCenter.default.addTags(["database", "log"], to: task1)
TaskCenter.default.removeTag("log", from: task1)

TaskCenter.default.suspend(byTag: "log")
TaskCenter.default.resume(byTag: "log")
TaskCenter.default.cancel(byTag: "log")

TaskCenter.default.clear()

let myCenter = TaskCenter()
myCenter.add(task0)

Suspend,Resume, Cancel

You can suspend, resume, cancel a task.

let task = Plan.every(1.minute).do { }

// will increase task's suspensionCount
task.suspend()

// will decrease task's suspensionCount,
// but don't worry about excessive resumptions, I will handle these for you~
task.resume()

// will clear task's suspensionCount
// a canceled task can't do anything, event if it is set to a new plan.
task.cancel()

Action

You can add more actions to a task and remove them at any time you want:

let dailyTask = Plan.every(1.day)
dailyTask.addAction {
    print("open eyes")
}
dailyTask.addAction {
    print("get up")
}
let key = dailyTask.addAction {
    print("take a shower")
}
dailyTask.removeAction(byKey: key)

Installation

CocoaPods

# Podfile
use_frameworks!

target 'YOUR_TARGET_NAME' do
  pod 'Schedule', '~> 2.0'
end

Carthage

github "luoxiu/Schedule" ~> 2.0

Swift Package Manager

dependencies: [
    .package(
      url: "https://github.com/luoxiu/Schedule", .upToNextMajor(from: "2.0.0")
    )
]

Contributing

Like Schedule? Thanks!!!

At the same time, I need your help~

Finding Bugs

Schedule is just getting started. If you could help the Schedule find or fix potential bugs, I would be grateful!

New Features

Have some awesome ideas? Feel free to open an issue or submit your pull request directly!

Documentation improvements.

Improvements to README and documentation are welcome at all times, whether typos or my lame English, 🤣 .

Acknowledgement

Inspired by Dan Bader's schedule!

GitHub

https://github.com/luoxiu/Schedule
Comments
  • 1. Plan.every(5.second).do is not working

    Here is my simple code:

    task = Plan.every(5.second).do {
        self.checkIfUnauthorized()
    }
    task?.resume()
    

    This is only working one time. I am running this code on iOS 12.2 with Xcode 10.2.1.

    Reviewed by olcayertas at 2019-05-13 11:25
  • 2. Can't running

    
    import SwiftUI
    import Schedule
    
    struct TestView: View {
    	
    		var body: some View {
    		HStack {
    			
    			
    			Button(action:{
    				
    			
    				let plan = Plan.after(3.seconds)
    				let task = plan.do {
    					print("test 3")
    				}
    				print("created task")
    				//task.executeNow()
    			}){
    				
    				Text("test")
    			}
    
    	}
    }
    
    
    
    

    Hello, I am developing an app with SWIFTUI, but I don't know how to invoke the schedule. it output nothing for me. needs any setup for it?

    Reviewed by a7e6j2 at 2020-04-07 15:57
  • 3. Proposal new logo to SCHEDULE

    Hello sir, I'm a graphic designer, I'm interested in open source projects. I would like to design a logo for your Project. I will be happy to collaborate with you :)

    Reviewed by ggabogarcia at 2018-07-23 14:50
  • 4. Allow usage of arrays in addition to variadic arguments in Interval and Plan functions

    Although it is cleaner to use a variadic function when creating schedules (ex. via Plan.of(_ dates: Date...)), sometimes it is necessary to pass an array instead of a list of parameters, like when dealing with data obtained at runtime. This PR adds that ability. For example, the Plan.of(_:) function can now take an array of Dates like so:

    let dates = [Date(), Date(), Date(), Date(), Date()]
    Plan.of(dates).do { ... }
    

    The existing API has not changed whatsoever, the only difference being that the function implementations are now located in the array-variant of the function, and the existing variadic-variant (whose parameters are interpreted as an array in the function body) now simply passes its parameters into the array-variant.

    Reviewed by WilsonGramer at 2018-11-25 23:36
  • 5. daily task at a certain time...

    first attempt:

    let everyWeekdayTask = Plan.every([.sunday, .monday, .tuesday, .wednesday, .thursday, .friday, .saturday]).at("12 pm").do {}
    

    result:

    (lldb) po everyWeekdayTask.estimatedNextExecutionDate
    nil
    

    second attempt:

            let sunPlan = Plan.every(.sunday).at("12 pm")
            let monPlan = Plan.every(.monday).at("12 pm")
            let tuePlan = Plan.every(.tuesday).at("12 pm")
            let wedPlan = Plan.every(.wednesday).at("12 pm")
            let thuPlan = Plan.every(.thursday).at("12 pm")
            let friPlan = Plan.every(.friday).at("12 pm")
            let satPlan = Plan.every(.saturday).at("12 pm")
            let everyWeekdayTask2 = sunPlan.concat(monPlan).concat(tuePlan).concat(wedPlan).concat(thuPlan).concat(friPlan).concat(satPlan).do {}
    

    result:

    (lldb) po everyWeekdayTask2.estimatedNextExecutionDate
    ▿ Optional<Date>
      ▿ some : 2019-10-20 09:00:00 +0000
        - timeIntervalSinceReferenceDate : 593254800.000214
    

    It seems to be working, but we lost one day. Today is Friday the 18th, and the nearest next timer operation is set to Sunday the 20th (do not look at the time, it is adjusted according to the timezone). That is, we lost somewhere Saturday.

    Can you tell me how to correctly make a task for each day at a certain time?

    Reviewed by maxgribov at 2019-10-18 11:16
  • 6. struct `Plan` api errors

    let t3 = Plan.every(.firday).at("9:00 pm").do { }
    

    error: Reference to member 'firday' cannot be resolved without a contextual type

    let t4 = Plan.every("tuesday").at("9:00 pm").do { }
    

    error: Static member 'at' cannot be used on instance of type 'Plan'

    the first option from the documentation... using: Xcode 11.1, swift 5

    Reviewed by maxgribov at 2019-10-16 15:34
  • 7. Memory Leak

    I noticed this library is leaking RunLoopTask:

    image

    and can be reproduced with this trivial macOS example:

    class ViewController: NSViewController {
        var task: Task?
        @IBAction func onButtonPress(_ sender: Any) {
            task = Plan.after(Interval(seconds: 2)).do {
                print("Hi")
            }
        }
    }
    
    Reviewed by ApolloZhu at 2019-02-17 01:25
  • 8. Does it still support iOS 10 ?

    Hi @jianstm , I have built Schedule via Carthage (Xcode 9.4.1) then integrate to a project and they said: Module file's minimum deployment target is ios11.4 v11.4: /Users/hai/Frameworks/Schedule.framework/Modules/Schedule.swiftmodule/x86_64.swiftmodule

    I have checked Schedule's targets they are setting from 10.10, could you investigate this ?

    Reviewed by haithngn at 2018-09-25 04:07
  • 9. Task every day not work?

    I am confused how it work? Assume that now is Friday 15:10:00

    let task = Plan.every(.friday).at(15,11).do { print("Execute task") }

    But nothing printed at 15:11 Is there something wrong here? (I tested in playground)

    Reviewed by nhatquangz at 2019-03-01 09:02
  • 10. Add support for interval offsets

    Adds the ability to offset a Task by a specific interval from its Plan, such so that the Task calculates its next run based on adding said interval to the Plan's next interval. This can be useful if one has a preexisting Plan and needs to uniformly adjust the times at which its Task runs (eg. timezone differences, etc.).

    Usage examples:

    plan.do(offsetBy: 3.hours) { ... }
    
    let calculateOffset = { () -> Interval? in
      // some computation to calculate offsets on the fly
    }
    plan.do(offsetBy: calculateOffset) { ... }
    
    Reviewed by WilsonGramer at 2018-12-05 20:26
  • 11. 修改Task的执行时间

    你好,有个问题请教一下,这里有个两秒后执行的task,假设当前时间是t0 let somePlan = Plan.after(2.second) let after2Task = somePlan.do(onElapse: { //Do something }) 在after2Task即将开始执行前,假如是t0后1.9s,我调用了如下方法 after2Task.setLifetime(2.second) 录得after2Task实际执行的时间t1 预想t1-t0 = 3.9,但实际t1-t0 = 2。请问类似的效果应该如何实现

    Reviewed by SciMagic at 2019-10-30 02:45
  • 12. Is it still working ?

    The code

    let planWorkTimer = Plan.every(.monday, .tuesday, .wednesday, .thursday, .friday).at("6:49 am").do {
                print("planWorkTimer is start")
    }
    

    not print.

    Reviewed by stavigor at 2022-06-08 16:06
  • 13. Suspension problem

    I have a task:

    let task = Plan.every(5.seconds).do(action: startSome)
    

    When I suspend() it somewhere and then resume for 10 seconds, I have 2 immediate calls of startSome. But what I want is call startSome in (5sec - time remaining when I called suspend). Is it possible?

    Reviewed by vBoykoGit at 2020-04-20 14:27
  • 14. Task is getting deallocated.

    I am using the task as shown below and used as a property to singleton class.

                schedulerTask = Plan.every(0.5).do(queue: .global()) {
    doSomething()
                }
    

    Which is resulting in this crash. Screenshot 2019-06-04 at 10 47 46 AM

    Reviewed by kidsid-Ixigo at 2019-06-04 05:35
Egg timer app written in Swift
Egg timer app written in Swift

Egg Timer Our Goal This module will be a mix of tutorials and challenges. Most importantly, we want you to get comfortable with looking up how to do s

Dec 2, 2021
This Control is a beautiful time-of-day picker heavily inspired by the iOS 10 "Bedtime" timer.
This Control is a beautiful time-of-day picker heavily inspired by the iOS 10

#10Clock Dark and Mysterious ?? Light Colors ?? Usage The control itsself is TenClock. Add that to your view hierarchy, and constrain it to be square

Jul 16, 2022
A timer that lets you know when your ramen is ready to eat!
A timer that lets you know when your ramen is ready to eat!

Dependencies Ramen Timer requires the following: Gifu SwiftySound On Xcode simply go to File > Add Packages and input the names on the searchbar then

Nov 3, 2021
A basic countdown app that allows the user to create, edit, and delete events. Each event contains a live countdown timer to a specified date and time.

Event Countdown App (iOS) Created by Lucas Ausberger About This Project Created: January 4, 2021 Last Updated: January 8, 2021 Current Verison: 1.1.1

Jan 8, 2022
Swifty Date & Time API inspired from Java 8 DateTime API.

AnyDate Swifty Date & Time API inspired from Java 8 DateTime API. Background I think that date & time API should be easy and accurate. Previous dates,

Jun 18, 2022
Swifty Date & Time API inspired from Java 8 DateTime API.

AnyDate Swifty Date & Time API inspired from Java 8 DateTime API. Background I think that date & time API should be easy and accurate. Previous dates,

Jun 18, 2022
Typical master detail SAMPLE application written in Swift to test NY Times Most Popular API
Typical master detail SAMPLE application written in Swift to test NY Times Most Popular API

NYTimes-Demo: Typical master detail SAMPLE application written in Swift to test NY Times Most Popular API. This SAMPLE application is written in Swift

Nov 3, 2021
NasaApod - iOS, Swift, MVVM, Consuming NASA Astronomy Picture of the Day API for any selected date

NasaApod iOS, Swift, MVVM, Unit Tests Consuming NASA Astronomy Picture of the Da

Jan 10, 2022
Swifty API for NSTimer

SwiftyTimer Modern Swifty API for NSTimer SwiftyTimer allows you to instantly schedule delays and repeating timers using convenient closure syntax. It

Aug 4, 2022
Time is a Swift package that makes dealing with calendar values a natural and straight-forward process.

Time Time is a Swift package that makes dealing with calendar values a natural and straight-forward process. Working with calendars can be extremely c

Jul 27, 2022
DateHelper - A high performant Swift Date Extension for creating, converting, comparing, or modifying dates.
DateHelper - A high performant Swift Date Extension for creating, converting, comparing, or modifying dates.

DateHelper A high performant Swift Date Extension for creating, converting, comparing, or modifying dates. Capabilities Creating a Date from a String

Aug 9, 2022
📆 Breeze through Date, DateComponents, and TimeInterval with Swift!
📆 Breeze through Date, DateComponents, and TimeInterval with Swift!

Datez ?? Breeze through Date, DateComponents, and TimeInterval Highlights Two Custom Structs Only (value types FTW!): DateView: An Date associated wit

Feb 6, 2022
SwiftDate 🐔 Toolkit to parse, validate, manipulate, compare and display dates, time & timezones in Swift.
SwiftDate 🐔 Toolkit to parse, validate, manipulate, compare and display dates, time & timezones in Swift.

Toolkit to parse, validate, manipulate, compare and display dates, time & timezones in Swift. What's This? SwiftDate is the definitive toolchain to ma

Aug 8, 2022
SwiftMoment - A time and calendar manipulation library for iOS 9+, macOS 10.11+, tvOS 9+, watchOS 2+ written in Swift 4.

SwiftMoment This framework is inspired by Moment.js. Its objectives are the following: Simplify the manipulation and readability of date and interval

Aug 8, 2022
🕰 Type-safe time calculations in Swift

Time This micro-library is made for you if: You have ever written something like this: let interval: TimeInterval = 10 * 60 To represent 10 minutes. U

Jul 18, 2022
Intuitive date handling in Swift

Timepiece Intuitive date handling in Swift Features ?? Intuitive: Timepiece provides a set of helpers to make date handling easier. ?? Correct: Using

Jul 15, 2022
NTP library for Swift and Objective-C. Get the true time impervious to device clock changes.
NTP library for Swift and Objective-C. Get the true time impervious to device clock changes.

TrueTime for Swift Make sure to check out our counterpart too: TrueTime, an NTP library for Android. NTP client for Swift. Calculate the time "now" im

Jul 26, 2022
Elegant NTP date library in Swift
Elegant NTP date library in Swift

Kronos is an NTP client library written in Swift. It supports sub-seconds precision and provides a stable monotonic clock that won't be affected by ch

Aug 3, 2022
Building a better date/time library for Swift

Time Time is a Swift package that makes dealing with calendar values a natural and straight-forward process. Working with calendars can be extremely c

Jul 27, 2022