Build Your First MacOS App

A simple guide to start with MacOS Development

Federico Vitale
May 21, 2019 · 9 min read

I’m pretty new to MacOS development, I’m coming from a past mixed between web dev and iOS (My latest app: StockPapers).

Today I want to share with you what I’ve learned day by day in this week trying to create a clone of my nodeJS cli, splash-cli.

We are going to build a status bar clock application with “reminders”, where the user will be able to schedule notifications.

We’ll see how to prevent to open the same window multiple times, notifications and so more!

What do you need?

Pretty obvious:

  • Basic knowledge of the Swift language (or maybe not)
  • A machine running Xcode
  • Xcode 10.2 or newer (even a lower version is ok, but this will may change some methods in the code, depending on your swift version)

📌 Create a new Project

First of all, we need to create a new Xcode Project, so go under File > New > Project... or press CMD+Shift+N

Image for post
Image for post
Xcode 10.2 on MacOS 10.14

Select MacOS as platform and choose the first, option: Cococa App.

Image for post
Image for post
Xcode 10.2 on MacOS 10.14

Then choose a name for the app, in this example, I’ve chosen “Advanced Clock” and make sure to check the “Use Storyboards” option.

Usually, on iOS, I’m not used to using storyboards but for
MacOS I find them really helpful.

🔧️ Project Setup

We need to setup our project before hacking the code: firstly we need to delete the default WindowController and the relative ViewController because we don’t need to open a window when the application starts. Then you should have something like in the screenshot below with just the Main Menu.

PS: Don’t delete the Main Menu, the action is not reversible and if you ever need it you need to rebuild it by yourself and it’s very frustrating…

Let’s Code

Okay so now we need to setup our clock app, almost all our code will be placed in the AppDelegate class.

Default AppDelegate

We can easily delete the applicationWillTerminate method, right now we don’t need it.

Then we can create our NSStatusItem this will be our item in the status bar, more specifically will be the container for our button.

To create our status bar item just write the following:

We have assigned a placeholder text for the moment, now it’s time to schedule a timer that updates our title with the current time every second.

To do that we declare another class variable named timer with optional type Timer? and make it nil by default.

Then in the applicationDidFinishLaunching under our title assignment, we can create our Timer like the following

I’ve also created an objective-c function that is called every time the Timer gets fired (every second).

Below some utility functions to make everything more readable and clean, one is for the Date class and the other one is for Int.

Date Extension
Int Extension

Ok now you can run your app and if you’ve done everything right, you should have your clock in the status bar.

Congratulations! 🎉 You’ve built your first MacOS Application! But wait! We have a bug! Maybe you haven’t noticed yet but if the statusBarItem is highlighted it will not update until we move our focus out of it.

To fix that we need to edit our Timer and add a row to start it like the following:

AppDelegate

Awesome! You’ve fixed your first bug in the app! Now let’s make it a little bit more interesting 💪

✏️ Add a Menu

Okay now it’s time to make our application more user-friendly, let’s add some features and preferences.

First of all let’s write some code to handle our Preferences, to do that I usually create a struct that handles UserDefaults for small settings like booleans numbers, strings, enums, etc.

This is a really useful helper!

Now we can move with the menu stuff, declare a new menu variable in the AppDelegate and create a menu with NSMenu() then add some items, like:

  • Flashing time separators
  • Show/Hide Seconds
  • Show/Hide Dock Icon (we’ll se how later)
  • Quit the application (quite useful)

Before we go through the code I want to share with you other 2 little extensions that will help to archive good code readability and functionality

So.. now I’ve refactored a bit the AppDelegate to make it more clear and concise. That’s how it looks with the menu implementation:

This is how the application is looking right now:

Image for post
Image for post

By the way, now it’s missing some implementations.

The useFlashingDots and showDockIcon items are useless at the moment. Instead of these functions the showSeconds setting it’s handled in the updateStatusText function.

Inside each NSMenuItem action we update the item it self with the new preferences, since these are checkboxes we update the state, the stateValue in Bool+Extension.swift its a statement that returns .on or .off respectively when a Boolean is true or false. This saves us from writing Preferences.showDockIcon ? .on : off instead of Preferences.showDockIcon.stateValue.

️️⚙️ ️Preferences: The Dock Icon

Okay, here we are, we have a status bar application but we also have a dock icon, and as a status bar application, we don’t want the dock icon to be visible.

Look at the top right corner of your monitor, do you see them?

Image for post
Image for post

Imagine if every app in your status bar would have an icon in your dock… That’s not great user experience.

We can handle the Dock Icon via NSApplication.ActivationPolicy. To do that I wrote a little helper, a struct with 2 methods to set/read the ActivationPolicy.

DockIcon Helper

We can now edit the toggleDockIcon method to handle this preference, let’s see how implement this helper.

At line 7 we’ve implemented the DockIcon helper

Now copy and paste the method you’ve just written (line 7 of the gist above) at the end of the applicationWillFinishLaunching method.

At line 12 we’ve implemented the DockIcon helper
Image for post
Image for post
Hiding/Showing the Dock icon

