CoatySwift Tutorial
This tutorial will show you how to set up a minimal CoatySwift application. Please note that the tutorial is focused on setting up the base structure needed to get Coaty up and running, with little to no focus on actual application logic. To gain in-depth insights into CoatySwift, take a look at the Developer Guide.
                    The source code of this tutorial can be found in the
                    CoatySwiftExample
                    folder of the CoatySwift repo. Just clone the repo, wait for XCode to resolve all of the dependencies and open the Example/Example.xcodeproj in XCode.
                
Note
                            
                            Unfortunately there is no sequential way (where everything compiles after each step) to
                            set up a Coaty container until you added all of the required components beforehand.
                            Do not worry if you are getting errors, if you have followed all steps in this tutorial
                            correctly, your project will compile successfully.
                        
Prerequisite
Start a Broker
CoatySwift needs an MQTT broker in order to run. We recommend checking out one of the following brokers:
- Coaty broker (development broker provided by Coaty JS)
- Mosquitto
- HiveMQ
- VerneMQ
Step 1
                            
                            Decide on a Message Broker and start it.
                            
Its host IP address and port are important for Step 3a.
                        
Section 1
Create a New Project and
            
Integrate CoatySwift
        
                    First, create a new project CoatySwiftExample and integrate CoatySwift.
                    CoatySwift is available as a CocoaPod.
                
Step 1
                            
                            Integrate CoatySwift in your Podfile and install it via
                            pod install.
                        
Podfile
target 'CoatySwiftExample' do
    use_frameworks!
    # Pods for CoatySwiftExample
    pod 'CoatySwift', '~> 2.0'
end            Section 2
Create a new Coaty Object Type
If you want to use custom objects with your Coaty application, you will have to define each of them. We will now create such an example object type and integrate it accordingly.
Step 1
                            
                            Add custom objects by creating classes that extend from CoatyObject
                            or one of the other builtin Coaty core types.
                            
                            Ensure that the class for your custom Coaty object type is registered with CoatySwift.
                            This is necessary to properly decode custom objects received over the wire.
                            
                            Also make sure that the class implements the Codable
                            interface, implementing the methods required init(from decoder: Decoder)
                            and override func encode(to encoder: Encoder).                            
                        
ExampleObject.swift
import Foundation
import CoatySwift
final class ExampleObject: CoatyObject {
    // MARK: - Class registration.
    
    override class var objectType: String {
        return register(objectType: "hello.coaty.ExampleObject", with: self)
    }
    // MARK: - Properties.
    
    let myValue: String
    // MARK: - Initializers.
    
    init(myValue: String) {
        self.myValue = myValue
        super.init(coreType: .CoatyObject,
                   objectType: ExampleObject.objectType,
                   objectId: .init(),
                   name: "ExampleObject Name :)")
    }
    
    // MARK: Codable methods.
    
    enum CodingKeys: String, CodingKey {
        case myValue
    }
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.myValue = try container.decode(String.self, forKey: .myValue)
        try super.init(from: decoder)
    }
    
    override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder)
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(self.myValue, forKey: .myValue)
    }
    
}
    Section 3
Setting up the Structure
Step 1
                            
                            Create a global variable container.
                            
                            This will hold a reference to our Coaty container for the lifetime of the app.
                            It is needed because otherwise all of our references go out of scope and
                            communication is terminated.
                        
AppDelegate.swift
import UIKit
import CoatySwift
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?
    
    /// Save a reference of your container in the app delegate to
    /// make sure it stays alive during the entire lifetime of the app.
    var container: Container?
    ...
}
                        Step 2a
                            
                            Create a new function in your AppDelegate.swift called
                            launchContainer(). Make sure to call this method in your
                            application(_ application:, didFinishLaunchingWithOptions:)
                            method. Also make sure to call the container.shutdown() method
                            in the applicationWillTerminate(_ application:) method to
                            gracefully release all container components when the app terminates.
                            
                            This will be our main method for setting up the Coaty
                            application.
                        
Step 2b
                            
                            Register controllers and custom object types in the 
                            launchContainer() method.
                            
                            Here, you define which Coaty controllers and object types you want to use in your application.
                            Note that the controller keys do not have to have the exact name of their
                            controller class. Feel free to give them any unique names you want. The mapping is the
                            important thing, so which name maps to what controller class.
                        
AppDelegate.swift
// MARK: - Coaty Container setup methods.
/// This method sets up the Coaty container necessary to run our application.
private func launchContainer() {
    
    // Register controllers and custom object types.
    let components = Components(controllers: [
        "ExampleControllerPublish": ExampleControllerPublish.self, 
        "ExampleControllerObserve": ExampleControllerObserve.self
    ],
                                objectTypes: [
        ExampleObject.self
    ])
    ...
}
                                    Step 3a
                            
                            Create a new method called createExampleConfiguration().
                            
                            Note the mqttClientOptions variable in particular. These define your broker's
                            host address and port.
                        
