Category: iOS

Saving Data in a Swift App

People new to iOS development frequently ask how to save data in their apps. Apple provides the following technologies for saving app data on a local device:

  • UserDefaults
  • Keychain
  • Files
  • Core Data

You can use multiple methods to save data in your app. Many iOS and Mac apps use both UserDefaults and Core Data to save data. Use all the methods for saving data that make sense for your app.

UserDefaults

Apple provides a UserDefaults class that provides a programming interface to your app’s defaults database. The defaults database contains user settings and preferences.

Use UserDefaults to store small amounts of data, like settings and preferences. If you are writing a plain text editor, you could store the font to use in UserDefaults.

Keychain

Use the keychain to store small amounts of data securely, such as passwords. If you were writing a Twitter app, you could use the keychain to store the person’s Twitter handle and password.

Apple provides a Keychain Services API for apps to work with the keychain. The documentation is in the System > Security section of Apple’s documentation in Xcode. Choose Help > Developer Documentation in Xcode to read the documentation.

Files

You can use files to store larger amounts of data. Document-based apps, such as text editors, spreadsheets, and drawing apps, usually save their data in files.

Apple provides the Codable protocol to simplify reading and writing data to files. An Internet search for Swift Codable tutorial yields many results, most of which cover JSON.

Core Data

Core Data is Apple’s framework for creating and working with a data model for iOS and Mac apps. One of the things Core Data does is save data for you.

Core Data is a huge subject. People have written books about it. You can find an overview of Core Data in Apple’s documentation in Xcode under App Services. Apple also has a Core Data Programming Guide on their developer website.

Core Data or Files?

Should you use Core Data or files to save larger amounts of data?

Core Data is a powerful framework that does a lot, much more than saving app data. But with that power comes a learning curve.

You must determine if your app’s data needs require Core Data. Avoid Core Data (or at least do some more research before using Core Data) in the following cases:

  • Your app supports non-Apple platforms like Android, Linux, and Windows. Core Data does not run on non-Apple platforms.
  • Your data can easily be saved in one file.
  • Your app data has only one class or struct.
  • You are writing a document-based app. Core Data supports document-based apps, but it doesn’t add much to Apple’s document architecture.

A text editor is an example of a project where you should save your data in a file. The app data is just a bunch of text. The data can easily be saved in one file. Core Data would not help you much.

Use Core Data to save your app’s data in the following cases:

  • You store large amounts of data in the app.
  • The data makes more sense to store in a database than in a single file.
  • Your app data has multiple classes or structs.
  • You are writing a shoebox (not document-based) app.

A blog editor is an example of a project where you should use Core Data. A blog can contain hundreds of posts that would be difficult to store in a file. A blog has multiple pieces of data: titles, tags, categories, and authors. Core Data can do things like show you all the blog posts with a given tag. Take advantage of Core Data if it will help for your app.

If you are still not sure whether or not to use Core Data, use it.

Scrolling an iOS Text View When the Keyboard Appears

A common issue iOS developers run into is dealing with the onscreen keyboard. The keyboard appears and blocks what you’re typing.

The fix is to scroll the text view when the keyboard appears. The code to do this is not too hard. iOS posts a notification when the keyboard appears and when the keyboard disappears. Write a function to handle each notification. Scroll the text view when the keyboard appears and return it to its original position when the keyboard disappears.

Handling the Keyboard Appearing Notification

You must write the function keyboardWasShown. This function gets called when the keyboard appears.

There are two things to do in keyboardWasShown to scroll the text view. First, set the text view’s content inset so the bottom edge is the keyboard’s height. Second, set the scroll indicator insets to the text view’s content inset.

@objc func keyboardWasShown(notification: NSNotification) {
    let info = notification.userInfo
    if let keyboardRect = info?[UIResponder.
        keyboardFrameBeginUserInfoKey] as? CGRect {

        let keyboardSize = keyboardRect.size
        textView.contentInset = UIEdgeInsets(top: 0, left: 0, 
            bottom: keyboardSize.height, right: 0)
        textView.scrollIndicatorInsets = textView.contentInset
    }  
}