⚙️ Preferences: Flashing Separators

Now it’s the time of “flashing separators”, how can we make a better clock if the standard one has more functionalities? So let’s implement this simple feature that even Apple’s default clock has.

How? Easier than you think, we need a variable that handles the status of the separators (visible/hidden, or maybe better 0/1, or we can even recycle NSControl.StateValue) and if it’s 0 do a string replacement in with “:” -> “ “ in our title (remember?).

Method 1

A better way to do that is checking whenever the seconds are even (or odd) and use this instead of the variable (this is ok only if you want to flash dots 1 time per second)

Method 2

Below how it should look like with flashing separators:

Image for post
Image for post
The final result

⏰ Reminders…

Wait! We’re missing the core experience of the whole application! Right now why should someone download an alternative clock with no extra features?

So let’s add the reminders feature, it’s pretty simple, and you’ll learn how to create and present a window.

We can start creating a new struct type and call it Reminder

Then create a new variable of type [Reminder] and add empty array as default value.

var reminders: [Reminder] = []

Now we can write some logic stuff:

  • Add a MenuItem to the StatusMenu
  • Add a submenu with all the reminders
  • Add a MenuItem that opens a new window to schedule a new Reminder.
  • Delegate reminder and the view controller in theAppDelegate

Creating the NSMenuItem and its submenu

As we have already done before for the other elements, let’s create a new NSMenuItem and this one like the statusBarItem will have a menu property, this will be populated with all the reminders inside our reminders variable declared before.

How can I add a new Reminder?

Now we need to create an interface to add reminders, to do that open your Main.storyboard and create a view controller with an NSTextField for the title, NSTextView for the description and NSDatePicker for the date.

You can do this also from code, programmatically, but since this article is becoming really long I’ll show you the fastest way to archive a basic window with controls.

Image for post
Image for post
My interface

Ok now let’s create a new file, this will be our view controller, in case you haven’t delete it yet, you should have a file named ViewController, you can edit this one instead of create a new file. Rename it to NewReminderVC and rename also the class inside, you should have something like this one below:

To save time I’ve just added a new Delegate Protocol on top of the file, this will be used to handle the submit event.

Okay, you’re all set, go in your storyboard, select the view controller you’ve just created and assign this new class to it:

Image for post
Image for post
Identity Inspector

Make sure to add a Storyboard ID, for practicality I name it just like the view controller class.

We’ll use the Storyboard ID to identify our view later in the code.

Image for post
Image for post

Now with the view controller selected, click on the Assistant Editor button on the top right corner of Xcode.

This will split your screen in 2 editors.

Make sure to have the right file selected (the right file is the file of the view controller), then drag and drop (with ctrl pressed) your items in the class.

You should have 3 IBOutlets and 1 IBAction, the action is connected with the button, the other 3 outlets are the title, the description, and the date picker.

Ok, now you have a view but how can we present it? Easy, create a new file with this helper, this will give you a function named getVC you need to pass the Storyboard ID and the view controller class

Windows Helper

Ok now let’s add the missing function in the AppDelegate. The first thing to do is declaring a new constant on top of the file:

let REMINDERS_WINDOW_CONTROLLER: NSWindowController = NSWindowController(window: nil)

All right, now we miss an action, let’s add some stuff:

Firstly we need to edit our reminders variable and add a didSet event:

I’ve created a utility function to easily create the reminders menu, now let’s create the menu item, and don’t forget to add it to the menu.

Now the addReminder it’s still not declared so Xcode will alert you with an error, let’s fix that by declaring it:

To understand floating level read below:

See Window Levels for a list of possible values. Each level in the list groups windows within it in front of those in all preceding groups. Floating windows, for example, appear in front of all normal-level windows. When a window enters a new level, it’s ordered in front of all its peers in that level.

Last but not least, we need to implement delegates and do something inside the NewReminderVC

In the viewDidLoad method we set our default values, then onSubmit we create our new reminder and then we call the onSubmit method of the delegate.

🚀 Delegation

Finally, we need to conform with the delegation from NewReminderVC, to do that add an extension of AppDelegate like the following:


SwiftUI?

Looking for SwiftUI version? Make sure to follow the Apple tutorial on creating MacOS apps with SwiftUI.

Resume

You can find the entire project on my GitHub at Advanced Clock.

Now you should be able to run your application. These are the basics for Mac Development, or at least what I’ve learned in the past weeks by searching on Google and StackOverflow. Below I leave you some articles here on Medium about some fundamentals concepts


The Startup

Medium's largest active publication, followed by +703K people. Follow to join our community.

Federico Vitale

Written by

FullStack (Java, React) for work, Swift for passion. Sharing what I learn day by day, from Rome 🇮🇹

The Startup

Medium's largest active publication, followed by +703K people. Follow to join our community.

Federico Vitale

Written by

FullStack (Java, React) for work, Swift for passion. Sharing what I learn day by day, from Rome 🇮🇹

The Startup

Medium's largest active publication, followed by +703K people. Follow to join our community.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store