AppDelegate.swift
/// This method creates an exemplary Coaty configuration. You can use it as a basis for your
/// application.
private func createExampleConfiguration() -> Configuration? {
    return try? .build { config in
        
        // This part defines optional common options shared by all container components.
        config.common = CommonOptions()
        
        // Adjusts the logging level of CoatySwift messages, which is especially
        // helpful if you want to test or debug applications (default is .error).
        config.common?.logLevel = .info
        // Configure an expressive `name` of the container's identity here.
        config.common?.agentIdentity = ["name": "Example Agent"]
        
        // You can also add extra information to your configuration in the form of a
        // [String: String] dictionary.
        config.common?.extra = ["ContainerVersion": "0.0.1"]
        
        // Define communication-related options, such as the host address of your broker
        // (default is "localhost") and the port it exposes (default is 1883). Define a
        // unqiue communication namespace for your application and make sure to immediately
        // connect with the broker, indicated by `shouldAutoStart: true`.
        let mqttClientOptions = MQTTClientOptions(host: brokerHost,
                                                  port: UInt16(brokerPort))
        
        config.communication = CommunicationOptions(namespace: "com.example",
                                                    mqttClientOptions: mqttClientOptions,
                                                    shouldAutoStart: true)
    }
}
                    Step 3b
                            
                            Go back to your launchContainer() method and load the configuration from
                            the
                            createExampleConfiguration() method.
                        
AppDelegate.swift
// MARK: - Coaty Container setup methods.
/// This method sets up the Coaty container necessary to run our application.
private func launchContainer() {
    
    ...
    // Create a configuration.
    guard let configuration = createExampleConfiguration() else {
        print("Invalid configuration! Please check your options.")
        return
    }
    ...
}
                        Step 4
                            
                            Resolve everything and assign the variable container with the return
                            value of
                            container.resolve(...), while passing in
                            our previously defined components and configuration.
                        
AppDelegate.swift
// MARK: - Coaty Container setup methods.
/// This method sets up the Coaty container necessary to run our application.
private func launchContainer() {
    
    ...
    // Resolve everything!
    container = Container.resolve(components: components,
                                  configuration: configuration)
}
                                    Section 4
Create a new Controller
                    To be able to add your own communication business logic for a CoatySwift application you
                    need to define one or
                    multiple controllers. All controllers are created and managed by CoatySwift through lifecycle
                    methods.
                    
                    For the example, we will create two controllers named ExampleControllerPublish
                    and ExampleControllerObserve. ExampleControllerPublish
                    will periodically publish an Advertise event for an ExampleObject that will
                    be received and printed by ExampleControllerObserve.
                    
                
                        
                            Step 4a
                            
                            To create ExampleControllerPublish, extend the Controller
                            class.
                            In the onCommunicationManagerStarting() method, we set up a timer that
                            periodically executes the advertiseExampleObject() method below.
                            
                            The advertiseExampleObject() method implements a basic communication pattern for
                            Coaty applications. We create an ExampleObject, add it to a new
                            AdvertiseEvent and publish it using the publishAdvertise() 
                            method of the CommunicationManager.
                         
ExampleControllerPublish.swift
                    
import Foundation
import CoatySwift
import RxSwift
class ExampleControllerPublish: Controller {
    private var timer: Timer?
    
    override func onCommunicationManagerStarting() {
        super.onCommunicationManagerStarting()
        
        // Start RxSwift timer to publish an AdvertiseEvent every 5 seconds.
        _ = Observable
            .timer(RxTimeInterval.seconds(0),
                   period: RxTimeInterval.seconds(5),
                   scheduler: MainScheduler.instance)
            .subscribe(onNext: { (i) in
                self.advertiseExampleObject(i + 1)
            })
            .disposed(by: self.disposeBag)
    }
    
    func advertiseExampleObject(_ counter: Int) {
        // Create the object.
        let object = ExampleObject(myValue: "Hello Coaty! (\(counter))")
        
        // Create the event.
        let event = try! AdvertiseEvent.with(object: object)
        
        // Publish the event by the communication manager.
        self.communicationManager.publishAdvertise(event)
        
        print("[ExampleControllerPublish] published Advertise event:\t\(object.myValue)")
    }
}                                          
                             
                        
                            Step 4b
                            
                            We add a second controller named ExampleControllerObserve
                            and observe Advertise events for objects of a certain object type. Again,
                            we use the CommunicationManager and call the
                            observeAdvertise(withObjectType)
                            method. The method returns an Observable.
                            
                            We subscribe to onNext events that are emitted by the
                            observable and extract the ExampleObject from the event data.
                            Since we observe only CoatyObjects of the specified custom object type,
                            we can force cast the received CoatyObject to an ExampleObject
                            and print a message with the myValue attribute.
                         
ExampleControllerObserve.swift
                    
import Foundation
import CoatySwift
import RxSwift
class ExampleControllerObserve: Controller {
    override func onCommunicationManagerStarting() {
        super.onCommunicationManagerStarting()
        self.observeAdvertiseExampleObjects()
    }
    
    private func observeAdvertiseExampleObjects() {
        try! self.communicationManager
            .observeAdvertise(withObjectType: ExampleObject.objectType)
            .subscribe(onNext: { (event) in
                let object = event.data.object as! ExampleObject
                
                print("[ExampleControllerObserve] received Advertise event:\t\(object.myValue)")
            })
            .disposed(by: self.disposeBag)
    }
}