Swift functions that work with notifications require the @objc keyword at the start of the function declaration. The userInfo property of the notification contains the rectangle where the keyboard appears on the screen.

Use the keyboard rectangle to get the keyboard’s height. Set the text view’s bottom edge inset to the keyboard’s height. The text view will scroll when the text insertion point gets near the top of the keyboard.

Handling the Keyboard Hiding Notification

You must write the function keyboardWillBeHidden. This function gets called when the person entering text hides the keyboard.

When the keyboard disappears, set the text view’s content inset back to all zeroes. Set the text view’s scroll indicator insets to the text view’s content inset.

@objc func keyboardWillBeHidden(notification: NSNotification) {
    textView.contentInset = UIEdgeInsets(top: 0, left: 0, 
        bottom: 0, right: 0)
    textView.scrollIndicatorInsets = textView.contentInset
}

Example Project

I have a text editor project on GitHub to see the keyboard handling code in practice.

It’s OK to Use Storyboards

A common question new iOS developers ask is how to build their app’s user interface: code or storyboards. People adamant about building their interfaces in code inevitably join in to say that building interfaces in code is the right way, the way big companies make iOS apps. But it’s OK to use storyboards for your user interface, especially if you are new to iOS development.

What’s Wrong with Storyboards?

Storyboards have two main issues. The first issue involves version control. If multiple people make changes to a storyboard, commit the changes, and merge the changes, conflicts occur that are difficult to resolve.

Merge conflicts are a serious issue, but if you’re a new iOS developer, you probably are working alone. You’re the only one editing the storyboard so you don’t have to worry about merge conflicts.

The second issue with storyboards is that editing them can be slow if you jam 10 or more screens into a single storyboard. If you run into this issue, the solution is to use multiple storyboards. Select the view controller scenes you want to put in a new storyboard and choose Edit > Refactor to Storyboard.

Why Use Storyboards?

The point of this article isn’t to show that storyboards are better than code for building user interfaces, but the following list shows some reasons to use storyboards:

  • UIKit is meant to work with storyboards. Xcode projects that use UIKit include a storyboard for a reason.
  • Using storyboards is easier for new iOS developers than building a user interface entirely in code. Dragging a button or other control to a screen is easier than figuring out the code you have to write to create and place that control.
  • Storyboards let you see how your interface looks before running the app. With code you have to build and run the project to see how the interface looks.

Conclusion

Build your user interface however you want. But it’s OK to use storyboards. You’re not doing it wrong if you use a storyboard for your app’s user interface, no matter what someone on Reddit or Slack tells you.

Creating Document-Based Apps with SwiftUI

Xcode 12 Note from the Author

Xcode 12 made a bunch of changes to SwiftUI for document-based apps. If you choose a multi-platform document app project, you will get the text editor project I create in this article without having to write any code. I’m keeping this article up for people who are still using Xcode 11.

Start of Original Article

I have not seen any articles or tutorials on creating a document-based app with SwiftUI so I’m writing one. In this tutorial you will build an iOS plain text editor.

If you haven’t already, I recommend reading two articles before going through the tutorial. Creating Document-Based iOS Apps Part 1 provides an overview of creating document-based iOS apps. Using Text Views in a SwiftUI App provides an explanation of using a UIKit text view in a SwiftUI app. SwiftUI does not currently have a built-in text view.

Create the Project

Start by creating a project. Create an iOS document-based app project. Choose SwiftUI from the User Interface menu if it’s not already selected.

The most interesting files Xcode creates for a document-based SwiftUI app are the following:

  • DocumentBrowserViewController.swift contains code for the document browser, where people create and open documents.
  • DocumentView.Swift contains code for the document’s main view.
  • Document.Swift contains code for the document.

Creating a New Document

