Xcode Project and File Templates

Learn how to create custom project and file templates in Xcode to start new projects and files more efficiently. By Keegan Rush.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Creating Template Files

Inside Swift Enum.xctemplate, open ___FILEBASENAME___.swift. This is the file that your file template creates in your project.

___FILEBASENAME___ is a text macro that all file templates have. It’s the name that you choose for the file in the New File dialog, just without the file extension. For example, if you choose APIError.swift as the file name when using the template, then Xcode sets ___FILEBASENAME___ to APIError.

Inside ___FILEBASENAME___.swift, replace the contents with this:

//___FILEHEADER___

import Foundation

enum ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_rawValue___ {
    case one
    case two
}

You’ve created a simple enum with two default cases. In this file, you use three template macros. Here’s what each of these do.

At the top of the file is ___FILEHEADER___, which sets the usual copyright, file and author details.

Next, instead of a name for the enum, you have ___FILEBASENAMEASIDENTIFIER___. This is the same as the ___FILEBASENAME___ variable for the file name, except that you can’t use ___FILEBASENAME___ inside the template file.

Finally, ___VARIABLE_rawValue___ is a reference to the rawValue option you created earlier. The format looks like ___VARIABLE____.

That’s all you need to set up your template. Quit and reopen Xcode. Next, you’ll use your template to create APIError.swift.

Using the Template

In Xcode, with Stellar Space open, go to File ▸ New ▸ File…. Then, choose your Swift Enum template.

For Raw Value, enter Error. Then, click Next.

Xcode pop-up to enter Raw Value in the Swift Enum template

Name the file APIError.swift and click Create.

Open APIError.swiftcases within APIError and replace them with this:

case network(description: String)
case parsing(description: String)

Finally, build and run.

Starter screen with updated Label text driven from ViewModel

The app compiles! But, you still have some finishing touches to add to ContentView and ViewModel to display more than just a fancy message on the screen. :]

Finishing the Project

On the final stretch now to finish off the project to display those lovely NASA images.

In Xcode, open ViewModel.swift. First, add the following import at the top of the file:

import UIKit

Then, add the following under the declaration of title:

@Published var image: UIImage?
@Published var errorMessage: String?
@Published var isLoading = true
@Published var date = Date()

var canMoveForward: Bool {
  !calendar.isDateInToday(date)
}

var dateString: String {
  let dateFormatter = DateFormatter()
  dateFormatter.dateStyle = .medium
  dateFormatter.timeStyle = .none
  return dateFormatter.string(from: date)
}

private let spaceImageFetcher = SpaceImageFetcher()
private let calendar = Calendar.current
private var disposables = Set<AnyCancellable>()

func changeDate(days: Int) {
  if let newDate = calendar.date(byAdding: .day, value: days, to: date) {
    date = newDate
    loadImage()
  }
}

func loadImage() {
  isLoading = true
  image = nil
  errorMessage = nil
  spaceImageFetcher.dailyPicture(date: date)
    .receive(on: DispatchQueue.main)
    .flatMap { dailyPic -> AnyPublisher<UIImage?, APIError> in
      self.title = dailyPic.title
      return self.spaceImageFetcher.loadImage(url: dailyPic.url)
    }
    .receive(on: DispatchQueue.main)
    .sink(receiveCompletion: { [weak self] value in
      switch value {
      case .failure(let reason):
        print("Failure: \(reason.localizedDescription)")
        self?.errorMessage = "Something went wrong."
      case .finished:
        break
      }
      self?.isLoading = false
    }, receiveValue: { image in
      self.image = image
    })
    .store(in: &disposables)
}

I won’t explain the code here because it’s not relevant to the tutorial. All you need to know is that this code adds the logic for accessing the NASA API and displaying images.

Then, open ContentView.swift. Replace the declaration of body with this:

var body: some View {
  ZStack {
    ColorPalette.primary.ignoresSafeArea()
    VStack {
      Text(viewModel.dateString)
        .foregroundColor(ColorPalette.accent)
      Spacer()
      if !viewModel.isLoading {
        if let image = viewModel.image {
          Image(uiImage: image)
            .resizable()
            .scaledToFit()
          Text(viewModel.title)
            .foregroundColor(ColorPalette.accent)
        } else if let error = viewModel.errorMessage {
          Text(error)
            .foregroundColor(ColorPalette.secondary)
        }
        Spacer()
        ZStack {
          HStack {
            Button("Back", action: changeDateBack)
              .padding(.leading, 20)
            Spacer()
            
            if viewModel.canMoveForward {
              Button("Forward", action: changeDateForward)
                .disabled(!viewModel.canMoveForward)
                .padding(.trailing, 20)
            }
          }
        }
      } else {
        ProgressView()
          .progressViewStyle(ProgressViewWithBackgroundStyle())
          .frame(width: 100, height: 100)
        Spacer()
      }
    }
  }
  .onAppear(perform: fetchImage)
  .buttonStyle(SpaceButtonStyle())
}

func changeDateBack() {
  viewModel.changeDate(days: -1)
}

func changeDateForward() {
  viewModel.changeDate(days: 1)
}

func fetchImage() {
  viewModel.loadImage()
}

With this, ContentView has the UI to display everything happening in ViewModel. Build and run.

Stellar Space app with data fetched from external API

At last, you’ve completed Stellar Space, and you have two new Xcode templates to show for it: a project template and a file template!

Where to Go From Here?

In this tutorial, you created both a project template and a file template, using Xcode’s default templates as a source of inspiration.

You added your files to your templates, controlled them with the Template Options file and customized them with variables.

Templates are a great example of the power of an IDE like Xcode. If you’d like to learn more about how Xcode can make your life easier, iOS App Distribution & Best Practices dives under the hood of the Xcode project structure.

Your project template separates business logic from UI code by including a view model. If you’d like to learn more about project architecture, have a look at Advanced iOS App Architecture. You might get some ideas for new templates, too!

Please join the discussion below if you have questions or comments or want to share some of your creative templates.