Webcom Logo

Webcom iOS SDK

version 3.0.2 iOS 13.0 Swift 5.11 Xcode 16.2

availability: SwiftPM | CocoaPods | XCFrameworks extensions: Combine test coverage

Webcom is the Orange Backend-as-a-Service / Serverless solution.

Webcom provides integrated functions of database and messages exchange platforms, authentication aggregation, data exposure and access control. This platform drastically reduce time and cost to implement and deploy mobile, web or IoT applications in production.

Table of Contents

  1. Create a Webcom Application
  2. Setup your Swift Project
    1. Integrate the Webcom Framework
      1. Using Swift Package Manager
      2. Using CocoaPods
      3. Using .xcframework Files
    2. Add Webcom Configuration in Info.plist
  3. Start Coding
    1. Import Webcom Module
    2. Authenticate
    3. Get References to Database Nodes
    4. Handle Data
      1. Write Data
      2. Read Data
    5. Special Subscriptions
    6. Push Notifications
    7. Extensions
      1. Using with Combine
    8. Logs
  4. More Information

1. Create a Webcom Application

  1. Sign up/in on Webcom console.
  2. Add an application with the identifier of your choice, e.g. EmployeeDirectory.

2. Setup your Swift Project

2.1. Integrate the Webcom Framework

2.1.1. Using Swift Package Manager

Swift Package Manager integration uses source code which will be compiled on your computer.

In your Package.swift file, add the following dependency:

let package = Package(
    ...
    dependencies: [
        ...
        .package(name: "Webcom", url: "ssh://git@gitlab.tech.orange/webcom/sdk/ios.git", .upToNextMajor(from: "3.0.2")),
    ],
    ...
    targets: [
        ...
        .target(
            ...
            dependencies: [
                ...
                .product(name: "WebcomCore", package: "Webcom"),
            ],
            ...
        ),
        ...
    ]
)

2.1.2. Using CocoaPods

CocoaPods integration uses source code which will be compiled on your computer.

In your Podfile:

source "https://cdn.cocoapods.org/"
source "ssh://git@gitlab.tech.orange/webcom/sdk/cocoapods.git"

platform :ios, "13.0"
use_frameworks!

target "MyApplication" do
    pod "WebcomCore"
end

2.1.3. Using .xcframework Files

Already compiled .xcframework binary files can also be added manually in the project:

  1. Download the archive containing required .xcframework files: this .zip file (SHA-1).
  2. Unzip the downloaded archive.
  3. Copy the .xcframework files you get in your Xcode project.
  4. Add these .xcframework dependencies to your target.

2.2. Add Webcom Configuration in Info.plist

In your Info.plist file, add a Webcom property as follows:

Key Type Value Description
▼ Webcom Dictionary root key for Webcom parameters
   identifier String EmployeeDirectory identifier of the Webcom application

More information about configuring Webcom applications is available on this page.

3. Start Coding

Webcom iOS SDK is written using Swift 5.11 and requires at least Xcode 16.2.

3.1. Import Webcom Module

import WebcomCore

3.2. Authenticate

The Authentication service makes it possible to reliably identify users of applications, in order to finely control their access to data.

Instantiate the Authentication service of the application:

let authenticationService = Webcom.defaultApplication.authenticationService

Authenticate using a guest account:

let guestMethod = AuthenticationMethodGuest()
authenticationService.authenticate(with: guestMethod)

Authenticate using an OAuth 2.0 provider:

let oAuth2Method = AuthenticationMethodOAuth2(provider: .orangeFrance, presentationContextProvider: self)
authenticationService.authenticate(with: oAuth2Method)

Authenticate using Orange Mobile Network as a second factor:

let mobileNetworkMethod = AuthenticationMethodOrangeMobileNetwork(mobileCountryCode: "ZZ")
authenticationService.authenticate(with: mobileNetworkMethod, context: .current)

You may be interested by the following types:

3.3. Get References to Database Nodes

The Datasync service consists of a real-time database with a tree-like data model (equivalent to a JSON object).

Create a Datasync manager in your view controller:

let datasyncManager = Webcom.defaultApplication.datasyncService.createManager()

Get a reference to a node in the database tree:

let node = datasyncManager.node(for: "/path/to/node") // leading "/" is optional

or:

let node = datasyncManager / "path/to/node" // leading "/" may be added

Tip: Since both syntaxes returns an Optional node, you may want to use the guard let node = ... else { ... } construction in your code.

Get a reference to a descendant node:

