AppCode 2019.3 Help

Create a SwiftUI application in AppCode

In this tutorial, we'll create a simple SwiftUI application that shows a list of iOS conferences. The application will consist of two views:

  • A list of conferences representing data from the local JSON file.

  • Details for each conference.

Final app

Along the way, you'll get familiar with the basic AppCode workflow and useful features that help writing code easier and faster.

Step 1. Create a project

Projects created in AppCode are fully compatible with Xcode and use the same project model. After you create a project in AppCode, you can open and edit it in Xcode and vice versa, and all the data will be synchronized.

  1. Launch AppCode and select New Project from the Welcome screen:

    Welcome Screen

    If you have another project open in AppCode at the moment, select File | New Project from the main menu.

  2. In the dialog that opens, you see a list of Xcode project templates. Select iOS | Application | Single View App and click Next:

    Create a new project: project templates

  3. Fill in the following fields:

    • Product Name: your project name which will be also the name of your application. Type iOSConferences.

    • Organization Name: your or your company's name.

    • Organization Identifier: your company's identifier in reverse-DNS format, for example, com.mycompany.

    New project settings

    Your project name and organization identifier together build a bundle identifier — an automatically generated string that will identify your application in the operating system.

  4. Select Swift in the list of languages and SwiftUI in the User Interface field. Make sure that all checkboxes in the dialog are cleared, as we're not going to use tests or Core Data in this tutorial. Click Finish.

    Create a new project: project settings

  5. In the Finder window that opens, select a directory where your project will be located.

A new Swift project will be created and immediately opened in AppCode. In the left part of the AppCode window, you see the Project tool window. From here, you can navigate to necessary files, add and delete files and folders, exclude files from indexing or from Xcode project, add files to different targets, and so on.

To show and hide the Project tool window, press ⌘1. For more information, see Project Tool Window.

Project tool window

