Skip to main content
Documentation for version v73

SDK Configuration

Prerequisites

  • Push provider SDKs are integrated into the app project (see the push provider integration guides).
  • The Altcraft package is added via Swift Package Manager.

App preparation

Target settings

Configure the following app target settings:

  • General:

    • Ensure the Altcraft library is added under Frameworks, Libraries, and Embedded Content of the app target.
  • Signing & Capabilities:

    • PushNotifications;
    • AppGroups — specify a group identifier (adding an App Group identifier is required for data exchange with the Notification Service Extension);
    • Background Modes — enable Background fetch, Background processing.
  • Info:

    • Add the key Permitted background task scheduler identifiers with the value "lib.Altcraft.bgTask.systemControl". This registers the bgTask that retries failed server requests in background mode. Optional — when using Firebase Cloud Messaging, add the key FirebaseAppDelegateProxyEnabled (Boolean) with the value NO. This disables the default method swizzling of AppDelegate performed by Firebase Messaging.

AppDelegate settings

In AppDelegate.application(_:didFinishLaunchingWithOptions:), do the following:

  • pass the AppGroup identifier to the SDK
  • register the SDK BGTask (background tasks)
  • (optional) initialize UNUserNotificationCenter features on the SDK side

Setting the AppGroup identifier

Adding an AppGroup identifier is required to exchange data with the Notification Service Extension and for correct SDK operation. Use setAppGroup():

// Configure the App Group and initialize Core Data for the shared container
AltcraftSDK.shared.setAppGroup(groupName: String)

Registering the BGTask

Registering SDK background tasks enables retrying failed requests in the background. Use registerBackgroundTask():

// Register the SDK background task and schedule periodic execution (about every 3 hours)
AltcraftSDK.shared.backgroundTasks.registerBackgroundTask()

Initializing UNUserNotificationCenter features (optional)

The SDK includes the NotificationManager class, which:

  • configures UNUserNotificationCenter and requests notification permissions (alert/sound/badge);
  • calls UIApplication.registerForRemoteNotifications() and ensures correct APNs registration;
  • displays notifications in foreground;
  • handles notification taps and action buttons — navigates to a specified URL or opens the app;
  • registers the push open event ("open").

To use the SDK NotificationManager, call this function in AppDelegate.application(_:didFinishLaunchingWithOptions:):

func registerForPushNotifications(for application: UIApplication, completion: ((_ granted: Bool, _ error: Error?) -> Void)? = nil): Void

This function sets the delegate, requests the user’s permission, and registers the app for push notifications on the SDK side.

Example of a correct AppDelegate.application setup
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let appGroup = "group.your.name.example"

// Set App Group ID
AltcraftSDK.shared.setAppGroup(groupName: appGroup)

// Register the SDK background task
AltcraftSDK.shared.backgroundTasks.registerBackgroundTask()

// Initialize SDK UNUserNotificationCenter features
AltcraftSDK.shared.notificationManager.registerForPushNotifications(for: application)

// Other functions

return true
}
// Other AppDelegate functions
}

Using this class is optional; you can replace it with your own implementation. To do so:

  • set the notification center delegate: UNUserNotificationCenter.current().delegate = self;
  • request notification permissions and call UIApplication.shared.registerForRemoteNotifications() strictly on the main thread;
  • implement userNotificationCenter(_:willPresent:withCompletionHandler:) to display notifications in foreground (banner/alert/sound/badge);
  • call AltcraftSDK.shared.pushEventFunctions.openEvent(from:) in userNotificationCenter(_:didReceive:withCompletionHandler:) when handling a notification tap (registering the "open" event);
  • implement custom logic for handling notification taps (URL/Deep Link/buttons), and ensure a fallback scenario (default navigation if no valid link is present).

JWT protocol setup (optional)

JWTInterface is the protocol for requesting a JWT token. It provides a current JWT token from the app upon SDK request. Implement this protocol if API requests use JWT authentication. JWT confirms that user identifiers are authenticated by the app.

JWT authentication is required if the matching type differs from push data from the subscription (for example, when the user identifier is an email or phone number).

SDK protocol:

public protocol JWTInterface {
func getToken() -> String?
}
App-side implementation
import Altcraft

class JWTProvider: JWTInterface {
func getToken() -> String? {
// your code that returns a JWT
}
}
Registering the provider in application(_:didFinishLaunchingWithOptions:)

In AppDelegate, specify:


class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate, UNUserNotificationCenterDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Other code

AltcraftSDK.setJWTProvider(provider: JWTProvider())
}
// Other functions
}
Note

getJWT() is a synchronous function. The SDK execution thread will pause until JWT is obtained. It’s recommended that getJWT() returns immediately from cache (in-memory, SharedPreferences or EncryptedSharedPreferences) to speed up requests. Ideally, prepare a fresh JWT as early as possible (at app start) and store it in cache so the token is available without delays. Returning nil is acceptable if no value is available.

Provider push token request and deletion protocols

These token request/deletion protocol implementations ensure the current token is used and allow dynamic provider switching at the client’s request. Implement only the protocols for the push providers used in your project.

