AppCode 2023.1 Help

Use CocoaPods in your project

In this tutorial, we'll elaborate the iOSConferences application (see Create a SwiftUI application in AppCode) by making it load the up-to-date list of conferences from the remote YAML file used for the cocoaconferences.com website.

To parse the YAML file, we'll use the Yams library which will be added to the project by means of the CocoaPods dependency manager.

Step 1. Install CocoaPods

  1. Download the iOSConferences project and open it in AppCode.

  2. Select Tools | CocoaPods | Select Ruby SDK from the main menu. Alternatively, in the Preference dialog Ctrl+Alt+S, go to Tools | CocoaPods.

  3. In the Preferences dialog, click Add Ruby SDK, and specify the path to the Ruby SDK that will be used with CocoaPods, by default, /usr/bin/ruby:

    Add Ruby SDK
  4. Click the Install CocoaPods button.

After the CocoaPods gem is installed, the list of pods is displayed on the Tools | CocoaPods page of the Preferences dialog:

List of pods

Step 2. Add the Yams pod to the project

  1. From the main menu, select Tools | CocoaPods | Create CocoaPods Podfile. The Podfile will be created in the same directory with the .xcodeproj file and opened in the editor.

  2. In the Podfile, add the Yams pod under the iOSConferences target:

    project 'iOSConferences.xcodeproj' target 'iOSConferences' do use_frameworks! pod 'Yams' end
  3. After you have added the pod 'Yams' code line, AppCode notifies you that the Podfile contains pods not installed yet. To install the Yams pod, click the Install pods link in the top-right corner of the editor. Alternatively, with the caret placed at pod 'Yams', press Alt+Enter, select Install, and press Enter.

    Install pods

When the library is installed, AppCode automatically reloads the project as a workspace.

Step 3. Load data from the remote YAML

In our application, the conference data model already exists — iOSConferences/Model/Conference.swift. It contains a set of properties corresponding to the data stored in the conferencesData.json file located in iOSConferences/Resources. The remote YAML file contains the same-name attributes, so we don't need to change anything in the current model.

However, we need to update the code for loading and parsing the data. For handling the results of the URL session, we'll use the Combine framework, for parsing the data — a dedicated YAMLDecoder.

You can replace the current code of the Data.swift file with the following:

import Foundation import Yams import Combine extension Date { func dateToString() -> String { let format = DateFormatter() format.dateFormat = "MMM dd, yyyy" return format.string(from: self) } } let url = URL(string: "https://raw.githubusercontent.com/Lascorbe/CocoaConferences/master/_data/conferences.yml") public class ConferencesLoader: ObservableObject { @Published var conferences = [Conference]() var result: AnyCancellable? public init() { loadConferences(completion: { conferences in self.conferences = conferences }) } func loadConferences(completion: @escaping ([Conference]) -> Void) { URLSession.shared.dataTaskPublisher(for: url!) // Make the DataTaskPublisher output equivalent to the YAMLDecoder input .map {$0.data} // Decode the remote YAML file .decode(type: [Conference].self, decoder: YAMLDecoder()) // Specify a scheduler on which the current publisher will receive elements .receive(on: RunLoop.main) // Erase the publisher's actual type and convert it to AnyPublisher .eraseToAnyPublisher() // Attach a subscriber to the publisher. // receiveCompletion: a close to execute on completion. Use it to handle errors. // receiveValue: a closure to execute when receiving a value. .sink(receiveCompletion: { completion in switch completion { case .finished: break case .failure(let error): print(error.localizedDescription) } }, receiveValue: { conferences in completion(conferences) }) } }

See the detailed description of the changes below:

1. Create a class for loading the data

  1. In the iOSConferences/Model/Data.swift file, delete unnecessary code: the loadFile(_:) function and the conferencesData variable.

  2. Create a new class named ConferencesLoader that conforms to ObservableObject. Add the conferences property that will store an array of the Conference objects and an empty loadConferences() method to this class:

    public class ConferencesLoader: ObservableObject { @Published var conferences = [Conference]() func loadConferences() { }
  3. Add an initializer to the class: with the caret placed inside the class block, click Alt+Insert, select Initializer, and choose Select None in the dialog that opens:

    Generate an initializer

    Call the loadConferences() method from the initializer:

    public class ConferencesLoader: ObservableObject { @Published var conferences = [Conference]() public init() { loadConferences() } func loadConferences() { } }

2. Implement a method for loading the data

  1. In the Data.swift file, import the Yams and Combine frameworks:

    import Yams import Combine
  2. In the loadConferences() method, call URLSession.shared.dataTaskPublisher(for:) to create a DataTaskPublisher:

    func loadConferences() { URLSession.shared.dataTaskPublisher(for: url) }
  3. With the caret placed at url, press Alt+Enter and select Create global variable 'url'. This intention action will let you introduce a global variable directly from its usage.

    Set the link to the remote YAML file as the variable's value:

    let url = URL(string: "https://raw.githubusercontent.com/Lascorbe/CocoaConferences/master/_data/conferences.yml") public class ConferencesLoader: ObservableObject { // ... }

    The url parameter is highlighted in red. Press Alt+Enter to see available quick-fixes. Select Force-unwrap using '!' to abort execution if the optional value contains 'nil'. This will add the ! character after the url variable:

    func loadConferences() { URLSession.shared.dataTaskPublisher(for: url!) }
  4. Add the code for processing the remote YAML file. See the comments for more details:

    func loadConferences(completion: @escaping ([Conference]) -> Void) { URLSession.shared.dataTaskPublisher(for: url!) // Make the DataTaskPublisher output equivalent to the YAMLDecoder input .map {$0.data} // Decode the remote YAML file .decode(type: [Conference].self, decoder: YAMLDecoder()) // Specify a scheduler on which the current publisher will receive elements .receive(on: RunLoop.main) // Erase the publisher's actual type and convert it to AnyPublisher .eraseToAnyPublisher() // Attach a subscriber to the publisher. // receiveCompletion: a close to execute on completion. Use it to handle errors. // receiveValue: a closure to execute when receiving a value. .sink(receiveCompletion: { completion in switch completion { case .finished: break case .failure(let error): print(error.localizedDescription) } }, receiveValue: { conferences in completion(conferences) }) }

3. Save the loaded data to a global variable

  1. Select the content of the loadConferences() method and press Ctrl+Alt+V to extract it in a variable. In the popup that appears, select the Declare with var and Specify type explicitly checkboxes. In the highlighted area, type the variable name, then press Tab, specify the variable type—AnyCancellable, and press Enter:

    Extract variable
  2. Move the variable declaration to the class level. With the caret placed at the variable line, press Alt+Enter and select Split into declaration and assignment:

    Split into declaration and assignment

    Add ? to the AnyCancellable type to make it optional and place the result variable declaration right below the @Published var conferences = [Conference]() line:

    public class ConferencesLoader: ObservableObject { @Published var conferences = [Conference]() var result: AnyCancellable? }
  3. In the initializer where the loadConferences() method is called, pass the closure expression as a parameter:

    public init() { loadConferences(completion: { conferences in self.conferences = conferences }) }
  4. You can also simplify the method's call by using the trailing closure syntax. Press Alt+Enter and select Convert to trailing closure:

    Convert to trailing closure

Step 4. Pass data to the view

  1. Go to iOSConferences/ConferenceList.swift.

  2. Inside the ConferenceList view, add an @ObservedObject property wrapper with an instance of the ConferencesLoader class:

    struct ConferenceList: View { @ObservedObject var conferenceLoader = ConferencesLoader() var body: some View { // ... } }
  3. Pass the list of the loaded conferences (conferenceLoader.conferences) to the List initializer:

    struct ConferenceList: View { @ObservedObject var conferenceLoader = ConferencesLoader() var body: some View { NavigationView { List(conferenceLoader.conferences) { // ... } } }
  4. Run Shift+F10 the application by pressing Shift+F10 or clicking App actions execute on the toolbar. Now the application shows the conferences from the remote YAML file:

    The list of conferences loaded from the remote YAML file
Last modified: 21 November 2022