If you run the project, you’ll notice that tapping the Create Document button does nothing. You must write some code to create the document when someone taps the button. The easiest way to create a new document is to add an empty file to the project. The empty file should have the same file extension as the document’s. This file will be copied to the app bundle when building the project. You’ll have to write some code to load the file from the app bundle.

One last thing to do to create documents properly is to configure the document type in Xcode so that the app is an editor of plain text files.

Add an Empty Document File

Choose File > New > File to add a new file to the project. Select Empty from the list of iOS file templates. The empty file is in the Other section. You have to scroll down a bit to reach the Other section.

Name the file. I named the file New Document.txt. You can choose a different name, but remember the name.

Load the Empty Document

Open the DocumentBrowserViewController.swift file and go to the didRequestDocumentCreationWithHandler function. Replace the following line of code:

let newDocumentURL: URL? = nil

With the following code:

let newDocumentURL: URL? = Bundle.main.url(
    forResource: "New Document", withExtension: "txt")

This code loads the empty document file from the app bundle and uses that as the base for a new document.

There is one more piece of code to change. In the same function, find the following code:

if newDocumentURL != nil {
    importHandler(newDocumentURL, .move)
} else {
    importHandler(nil, .none)
}

There is a problem with the code inside the if block.

importHandler(newDocumentURL, .move)

This line of code moves the file from the application bundle. Your app will crash the second time you create a document because the empty document file was moved out of the application bundle. The fix is to copy the file from the application bundle when creating a new document.

importHandler(newDocumentURL, .copy)

Edit the Document Type

Xcode initially sets the document type for an iOS document-based app to be a viewer of image files. You must configure the document type in Xcode so that the app is an editor of plain text files. You’re making a text editor, not a text viewer.

Select the project from the project navigator to open the project editor. Select the app target from the left side of the project editor. Click the Info button at the top of the project editor to access the document types. Click the disclosure triangle next to Document Types.

DocumentTypes

  1. Enter PlainText in the Name text field.
  2. Enter public.plain-text in the Types text field. public.plain-text is the UTI (Uniform Type Identifier) for plain text files.
  3. In the Additional document type properties section, set the CFBundleTypeRole value to Editor so people can edit documents.
  4. Set the LSHandlerRank value to Alternate to ensure this text editor isn’t the default text editor for all plain text files.

Create the Data Model

The data model for this project is simple. The document is the data model. All you have to do is add a property to store the text. Open the Document.swift file and add the following code inside the Document class:

var text = ""

The code declares a property to store the text and has the initial value of an empty string.

Building the Text View

Now it’s time to add the text view so people can edit text. Currently SwiftUI does not include a text view so you have to write code to create a UIKit text view. But it’s not too much code. Add a new Swift file to your project for the text view. Import the SwiftUI and UIKit frameworks. Add the following struct:

struct TextView: UIViewRepresentable {

}

The text view must conform to the UIViewRepresentable protocol, which is the protocol that allows the use of UIKit views in SwiftUI apps. To conform to the protocol, you must write the following functions:

  • makeUIView
  • updateUIView
  • makeCoordinator

The makeUIView function creates and configures the view. The function takes an argument of type Context and returns the type of view you want to make, which is UITextView for a text view. The Context type is a type alias for the context where updates to the UIKit view take place.

func makeUIView(context: Context) -> UITextView {
    let view = UITextView()
    view.isScrollEnabled = true
    view.isEditable = true
    view.isUserInteractionEnabled = true
    view.contentInset = UIEdgeInsets(top: 5, 
        left: 10, bottom: 5, right: 5)
    view.delegate = context.coordinator
    return view
}

The code listing creates a text view, configures the view so people can edit large amounts of text, and adds some padding so the text isn’t on the left edge of the screen. The code also sets the view’s delegate to the context’s coordinator, which you will create shortly.

The updateUIView function handles updates to the view.

func updateUIView(_ uiView: UITextView, context: Context) {

}

I will fill in this function later.

The makeCoordinator function creates a coordinator so the UIKit view can communicate with data in SwiftUI.

