Update note: This tutorial was updated to Swift by Joe Howard. Original tutorial by Ernesto García.
It’s a good time to be an iOS developer. Not only can you release your apps to the iTunes App Store, but you also have the foundational skills to become a Mac developer, since iOS development and Mac development are quite similar!
If you’re an iOS developer and you’re curious about learning the basics of becoming a Mac developer so you can start migrating your iOS apps to the desktop, this tutorial is for you.
In this tutorial, you’re going to build your first Mac application, specifically a Mac version of the app we created in the How To Create A Simple iPhone App tutorial.
If you’ve followed that tutorial, you will be familiar with most of the steps in this one, and you will be able to see the main differences between iOS and Mac programming. By the end, you’ll have a great introduction to developing with OS X and Swift!
If you haven’t followed it, don’t worry. It’s not required in order to read and understand this one – we’ll guide you along the way step by step.
While making this app, you’ll learn the following topics:
- How to create a Mac App in Xcode
- Learn the basic structure of a Mac app
- Learn the main differences between OS X and iOS
- How to use Table Views – including adding and deleting rows
- How to use a text field, a button and an image view
- How to select an image from your hard drive, or capture a picture from your computer’s camera
- How to handle window resizing using Auto Layout
- How to persist data between app sessions
This tutorial is for beginner Mac developers, but it assumes that you are familiar with Swift programming and with Xcode. Knowledge of iOS programming is recommended to follow this tutorial, but not mandatory.
In this first part of a three-part series, we’ll cover how to load your model with a list of bugs and display them in a table view.
Getting Started
Creating a Mac project is very similar to creating an iOS project – it still uses Xcode, just a different template!
So start by going to File\New Project… in Xcode, and in the window that pops up, select “Application” in the “OS X” section. Then click Next.
On the next page, you will enter the application information. Type ScaryBugsMac in the product name field, add your organization name, and select a unique organization identifier. Apple recommends using a reverse domain format. Leave the rest of the text fields blank.
Finally, make sure that Swift is selected as the language, that none of the checkmarks are checked, and leave the document extension field blank. When you’re done, click Next.
Now Xcode will ask you for a location to save the project. Choose a folder on your computer and click “Create”.
The project is ready, and you should have a Mac application with a single empty window. Let’s check out how it looks. Find the “Run” button, which is located in the left side of the toolbar at the top of Xcode. Click it and Xcode will begin to build the app.
When Xcode finishes building the application, you should see the main window of your application.
This shows you three things: first, you chose the correct template and it works (yay!); second, it is a good blank starting point to build upon; and third, there are some big (and somewhat obvious) differences from developing for iOS:
- The window doesn’t have to be a particular fixed size, such as the iPhone or iPad’s screen size – it can be fully resizable!
- Mac apps can have more than one window, and you can minimize them, arrange them however you like, etc.
Let’s do something with this window, and make it show some information about bugs. Just like in iOS, the first thing to do is to create a new View Controller. In this view, you will define the user interface of the main app.
To create a new View Controller, go to File\New\File… , and in the window that pops up, choose OS X\Source\Cocoa Class, and click Next.
Name the class MasterViewController, and type NSViewController for “Subclass of”. Make sure that the option “Also create XIB file for user interface” is selected. Click Next.
In the final popup, click Create again. Now your new view controller is created and your Project Navigator should look similar to this:
Now that you’ve created the view controller, it’s time to place the UI items on it. In the Project Navigator, click on MasterViewController.xib. That will load the visual representation of the view controller you just created in Interface Builder.
Interface Builder lets you build your user interfaces in a visual way. You just need to drag a component onto your view and position or resize it according to your application’s needs.
The first thing your app needs do is to show a list with the Bugs. For that, you are going to need a table view. In OS X, the control is called NSTableView
(similar to UITableView
in iOS).
If you’re familiar with iOS programming, you may be able to see a pattern here. Many user interface classes that are in UIKit were originally derived from classes that already existed in OS X’s AppKit. Many of them just changed the NS prefix used in OS X to the UI prefix used in iOS.
So, as a rule of thumb, if you are wondering if some iOS control you know and love may already exist on the Mac, you can try and look for the same class with NS. You’ll be surprised how many you’ll find – NSScrollView, NSLabel, NSButton and more! Note that the APIs for these controls might be quite a bit different from the iOS variants. In many cases though, Apple has been moving to make the OS X versions more like the iOS versions.
The user interface controls are located in the bottom right part of the screen. Make sure the third tab is selected (which is the tab with the UI controls) and find the NSTableView control. (You can scroll down the controls list until you find it, or you can type NSTableView in the panel’s search field).
Drag the table view from the panel onto the view and position it near the top left corner. Don’t worry now about the table size, you will take care of that later.
Now you have a view with a table on it, but you still haven’t added the view controller to the main window so it won’t show up. Open AppDelegate.swift to get started with that.
Now you are going to create a property for the view controller. In Swift, the implicit namespace and scope for classes is the app project, so there is no need to import MasterViewController
in the Application delegate.
Add the following code just below the declaration of the window property:
var masterViewController: MasterViewController! |
Now the Application Delegate has a MasterViewController property, but the view won’t be shown on the application’s screen yet. To do that you need to instantiate the variable to create a new view, and after that, you need to add the newly created view to the main window of the application.
The Application delegate has a method applicationDidFinishLaunching
that is called by the operating system when the application first launches. That is the point where you should add all the initialization code that is meant to run only once when the application starts.
application(_:didFinishLaunchingWithOptions:)
.
Insert this code inside applicationDidFinishLaunching
:
masterViewController = MasterViewController(nibName: "MasterViewController", bundle: nil) window.contentView.addSubview(masterViewController.view) masterViewController.view.frame = (window.contentView as NSView).bounds |
First, you instantiate a new MasterViewController
using the objects in the interface builder file of the same name. Once it’s created, you add it to the main window and set its frame to take up the entire content view.
The windows in OS X (NSWindow
class) are always created with a default view, called contentView
, which is automatically resized to fill the window. If you want to add your own views to a window, you will always need to add them to the contentView
using addSubview
.
The last line just sets the size of your view to match the initial size of the window. Comparing this again with iOS programming, it’s a bit different. In iOS you would set the window’s rootViewController
, or rely on a storyboard to load the initial app view controller. But rootViewController
does not exist in OS X, so instead you add your view to the window’s contentView.
Now, if you build and run, you will see that the main window shows your view with your table view. Nice – now you’re starting to get somewhere!
A Scary Data Model: Organization
So far you have a window with a nice table view on it, but it doesn’t display anything. You want it to show some information about your scary bugs – but wait, you don’t have any data to display either!
In the next steps, you are going to create a data model for the application, but before that, you are going to see a way to keep things organized in the Project Navigator.
This your current organization in the Project Navigator section of Xcode:
The default template creates a group with the application’s name, and a sub-group for the supporting files (plist, resources, etc). When your project grows up, and you have to deal with lots of files, it becomes more and more difficult to find the files you need.
In this section we’re going to show a way to organize your files. This organization is quite subjective, so feel free to change it to any organization you feel comfortable with.
First, you are going to create a group to store your user interface files, and you’re going to name it “GUI”. To create it, control-click (or click the mouse right button) the ScaryBugsMac group.
In the menu that pops up, choose “New Group”. The group created is automatically selected and you can type the new name “GUI”.
Now, drag the user interface files to that group ( AppDelegate.swift, MasterViewController.swift/.xib and MainMenu.xib). After dragging them, your Project Navigator should look like this:
Now create a second group inside ScaryBugsMac, and name it “Model”. In the next steps we’re going to create the data model files for your application, and you will add those files to this group.
This is the way the Project Navigator looks after adding it.
Here’s how the data model will be organized:
- ScaryBugData: Contains bug name and rating.
- ScaryBugDoc: Contains full size image, thumbnail image, ScaryBugData.
The reason you’re setting things up like that is it will make things easier later on in this tutorial when you start saving data to the disk.
A Scary Data Model: Implementation
So, when you’re creating the model and classes that don’t need user interface, you will find that most of your code will likely just work on Mac, or it will work with some minor changes.
For instance, in this case, changing the ScaryBug model classes from iOS to Mac only required one change. UIImage does not exist in OS X, so you just needed to change it to OS X’s image class, NSImage. And that was it (along with updating the language to Swift)!
The first step is to create the model classes, beginning with ScaryBugData.
In the project navigator, Control-Click the Model Group you just created, and in the menu click “New File…”. Select the OS X\Source\Cocoa Class template, and click Next.
Name the class ScaryBugData, and enter NSObject for “Subclass of”. Click Next.
In the final popup, click Create again. If all went well, your Project Navigator should now look similar to this:
Open ScaryBugData.swift and replace its contents with the following:
import Foundation class ScaryBugData: NSObject { var title: String var rating: Double override init() { self.title = String() self.rating = 0.0 } init(title: String, rating: Double) { self.title = title self.rating = rating } } |
This is pretty simple stuff – you’re just declaring an object with two properties – a string for the name of the bug, and a double for how scary you rated it. In Swift, the String and Double types are both structs, and structs are value types, so explicit memory management attributes (e.g. strong) are not needed.
You also define an initializer for the class, so you can set the title and rating when you create the bug. Again, extremely simple stuff here. You create your initializer to fill in your instance variables from the passed-in parameters. Note there is no need for deinit
, since the ScaryBugData
properties are value types, and no need to synthesize your properties due to auto-synthesis.
Ok, that’s it for ScaryBugData. Now follow the same steps you did above to create another subclass of NSObject, this time named ScaryBugDoc.
Replace the code in ScaryBugDoc.swift with the following:
import Foundation import AppKit class ScaryBugDoc: NSObject { var data: ScaryBugData var thumbImage: NSImage? var fullImage: NSImage? override init() { self.data = ScaryBugData() } init(title: String, rating: Double, thumbImage: NSImage?, fullImage: NSImage?) { self.data = ScaryBugData(title: title, rating: rating) self.thumbImage = thumbImage self.fullImage = fullImage } } |
Here you are creating some properties and two initializers. Of note here is that thumbImage
and fullImage
are optional NSImage
properties, so unlike normal variables, they don’t require an initial value or an assigned value in the default initializer.
That’s it – your data model is complete! At this point you should compile and run your application to check that everything runs fine. You should expect to see the same empty list, because you haven’t yet connected the data model with the UI.
Now you have a data model, but you don’t have any data. You need to create a list of ScaryBugDoc
objects, and you will store the list in an array.
Open MasterViewController.swift and add the following property to the class:
var bugs = [ScaryBugDoc]() |
This will be the property that we’ll use to keep track of your list of bugs. Now to fill it with some starter data!
Scary bug pictures and sample data
At this point, MasterViewController
is ready to receive a list of Bugs, but then again, you don’t have any data yet.
Before adding the data, you’re going to need some scary pictures! You can download these pictures from the How To Create A Simple iPhone App on iOS 5 Tutorial or find your own favorite scary bugs on the Internet :]
Once you’ve downloaded the files or gotten your own, open Images.xcassets and drag all the bug images from the Finder into the list of images or the right, under the pre-existing AppIcon image:
Now, let’s finally create the sample data. Open MasterViewController.swift and add the following method to the class:
func setupSampleBugs() { let bug1 = ScaryBugDoc(title: "Potato Bug", rating: 4.0, thumbImage:NSImage(named: "potatoBugThumb"), fullImage: NSImage(named: "potatoBug")) let bug2 = ScaryBugDoc(title: "House Centipede", rating: 3.0, thumbImage:NSImage(named: "centipedeThumb"), fullImage: NSImage(named: "centipede")) let bug3 = ScaryBugDoc(title: "Wolf Spider", rating: 5.0, thumbImage:NSImage(named: "wolfSpiderThumb"), fullImage: NSImage(named: "wolfSpider")) let bug4 = ScaryBugDoc(title: "Lady Bug", rating: 1.0, thumbImage:NSImage(named: "ladybugThumb"), fullImage: NSImage(named: "ladybug")) bugs = [bug1, bug2, bug3, bug4] } |
Here you just use the ScaryBugDoc initializer to create four sample bugs, passing in the title, rating, and images for each. You then add them all to the bugs
array.
In order to create the sample data, open AppDelegate.swift and find applicationDidFinishLaunching
. Add the following above the line with the call to addSubview
:
masterViewController.setupSampleBugs() |
And finally you have some data! Build and run your app, and make sure all works well without errors.
You still won’t see anything in the user interface, but at this point the view controller has the data it needs, and you’re able to begin the work in the user interface to finally show your Scary Bugs List.
A Different Kind of Bug List
In order to display your bug list, you need to set up the table view to get the list from your model.
In OS X, the table view control is called NSTableView
. It’s similar to UITableView
in that it displays lists of data, but one major difference is that in NSTableView
each row can have multiple columns!
NSTableView has cells for each row. However, there have been some recent changes as to how these work:
- Before OS X 10.7 Lion, table view cells were a special class derived from the NSCell class. They were not based on views, and it was the responsibility of the developer to handle drawing and even mouse events (gah!)
- From OS X 10.7 on, there’s a new type of table view: the View-Based table view. This table view works in a very similar way to
UITableView
. The cells are a special type of view, and working with them is very similar to the way it works in iOS – which is much easier!
In this tutorial you are going to use the new View Based Table View. We’ll cover the basics here, but if you want to learn more about NSTableView, you can read the Table View Programming Guide, which does a great job explaining how table views work.
Open MasterViewController.xib and select your table view. Be aware that the table view is embedded in a clip view, which is embedded in a scroll view, so first time you click it, you will end up selecting the scroll view. Clicking a second time will select the clip view, then a third click will select the actual table view.
Another way to select the table view is to open the “Objects” Panel on the left side of Interface Builder, and select it by clicking the “Table View” object (you may need to expand the “Clip View” object first).
Once you have it selected, the first thing you need to do is make sure that the table view is “View Based”, because older versions of Interface Builder create “Cell Based” table views by default. Xcode 6 and above will likely set the table view to View Based by default.
To check, make sure that you have selected the “Attributes Inspector” tab on the properties panel on the right of the screen. And then, in “Content Mode”, make sure “View Based” is selected; if not, select it. Also, since your list does not need multiple columns, change the Columns property to one.
In order to customize the list a little bit, check the property “Alternating Rows”, which will draw the rows in alternating white/gray colors, and uncheck the “Headers” property. This will remove the heading of the table, because you don’t need it for the tutorial.
After removing the extra column, the remaining column may be narrower than the table view. In order to resize it, just click on the table column (using the Objects panel on the left, or by clicking four times on the table view) then resize it to the table’s full width.
The next step is to configure the cell view that the table view will use. Your list needs to display the image of the Bug and its name. You need an image and a text field in your cell to show that information. Interface Builder has a preconfigured NSTableCellView that includes an image view and a text field, so you are going to use that one.
In the Object library panel on the bottom left side of the window, locate the “Image & Text Table Cell View”, and drop it on your table view.
After doing that, your table now has two different types of cells. Remove the old cell type (the cell that does not have the gear icon on the left) by clicking on it and pressing the Delete key on your keyboard.
The last step is changing the height of the cell, because now it’s too small to show the bug image. You want to set its height to 32. Select the cell by clicking on it, and then open the “Size Inspector” tab in the Utilities panel on the right side of Xcode window. You can change the height of the cell to 32 in the Height panel. Another way to do it is dragging the bottom border of the cell until you get to the desired height.
After setting the cell height, the image and the text fields are a bit misaligned. To fix that, click on them and move them until they are centered in the cell. Also, set the width of the text field so it takes up the width of the column. You can also resize the image view and play with the text field font to fit it to your needs.
Now the table view design should look like this:
Now you need to set the column identifier. This is a name you give to every column of the table view, so that when you want to perform some actions or you receive some notification from a column, you are able to identify the column.
This may not be strictly necessary in this tutorial, because you only have one column, but it’s a good practice to do it so that you don’t find any issues when you want to create a multicolumn table view in other projects.
To do that, select the table column (remember, either click the table view four times to select it, or use the Objects panel on the left), then open the “Identity Inspector” tab in the Utilities panel.
There, change the Identifier from “Automatic” to “BugColumn”.
That’s it for the table UI configuration. Now, you need to connect the table view with the view controller, so that they are aware of the existence of each other.
Like in iOS, the table view has two properties that are used to let the table and its controller communicate: the data source and the delegate. Basically, the data source is the class that will tell the table view what data it needs to show. The delegate is the object that controls how the data is displayed, and also will receive notifications from the table view, like for instance, when a cell is selected.
Usually (but not always) the delegate and the data source are the same object. In this project, the data source and the delegate will be your MasterViewController
. Connecting the table view with its data source and delegate can be done programmatically, but for this tutorial you are going to do the connection in Interface Builder.
Select your table view, and in the Utilities Panel choose the “Connections Inspector” (the one with an arrow pointing to the right). There, in the “Outlets” section, you will see the delegate and the data source.
To connect the delegate, click on the circle on the right of the delegate, and drag it to the “File’s Owner” (for MasterViewController
), located on the “PlaceHolders” panels on the left side.
Just with that you told the table view that its delegate is the MasterViewController
. When you instantiate your view controller in your app, that connection will be automatically setup for you.
Now, repeat the same procedure for the dataSource
outlet. After doing so, you will see both connections pointing to the File’s Owner, like this:
That’s it, now you’re ready to add the necessary code in your view controller to show your Bug list. Open MasterViewController.swift and paste the following code at the very end of the file:
// MARK: - NSTableViewDataSource extension MasterViewController: NSTableViewDataSource { func numberOfRowsInTableView(aTableView: NSTableView!) -> Int { return self.bugs.count } func tableView(tableView: NSTableView!, viewForTableColumn tableColumn: NSTableColumn!, row: Int) -> NSView! { // 1 var cellView: NSTableCellView = tableView.makeViewWithIdentifier(tableColumn.identifier, owner: self) as NSTableCellView // 2 if tableColumn.identifier == "BugColumn" { // 3 let bugDoc = self.bugs[row] cellView.imageView!.image = bugDoc.thumbImage cellView.textField!.stringValue = bugDoc.data.title return cellView } return cellView } } // MARK: - NSTableViewDelegate extension MasterViewController: NSTableViewDelegate { } |
Here, you’re adding two extensions to MasterViewController
to declare its protocol conformance to both NSTableViewDelegate
and NSTableViewDataSource
. In order to show data in a table view, you need to implement at least two data source methods.
The first method numberOfRowsInTableView
is called by the OS to ask the data source, “How many rows do I need to show?” You respond by simply giving the count of the list of bugs in your array.
With that method, the table view knows how many rows to display, but still does not know anything about which cells to display in every row, and what information those cells should have.
That is done in tableView(_:viewForTableColumn:row:). This method will be called by the OS for every row and column of the table view, and there you have to create the proper cell, and fill it with the information you need.
Here’s what’s going on in that method, step by step:
- The first thing you do is get a cellView by calling
makeViewWithIdentifier
. This method will create (or reuse) the proper cell for a column based on the identifier you set up in Interface Builder. - Once you have the cell, it’s time to fill it with information. You have to do it differently depending on the column. That’s why you check for the “BugColumn” identifier. If the column identifier is “BugColumn”, then you set the image and text from the ScaryBugDoc. In this case, this table only has one type of column so this check isn’t absolutely needed. However it’s good to know how to handle the multicolumn case, since you might need that for your own apps.
- The last step is setting the cell information. Based on the row, you get from your bugs array the proper
ScaryBugDoc
, and then fill the image with the thumbImage and the text field with the name of the bug (“title”).
That’s all you need to display information in a table view. It’s just a matter of defining the properties and connections in Interface Builder and implementing only two methods in your view controller.
Now, it’s time to build and run the application. If everything went fine, you should see the table view with all the Scary Bugs in your list!
Where to go from here?
Here’s the current project download with all of the code you’ve developed so far in this tutorial series.
Next in the Series, you will learn how to add a detail view, and how to add/edit/delete bugs from your list. You will also cover how to polish the user interface and how to handle window resizing to make your app look great at any size!
Getting Started With OS X and Swift Tutorial: Part 1/3 is a post from: Ray Wenderlich
The post Getting Started With OS X and Swift Tutorial: Part 1/3 appeared first on Ray Wenderlich.