Register providers in application(_:didFinishLaunchingWithOptions:) (AppDelegate). This registration point guarantees early, single, and deterministic registration at process start, including in background.


Apple Push Notification service

APNSInterface — the protocol for requesting an APNs push token. This protocol has no token deletion function, unlike other push provider protocols.

SDK protocol:

public protocol APNSInterface {

func getToken(completion: @escaping (String?) -> Void)
}
Recommended app-side implementation
import Altcraft

class APNSProvider: APNSInterface {
func getToken(completion: @escaping (String?) -> Void) {
// your code that returns the APNs token
}
}
Registering the provider in application(_:didFinishLaunchingWithOptions:)
class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate, UNUserNotificationCenterDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// other code

AltcraftSDK.pushTokenFunction.setAPNSTokenProvider(APNSProvider())
}
// other functions
}

Firebase Cloud Messaging

FCMInterface — the protocol for requesting and deleting a Firebase Cloud Messaging push token.

SDK protocol:

public protocol FCMInterface {
func getToken(completion: @escaping (String?) -> Void)

func deleteToken(completion: @escaping (Bool) -> Void)
}
Note
Recommended app-side implementation
import FirebaseMessaging
import Altcraft

class FCMProvider: FCMInterface {

/// Retrieves the current FCM token
func getToken(completion: @escaping (String?) -> Void) {

/// APNs token (as Data), retrieved from UserDefaults
let apnsToken = getAPNsTokenDataFromUserDefaults()

/// Set the APNs token for FCM before requesting the FCM token
Messaging.messaging().apnsToken = apnsToken

/// Request the FCM token
Messaging.messaging().token { token, error in
if error != nil {
completion(nil)
} else {
completion(token)
}
}
}

/// Deletes the current FCM token
func deleteToken(completion: @escaping (Bool) -> Void) {
Messaging.messaging().deleteToken { error in
if error != nil {
completion(false)
} else {
completion(true)
}
}
}
}
Registering the provider in application(_:didFinishLaunchingWithOptions:)
class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate, UNUserNotificationCenterDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Other code

AltcraftSDK.pushTokenFunction.setFCMTokenProvider(FCMProvider())
}
// Other functions
}

Huawei Mobile Services

HMSInterface — the protocol for requesting and deleting a Huawei Mobile Services push token.

SDK protocol:

public protocol HMSInterface {

func getToken(completion: @escaping (String?) -> Void)

func deleteToken(completion: @escaping (Bool) -> Void)
}
Recommended app-side implementation
class HMSProvider: HMSInterface {

func getToken(completion: @escaping (String?) -> Void) {

// variable containing the APNs token (as a String), which is later
// passed to HmsInstanceId.getInstance().getToken(apnsToken)
guard let apnsToken = getAPNsTokenFromUserDefault() else {
completion(nil)
return
}

let token = HmsInstanceId.getInstance().getToken(apnsToken)
completion(token)
}


func deleteToken(completion: @escaping (Bool) -> Void) {
HmsInstanceId.getInstance().deleteToken()
completion(true)
}
}
Registering the provider in application(_:didFinishLaunchingWithOptions:)
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Other code

AltcraftSDK.pushTokenFunction.setHMSTokenProvider(HMSProvider())
}
// Other functions
}

Preparing the Notification Service Extension

Adding an NSE is required for Rich Push support and guaranteed delivery event registration.

Create a Notification Service Extension:

- **File** — **New** — **Target** — **Notification Service Extension**;
- Choose a Product Name for the extension target;
- Activate it.

In the Notification Service Extension:

  • General:

    • Set Minimum Deployments — the Xcode build setting that defines the minimum OS version supported by the Notification Service Extension;
    • Add the Altcraft library under Frameworks, Libraries and Embedded Content.
  • Signing & Capabilities:

    • AppGroups — specify the AppGroup identifier.

Then configure the SDK in UNNotificationServiceExtension:

  • Import the Altcraft library;

  • Create an instance of AltcraftPushReceiver();

  • In didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void):

    • Pass the appGroup identifier to AltcraftSDK.shared.setAppGroup(groupName: appGroupsName). This identifier must match the one passed to the SDK in the app target;
    • Set the JWT provider (if JWT is used for request authentication): AltcraftSDK.shared.setJWTProvider(provider: jwtProvider);
    • Validate the notification source using isAltcraftPush(request) of AltcraftPushReceiver(): service.isAltcraftPush(request);
    • After validation, pass the UNNotificationRequest and contentHandler to AltcraftPushReceiver().didReceive: self.service.didReceive(request, withContentHandler: contentHandler) if the notification source is Altcraft.

The example below can be used as a ready-to-use UNNotificationServiceExtension class. Replace all autogenerated code with this implementation if no extra logic is required. If you already use UNNotificationServiceExtension, integrate the Altcraft functions into your implementation.

Example implementation of UNNotificationServiceExtension
import Altcraft
import UserNotifications