let descendantNode = node?.relativeNode(for: "/some/descendant")

or:

let descendantNode = node / "some/descendant"

Get a reference to the parent node:

let parentNode = node?.parent

You may be interested by the following types:

3.4. Handle Data

Important: Datasync read and write operations are asynchronous.

In the following, we assume that the following type is defined:

struct Employee: Codable {
    enum Sex: String, Codable {
        case female
        case male
    }
    let name: String
    let yearOfBirth: Int
    let sex: Sex
}

3.4.1. Write Data

Set the value of a node:

let god = Employee(name: "Steve", yearOfBirth: 1955, sex: .male)
let godNode = datasyncManager.node(for: "/employees/god")
godNode?.set(god)

or:

let god = Employee(name: "Steve", yearOfBirth: 1955, sex: .male)
let godNode = datasyncManager / "employees/god"
godNode? <- god

Push a new child to a node:

let ceo = Employee(name: "Tim", yearOfBirth: 1960, sex: .male)
let employeesNode = datasyncManager.node(for: "/employees")
employeesNode?.push(ceo)

or:

let ceo = Employee(name: "Tim", yearOfBirth: 1960, sex: .male)
let employeesNode = datasyncManager / "employees"
employeesNode? += ceo

3.4.2. Read Data

Get the value of a node once:

let godNode = datasyncManager / "employees/god"
godNode?.value { [weak self] result in
    guard let god: Employee = result.value?.decoded() else {
        return
    }
    self?.employeeNameLabel.text = god.name
}

You can simplify this code a bit using the .decodedValue(...) helper:

let godNode = datasyncManager / "employees/god"
godNode?.decodedValue { [weak self] (result: WebcomResult<Employee>) in
    self?.employeeNameLabel.text = result.value?.name
}

Subscribe to data to receive events:

let employeesNode = datasyncManager / "employees"
employeesNode?.subscribe(to: .childAddition) { [weak self] event in
    guard let employee: Employee = event.value.decoded() else {
        return
    }
    self?.employeeNameLabel.text = employee.name
}

You can simplify this code a bit using the .decodedValueSubscribe(...) helper:

let employeesNode = datasyncManager / "employees"
employeesNode?.decodedValueSubscribe(to: .childAddition) { [weak self] (employee: Employee) in
    self?.employeeNameLabel.text = employee.name
}

You may be interested by the following types:

3.5. Special Subscriptions

Subscribe to Datasync state changes:

datasyncManager.subscribeToStateChange { [weak self] state in
    self?.onlineView.isHidden = !state.isAuthenticated || !state.isConnected
    self?.offlineView.isHidden = !state.isAuthenticated || state.isConnected
    self?.unauthenticatedView.isHidden = state.isAuthenticated
}

Subscribe to authenticated user changes:

datasyncManager.subscribeToCurrentUIDChange { [weak self] uid in
    let state = Webcom.defaultApplication.datasyncService.state
    self?.userNameLabel.text = state.authenticationDetails?.displayName
}

You may be interested by the following types:

3.6. Push Notifications

The typical use case is:

  • The main application subscribes to push notifications when the value or the children of some node change.
  • A UNNotificationServiceExtension is triggered when push notifications are sent to the application.

For this to work, the SDK needs to share information (WebcomConfiguration values and secret keys when encryption is used) between the main application and the extension. Thereby, they must have a common App Group, configured in their respective Signing & Capabilities configuration tabs. The WebcomConfiguration instance passed to the WebcomApplication must have its authStorage field set to share or key (if key field is used, you must also provide a value in authStorageKey field). Those fields allows sharing of authentication state between app and extension.

Configure push notifications in the AppDelegate:

import FirebaseCore
import FirebaseMessaging
import UIKit
import WebcomCore

// make the AppDelegate conforms to UNUserNotificationCenterDelegate and MessagingDelegate
final class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate {
    // complete the application(_:didFinishLaunchingWithOptions:) method:
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        Webcom.configure(groupName: "group.XXX.YYY.ZZZ") // same value as in didReceive(_:withContentHandler:) method of UNNotificationServiceExtension
        FirebaseApp.configure()
        Messaging.messaging().delegate = self
        UNUserNotificationCenter.current().delegate = self
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { _, _ in }
        application.registerForRemoteNotifications()
        return true
    }

    // add the following method in the AppDelegate:
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken firebaseMessagingToken: String?) {
        Webcom.deviceToken = firebaseMessagingToken
    }
}