func makeCoordinator() -> TextView.Coordinator {
    Coordinator(self)
}

Create the Coordinator

Add a Coordinator class inside the struct for the text view.

class Coordinator: NSObject, UITextViewDelegate {
    var control: TextView

    init(_ control: TextView) {
        self.control = control
    }
}

The class inherits from NSObject, which is the base class for all UIKit classes. A text view’s coordinator should conform to the UITextViewDelegate protocol so it can respond to text view notifications, such as the text changing.

The coordinator needs a property to hold the UIKit view and an initializer. I will be adding more to the coordinator later in the article.

Add the Text View to the Document View

The last step to adding the text view is to have the document view display it. Open the DocumentView.Swift file. Inside the body property you should see a VStack that contains a HStack and a button. The HStack shows the name of the file. The button lets you go back to the browser when you’re done writing.

The text view should appear between the HStack and button. Add the following line of code between the HStack and button:

TextView()

If you build and run the project, you should be able to create a new document and type in it.

Connect the Text View to the Document

For the app to do more than let people type text in a text view, you must connect the text view to the document so the text you type updates the document as well. In the document view, you should see the following variable:

var document: UIDocument

This variable contains a reference to the document. Make the following changes to the variable:

@State var document: Document

The @State property wrapper will allow the text view to bind to the document in order to display the document’s text. Changing the type of the variable to Document is also necessary for the text view to work with the document properly.

Now add the following property to the text view:

@Binding var document: Document

The @Binding property wrapper binds the document property in the text view to the document property in the document view. The text view and document view are both pointing to the same document. The text view now has access to the document.

Now that you created the binding you must go back to the document view and change the call to create the text view by supplying the document as an argument.

TextView(document: $document)

The $ at the start of $document indicates that you are passing a binding to the text view. You must pass a binding so the document view and text view both point to the same document.

Fill the updateUIView Function

Now that the text view has access to the document, you can write the text view’s updateUIView function. Set the text view’s contents to the document’s contents.

func updateUIView(_ uiView: UITextView, context: Context) {
    uiView.text = document.text
}

Handling Text Changes

One last thing to do is to update the document when the text view contents change. Add the following function to the Coordinator class:

func textViewDidChange(_ textView: UITextView) {
    control.document.text = textView.text
    control.document.updateChangeCount(.done)
}

The first line of code sets the document’s contents to the text view’s contents. The second line marks the document as changed so it will be saved.

Save the Document

At this point you’re finished with the SwiftUI material, but there’s still some work to do to finish the app. The text editor is missing one really big feature: saving text. Open the Document.Swift file. Xcode created a blank contents function for saving the document.

What you have to do is convert the text in the document to a Data object and archive that object. Apple provides the NSKeyedArchiver class and an archivedData function so you can do both steps in one line of code.

override func contents(forType typeName: String) throws -> Any {
    return try NSKeyedArchiver.archivedData(withRootObject: text,
        requiringSecureCoding: true)
}

The root object is the document’s text. Requiring secure coding ensures your app will be able to unarchive the data when loading the document.

Load the Document

After saving the document, the next task is to load the document when someone chooses a document from the browser. Xcode supplies a blank load function, where you write the code to load the document from disk.

The first argument to the load function is the contents of the file. You must first cast the contents to the Data type. Call the NSKeyedUnarchiver class’s unarchiveTopLevelObjectWithData function to unarchive the text from the file. The last step is to set the document’s text to the contents of the file.

override func load(fromContents contents: Any, 
    ofType typeName: String?) throws {

    guard let data = contents as? Data else { return }
    guard let fileContents = try 
        NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) 
            as? String else { return }
    text = fileContents
}

Conclusion

Now you have a simple, working text editor. You can create documents, edit text, save documents, and open them. You can use this project as a foundation for building your own text editing app, such as a Markdown editor. I have the project on GitHub for you to download if you run into issues.

Using Text Views in a SwiftUI App

