Search code examples
cocoamacosscheduled-taskscronlaunchd

Scheduled tasks with Cocoa?


What is the best way in Cocoa (and Mac OS X) to schedule a program to be run:

  • when the user logs in.
  • at certain times of the day (for example: 12:00 noon).
  • at certain time intervals (for example: every two hours).

Besides scheduling, it should also be easy to un-schedule the program and not cause errors should the user delete the application.

Essentially the program is a satellite command-line executable placed next to the main application's file within the same .app bundle. The program's purpose is to do some background data updates in the user's home directory (within ~/Library/Application Support/MyApp.)

Is crontab a good candidate for this? The man page for crontab said that the functionality has been absorbed to launchctl, but I can't seem to find how to schedule specific times to run the utility.

Thanks.


Solution

  • launchd is the proper way to do this (crontab is deprecated on the Mac in favor of launchd). You'd create a plist file with the information about your program (executable name, arguments, etc), and part of it would be (warning typed in a browser and untested):

    <key>StartCalendarInterval</key>
    <dictionary>
      <key>Hour</key>
      <integer>12</integer>
    </dictionary>
    
    <key>StartInterval</key>
    <integer>7200</integer>
    
    <key>LimitLoadToSessionType</key>
    <string>Aqua</string>
    <key>RunAtLoad</key>
    <true/>
    
    • The StartCalendarInterval should run your program at the top of the 12th hour (so noon).
    • The StartInterval should run your program every 2 hours (= 7200 seconds)
    • The LimitLoadToSessionType only loads your program when an Aqua session begins (the user actually logs in to the window server (so this would prevent loading if the user ssh's in)
    • The RunAtLoad tells the executable to run when the plist is loaded. This, combined with the LimitLoadToSessionType, should start the executable when the user logs in.

    It's possible that StartInterval and StartCalendarInterval are mutually exclusive. If that's the case, you can take out the StartInterval key and change the StartCalendarInterval stuff to:

    <key>StartCalendarInterval</key>
    <array>
      <dictionary>
        <key>Hour</key>
        <integer>0</integer>
      </dictionary>
      <dictionary>
        <key>Hour</key>
        <integer>2</integer>
      </dictionary>
      <dictionary>
        <key>Hour</key>
        <integer>4</integer>
      </dictionary>
      <dictionary>
        <key>Hour</key>
        <integer>6</integer>
      </dictionary>
      ...
      <dictionary>
        <key>Hour</key>
        <integer>12</integer>
      </dictionary>
      ...
      <dictionary>
        <key>Hour</key>
        <integer>22</integer>
      </dictionary>
    </array>
    

    For more information, see man launchd.plist.