Subscribe to encrypted push notifications in a view controller:

let employeesNode = datasyncManager / "employees"
var descriptor = DataSubscriptionDescriptor(eventType: .child(addition: true, change: false, removal: false))
descriptor.encryptionAlgorithm = .A128CBCHS256
employeesNode?.dataNode.subscribeThroughNotifications(descriptor: descriptor)

Process received push notifications in a UNNotificationServiceExtension:

import UserNotifications
import WebcomCore

final class NotificationService: UNNotificationServiceExtension {
    private var manager: NotificationServiceManager?
    private var notification: WebcomNotification?

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        Webcom.configure(groupName: "group.XXX.YYY.ZZZ") // same value as in application(_:didFinishLaunchingWithOptions:) method of UIApplicationDelegate
        guard let manager = NotificationServiceManager(request: request, contentHandler: contentHandler), let notification = WebcomNotification(request: request) else {
            contentHandler(request.content)
            return
        }
        self.manager = manager
        self.notification = notification
        manager.mutableContent.title = "Human Resources"
        if let employee: Employee = notification.childrenUpdateFullDetails?.firstAdditionOrChange?.value.decoded() {
            manager.mutableContent.body = "\(employee.name) has just been hired."
        }
        manager.wait() // synchronously, before each asynchronous call
        DispatchQueue(label: "foo").async {
             // asynchronous code, may use `notification`
            manager.notify() // at the end of each asynchronous call
        }
        manager.complete(forces: false)
    }

    override func serviceExtensionTimeWillExpire() {
        manager?.complete(forces: true)
    }
}

You may be interested by the following types:

3.7. Extensions

Webcom iOS SDK is provided with extensions to facilitate its use with Combine framework.

3.7.1. Using with Combine

The classic API use a callback to process received events:

import WebcomCore

let datasyncManager = Webcom.defaultApplication.datasyncService.createManager()
datasyncManager.node(for: "employees")?.subscribe(to: .childAddition) { [weak self] event in
    guard let employee: Employee = event.value.decoded() else {
        return
    }
    self?.employeeNameLabel.text = employee.name
}

If you prefer, you can instead use publishers for the Combine framework:

import Combine
import WebcomCore

let datasyncManager = Webcom.defaultApplication.forCombine.datasyncService.createManager()
datasyncManager.node(for: "employees")?.eventPublisher(for: .childAddition).sink { _ in
    // process revocation
} receiveValue: { [weak self] event in
    guard let employee: Employee = event.value.decoded() else {
        return
    }
    self?.employeeNameLabel.text = employee.name
}.store(in: datasyncManager)

You can simplify this code a bit using the .decodedValuePublisher(...) helper:

import Combine
import WebcomCore

let datasyncManager = Webcom.defaultApplication.forCombine.datasyncService.createManager()
datasyncManager.node(for: "employees")?.decodedValuePublisher(for: .childAddition).sink { _ in
    // process revocation
} receiveValue: { [weak self] (employee: Employee) in
    self?.employeeNameLabel.text = employee.name
}.store(in: datasyncManager)

The WebcomCore module also allows the use of Combine.Future instead of completion callbacks:

import Combine
import WebcomCore

let datasyncManager = Webcom.defaultApplication.forCombine.datasyncService.createManager()
let newEmployee = Employee(name: "Katherine", yearOfBirth: 1_964, sex: .female)

datasyncManager.node(for: "employees")?.push(newEmployee).sink { completion in
    if case let .failure(error) = completion {
        print("error:", error)
    }
} receiveValue: { childNode in
    print("new employee id:", childNode.key)
}.store(in: datasyncManager)

Of course, you can mix both approaches in the same application, since they offer similar API. You can switch from the classic approach to publishers using WebcomCombinable.forCombine properties, and reciprocally from publishers to classic approach using WebcomWrapper.wrapped properties.

3.8. Logs

To help you debug your application, you can pass it the Webcom.LogLevel argument on launch (in the scheme properties) with a value equal to fault, error, warning, info, debug or verbose to get more or less logs.

By default, these logs are sent to the logging system with a log object that uses com.orange.webcom as subsystem and Webcom as category. You can see them in the Xcode console and in the Console application (don’t forget to include needed messages in the Action menu).

You can set the Webcom.log closure to customize logging and make it fit your needs.

4. More Information

  • Study the source code of the Webcom Notes example applications (available for UIKit and SwiftUI) in the SDK Repository
  • Contact support