SDK configuration
Prerequisites
- The push notification provider SDKs are integrated into the app project (see the push provider integration instructions).
- The Altcraft package is downloaded using Swift Package Manager or CocoaPods.
Preparing the app
Target settings
Configure the following parameters of the app target:
- General
When using Swift Package Manager, make sure the Altcraft library is added to the app target's Frameworks, Libraries, and Embedded Content.
- Signing & Capabilities
Add the following Capabilities:
- PushNotifications;
- AppGroups;
- Background Modes (Background fetch, Background processing).
- Info
Add the Permitted background task scheduler identifiers key with the value "lib.Altcraft.bgTask.systemControl". This is required to register a bgTask task that will retry failed requests to the server in background mode.
- Optional:
When using Firebase Cloud Messaging, add the FirebaseAppDelegateProxyEnabled (Boolean) key with the value NO. This disables the automatic method swizzling of AppDelegate methods that Firebase Messaging performs by default.
AppDelegate settings
Then, in AppDelegate.application(_:didFinishLaunchingWithOptions:), perform the following actions:
- pass the
AppGroupidentifier to the SDK - register
BGTask(the SDK's background tasks) - (optionally) initialize the
UNUserNotificationCenterfunctions on the SDK side
Setting the AppGroup identifier
Adding an AppGroup identifier is required to exchange information with the Notification Service Extension and is necessary for the SDK to work correctly. It is done with the setAppGroup() function:
AltcraftSDK.shared.setAppGroup(groupName: String)
Registering BGTask
Registering the SDK's background tasks is necessary to resend unsuccessful requests in the background. It is done with the registerBackgroundTask() function:
AltcraftSDK.shared.backgroundTasks.registerBackgroundTask()
Initializing the UNUserNotificationCenter functions (optional)
The SDK contains a NotificationManager class that performs the following tasks:
- configures
UNUserNotificationCenterand requests notification permission (alert/sound/badge); - calls
UIApplication.registerForRemoteNotifications()and ensures correct registration with APNs; - displays notifications in the foreground;
- handles taps on notifications and buttons — navigates to the specified URL or opens the app;
- registers the push open event ("open").
To use the SDK NotificationManager class, call the function in AppDelegate.application(_:didFinishLaunchingWithOptions:):
AltcraftSDK.shared.push.notificationManager.registerForPushNotifications(for: application, completion: ((_ granted: Bool, _ error: Error?) -> Void)? = nil)
This function sets the delegate, requests permission from the user, and registers the app to receive push notifications on the SDK side.
Example of correctly filling in AppDelegate.application
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let appGroup = "group.your.id"
//ID App Group Setup
AltcraftSDK.shared.setAppGroup(groupName: appGroup)
//Background SDK task registration
AltcraftSDK.shared.backgroundTasks.registerBackgroundTask()
//SDK UNUserNotificationCenter functions initialization
AltcraftSDK.shared.push.notificationManager.registerForPushNotifications(for: application)
//Other functions
return true
}
//Other functions AppDelegate
}
Using this class is optional; you can replace it with your own implementation. To do this:
- set the notification center delegate:
UNUserNotificationCenter.current().delegate = self; - request notification permission and call
UIApplication.shared.registerForRemoteNotifications()strictly on the main thread; - implement the
userNotificationCenter(_:willPresent:withCompletionHandler:)method to display notifications in the foreground (banner/alert/sound/badge); - call
AltcraftSDK.shared.push.event.openEvent(from:)inuserNotificationCenter(_:didReceive:withCompletionHandler:)when handling a tap on a notification (registering the "open" event); - implement your own notification click handling logic (URL/Deep Link/buttons), and do not forget to design a fallback scenario (default navigation when there is no valid link).
Configuring the JWT protocol (optional)
JWTInterface — a protocol for requesting a JWT token. It provides a current JWT token from the app upon the SDK's request. Implementing this protocol is required if JWT authentication of API requests is used. The JWT confirms that the user's identifiers are authenticated by the app.
Implementing JWT authentication is mandatory if the matching type is something other than the push data from the subscription (for example, the user identifier is an email or phone number).
The SDK protocol:
public protocol JWTInterface {
func getToken() -> String?
}
Implementation on the app side
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.shared.setJWTProvider(provider: JWTProvider())
}
// Other functions
}
getToken() is a synchronous function. The SDK's execution thread is suspended until the JWT is obtained. It is recommended that getToken() return a value immediately — from a cache (in-memory, UserDefaults, or Keychain) — this speeds up request execution. Ideally, prepare a current JWT as early as possible (at app startup) and store it in the cache, so that when the SDK accesses it, the token is available without delay. It is acceptable to return nil if the value is not available.
Protocols for requesting and deleting the provider push token
This implementation of the protocols for requesting and deleting the push token guarantees that a current token is used and enables dynamically switching providers at the client's request. Implement the protocols only for the push providers used in your project.
Register the providers in application(_:didFinishLaunchingWithOptions:) (AppDelegate). This registration point guarantees an early, one-time, deterministic registration at process startup, including in the background.
Apple Push Notifcation service
APNSInterface — a protocol for requesting the APNs push token. This protocol has no token deletion function, unlike the other push provider protocols.
The SDK protocol:
public protocol APNSInterface {
func getToken(completion: @escaping (String?) -> Void)
}
Recommended implementation on the app side
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.shared.push.token.setAPNSTokenProvider(APNSProvider())
}
// other functions
}
Firebase Cloud Messaging
FCMInterface — a protocol for requesting and deleting the Firebase Cloud Messaging push token.
The SDK protocol:
public protocol FCMInterface {
func getToken(completion: @escaping (String?) -> Void)
func deleteToken(completion: @escaping (Bool) -> Void)
}
Recommended implementation on the app side
import FirebaseMessaging
import Altcraft
class FCMProvider: FCMInterface {
/// Retrieves the current FCM token
func getToken(completion: @escaping (String?) -> Void) {
/// The 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
/// Function to 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.shared.push.token.setFCMTokenProvider(FCMProvider())
}
// Other functions
}
Huawei Mobile Services
HMSInterface — a protocol for requesting and deleting the Huawei Mobile Services push token.
The SDK protocol:
public protocol HMSInterface {
func getToken(completion: @escaping (String?) -> Void)
func deleteToken(completion: @escaping (Bool) -> Void)
}
Recommended implementation on the app side
class HMSProvider: HMSInterface {
func getToken(completion: @escaping (String?) -> Void) {
// variable containing the APNs token (as a string), which later
// is 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.shared.push.token.setHMSTokenProvider(HMSProvider())
}
// Other functions
}
Preparing the Notification Service Extension
Adding the NSE extension is required for Rich Push support and guaranteed registration of delivery events.
Create a Notification Service Extension app extension:
- Choose File — New — Target — Notification Service Extension;
- Select a name (Product Name) for the extension target;
- Enable it.
In the Notification Service Extension:
- General:
- Specify Minimum Deployments — this is an Xcode build parameter that defines the minimum operating system version on which the Notification Service Extension will run;
- Add the Altcraft library in the Frameworks, Libraries and Embedded Content section.
- Signing & Capabilities:
- Specify the
AppGroupidentifier.
Then configure the SDK in UNNotificationServiceExtension:
- Import the
Altcraftlibrary; - In the
didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void)function: - Pass the
appGroupidentifier to theAltcraftNSE.shared.setAppGroup(groupName: appGroupsName)function. This identifier must match the identifier passed to the SDK in the app target; - Set the JWT provider (if JWT authentication of requests is used):
AltcraftNSE.shared.setJWTProvider(provider: jwtProvider); - Check the source of the notification using the
AltcraftNSE.shared.isAltcraftPush(request)function; - After checking the source of the notification, pass
UNNotificationRequestandcontentHandlerto theAltcraftNSE.shared.handleNotificationRequest(request: request, contentHandler: contentHandler)function if the source of the notification is Altcraft.
The implementation example of UNNotificationServiceExtension shown below can be used as a ready-made class. Replace all of the auto-generated code with this implementation if you do not need additional logic. If you are already using UNNotificationServiceExtension, integrate the Altcraft functions into your implementation.
Example of implementing UNNotificationServiceExtension
import Altcraft
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
/// - important! Set the App Group identifier.
let appGroupID = "group.your.id"
/// - important! Set the JWT provider if JWT authentication is used.
let jwtProvider = JWTProvider()
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
AltcraftNSE.shared.setAppGroup(groupName: appGroupID)
AltcraftNSE.shared.setJWTProvider(provider: jwtProvider)
if AltcraftNSE.shared.isAltcraftPush(request) {
AltcraftNSE.shared.handleNotificationRequest(request: request, contentHandler: contentHandler)
} else {
contentHandler(request.content)
}
}
override func serviceExtensionTimeWillExpire() {
AltcraftNSE.shared.serviceExtensionTimeWillExpire()
}
}
The app target and the Notification Service Extension target work in separate processes and do not share common objects. Therefore, for the SDK to handle notifications correctly, it is necessary to also register the JWTProvider and the App Group identifier in the NSE extension — for example, in the didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) method. This ensures that the SDK has access to the authorization token when sending a push delivery event ("delivery_event").
Initializing the SDK
Initialization parameters
The AltcraftConfiguration class is used to pass configuration parameters:
public final class AltcraftConfiguration {
private let apiUrl: String
private let rToken: String?
private let appInfo: AppInfo?
private let enableLogging: Bool?
}
The settings of individual modules are passed as separate configurations and added to the Builder with pushConfig() and inAppConfig() calls.
The configuration file is built after the build() function of the AltcraftConfiguration.Builder() class is called: AltcraftConfiguration.Builder().build().
Parameter description:
apiUrl String
Required: Yes
Description: URL of the Altcraft API endpoint
rToken String?
Default: nil
Required: No
Description: The Altcraft role token (identifies a resource, database, or account). It is used if the only matching type is the device push token issued by the provider.
appInfo AppInfo?
Default: nil
Required: No
Description: Basic app metadata for Firebase Analytics. To set the value of this parameter, use the SDK's public AppInfo struct:
public struct AppInfo: Codable {
/// Firebase app_id
public var appID: String
/// Firebase app_instance_id
public var appIID: String
/// Firebase app_version
public var appVer: String
}
enableLogging Bool?
Default: nil
Required: No
Description: Enables/disables logging. Logs can be found by the [Altcraft SDK] prefix.
Push module configuration
The PushConfiguration class contains the push module settings and is added to the Builder with a pushConfig() call:
public final class PushConfiguration {
public let icon: String?
public let providerPriority: [String]
public let pushReceiverModules: [String]
public let pushChannelName: String?
public let pushChannelDescription: String?
public init(
icon: String? = nil,
providerPriority: [String] = [],
pushReceiverModules: [String] = [],
pushChannelName: String? = nil,
pushChannelDescription: String? = nil
)
}
icon String?
Default: nil
Required: No
Description: The name of the push notification icon image available in the app bundle
providerPriority [String]
Default: empty list
Required: No
Description: A list of provider priorities. It is used to automatically update the subscription push token if a more priority provider's token is unavailable. The priority is determined by the index in the list: the element at index 0 is the most priority.
Usage example:
providerPriority = [
Constants.ProviderName.apns,
Constants.ProviderName.firebase,
Constants.ProviderName.huawei
]
With this setup:
- the SDK first requests the APNs token; if APNs is unavailable — FCM; if FCM is unavailable — HMS;
- this works provided the protocols of the corresponding providers are implemented in the app.
For convenience, the SDK contains public constants for setting the parameter value. All constants are in the Constants enum:
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 the value is not set, the provider priority is as follows:
apns —> firebase —> huawei
The parameter value can contain one element — in this case, only one provider is used, regardless of push token availability.
The parameter does not need to be specified if:
- only one provider is used in the project;
- the default priority meets the requirements.
pushReceiverModules [String]
Default: empty list
Required: No
Description: A list of module prefixes where the implementations of the AltcraftPushReceiver class are located.
If specified, the SDK detects these classes and passes the incoming notification to them. If the classes are not found in any of the modules, the notification is displayed using the SDK's means.
pushChannelName String?
Default: nil
Required: No
Description: The name of the push notification channel
pushChannelDescription String?
Default: nil
Required: No
Description: The description of the push notification channel
If the parameters are not set, the SDK uses the default values:
- icon — the icon from the SDK resources (if
icon = nil); - providerPriority —
apns —> firebase —> huawei(if an empty list); - pushReceiverModules — an empty list;
- pushChannelName and pushChannelDescription — the SDK default values (if
nil).
In-App module configuration
The InAppConfiguration class contains the In-App module settings and is added to the Builder with an inAppConfig() call:
public final class InAppConfiguration {
public let inAppAutoRequest: Bool
public init(inAppAutoRequest: Bool = true)
}
inAppAutoRequest Bool
Default: true
Required: No
Description: When set to true, the SDK automatically requests In-App placements at initialization and then every 30 minutes. When set to false, requests are performed only on an explicit getPlacements() call or when triggers fire.
Performing the initialization
Call AltcraftSDK.shared.initialization() when needed, but only after setting the AppGroup identifier and the providers that implement the SDK protocols. Requests should be made after the configuration is set.
The SDK initialization uses the function:
public func initialization(
configuration: AltcraftConfiguration?,
completion: (@Sendable (Bool) -> Void)? = nil
)
The function expects an AltcraftConfiguration configuration file with the 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"))
.pushConfig(
PushConfiguration(
providerPriority: [Constants.ProviderName.firebase]
)
)
.build()
Example of the correct order of initializing the SDK in AppDelegate.application(_:didFinishLaunchingWithOptions:) (after setting the AppGroup identifier and registering the providers)
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Firebase configuration initialization (if used)
let appGroup = "group.your.id"
AltcraftSDK.shared.setAppGroup(groupName: appGroup)
AltcraftSDK.shared.backgroundTasks.registerBackgroundTask()
AltcraftSDK.shared.setJWTProvider(provider: JWTProvider())
AltcraftSDK.shared.push.token.setAPNSTokenProvider(APNSProvider())
AltcraftSDK.shared.push.token.setFCMTokenProvider(FCMProvider())
AltcraftSDK.shared.push.token.setHMSTokenProvider(HMSProvider())
AltcraftSDK.shared.push.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"))
.pushConfig(
PushConfiguration(
providerPriority: [Constants.ProviderName.firebase]
)
)
.inAppConfig(
InAppConfiguration(
inAppAutoRequest: true
)
)
.build()
AltcraftSDK.shared.initialization(configuration: config) { success in
if success {
// Actions after successful initialization
}
}
// Other functions
return true
}
// Other functions AppDelegate
}
Example of a minimal working configuration
let config = AltcraftConfiguration.Builder()
.setApiUrl("https://pxl-example.altcraft.com")
.build()
AltcraftSDK.shared.initialization(configuration: config)