SwiftUI currently lacks native support for text views. (Note: Xcode 12 adds text view support to SwiftUI). If you want people to enter large amounts of text in a SwiftUI app, you have to wrap a UIKit or AppKit text view. This article shows you how to use a text view in an iOS app that uses SwiftUI. Most of this material should also apply to Mac apps. Replace anything that has a UI prefix with NS.

UIViewRepresentable

To use UITextView in a SwiftUI app, you must create a struct for the text view and have it conform to the UIViewRepresentable protocol. The UIViewRepresentable protocol is how you wrap UIKit views in SwiftUI apps.

struct TextView: UIViewRepresentable {

}

Don’t forget to import the UIKit framework.

Create the View

To conform to the UIViewRepresentable protocol, you must add two functions to the struct you created. The first function is makeUIView, which creates and configures the view. It takes an argument of type Context and returns the type of view you want to create, which is UITextView for a text view. The Context type is a type alias for the context where updates to the UIKit view take place. The following code shows an example of creating and configuring a text view:

func makeUIView(context: Context) -> UITextView {
    let view = UITextView()
    view.isScrollEnabled = true
    view.isEditable = true
    view.isUserInteractionEnabled = true
    view.contentInset = UIEdgeInsets(top: 5, 
        left: 10, bottom: 5, right: 5)
    return view
}

At a minimum you must create the view and return it. In the code sample I decided to make the text view scrollable and editable. I also added some padding so the text isn’t on the left edge of the screen.

Update the View

The second function you must write is updateUIView, which handles updates to the view. The updateUIView function takes two arguments: a view and a context.

func updateUIView(_ uiView: UITextView, context: Context) {

}

I will be filling the function later in the article.

Try Creating Your Own Text View

At this point you have enough to add a text view to a SwiftUI app. Create an iOS single view app project in Xcode. Add a file for the TextView struct. Fill the struct with the code I’ve shown so far in the article. Open the ContentView.swift file and replace the text label in the body property with a text view.

var body: some View {
    TextView()
}

Build and run the app. You should be able to type in the text view.

Connecting to Your Data Model

At this point you can type in the text view, but you’ll lose what you type when you quit the app. For a text view to be useful, it needs a connection to your app’s data model.

In the struct for the text view, add a property for the data model. For a simple app, you can create a @State property wrapper that holds the text.

@State var text: String

In a real app you’re more likely to have a reference to the data model in another SwiftUI view. Use the @Binding property wrapper to access data from another SwiftUI view.

@Binding var model: MyModel

Replace MyModel with whatever the name of your data model is.

Now that you have a connection to your data model, you can fill in the updateUIView function. Make the text view show the text from the data model.

func updateUIView(_ uiView: UITextView, context: Context) {
    uiView.text = model.text
}

Coordinators

In order for a UIKit view to communicate with data in SwiftUI, the view needs a coordinator. To conform to the UIViewRepresentable protocol, you must add a new function to the TextView struct, makeCoordinator. The makeCoordinator function creates the coordinator. The return type takes the form StructName.Coordinator.

func makeCoordinator() -> TextView.Coordinator {
    Coordinator(self)
}

The next step is to set the text view’s delegate to the context’s coordinator. Add the following line of code to makeUIView before the return statement:

view.delegate = context.coordinator

The last step is to add a class for the coordinator. Create a class for the coordinator inside the struct for the text view. Make sure the name of the class matches the name you supply to the makeCoordinator function.

class Coordinator: NSObject, UITextViewDelegate {
    var control: TextView

    init(_ control: TextView) {
        self.control = control
    }

    func textViewDidChange(_ textView: UITextView) {
        control.model.text = textView.text
    }
}

The Coordinator class inherits from NSObject, which is the base class for all UIKit and AppKit classes. A coordinator for a text view should conform to UITextViewDelegate so it can respond to text view notifications, such as the contents of the text view changing.

The coordinator needs a property for the control it’s going to coordinate. The control property contains a reference to the text view.

The Coordinator class needs an initializer to set its view, which is the control property in the code sample.

