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 Example folder of the CoatySwift repo. Just clone the repo, run pod install on the Example folder and open the new xcworkspace 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 HelloCoaty and integrate CoatySwift. CoatySwift is available as a CocoaPod.

Podfile

target 'HelloCoaty' do
    use_frameworks!

    # Pods for HelloCoaty
    pod 'CoatySwift', '~> 1.0'

end            
Section 2

Create a new CoatySwift ObjectFamily

If you want to use custom objects with your Coaty application, you will have to define each of them, and integrate them in an ObjectFamily. We will now create such an example object and integrate it accordingly.

ExampleObject.swift

import Foundation
import CoatySwift

final class ExampleObject: CoatyObject {
    
    let myValue: String
    
    init(myValue: String) {
        self.myValue = myValue
        super.init(coreType: .CoatyObject,
                    objectType: ExampleObjectFamily.exampleObject.rawValue,
                    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)
    }
    
}
    
ExampleObjectFamily.swift

import Foundation
import CoatySwift

enum ExampleObjectFamily: String, ObjectFamily {
    case exampleObject = "io.coaty.hello-coaty.example-object"
    
    func getType() -> AnyObject.Type {
        switch self {
        case .exampleObject:
            return ExampleObject.self
        }
    }
}
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 life-time of the app.
    var container: Container<ExampleObjectFamily>?
    ...
}

                        
AppDelegate.swift

// MARK: - Coaty Container setup methods.

/// This method sets up the Coaty container necessary to run our application.
private func launchContainer() {
    
    // Instantiate controllers.
    let components = Components(controllers: [
        "ExampleControllerPublish": ExampleControllerPublish<ExampleObjectFamily>.self, 
        "ExampleControllerObserve": ExampleControllerPublish<ExampleObjectFamily>.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 when you want to debug applications.
        config.common?.logLevel = .info
        
        // 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 the communication-related options, such as the IP address of your broker and
        // the port it exposes, and your own MQTT client Id. Also, make sure
        // to immediately connect with the broker, indicated by `shouldAutoStart: true`.
        let mqttClientOptions = MQTTClientOptions(host: brokerIp,
                                                  port: UInt16(brokerPort))
        
        config.communication = CommunicationOptions(mqttClientOptions: mqttClientOptions,                                              
                                                    identity: ["name": "ExampleClient"],
                                                    shouldAutoStart: true)
        
        // The communicationManager will advertise its identity upon connection to the
        // MQTT broker.
        config.communication?.shouldAdvertiseIdentity = 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,
                                  objectFamily: ExampleObjectFamily.self)

}
                                    
Section 4

Create a new Controller

To be able to add your own business logic for a CoatySwift application you need to define one or multiple controllers. All controllers are created and managed by CoatySwift through life-cycle methods.

For the example, we will create two controllers: the ExampleControllerPublish and the ExampleControllerObserve. The ExampleControllerPublish will periodically publish an advertise event for an ExampleObject that will be received and printed by the ExampleControllerObserve.

ExampleControllerPublish.swift

import Foundation
import CoatySwift

class ExampleControllerPublish<Family: ObjectFamily>: Controller<Family> {

    private var timer: Timer?
    
    override func onCommunicationManagerStarting() {
        // Start RxSwift timer to publish an AdvertiseEvent every 5 seonds.
        _ = Observable
            .timer(RxTimeInterval.seconds(0),
                   period: RxTimeInterval.seconds(5),
                   scheduler: MainScheduler.instance)
            .subscribe(onNext: { (_) in
                self.publishAdvertise()
            })
    }
    
    func publishAdvertise() {
        // Create the object.
        let object = ExampleObject(myValue: "Hello Coaty!")
        
        // Create an event by using the event factory.
        let event = self.eventFactory.AdvertiseEvent.with(object: object)
        
        // Publish the event by using the communication manager.
        try? self.communicationManager.publishAdvertise(event)
        
        print("[ExampleControllerPublish] published advertise event:\t\(object.myValue)")
    }
}                                          
                            
ExampleControllerObserve.swift

import Foundation
import CoatySwift

class ExampleControllerObserve<Family: ObjectFamily>: Controller<Family> {

    override func onCommunicationManagerStarting() {
        self.observeAdvertiseExampleObjects()
    }
    
    private func observeAdvertiseExampleObjects() {
        _ = try? self.communicationManager
            .observeAdvertise(withObjectType: "io.coaty.hello-coaty.example-object")
            .subscribe(onNext: { (event) in
                let object = event.data.object as! ExampleObject
                
                print("[ExampleControllerObserve] received advertise event:\t\(object.myValue)")
            })
    }
}