Chapters

Hide chapters

macOS Apprentice

First Edition · macOS 13 · Swift 5.7 · Xcode 14.2

Section II: Building With SwiftUI

Section 2: 6 chapters
Show chapters Hide chapters

Section III: Building With AppKit

Section 3: 6 chapters
Show chapters Hide chapters

11. Beginning AppKit
Written by Sarah Reichelt

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In the previous two sections, you learned some basic Swift and built a complete app using SwiftUI. Apple provides two different layout frameworks for building macOS apps and in this section, you’ll use the other one: AppKit.

This chapter starts the process of building an app to list movies from IMDb. By the time you’re finished, you’ll be able to search, sort, filter and edit your movies list.

You’ll start from scratch with a new style of app template and build your interface graphically using a storyboard. Then, you’ll learn one way to get data into an AppKit table.

When you’re finished, your app will look like this:

Movie Tables app
Movie Tables app

I’m sure you’re excited to proceed, so start Xcode and get ready to code.

Creating an AppKit App

Once you’ve started Xcode, create a new project from the Welcome to Xcode window, or by selecting File ▸ New ▸ Project…. As before, choose macOS ▸ App before clicking Next.

Set the Product Name to MovieTables and leave Team as None or select your own developer team. Enter your reverse domain for the organization identifier — you can make one up, it doesn’t have to be a real domain.

Now for the different setting: Select Storyboard for the interface. Leave language set to Swift and when your options look like this, click Next:

Setting project options.
Setting project options.

Decide where to save your project and click Create.

Note: It always works best to name your Xcode projects with no spaces and no characters apart from letters and numbers. You can change the app name later, but you’ll avoid Xcode issues if you start like this.

Click the Play button in the toolbar or press Command-R to run the app:

Running the AppKit template
Running the AppKit template

There’s a single empty window and if you click the menus, you’ll see a lot of menu items.

Return to Xcode and look at the starting files:

Starting files
Starting files

Your SwiftUI app had Assets.xcassets for images, icons and other graphical assets, and it had an entitlements file to configure the app permissions. The others are new:

  • AppDelegate.swift: A delegate is an object that receives events from, and provides information about its parent. AppDelegate is a subclass of NSApplicationDelegate and it handles interactions with the application itself.
  • ViewController.swift: In SwiftUI, you create Views that the app shows in Scenes. In AppKit, you create subclasses of NSViewController. Each view controller has a view that contains the interface. The view controller acts as the middleman between the view and the data models. This is the Model-View-Controller or MVC pattern and is the default pattern for AppKit apps.
  • Main.storyboard: This is where you’ll design the user interface. You’ll spend a lot of time in this file as you work through this chapter.

What is AppKit?

By this stage, you may be wondering what AppKit is and why you’re using it.

Adding Data

In the SwiftUI app, you built the interface first and then constructed the data model to suit. This time, you’re getting data from an external source so it works best to look at that first and then build the interface around it.

Structure of the JSON data
Nxbatxigi al xre QCOX bewe

Creating the Data Model

Press Command-N or use the File menu to make a new file. Choose the macOS ▸ Source ▸ Swift File template and click Next. Name the file Movie.swift and click Create.

// 1
class Movie: Codable {
  // 2
  let id: String
  // 3
  var title: String
  var runTime: Int
  var rating: Double
  var genres: String
  var year: String
  var isFav = false
  // 4
}
Generate initializer
Laxapebo ipehuucutay

Converting the JSON

Before you can display the data in a table, you need a method that reads movies.json and converts the JSON into an array of Movie objects.

// 1
extension Movie {
  // 2
  static func readBundleData() -> [Movie] {
    // 3
    guard let fileURL = Bundle.main.url(
      forResource: "movies",
      withExtension: "json")
    else {
      // 4
      return []
    }

    // 5
    do {
      // 6
      let jsonData = try Data(contentsOf: fileURL)
      // 7
      let movies = try JSONDecoder().decode([Movie].self, from: jsonData)
      // 8
      let sortedMovies = movies.sorted(using: KeyPathComparator(\.title))
      return sortedMovies
    } catch {
      // 9
      print(error)
      return []
    }
  }
}

Reading the JSON

Open ViewController.swift and add this property at the top:

var movies: [Movie] = []
movies = Movie.readBundleData()
print(movies.count)
Reading the movies data.
Muadixd spe zitoow logu.

Storyboarding

There’s been a lot of coding so far with little visual result, but finally you get to the user interface.

Main.storyboard
Huuz.ztosrweamh

Configuring the Window

Start by selecting Window by clicking inside the box labeled Window or by selecting Window Controller Scene ▸ Window Controller ▸ Window in the outline view.

Setting window attributes
Bidgely yakjoc opyfefiwup

Sizing the window
Fuhipm lra fizdoz

Setting Up the View

Click inside the View Controller box or select View Controller Scene ▸ View Controller ▸ View in the outline view. In the Size inspector, set Width to 800 and Height to 400 to make this view the same size as its containing window.

Inserting a Table View

Click the + button in the toolbar or press Shift-Command-L to open the Library. You’ve used the library before to find SwiftUI views and modifiers. Now it shows AppKit objects instead.

Inserting a table view.
Inpuxvizk u deflo juad.

Expanded table outline
Eqhoqkip nogjo uoxmoqo

Positioning the View

In SwiftUI, the layout engine works out the optimum size and location, depending on the view type and its contents. With AppKit, you have to explicitly tell the view where to go and what size to be.

Adding constraints
Ujzaqp wicqdkuiplk

Resizable table
Yudawurlu buvlo

Setting Table Columns

The final table has three columns showing the title, year and rating for each movie. The default table comes with two columns, so now you’ll fix that.

Selection menu.
Depiwbiiq wibo.

Setting the number of columns.
Pupzikf nri tupras iv reveqxd.

Setting the size for the Title column.
Sipmirl jte bixa xen vta Vihpa xisakv.

Changing table cell layout.
Nxofhicb jajwo yusn qiniiy.

Preparing the Table for Data

Now that you’ve set the number of columns and their titles and widths, there are only a few more details to add in the storyboard before you jump back to the code and make the data appear.

Setting the column identifier.
Povhemw dbo feleyp egumqipoon.

Selecting the table view cell.
Sobisjivt ssa roqsu coev pojb.

Setting the cell identifier
Nihtedp tko kugg ovefwenaus

Table view connections
Pepve ciey tebjupyaush

Connecting the table view.
Dadkayzajx bva cumpa woeh.

Creating an outlet
Cziuleys ak iokpag

@IBOutlet weak var moviesTableView: NSTableView!

Adding a Data Source and Delegate

You’re finished with Main.storyboard for this chapter, so close its editor.

// 1
import AppKit

// 2
extension ViewController: NSTableViewDelegate, NSTableViewDataSource {
  // methods go here
}

Providing the Methods

In place of // methods go here, start typing number until you see this autocomplete suggestion:

numberOfRows autocomplete
sikvejIqZoqm euganikfjoqe

movies.count
viewForRow autocomplete
cuiqYeyFoq oijecakmqezi

// 1
guard let columnID = tableColumn?.identifier else {
  return nil
}

// 2
let movie = movies[row]
var cellID = ""
var cellText = ""

// 3
// get data for each column and cell

// 4
let cellIdentifier = NSUserInterfaceItemIdentifier(cellID)
// 5
if let cell = tableView
  .makeView(withIdentifier: cellIdentifier, owner: nil) as? NSTableCellView {
  // 6
  cell.textField?.stringValue = cellText
  return cell
}

return nil
// 1
switch columnID.rawValue {
// 2
case "TitleColumn":
  cellID = "TitleCell"
  cellText = movie.title
// 3
case "YearColumn":
  cellID = "YearCell"
  cellText = movie.year
// 4
case "RatingColumn":
  cellID = "RatingCell"
  cellText = "\(movie.rating)"
// 5
default:
  return nil
}

Viewing the Table

Press Command-R to run the app:

Table view with data
Rexka joak norb nedi

Key Points

  • AppKit is an alternative layout framework for macOS apps.
  • You design the layout visually using a storyboard and use auto layout to position components.
  • JSON is a commonly used data format that you can transform into Swift objects.
  • AppKit tables are a great way to display large amounts of data.

Where to Go From Here

In the next chapter, you’ll add more functionality to your table. Users will be able to search, sort and see the selected movie’s details.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now