The last block of code tells the coordinator to respond when the contents of the text view change. The function sets the model’s contents to the text view’s contents. If you want the text view to handle other notifications, add the functions to the coordinator.

Summary

To use UIKit views in a SwiftUI, you must perform the following tasks:

  • Create a struct for the view that conforms to the UIViewRepresentable protocol.
  • Write the makeUIView function to create and configure the view.
  • Write the updateUIView function so SwiftUI can update the view.
  • Write the makeCoordinator function to create a coordinator for your view to communicate with SwiftUI data.
  • Create a class for the coordinator.

I have a simple plain text editor project on GitHub if you want to see an example of a SwiftUI project that uses a text view.

Learn SwiftUI or UIKit?

With the release of SwiftUI in iOS 13, a common question from people new to iOS development is whether they should learn SwiftUI or UIKit.

Why Learn SwiftUI

The most compelling reason to learn SwiftUI is it’s the future. 2–3 years from now, most iOS developers will use SwiftUI to build their user interfaces. You’re eventually going to need to learn SwiftUI so you might as well learn it now. It makes sense to learn SwiftUI instead of learning UIKit and learning SwiftUI a year or two later.

SwiftUI Limitations

SwiftUI is new technology. At the time I’m writing this, SwiftUI has been out for six months, and three of those months it was in beta. Because SwiftUI is new technology, it has bugs and missing features. Apple is going to be making changes to SwiftUI over the next few years so there’s going to be a lot of work keeping up with the changes. Should you decide to learn SwiftUI instead of UIKit, prepare for a lot of frustration.

Apple’s SwiftUI documentation is limited at best. Many outsiders have been writing books, articles, and tutorials on Swift UI. But at this time, everyone is a beginner at SwiftUI development. There’s much less SwiftUI learning material than UIKit.

SwiftUI has a limited set of user interface elements. For example SwiftUI currently lacks text views and collection views.

SwiftUI apps don’t run on iOS 12 and earlier iOS versions. For people new to iOS development, this isn’t a big limitation, but it is a limitation.

Why Learn UIKit

UIKit is mature and stable. It has all the available iOS user interface elements. There is a lot more UIKit learning material available than SwiftUI.

If you want to use a text view, collection view, or another view that SwiftUI doesn’t have, you need to know some UIKit.

Should You Learn SwiftUI or UIKit?

If you can put up with the SwiftUI limitations I mentioned earlier and deal with limited documentation, learn SwiftUI. Otherwise learn UIKit.

Creating a Simple SwiftUI App

SwiftUI is Apple’s new framework for building user interfaces in Swift. Let’s introduce SwiftUI by creating a simple app. The app displays the number of times you tap a button. I couldn’t think of a simper app idea.

I’m going to focus on making an iOS app in the article, but you can also make a Mac version of this app. Create a macOS App project instead of an iOS project.

Apple added SwiftUI support in Xcode 11 so you must be running Xcode 11 or higher to use SwiftUI.

Create the Project

Create a single-view iOS app project. Choose SwiftUI from the User Interface menu. Deselect all the checkboxes.

When you create the project, the project navigator should look similar to the following screenshot:

ProjectNavigatorSwiftUIProjectStart

The file you’re going to edit in this article is ContentView.swift. This file contains the main view for the app’s user interface. You will be adding controls to the content view.

The Content View

Open the ContentView.swift file. You’ll see the following struct for the content view:

struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
    }
}

The body property contains the main view. In the code Apple supplies, the view consists of a label (the Text struct is a label) with the text Hello, World!. If you build and run the project, you will see the label.

Previews and the Canvas

If you look at the end of the ContentView.swift file, you’ll see the following struct:

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

That struct is for displaying a preview of the view in Xcode’s canvas. The canvas is next to the Swift file in Xcode so you can view your source code and the canvas side by side..

I’m not going to use previews and the canvas in this article. I find the canvas gets in the way, is flaky, and doesn’t help much. But the canvas is there for you if you want it. You can toggle the canvas on and off using a button on the right side of the jump bar.