Step 2. Create a list

  1. In the Project ⌘1 tool window, open the ContentView.swift file — a standard SwiftUI view created together with the project. This view displays the Hello, World! text and includes two structures: ContentView and ContentView_Previews.

    import SwiftUI struct ContentView: View { var body: some View { Text("Hello, World!") } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }

  2. Rename all usages of ContentView to ConferenceList. The easiest way to do this is using the Rename refactoring: place the caret at ContentView, press ⇧F6, type the new name in the highlighted area, and press :

    The ContentView.swift file will be also renamed to ConferenceList.swift.

  3. Delete Text("Hello, World!") and add a List containing two Text elements.

    Try to select code constructs by pressing ⌥↑/ ⌥↓ instead of using the mouse. When adding the second Text element, don't copy and paste the first one, but duplicate the line by pressing ⌘D:

    You may notice that we've missed a space between List and {. Just press ⌥⌘L, and AppCode will reformat the code on the whole page according to the code style guidelines specified in the Editor | Code Style | Swift section of the Preferences dialog ⌘,.

    As a result, you will have the following code:

    struct ConferenceList: View { var body: some View { List { Text("Conference1") Text("Conference2") } } }
  4. Run your application to check how it looks. To do this, select a device or simulator on the toolbar:

    Select device

    Then, press ⌃R or click the Run button. The list now looks this way:

    The simple SwiftUI list
  5. Add a title to the list. To do this, you need to embed the list in a NavigationView and then call the navigationBarTitle(_:) method for the list:

    struct ConferenceList: View { var body: some View { NavigationView { List { Text("Conference1") Text("Conference2") }.navigationBarTitle("Conferences") } } }

    When you run your application ⌃R, it will look as follows:

    A simple list with a title
  6. Instead of two list items, let's create one consisting of a title and subtitle. In the title, we will display the conference name and in the subtitle — location. To do this, wrap the two Text elements in a VStack container, change their values, and apply the corresponding font styles to them:

    VStack { Text("Conference").font(.headline) Text("Location").font(.subheadline) }
    Simple SwiftUI list
  7. Align both Text elements left using the alignment parameter for the VStack container:

    VStack(alignment: .leading) { // ... }
    Simple SwiftUI list

Step 3. Load data from JSON

Let's make our list dynamic by loading data from the local conferencesData.json file that you can download here.

  1. Create a new group Resources where you will keep the JSON file:

    • Right-click the iOSConferences group in the Project ⌘1 tool window and select New | Group from the context menu.

    • In the dialog that opens, type Resources.

    • Make sure the Create folder checkbox is selected and click OK:

      Create a new group

  2. Add the downloaded JSON file to the newly created group:

    • Right-click the Resources group and select Add | Files.

    • Select the conferencesData.json file on your mac.

    • In the dialog that opens, leave the default settings and click OK:

      Add file

    The added file contains a list of iOS conferences. Each conference has a name, link to the official website, start and end dates, and location:

    { "name": "SwiftLeeds", "link": "https://swiftleeds.co.uk/", "start": "2020-10-07", "end": "2020-10-08", "location": "🇬🇧 Leeds, UK" }
  3. Create a new Data.swift file where we will add a function for parsing the JSON data:
    • Create a new group called Model as described above.

    • Select this group, press ⌘N and select Swift File

    • In the dialog that opens, type Data in the Name field and click OK:

      Add a new Swift file

  4. In the Data.swift file, add a function for reading data from JSON:

    func loadFile<T: Decodable>(_ filename: String) -> T { let data: Data guard let file = Bundle.main.url(forResource: filename, withExtension: nil) else { fatalError("Cannot find \(filename)") } do { data = try Data(contentsOf: file) } catch { fatalError("Cannot load \(filename):\n\(error)") } do { let decoder = JSONDecoder() return try decoder.decode(T.self, from: data) } catch { fatalError("Cannot't parse \(filename): \(T.self):\n\(error)") } }

  5. Create a new type for handling the parsed JSON data:

    • Select the Model group.

    • Press ⌘N and select Swift Type.

    • In the dialog that opens, type Conference in the Name field and click OK:

      Add a new Swift type

    • Write the code for the new type. The type should conform to the Codable protocol and contain a set of properties corresponding to the JSON data:

      class Conference: Codable { var name: String var location: String var start: Date? var end: Date? var link: String }

  6. In the Data.swift file, create a variable that will store the parsed data — an array of the Conference objects:

    let conferencesData: [Conference] = loadFile("conferencesData.json")

    You can use the let live template to introduce the variable. Just type let and press . Press to navigate between the template placeholders. To add the variable type, use the corresponding intention action: press ⌥⇧↩ and select Add explicit type:

  7. In the ConferenceList.swift file, pass conferencesData to the List initializer:
    List(conferencesData) { conference in // ... }

    List is highlighted red — hover over it to see the error message:

    Initializer 'init(_:rowContent:)' requires that 'Conference' conform to 'Identifiable'

    Switch to the Conference.swift and declare conformance to the Identifiable protocol:

    class Conference: Codable, Identifiable { // ... }
  8. Replace the strings in the Text elements by the values from the loaded JSON:

    VStack(alignment: .leading) { Text(conference.name).font(.headline) Text(conference.location).font(.subheadline) }

  9. Run ⌃R your application. You'll get another error:

    Expected to decode Double but found a string/data instead.

    To fix it, you need to specify the exact date format for JSONDecoder. Go to Data.xml and add the following code after let decoder = JSONDecoder():

    let format = DateFormatter() format.dateFormat = "yyyy-mm-dd" decoder.dateDecodingStrategy = .formatted(format)

    Run ⌃R the application again: the list of conferences will be displayed:

    Conferences list

Step 4. Add the details view

The second view of our application will display the detailed data on each conference.

  1. Create a new SwiftUI file:

    • In the Project ⌘1 tool window, press ⌘N and select File from Xcode Template.

    • In the dialog that opens, select iOS | User Interface | SwiftUI View and click Next:

      Create a new SwiftUI file

    • On the next page, type the filename — ConferenceDetails, make sure that the iOSConferences group and location are selected, and click Finish.

  2. In the newly created file, delete the ConferenceDetails_Previews structure.

  3. Instead the default Hello, World! text, add the conference location:

    struct ConferenceDetails: View { var body: some View { Text(conference.location) } }

    Since the conference variable is not defined in the current context, it is highlighted red. To define it directly from its usage, do the following:

    • Place the caret at conference and press ⌥⏎.

    • Select Create local variable 'conference' and press .

    • In the list that appears, select var.

    • To specify the type of the variable, press ⌥⏎ again, select Add explicit type, and start typing Conference.

    • Move the code line up by pressing ⌥⇧↑.

    • Press ⌥⌘L to fix the code formatting.

    As a result, your code will look as follows:

    struct ConferenceDetails: View { var conference: Conference var body: some View { Text(conference.location) } }
  4. Duplicate ⌘D the Text(conference.location) line two times and wrap these three Text elements in a VStack container.

    struct ConferenceDetails: View { var conference: Conference var body: some View { VStack { Text(conference.location) Text(conference.location) Text(conference.location) } } }

By now, we leave the ConferenceDetails view as is. Later, we will display the conference dates in the second line and the link to the conference website in the third one.

Step 5. Set up navigation between the list and details

  1. Open the ConferenceList.swift file.

  2. In the ConferenceList structure, wrap the VStack container in a NavigationLink:

    NavigationLink { VStack(alignment: .leading) { Text(conference.name).font(.headline) Text(conference.location).font(.subheadline) } }

  3. Pass the ConferenceDetails view as the destination parameter for the NavigationLink:

    NavigationLink(destination: ConferenceDetails(conference: conference)) { VStack(alignment: .leading) { Text(conference.name).font(.headline) Text(conference.location).font(.subheadline) } }

  4. Run ⌃R your application. The list items will be clickable, and the details view will open on tapping each of them:

Display the conference date

In the ConferenceDetails view, the start and end dates should be displayed in format MMMM dd, yyyy - MMMM dd, yyyy. In case of a one-day conference (the start and end values are the same), just one date should be displayed.

  1. In the Data.swift file, add a new function dateToString as an extension of the the Date type. This function will convert Date to String and change the date format:
    extension Date { func dateToString() -> String { let format = DateFormatter() format.dateFormat = "MMM dd, yyyy" format.string(from: self) } }

    The code above won't be resolved, as the function doesn't return anything. Place the caret withing the highlighted symbol and press ⌥⏎:

    Apply Fix-it

    Press to apply the quick-fix. The return statement will be added:

    extension Date { func dateToString() -> String { let format = DateFormatter() format.dateFormat = "MMM dd, yyyy" return format.string(from: self) } }
  2. Create a method of the Conference type that will return the data of a conference in text format. Open Conference.swift and add the following method after the var link: String line:

    func textDates() -> String { var result = "\(start!.dateToString()) - \(end!.dateToString())" if end == self.start { result = "\(start!.dateToString())" } return result }

  3. In the ConferenceDetails view, replace the value of the second Text element with the conference date returned by the newly created textDates() method:

    VStack { Text(conference.location) Text(conference.textDates()) Text(conference.location) }

  4. Run ⌃R the application, open the details views of some conferences to make sure the dates are displayed as expected:

    The conference dates

To make a conference link clickable, you need to add a Button and pass a method for opening the URL as the button's action.

  1. In the ConferenceDetails.swift file, add a new structure named LinkButton that conforms to the View protocol and includes a Button with the Go to official website text:

    struct LinkButton: View { var body: some View { Button() { Text("Go to official website") } } }
  2. Add an action for the created Button: open the conference link with the open(_:) method:

    struct LinkButton: View { var link = "" var body: some View { Button(action: { UIApplication.shared.open(URL(string: self.link)) }) { Text("Go to official website") } } }

    URL is highlighted red. Place the caret at this symbol, press ⌥⏎, and select the Force-unwrap using '!'...:

    Force-unwrap intention

    The code will be changed this way:

    UIApplication.shared.open(URL(string: self.link)!)
  3. Put the newly created LinkButton in the ConferenceDetails view and pass the conference link as a parameter for the LinkButton initializer:

    struct ConferenceDetails: View { var conference: Conference var body: some View { VStack { Text(conference.location) Text(conference.textDates()) LinkButton(link: conference.link) } } }
  4. Run ⌃R the application and open the details view. Tap the Go to official website link — the conference website will open in Safari:

    Details view with data

Step 7. Adjust the details view appearance

Now when we've passed all necessary data to the details view, the only thing left is to change the way it looks like.

  1. In the ConferenceDetails view, place the content of the VStack container to the top left corner of the screen by adjusting its frame width, height, and alignment:

    struct ConferenceDetails: View { var conference: Conference var body: some View { VStack { Text(conference.location) Text(conference.textDates()) LinkButton(link: conference.link) }.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading) } }
    Adjust the details view appearance

  2. Align the VStack content left by using the alignment parameter:

    VStack(alignment: .leading) { // ... }
    Adjust the details view appearance

  3. Put the conference name in the view title by using the navigationBarTitle(_:) method for the VStack container:

    VStack(alignment: .leading) { // ... }.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading) .navigationBarTitle(conference.name)
    Adjust the details view appearance

  4. Add the default padding for the VStack container using the padding() method:

    VStack(alignment: .leading) { // ... }.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading) .navigationBarTitle(conference.name) .padding()
    Adjust the details view appearance

  5. Add the bottom padding for the elements within the VStack container:
    VStack(alignment: .leading) { Text(conference.location).padding(.bottom) Text(conference.textDates()).padding(.bottom) LinkButton(link: conference.link).padding(.bottom) }

    You can place multiple carets and type your code in several lines at the same time. To do this, place the caret where you want to start typing, press and hold ⇧⌥, and place the caret at the other lines:

    Finally, the details view will look this way:

    Adjust the details view appearance
Last modified: 6 February 2020