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

Overview

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!

Comments
  • Plan.every(5.second).do is not working

    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.

    opened by olcayertas 9
  • Can't running

    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?

    opened by a7e6j2 7
  • Proposal new logo to SCHEDULE

    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 :)

    enhancement 
    opened by ggabogarcia 7
  • Allow usage of arrays in addition to variadic arguments in Interval and Plan functions

    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.

    opened by WilsonGramer 6
  • daily task at a certain time...

    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?

    opened by maxgribov 5
  • struct `Plan` api errors

    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

    opened by maxgribov 5
  • Memory Leak

    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")
            }
        }
    }
    
    opened by ApolloZhu 5
  • Does it still support iOS 10 ?

    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 ?

    opened by haithngn 4
  • Task every day not work?

    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)

    opened by nhatquangz 3
  •  Add support for interval offsets

    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) { ... }
    
    opened by WilsonGramer 3
  • 修改Task的执行时间

    修改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。请问类似的效果应该如何实现

    opened by SciMagic 2
  • Is it still working ?

    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.

    opened by stavigor 0
  • Suspension problem

    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?

    opened by vBoykoGit 0
  • Task is getting deallocated.

    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

    opened by kidsid-Ixigo 2
Releases(2.1.1)
  • 2.0.0(Apr 6, 2019)

    ❗️❗️❗️This release contains some breaking changes.

    Warning

    The initial design of Task refers to Timer: It will be implicitly held by an internal object, if you want to remove it, you need to explicitly call the invalidate/cancel method. But soon, I realized that it was easy to ignore this feature and caused memory leaks. So in 2.0.0, Task is no longer automatically held, that is, if no external variables are explicitly pointed to it, this task will be destroyed.

    Fixed

    • Calculation issue in every(_ weekday: Weekday) and every(_ monthday: Monthday).

    Added

    • TaskCenter. From now on, you can use your own task center to manage tasks.
    • task.executionDates. Records the date each time the task is executed.
    • More tests.

    Removed

    • task.timeline. All timeline properties are now accessible directly from the task.
    • plan.do(host: obj). Since tasks are no longer implicitly held by task centers, I don't think the host mechanism is necessary.

    Updated

    • Some renaming, to make the api more swift!
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(Sep 26, 2018)

    • Rename struct Schedule to Plan It is not wise to let a type have the same name as framework.

    • Remove ParasiticTask Now, each constructor has the host parameter(default is nil).

    • Add RunLoopTask Before 1.x, Schedule will execute tasks on a global dispatch queue when time is up by default. Now tasks will be executed on the current thread, its implementation is based on RunLoop, which means that you need to ensure that the current thread has a runloop available. So it is still recommended to use dispatch queue to construct the task.

    Source code(tar.gz)
    Source code(zip)
Owner
Luo Xiu
iOS Developer, occasionally write web, server and cli apps.
Luo Xiu
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

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

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

Joe 557 Dec 23, 2022
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

Angel Santiago 1 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

Lucas Ausberger 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,

Jungwon An 182 Dec 1, 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,

Jungwon An 182 Dec 1, 2022
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

Atif Naveed 0 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

Vishal Singh 1 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

Radek Pietruszewski 1.2k Dec 29, 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

Dave DeLong 2k Dec 31, 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. Capabilities Creating a Date from a String

Melvin Rivera 1.4k Jan 2, 2023
📆 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

Kitz 263 Dec 7, 2022
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

Daniele Margutti 7.2k Jan 4, 2023
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

Adrian Kosmaczewski 1.6k Dec 31, 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

Oleg Dreyman 1.1k Dec 21, 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

Naoto Kaneko 2.6k Dec 22, 2022
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

Instacart 530 Jan 4, 2023
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

Mobile Native Foundation 575 Dec 23, 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

Dave DeLong 2k Dec 31, 2022