ToggleCanvas

The button is at the top left in the screenshot.

Change the Label

Let’s start by changing the label’s text to Taps:. Change the code for body to the following:

var body: some View {
    Text("Taps: ")
}

Build and run the project, and you should see the label’s text is Taps:.

You can modify how the text in the label looks using functions and properties of the Text struct. Start with a period. The following code displays the label in bold text:

var body: some View {
    Text("Taps: ")
        .bold()
}

The following code changes the font to the system title font:

var body: some View {
    Text("Taps: ")
        .font(.title)
}

Add a Variable to Store the Tap Count

To display the number of times someone taps the button, you need a variable to store the number of taps. Add the following code before the body variable:

@State var taps = 0

You must use the @State property wrapper so you can change the value of taps when someone taps the button. If you omit the @State part, you will get a compiler error saying that you’re trying to change an immutable value.

Now modify the label so it also shows the number of taps.

Text("Taps: \(taps)")

Build and run the project. The label will show zero taps.

Place the Label in a Stack

The next logical step is to add a button. But you can’t just add the button after the label.

var body: some View {
    Text("Taps: ")
        .font(.title)
    // This won't compile.
    Button("Tap Me") {

    }
}

The compiler will complain about that code. You have to place the label and button inside a stack. Start by placing the label inside a stack.

VStack {
    Text("Taps: \(taps)")
        .font(.title)
}

I placed the label in a vertical stack. When you add the button, it will appear below the label. Build and run the project. There aren’t any visible changes, but you’re ready to add the button now.

Add the Button

Let’s add the button to the stack in the content view.

VStack {
    Text("Taps: \(taps)")
        .font(.title)
    Button("Tap Me") {
        self.taps += 1
    }
}

The button’s title, Tap Me, is in parentheses. A closure for the button’s action (what happens when someone taps the button) follows. For this app you increment the taps variable. If you have more complicated behavior, write a function and call that function from the closure.

Build and run the project. The number of taps increases each time you tap the button.

Add Space Between the Label and Button

The app works, but there’s not much space between the label and button. Use the padding function to add some padding between the label and button.

VStack {
    Text("Taps: \(taps)")
        .font(.title)
        .padding()
    Button("Tap Me") {
        self.taps += 1
    }
}

If you supply no arguments, the padding function adds a small amount of space. To add more space, you can supply the edge to apply the padding and the amount. The following line of code provides 100 points of padding at the bottom:

.padding(.bottom, 100)

When you build and run the project, you should see better spacing between the label and button.

Challenge

Add a Reset button that sets the taps variable back to zero. Don’t forget to add padding between the two buttons. I have the project on GitHub if you get stuck.

Going from Tutorials to Your First Real App: First Steps

If you want to learn iOS development, there are many places for you to learn. Hacking with Swift has two free 100 day courses for learning iOS development, one with UIKit and one with SwiftUI.

Getting started with iOS development normally isn’t the big issue. Go through a book or course, and you’ll learn the basics of iOS programming. The trouble starts when you finish a book or course and start to make your first app on your own. You feel stuck and don’t know where to start. You share some of the following feelings:

  • “I’m a little lost as to where to go next. I’ve completed Swift for Beginners courses on at least four different sites, and if I build one more My First App I may cry.”
  • “I just am having a hard time finding the tutorials/courses I specifically need to acquire the skills I need for MY project. I’m frankly not even sure I know what skills I actually need because so much of the information I’m seeing is so outdated that I’m not sure it’s relevant to me or if I’ll begin working on it only to find it’s no longer the way things are done or that there’s a better way after I’ve put in a lot of effort.”
  • “Making the jump from basic concepts to actual coding is hard.”
  • “Basic lessons have very basic concepts but when I look at the code being used in other projects, they use methods and syntax I’ve never seen.”
  • “I watched almost every video (Stanford course) and I did everything that professor do. I built them all. But when I’m trying to build apps alone, even the things I built earlier I can’t do anything. I know Swift syntax and other stuff but I just can’t program.”