class NotificationService: UNNotificationServiceExtension {
var service = AltcraftPushReceiver()

/// - Important! Set your appGroups name
var appGroupsName = "your.app.group"
let jwtProvider = JWTProvider()

override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {

AltcraftSDK.shared.setAppGroup(groupName: appGroupsName)
AltcraftSDK.shared.setJWTProvider(provider: jwtProvider)

if service.isAltcraftPush(request) {
self.service.didReceive(request, withContentHandler: contentHandler)
} else {
contentHandler(request.content)
}
}
override func serviceExtensionTimeWillExpire() {service.serviceExtensionTimeWillExpire()}
}
Note

The app target and the Notification Service Extension target run in separate processes and do not share common objects. Therefore, to process notifications correctly, the SDK must also register the JWTProvider in the NSE — for example, inside didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void). This ensures the SDK has access to the authorization token when sending the push delivery event ("delivery_event").

SDK initialization

Initialization parameters

Use the AltcraftConfiguration class to pass configuration parameters:

public final class AltcraftConfiguration {
private let apiUrl: String
private let rToken: String?
private var appInfo: AppInfo?
private var providerPriorityList: [String]?
}

The configuration object is built after calling the build() function of AltcraftConfiguration.Builder(): AltcraftConfiguration.Builder().build().

Parameter descriptions:

apiUrl String

Required: Yes
Description: Altcraft API endpoint URL


rToken String?

Default: nil
Required: No
Description: Altcraft role token (identifies a resource, DB, or account). Used when the only matching type is the device’s push token issued by a provider.


appInfo AppInfo?

Default: nil
Required: No
Description: Basic app metadata for Firebase Analytics. To set this parameter, use the public SDK struct AppInfo:

public struct AppInfo: Codable {

/// Unique App ID (Firebase `app_id`)
public var appID: String

/// Unique app instance ID (Firebase `app_instance_id`)
public var appIID: String

/// App version (Firebase `app_version`)
public var appVer: String
}

providerPriorityList [String]?

Default: nil
Required: No
Description: Provider priority list. Used to automatically refresh the subscription’s push token if the higher-priority provider token is unavailable. Priority is defined by the index in the list: the element at index 0 has the highest priority.

Usage example:

providerPriorityList = [
Constants.ProviderName.apns,
Constants.ProviderName.firebase,
Constants.ProviderName.huawei
]

With this configuration:

  • The SDK first requests the APNs token; if APNs is unavailable — FCM; if FCM is unavailable — HMS;
  • Works provided the corresponding provider protocols are implemented in the app.

For convenient assignment, the SDK exposes public constants in enum Constants:

public enum Constants {
public enum ProviderName {
/// Provider name for Firebase
public static let firebase = "ios-firebase"

/// Provider name for APNs
public static let apns = "ios-apns"

/// Provider name for HMS
public static let huawei = "ios-huawei"
}
}

If not specified, the default provider priority is:

apns → firebase → huawei

The parameter value may contain a single element — in this case, only that provider will be used regardless of push token availability.

You can omit this parameter if:

  • only one provider is used in the project;
  • the default priority fits your needs.

Performing initialization

Note

Call AltcraftSDK.shared.initialization() when needed, but only after setting the AppGroup identifier and registering the providers that implement the SDK protocols. Make requests after the configuration is set.

Use public func initialization(configuration: AltcraftConfiguration?, completion: ((Bool) -> Void)? = nil) to initialize the SDK:

// SDK initialization and configuration
AltcraftSDK.shared.initialization(configuration: AltcraftConfiguration?, completion: ((Bool) -> Void)? = nil)

The initialization(configuration: AltcraftConfiguration?, completion: ((Bool) -> Void)? = nil) function expects an AltcraftConfiguration object with required values set and built:

 let config = AltcraftConfiguration.Builder()
.setApiUrl("your apiUrl")
.setRToken("your rToken")
.setAppInfo(AppInfo(appID: "your appID", appIID: "your appIID", appVer: "your appVer"))
.setProviderPriorityList([Constants.ProviderName.firebase])
.build()
Example of the correct initialization order in AppDelegate.application(_:didFinishLaunchingWithOptions:) (after setting the AppGroup ID and registering providers)
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Firebase configuration can be placed here if Firebase is used in the project

let appGroup = "group.altcraft.apns.example"

AltcraftSDK.shared.setAppGroup(groupName: appGroup)
AltcraftSDK.shared.backgroundTasks.registerBackgroundTask()
AltcraftSDK.shared.setJWTProvider(provider: JWTProvider())
AltcraftSDK.shared.pushTokenFunction.setAPNSTokenProvider(APNSProvider())
AltcraftSDK.shared.pushTokenFunction.setFCMTokenProvider(FCMProvider())
AltcraftSDK.shared.pushTokenFunction.setHMSTokenProvider(HMSProvider())
AltcraftSDK.shared.notificationManager.registerForPushNotifications(for: application)

let config = AltcraftConfiguration.Builder()
.setApiUrl("your apiUrl")
.setRToken("your rToken")
.setAppInfo(AppInfo(appID: "your appID", appIID: "your appIID", appVer: "your appVer"))
.setProviderPriorityList([Constants.ProviderName.firebase])
.build()

AltcraftSDK.shared.initialization(configuration: config) { success in
if success {
// actions after successful initialization
}
}

// Other functions

return true
}
// Other AppDelegate functions
}