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.


Prerequisite

Start a Broker

CoatySwift needs an MQTT broker in order to run. We recommend checking out one of the following brokers:

Section 1

Create a New Project and
Integrate CoatySwift

First, create a new project CoatySwiftExample and integrate CoatySwift. CoatySwift is available as a CocoaPod.

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.

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

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?
    ...
}

                        
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
    ])
    ...
}
                                    
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)
    }
}
                    
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
    }

    ...
}
                        
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.

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)")
    }
}                                          
                            
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)
    }
}