Why Do You Feel Stuck?

When you were following the book, course, or tutorial, you were doing the equivalent of watching someone make an app. You weren’t making the app. When you try to make your own app, you get stuck because you don’t have the book, course, or tutorial telling you what to do.

To apply to what you learned, you have to make an app on your own. And there’s a lot less material on going from beginner to making your first real app than there is on getting started.

Step 1: Choose a Suitable Project

The first step you have to take when creating an app is choosing the app to make. When choosing an app to make, you must balance the following:

  • Making something small enough that you can finish.
  • Making something that interests you.

A big mistake many new iOS developers make is starting with a huge app. They want to make something like Uber or a social media app with realtime chat, something that requires people to sign up for an account to use the app. Making a social media app requires you to have a backend server with a database to store all the data. No iOS development tutorial or book is going to teach you things like that. No wonder people feel stuck.

For your first app make something small and relatively simple. Make an app that uses only Apple technologies. Make something that you have a chance to finish. Once you finish your first app, choose something more ambitious as your next project.

But you also have to make an app that interests you. If you choose an app that bores you, you’ll stop making it.

Step 2: Learn the Necessary Frameworks to Make Your App

You may be able to skip this step if you pick a simple enough app to make. But most projects force you to learn some additional frameworks to make the app.

If you open Xcode’s documentation window (choose Help > Developer Documentation in Xcode), you’ll see that Apple has dozens of frameworks for iOS developers. There’s no way for you to learn them all. Use your choice of project to determine the frameworks you need to learn.

  • A fitness app may use HealthKit.
  • An app that uses locations may use MapKit.
  • A text editing app uses TextKit. You may even need to learn about UIDocument and making document-based apps.
  • A drawing app may use PencilKit and Quartz2D.
  • An app that generates PDF files uses PDFKit and possibly Quartz2D.

Step 3: Do a Little Planning

You may be tempted to create a new project in Xcode and start writing code. But development will go more smoothly if you do some planning before you start writing code. You don’t have to plan everything upfront, but doing the following will help you:

  • Draw the screens for the app on paper
  • Make a list of the app’s must-have features
  • Make a list of the types of data your app uses

Once you do some planning, pick the simplest feature you can implement and work on that. When you get a small part working, you’ll feel good and use that energy to move on to the next part of the app. Build the app piece by piece, and you’ll eventually finish your first app.

Are You Doing It Right?

You may be wondering as you develop your app if you’re doing things the right way. I can’t tell you if you are, but it’s software, not something physical. Code can be changed and improved. If you find out you’re doing things wrong, you can change the code so you’re doing thing the right way.

I recommend placing your project under version control. Using version control makes your code easier to change. If you make a bunch of mistakes, you can go back to a working version of your code. When you create a project in Xcode, there’s a checkbox to place your project under version control. You can place an existing Xcode project under version control by choosing Source Control > Create Git Repositories.

Timers

A timer sends a message to an object after a certain amount of time passes. Start by declaring a variable for the timer.

var saveTimer = Timer()

Starting a Timer

Call the scheduledTimer method to start a timer. The scheduledTimer method takes the following arguments:

  • A time interval for how often the timer fires, specified in seconds.
  • Target, which is the object that receives the message from the timer.
  • Selector, which is the function the target should run after receiving the message.
  • UserInfo, which is optional additional information you can send.
  • Repeats, which indicates whether the timer runs once or repeats.

The following code starts a timer to autosave data every 30 seconds:

func startTimer() {
    saveTimer = Timer.scheduledTimer(timeInterval: 30.0, 
        target: self, selector: #selector(self.autosave), 
        userInfo: nil, repeats: true)
}

The function for the selector needs @objc at the start of the declaration.

@objc func autosave(timer: Timer) {

}

Stopping a Timer

Call the invalidate method to stop a timer. The following code stops a timer:

func stopTimer() {
    saveTimer.invalidate()
}