Skip to main content
Altcraft Docs LogoAltcraft Docs Logo
User guideDeveloper guideAdmin guide
Company siteHelp center
English
  • Русский
  • English
v75
Login
  • User API documentation
  • API interaction
  • Matching
  • Profiles
  • Databases
  • Resources
  • Segments
  • Suppression lists
  • Templates and fragments
  • Campaigns
  • Mailings
  • Automation scenarios
  • Loyalty Programs
  • Promo codes
  • Goals
  • Application push notifications
  • Market
  • Analytic reports
  • SendersDev
  • External datatables queries
  • Objects
  • Miscellaneous
  • Importing the API collection in Postman
  • List of API endpoints
  • SDK
    • mSDK
      • Android
      • iOS
        • Quick Start
        • SDK Functionality
        • SDK Configuration
        • Public SDK API
        • Provider configuration
      • React Native (Android/iOS)
      • Managing JWT and Role Token
  • SDK
  • mSDK
  • iOS
  • SDK Functionality
Documentation for version v75

SDK Functionality

tip

Before proceeding, you need to configure the SDK for your application. The detailed setup guide is available here.

Working with subscription statuses​


Changing subscription status​

AltcraftSDK
// Singleton, the entry point to the SDK
└── static let shared: AltcraftSDK
// Push subscription managment
└── pushSubscriptionFunctions: PublicPushSubscriptionFunctions
// Subscribe (status = SUBSCRIBED)
├── pushSubscribe(
│ sync: Bool = true,
│ profileFields: [String: Any?]? = nil,
│ customFields: [String: Any?]? = nil,
│ cats: [CategoryData]? = nil,
│ replace: Bool? = nil,
│ skipTriggers: Bool? = nil
│ ): Void
// Unsubscribe (status = UNSUBSCRIBED)
├── pushUnSubscribe(
│ sync: Bool = true,
│ profileFields: [String: Any?]? = nil,
│ customFields: [String: Any?]? = nil,
│ cats: [CategoryData]? = nil,
│ replace: Bool? = nil,
│ skipTriggers: Bool? = nil
│ ): Void
// Suspend (status = SUSPENDED)
└── pushSuspend(
sync: Bool = true,
profileFields: [String: Any?]? = nil,
customFields: [String: Any?]? = nil,
cats: [CategoryData]? = nil,
replace: Bool? = nil,
skipTriggers: Bool? = nil
): Void

Subscription status change functions:

  • func pushSubscribe() — subscribes to push notifications;
  • func pushUnSubscribe() — unsubscribes from push notifications;
  • func pushSuspend() — suspends the push subscription (no notifications are delivered, but no unsubscribe event is created in the user’s profile);
  • func unSuspendPushSubscription() — used to implement LogIn / LogOut transitions.

All functions in this group have the same signature and parameters:


sync: Bool

Default: true
Required: No
Description: A flag that sets whether the request is executed synchronously.

Successful request execution:

On success, these functions produce an event with code 230, 231 or 232, containing an event.value defined by the sync flag:

If sync == true
ResponseWithHttpCode
├─ httpCode: 200
└─ response
├─ error: 0
├─ errorText: ""
└─ profile
├─ id: "your id"
├─ status: "subscribed"
├─ isTest: false
└─ subscription
├─ subscriptionId: "your subscriptionId"
├─ hashId: "c52b28d2"
├─ provider: "ios-apns"
├─ status: "subscribed"
├─ fields
│ ├─ _os_ver: {"raw":"18.6.2","ver":"[\"18.0\", \"6.0\", \"2.0\"]"}
│ ├─ _device_type: "mob"
│ ├─ _ad_track: false
│ ├─ _device_name: "iPhone"
│ ├─ _os_language: "en"
│ ├─ _os_tz: "+0300"
│ ├─ _os: "IOS"
│ └─ _device_model: "iPhone14,7"
└─ cats
└─ [ { name: "developer_news", title: "dev_news", steady: false, active: false } ]

For synchronous requests, the event.value["response_with_http_code"] contains:

  • httpCode – transport-level status code;

  • Response – a public struct containing:

    • error: Int? — internal server error code (0 if no errors),

    • errorText: String? — error text (empty string if no errors),

    • profile: ProfileData? — profile data if the request is successful:

      • profile info (ProfileData)
      • subscription (SubscriptionData)
      • subscription categories (CategoryData)
      • if the request fails, profile = nil
Data structures
public struct Response {
let error: Int? // internal error code
let errorText: String? // error text
let profile: ProfileData?
}

public struct ProfileData {
let subscription: SubscriptionData?
let cats: [CategoryData]?
}

public struct SubscriptionData {
// Subscription Data
}

public struct CategoryData {
// Subscription Category Data
}
If sync == false
ResponseWithHttpCode
├─ httpCode: Int?
└─ response: Response?
├─ error: Int?
├─ errorText: String?
└─ profile: ProfileData? = nil

For asynchronous requests, event.value["response_with_http_code"] contains:

  • httpCode – transport-level status code;

  • Response – a public struct containing:

    • error: Int? — internal server error code (0 if no errors),
    • errorText: String? — error text (empty string if no errors),
    • profile: ProfileData? — profile data, always nil for async requests.

Failed request execution:

If a request in this group fails, an event with one of the following codes is created:

Operations without automatic retry on the SDK side:

  • 430 – notification subscription;
  • 431 — subscription suspension;
  • 432 — unsubscription.

Operations with automatic retry on the SDK side:

  • 530 – notification subscription;
  • 531 — subscription suspension;
  • 532 — unsubscription.

Event contents:

  • only httpCode if the Altcraft server was unavailable;
  • error and errorText if the server returned an error.
Retrieving event values
AltcraftSDK.shared.eventSDKFunctions.subscribe { event in
let codes: Set<Int> = [
230, 231, 232,
430, 431, 432,
531, 532, 533
]

guard let code = event.eventCode, codes.contains(code) else { return }
guard let responseWithHttp = event.value?["response_with_http_code"] as? ResponseWithHttp else { return }

let httpCode = responseWithHttp.httpCode

let response = responseWithHttp.response
let error = response?.error
let errorText = response?.errorText

let profile = response?.profile
let profileId = profile?.id
let profileStatus = profile?.status
let profileIsTest = profile?.isTest

let subscription = profile?.subscription
let subscriptionId = subscription?.subscriptionId
let hashId = subscription?.hashId
let provider = subscription?.provider
let subscriptionStatus = subscription?.status

let fields = subscription?.fields

let cats = subscription?.cats
let firstCat = cats?.first
let catName = firstCat?.name
let catTitle = firstCat?.title
let catSteady = firstCat?.steady
let catActive = firstCat?.active
}

The fields inside subscription may include your custom subscription fields. They are of type [String: JSONValue]?. The public enum JSONValue provides helpers to simplify field value extraction:

public var stringValue: String? {
if case let .string(value) = self { return value }
return nil
}

/// Returns the numeric value if case is `.number`, otherwise `nil`.
public var numberValue: Double? {
if case let .number(value) = self { return value }
return nil
}

/// Returns the bool value if case is `.bool`, otherwise `nil`.
public var boolValue: Bool? {
if case let .bool(value) = self { return value }
return nil
}

/// Returns the object dictionary if case is `.object`, otherwise `nil`.
public var objectValue: [String: JSONValue]? {
if case let .object(value) = self { return value }
return nil
}

/// Returns the array if case is `.array`, otherwise `nil`.
public var arrayValue: [JSONValue]? {
if case let .array(value) = self { return value }
return nil
}

Example of extracting the field "_device_name":

AltcraftSDK.shared.eventSDKFunctions.subscribe { event ->
if event.eventCode == 230 {
if let responseWithHttp = event.value?["response_with_http_code"] as? ResponseWithHttp {
let response = responseWithHttp.response
let profile = response?.profile
let subscription = profile?.subscription

// extract fields?["_device_name"] as a string value
if let deviceName = subscription?.fields?["_device_name"]?.stringValue {
print(deviceName)
}
}
}
}

profileFields: [String: Any?]?

Default: nil
Required: No
Description: A dictionary containing profile fields.

The parameter may include system fields (e.g., _fname — first name, _lname — last name) and optional fields (manually created in the platform UI). Allowed JSON-compatible value types:

  • String
  • Bool
  • Numbers
  • nil
  • Objects [String: Any]
  • Arrays

If an invalid optional field is passed, the request fails:

SDK error: 430
http code: 400
error: 400
errorText: Platform profile processing error: with field "field_name": Incorrect field

customFields: [String: Any?]?

Default: nil
Required: No
Description: A dictionary containing subscription fields.

The parameter may include system fields (e.g., _device_model — device model, _os — OS), as well as optional ones (manually created in the platform UI). Allowed types:

  • String
  • Bool
  • Int
  • Float
  • Double
  • nil

If an invalid optional field is passed, the request fails:

SDK error: 430
http code: 400
error: 400
errorText: Platform profile processing error: field "field_name" is not valid: failed convert custom field

Nested objects, arrays, and collections are not allowed.

Note

Most system subscription fields are automatically collected by the SDK and added to push requests. These include: "_os", "_os_tz", "_os_language", "_device_type", "_device_model", "_device_name", "_os_ver", "_ad_track", "_ad_id".


cats: [CategoryData]?

Default: nil
Required: No
Description: Subscription categories.

Category structure:

public struct CategoryData: Codable {
public var name: String?
public var title: String?
public var steady: Bool?
public var active: Bool?
}

When sending a push request with categories, use only name (category name) and active (category active status). Other fields are not used in request processing. The title and steady fields are populated when retrieving subscription info.

Request example:

let cats: [CategoryData] = [CategoryData(name: "football", title: nil, steady: nil, active: true)]

Response example:

[ { name: "developer_news", title: "dev_news", steady: false, active: false } ]

Categories used in the request must be created beforehand and added to the resource in the Altcraft platform. If a category not present in the resource is used, the request returns an error:

SDK error: 430
http code: 400
error: 400
errorText: Platform profile processing error: field "subscriptions.cats" is not valid: category not found in resource

replace: Bool?

Default: nil
Required: No
Description: When enabled, all subscriptions of other profiles with the same push token in the current database are set to unsubscribed after a successful request.


skipTriggers: Bool?

Default: nil
Required: No
Description: When enabled, the profile containing this subscription will be ignored by mailing and scenario triggers.


Request examples

Example: subscribing to push notifications

Minimal working call:

AltcraftSDK.shared.pushSubscriptionFunctions.pushSubscribe()

Passing all available parameters:

AltcraftSDK.shared.pushSubscriptionFunctions.pushSubscribe(
sync: true,
profileFields: ["_fname":"Andrey", "_lname":"Pogodin"],
customFields: ["developer":true],
cats: [CategoryData(name: "developer_news", active: true)],
replace: false,
skipTriggers: false
)
Note

For pushSubscribe, pushSuspend, and pushUnSubscribe, the SDK automatically retries the request if the HTTP status code is in the 500..599 range. No retry occurs if the code is outside this range.


Function unSuspendPushSubscription()

func unSuspendPushSubscription() is designed for LogIn / LogOut transitions. It works as follows:

  • searches for subscriptions with the same push token as the current one that do not belong to the profile referenced by the current JWT;
  • changes the status of the found subscriptions from subscribed to suspended;
  • changes the status of the subscriptions in the profile referenced by the current JWT from suspended to subscribed (if that profile exists and contains subscriptions);
  • returns a ResponseWithHttpCode? where response.profile is the current profile referenced by the JWT (if the profile does not exist, response.profile is nil).

Example:

AltcraftSDK.shared.pushSubscriptionFunctions.unSuspendPushSubscription(completion: @escaping (ResponseWithHttp?) -> Void)
Recommended LogIn / LogOut flow

LogIn flow:

  • An anonymous user opens the app. The user has JWT_1, pointing to database #1Anonymous;
  • The user subscribes to push; a profile is created in #1Anonymous;
  • The user registers and receives JWT_2, pointing to #2Registered;
  • Call unSuspendPushSubscription() — the anonymous subscription in #1Anonymous is suspended;
  • The SDK searches for a profile in #2Registered to restore the subscription;
  • Since a subscription with this push token does not exist in #2Registered, unSuspendPushSubscription() returns nil;
  • After receiving nil, call pushSubscribe() to create a new profile in #2Registered.

LogOut flow:

  • The user logs out in the app (LogOut);
  • The user receives JTW_1, pointing to #1Anonymous;
  • Call unSuspendPushSubscription(), which suspends the subscription in #2Registered and sets the status in #1Anonymous to subscribed;
  • The request returns #1Anonymous != nil — the subscription already exists, no new one is required.

Example implementation:

func logIn() {
JWTManager.shared.setRegJWT()
// JWT is set for an authorized user
AltcraftSDK.shared.pushSubscriptionFunctions.unSuspendPushSubscription { result in
if result?.httpCode == 200, result?.response?.profile?.subscription == nil {
AltcraftSDK.shared.pushSubscriptionFunctions.pushSubscribe(
// specify the required parameters
)
}
}
}

func logOut() {
JWTManager.shared.setAnonJWT()
// JWT is set for an anonymous user
AltcraftSDK.shared.pushSubscriptionFunctions.unSuspendPushSubscription { result in
if result?.httpCode == 200, result?.response?.profile?.subscription == nil {
AltcraftSDK.shared.pushSubscriptionFunctions.pushSubscribe(
// specify the required parameters
)
}
}
}

Requesting subscription status​

AltcraftSDK
// Singleton, the entry point to the SDK
└── static let shared: AltcraftSDK
// Subscription managment
└── pushSubscriptionFunctions: PublicPushSubscriptionFunctions
// Last profile subscription status
├── getStatusOfLatestSubscription(
│ completion: @escaping (ResponseWithHttp?) -> Void
│ ): Void
// Subscription status corresponding to the current token/provider
├── getStatusForCurrentSubscription(
│ completion: @escaping (ResponseWithHttp?) -> Void
│ ): Void
// Status of the latest subscription for the provider (if nil — the current one is used)
└── getStatusOfLatestSubscriptionForProvider(
provider: String? = nil,
completion: @escaping (ResponseWithHttp?) -> Void
): Void

Subscription status functions:

  • func getStatusOfLatestSubscription() — the last subscription status in the profile;
  • func getStatusOfLatestSubscriptionForProvider() — the subscription status for the current token/provider;
  • func getStatusForCurrentSubscription() — the latest subscription status by provider. If nil is specified, the current one is used.

public func getStatusOfLatestSubscription(completion: @escaping (ResponseWithHttp?) -> Void): Void

Retrieves the status of the profile’s latest subscription. The completion receives a ResponseWithHttp? containing response?.profile?.subscription — the most recently created subscription in the profile. If no such subscription exists, nil is passed.

Example:

// Last profile subscription status
AltcraftSDK.shared.pushSubscriptionFunctions.getStatusOfLatestSubscription(completion: @escaping (ResponseWithHttp?) -> Void)

public func getStatusForCurrentSubscription(completion: @escaping (ResponseWithHttp?) -> Void): Void

Retrieves the subscription status for the current token/provider. The completion receives a ResponseWithHttp? containing response?.profile?.subscription — the subscription found by the current push token and provider. If no such subscription exists, nil is passed.

Example:

// Subscription status corresponding to the current token/provider
AltcraftSDK.shared.pushSubscriptionFunctions.getStatusForCurrentSubscription(completion: @escaping (ResponseWithHttp?) -> Void)

public func getStatusOfLatestSubscriptionForProvider(provider: String? = nil, completion: @escaping (ResponseWithHttp?) -> Void): Void

Retrieves the latest subscription status by provider. The completion receives a ResponseWithHttp? containing response?.profile?.subscription — the most recently created subscription for the specified push provider. If no provider is specified, the current token’s provider is used. If no such subscription exists, nil is passed.

Example:

// Status of the latest subscription for the provider (if nil — the current one is used)
AltcraftSDK.shared.pushSubscriptionFunctions.getStatusOfLatestSubscriptionForProvider(provider: String? = nil, completion: @escaping (ResponseWithHttp?) -> Void)

Below is an example of extracting profile, subscription, and category data from the status response. This approach applies to all status-retrieval functions:

Data from status-retrieval functions
AltcraftSDK.shared.pushSubscriptionFunctions.getStatusForCurrentSubscription{ responseWithHttp in
// HTTP code
let httpCode = responseWithHttp?.httpCode

// Response
let response = responseWithHttp?.response
let error = response?.error
let errorText = response?.errorText

// Profile
let profile = response?.profile
let profileId = profile?.id
let profileStatus = profile?.status
let profileIsTest = profile?.isTest

// Subscription
let subscription = profile?.subscription
let subscriptionId = subscription?.subscriptionId
let hashId = subscription?.hashId
let provider = subscription?.provider
let subscriptionStatus = subscription?.status

// Fields (dictionary [String: JSONValue])
let fields = subscription?.fields

// Cats (array of CategoryData)
let cats = subscription?.cats

// CategoryData (every element of cats array)
let firstCat = cats?.first
let catName = firstCat?.name
let catTitle = firstCat?.title
let catSteady = firstCat?.steady
let catActive = firstCat?.active
}

On a successful request, an event with code 234 is created. On error — an event with code 434.


Provider push token management​


AltcraftSDK
// Singleton, the entry point to the SDK
└── static let shared: AltcraftSDK
// Managing push tokens and providers (FCM / HMS / APNs)
└── pushTokenFunctions: PublicPushTokenFunctions
// Manually save the token (String for FCM/HMS, Data for APNs)
├── setPushToken(
│ provider: String,
│ pushToken: Any?
│ ): Void
// Asynchronously retrieve the current push token (completion is optional)
├── getPushToken(
│ completion: ((TokenData?) -> Void)? = nil
│ ): Void
// Set the Firebase Cloud Messaging provider (nil — remove)
├── setFCMTokenProvider(
│ _ provider: FCMInterface?
│ ): Void
// Set the Huawei Mobile Services provider (nil — remove)
├── setHMSTokenProvider(
│ _ provider: HMSInterface?
│ ): Void
// Set the Apple Push Notification service provider (nil — remove)
├── setAPNSTokenProvider(
│ _ provider: APNSInterface?
│ ): Void
// Apply a new provider priority list and initiate token update
├── changePushProviderPriorityList(
│ _ list: [String]
│ ): Void
// Delete the push token of the specified provider and call completion
├── deleteDeviceToken(
│ provider: String,
│ completion: @escaping () -> Void
│ ): Void
// Force token update
└── forcedTokenUpdate(
completion: (() -> Void)? = nil
): Void

SDK functions for working with a provider’s token:

  • func setPushToken() — manually sets the device push token and provider in UserDefaults;
  • func getPushToken() — retrieves the current push token;
  • func setAPNSTokenProvider() — sets or removes the APNs push-token provider;
  • func setFCMTokenProvider() — sets or removes the FCM push-token provider;
  • func setHMSTokenProvider() — sets or removes the HMS push-token provider;
  • func changePushProviderPriorityList() — dynamically switches the provider and updates the subscription’s push token;
  • func deleteDeviceToken() — deletes the push token for the specified provider;
  • func forcedTokenUpdate() — deletes the current push token and then refreshes it.

public func setPushToken(provider: String, pushToken: Any?): Void

Used to manually set the device push token and provider in UserDefaults. This serves as a simplified way to pass a push token into the SDK without implementing provider protocols.

Not recommended for passing the token. The preferred approach is to implement FCMInterface, HMSInterface, or APNSInterface.

Example:

// Manually save the token (String for FCM/HMS, Data for APNs)
AltcraftSDK.shared.pushTokenFunction.setPushToken(provider: String, pushToken: Any?)

public func getPushToken(completion: ((TokenData?) -> Void)? = nil): Void

Passes a TokenData(provider: String, token: String) object to completion, containing the current device push token and its provider. If the push token is unavailable, nil is passed.

Example:

// Asynchronously retrieve the current push token (completion is optional)
AltcraftSDK.shared.pushTokenFunction.getPushToken(completion: ((TokenData?) -> Void)? = nil)

Example of requesting push-token data:

AltcraftSDK.shared.pushTokenFunction.getPushToken { data in
let provider = data?.provider
let token = data?.token
}

public func setFCMTokenProvider(_ provider: FCMInterface?): Void

Sets or removes the Firebase Cloud Messaging provider. Pass nil to disable the provider.

Example:

// Set the Firebase Cloud Messaging provider (nil — remove)
AltcraftSDK.shared.pushTokenFunction.setFCMTokenProvider(_ provider: FCMInterface?)
Important

Call setFCMTokenProvider() in AppDelegate.application(_:didFinishLaunchingWithOptions:) before AltcraftSDK.initialization(). This guarantees registration at process start, regardless of lifecycle state or whether the app launches in foreground or background.


public func setHMSTokenProvider(_ provider: HMSInterface?): Void

Sets or removes the Huawei Mobile Services provider. Pass nil to disable the provider.

// Set the Huawei Mobile Services provider (nil — remove)
AltcraftSDK.shared.pushTokenFunction.setHMSTokenProvider(_ provider: HMSInterface?)
Important

Call setHMSTokenProvider() in AppDelegate.application(_:didFinishLaunchingWithOptions:) before AltcraftSDK.initialization(). This guarantees registration at process start, regardless of lifecycle state or whether the app launches in foreground or background.


public func setAPNSTokenProvider(_ provider: APNSInterface?): Void

Sets or removes the Apple Push Notification service provider. Pass nil to disable the provider.

// Set the Apple Push Notification service provider (nil — remove)
AltcraftSDK.shared.pushTokenFunction.setHMSTokenProvider(_ provider: APNSInterface?)
Important

Call setAPNSTokenProvider() in AppDelegate.application(_:didFinishLaunchingWithOptions:) before AltcraftSDK.initialization(). This guarantees registration at process start, regardless of lifecycle state or whether the app launches in foreground or background.


public func changePushProviderPriorityList(_ list: [String]): Void

Enables dynamic switching of the push provider with token refresh. Pass a new array with a different provider order, e.g., [Constants.ProviderName.firebase, Constants.ProviderName.apns, Constants.ProviderName.huawei].

Example:

// Apply a new provider priority list and initiate token update
AltcraftSDK.shared.pushTokenFunction.changePushProviderPriorityList(_ list: [String])

public func deleteDeviceToken(provider: String, completion: @escaping () -> Void): Void

Deletes the push token for the specified provider. The token is invalidated and removed from the local cache on the device and from the push provider’s server. After deletion, a new token can be requested.

Example:

// Delete the push token of the specified provider (invalidated locally and on the server)
AltcraftSDK.shared.pushTokenFunction.deleteDeviceToken(provider: String, completion: @escaping () -> Void)

public func forcedTokenUpdate(completion: (() -> Void)? = nil): Void

Deletes the current push token and then refreshes it.

Example:

// Force token update (determine provider —> delete (except APNs) —> request new —> update)
AltcraftSDK.shared.pushTokenFunction.forcedTokenUpdate(completion: (() -> Void)? = nil)

Example of registering providers​

We do not recommend using setPushToken to set the push token. Instead, configure token retrieval for each provider you use. Example:

Recommended provider registration in AppDelegate
class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate, UNUserNotificationCenterDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let appGroup = "your appGroup id"

AltcraftSDK.shared.setAppGroup(groupName: appGroup)
AltcraftSDK.shared.backgroundTasks.registerBackgroundTask()
AltcraftSDK.shared.setJWTProvider(provider: JWTProvider())

// apns provider
AltcraftSDK.shared.pushTokenFunction.setAPNSTokenProvider(APNSProvider())

// fcm provider
AltcraftSDK.shared.pushTokenFunction.setFCMTokenProvider(FCMProvider())

// hms provider
AltcraftSDK.shared.pushTokenFunction.setHMSTokenProvider(HMSProvider())

AltcraftSDK.shared.notificationManager.registerForPushNotifications(for: application)

let config = AltcraftConfiguration.Builder().setApiUrl("your api url").build()
AltcraftSDK.shared.initialization(configuration: config)

// other functions

return true
}
}

Managing Push Notifications​


Push Notification Initialization​

To initialize push notifications, call the following method in application(_:didFinishLaunchingWithOptions:):

AltcraftSDK.shared.notificationManager.registerForPushNotifications(for: application)

This method assigns UNUserNotificationCenter.current().delegate to the Altcraft SDK NotificationManager, requests permission to display notifications (alert/sound/badge), and registers the application with APNS via registerForRemoteNotifications().

Important

The system permission dialog is shown only on the first request (or if the status has not yet been determined). If the user has already responded earlier, the dialog will not appear again.


Passing Push Notifications to the SDK​

The SDK includes the AltcraftNSE class, intended for use inside a Notification Service Extension. It provides functions for handling incoming push notifications (UNNotificationRequest) and registers mobile events in the NSE.

// Used inside Notification Service Extension (NSE) — facade for handling Altcraft push notifications,
// detecting Altcraft-specific requests, and registering mobile events without direct dependency on UIApplication
AltcraftNSE
// Shared singleton instance for centralized NSE logic
├─ public static let shared: AltcraftNSE
// Provides access to mobile event registration API
├─ public let mobileEventFunctions: PublicMobileEventFunctions
// Check whether the notification request belongs to Altcraft
├─ public func isAltcraftPush(_ request: UNNotificationRequest) -> Bool
// Handle an incoming Altcraft notification request and deliver the modified content
├─ public func handleNotificationRequest(
│ request: UNNotificationRequest,
│ contentHandler: @escaping (UNNotificationContent) -> Void
│ ): Void
// Called when NSE is about to time out — returns the best available notification content
├─ public func serviceExtensionTimeWillExpire(): Void
// Set the App Group identifier and initialize Core Data in the shared container
├─ public func setAppGroup(groupName: String): Void
// Register a JWT provider for secure token management in Altcraft SDK
└─ public func setJWTProvider(provider: JWTInterface): Void

AltcraftNSE class methods:

  • func isAltcraftPush() — checks the notification source;
  • func handleNotificationRequest() — accepts a UNNotificationRequest from the Notification Service Extension for further processing by the SDK;
  • func serviceExtensionTimeWillExpire() — called when the Notification Service Extension execution time expires (~30 seconds).

public func isAltcraftPush(_ request: UNNotificationRequest) -> Bool

An SDK method that checks whether the notification source is Altcraft based on a marker in userInfo.

Usage example:

AltcraftNSE.shared.isAltcraftPush(_ request: UNNotificationRequest) -> Bool

public func handleNotificationRequest(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void): Void

An SDK method that accepts a UNNotificationRequest from the Notification Service Extension, processes it in the SDK, and then displays the notification.

Usage example:

AltcraftNSE.shared.handleNotificationRequest(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void)

public func serviceExtensionTimeWillExpire()

A method called by the system when the Notification Service Extension execution time expires (~30 seconds).

Usage example:

AltcraftNSE.shared.serviceExtensionTimeWillExpire()

public func setAppGroup(groupName: String): Void

A method for setting the AppGroup identifier (required).

Usage example:

AltcraftNSE.shared.setAppGroup(groupName: "group.your.app.id")

public func setJWTProvider(provider: JWTInterface): Void

A method for setting a JWT provider:

AltcraftNSE.shared.setJWTProvider(provider: jwtProvider)

Receiving Foreground Notifications in the App (Optional)​

By default, this functionality does not require configuration. If push notification handling is delegated to the SDK (via registerForPushNotifications), NotificationManager automatically:

  • is set as UNUserNotificationCenterDelegate,
  • processes incoming notifications,
  • displays notifications in the foreground using standard parameters (banner/alert + badge/sound).

The setup described below is required only if:

  • access to notification content in the foreground is needed (for example, analytics or a custom UI),
  • or you want to manually control notification presentation (show/hide, change presentation options).

To configure foreground push notification behavior, use NotificationManager:

// Access to the SDK push notification manager
let manager = AltcraftSDK.shared.notificationManager

// Control of foreground push notification handling.
// true — handling and presentation choice are delegated to the app.
// false — the SDK uses the standard notification presentation logic.
manager.customPushProcessing = false

// Callback for foreground push notification handling.
// Called when a notification is received while the app is active.
// completion must be called only when customPushProcessing = true.
manager.onForegroundNotification = { notification, complete in
// Custom notification handling logic

// Important: call complete(...) only if manager.customPushProcessing = true
if #available(iOS 14.0, *) {
complete([.banner, .badge, .sound])
} else {
complete([.alert, .badge, .sound])
}

// or [] to suppress presentation
}

If customPushProcessing = false, the SDK applies standard notification presentation parameters, and the callback (if set) is invoked only for additional app logic. If customPushProcessing = true, the app must call complete(...); otherwise, the notification may not be shown in the foreground.


Handling Notification Click Events in the App (Optional)​

By default, this functionality does not require additional configuration. If push notification handling is delegated to the SDK, NotificationManager automatically:

  • handles notification taps or actions,
  • sends a push event of type open to Altcraft,
  • executes the standard notification action handling logic.

The setup described below is required only if:

  • the app needs access to notification data on click,
  • custom navigation or action handling logic is required,
  • or notification click handling must be fully intercepted by the app.

To delegate click handling to the app, use NotificationManager:

let manager = AltcraftSDK.shared.notificationManager

// Control of notification click handling.
// true — click handling is fully delegated to the app.
// false — the SDK uses the standard click handling logic.
manager.customClickProcessing = true

// Notification click handling callback.
// Called when the user taps a notification or performs an action.
// completion must be called when customClickProcessing = true.
manager.onNotificationClick = { response, complete in
// Notification data
let userInfo = response.notification.request.content.userInfo

// Custom click handling logic
print("push click")

// Complete handling
complete()
}

If customClickProcessing = false, the SDK handles notification clicks on its own, sends the corresponding events, and completes processing. In this case, the onNotificationClick callback (if set) is invoked only for additional app logic.

If customClickProcessing = true, the app must call complete(). Without calling complete(), iOS will treat the click handling as incomplete, which may lead to incorrect app behavior.


Mobile events​

// PublicMobileEventFunctions object

AltcraftSDK.shared
└── let mobileEventFunctions: PublicMobileEventFunctions

// Send a mobile event to the server
└── func mobileEvent(
sid: String,
eventName: String,
sendMessageId: String? = nil,
payload: [String: Any?]? = nil,
matching: [String: Any?]? = nil,
matchingType: String? = nil,
profileFields: [String: Any?]? = nil,
subscription: (any Subscription)? = nil
utm: UTM? = nil
)

Case: Transferring advertising campaign information

Information about the app advertising campaign that led to the installation can be sent to the platform as values of the profileFields / customFields parameters of the pushSubscribe function, as well as the utm or payload parameters of the mobileEvent() function. After receiving a UTM tag in the app as a string, you need to call the mobileEvent() function:

Transfer via payload

AltcraftSDK.shared.mobileEventFunctions.mobileEvent(
sid: "your sid",
eventName: "app_install",
payload: ["utm": "your utm tag"]
)


Transfer to UTM fields

AltcraftSDK.shared.mobileEventFunctions.mobileEvent(
sid: "your sid",
eventName: "app_install",
utm: UTM(
campaign: "your campaign utm",
content: "your content utm",
keyword: "your keyword utm",
medium: "your medium utm",
source: "your source utm",
temp: "your temp utm"
)
)


Transfer to a custom profile field

// transfer a UTM tag to a profile field
// profile fields must be created in the platform in advance
AltcraftSDK.shared.pushSubscriptionFunctions.pushSubscribe(
profileFields: ["utm": "your utm tag"]
)


Data sent using any of these methods can be used to create a segment in the platform.

Please note that when setting up on iOS, the application must use AppsFlyer, otherwise the UTM tag will not be transmitted to the application.

Use mobileEvent() from PublicMobileEventFunctions to register a mobile event. Parameters:


sid: String

Required: Yes
Description: String pixel identifier to which mobile events are linked.


eventName: String

Required: Yes
Description: Mobile event name.


sendMessageId: String?

Required: No
Description: SMID of the sent message (when the event is tied to a specific mailing).


payload: [String: Any?]?

Required: No
Description: Event data — a map with string keys; only scalar value types are allowed:

  • String
  • Bool
  • Int
  • Int64 / UInt64 (or NSNumber equivalents)
  • Float
  • Double
  • nil / NSNull

Note

Non-serializable objects (e.g., Date without conversion, custom classes) will cause encoding errors.


matching: [String: Any?]?

Required: No Description: A map for passing values with matching types/identifiers (e.g., ["email" : "user@example.com"]).


matchingType: String?

Required: No
Description: Matching type.


profileFields: [String: Any?]?

Required: No
Description: Profile fields — a map with string keys and values (JSON-compatible types):

  • Scalar values:

    • String
    • Bool
    • Int
    • Int64 / UInt64 (or NSNumber equivalents)
    • Float
    • Double
    • nil / NSNull
  • Objects: [String: Any?]

  • Lists: [Any?]

  • Array of maps: [[String: Any?]]

Note

profileFields is used only with JWT authorization.


utm: UTM?

Required: No
Description: UTM tags. Added via the UTM struct, where each UTM type is a property:

public struct UTM{
/// UTM Mailing
public var campaign: String?
/// UTM Content
public var content: String?
/// UTM Keyword/Term
public var keyword: String?
/// UTM Medium
public var medium: String?
/// UTM Source
public var source: String?
/// UTM Temp
}

subscription: (any Subscription)?

Required: No Description: A subscription to be added to the profile, one of: EmailSubscription, SmsSubscription, PushSubscription, CcDataSubscription.

Note

Used only with JWT authorization.



Subscription interface implementations​

Purpose — a common protocol for all subscription types. Serialization — via Codable, discriminator field — channel.

Common fields (for all implementations):

FieldTypeRequiredDescription
resource_idIntYesIdentifier of the resource/source of the subscription
statusString?NoSubscription status (e.g., active/suspended)
priorityInt?NoDelivery priority for this subscription
custom_fields[String: JSONValue]?NoCustom key–value fields for advanced segmentation
cats[String]?NoSubscription categories
channelStringYesChannel type; fixed by the specific implementation
Note

JSONValue is a serializable enum/type of JSON values in your SDK. All values must be JSON-compatible.



Subscription types:

EmailSubscription (channel = "email")


Required fields

FieldTypeRequiredDescription
resourceIdIntYesAltcraft resource ID
emailStringYesRecipient email address

Optional fields

FieldTypeRequiredDescription
statusString?NoSubscription status
priorityInt?NoSubscription priority
customFields[String: JSONValue]?NoStandard and custom subscription fields
cats[String]?NoSubscription categories

SmsSubscription (channel = "sms")

Required fields

FieldTypeRequiredDescription
resourceIdIntYesAltcraft resource ID
phoneStringYesPhone number in international format

Optional fields

FieldTypeRequiredDescription
statusString?NoSubscription status
priorityInt?NoSubscription priority
customFields[String: JSONValue]?NoStandard and custom subscription fields
cats[String]?NoSubscription categories

PushSubscription (channel = "push")

Required fields

FieldTypeRequiredDescription
resourceIdIntYesAltcraft resource ID
providerStringYesProvider (for example, "ios-apns")
subscriptionIdStringYesUnique subscription identifier on the provider side

Optional fields

FieldTypeRequiredDescription
statusString?NoSubscription status
priorityInt?NoSubscription priority
customFields[String: JSONValue]?NoStandard and custom subscription fields
cats[String]?NoSubscription categories

CcDataSubscription (channel ∈ {"telegram_bot","whatsapp","viber","notify"})

Required fields

FieldTypeRequiredDescription
resourceIdIntYesAltcraft resource ID
channelStringYesOne of: "telegram_bot", "whatsapp", "viber", "notify"
ccData[String: JSONValue]YesChannel-specific data (for example, chat ID, phone number, tokens)

Optional fields

FieldTypeRequiredDescription
statusString?NoSubscription status
priorityInt?NoSubscription priority
customFields[String: JSONValue]?NoStandard and custom subscription fields
cats[String]?NoSubscription categories

Receiving SDK events in the app​


// Events object functions

AltcraftSDK
// Singleton, SDK entry point
└── static let shared: AltcraftSDK
// SDK event stream (one active subscriber)
└── eventSDKFunctions: SDKEvents
// Subscribe to SDK events (replaces an existing subscriber)
├── subscribe(
│ callback: @escaping (Event) -> Void
│ ): Void
// Unsubscribe from events (callback remains assigned, delivery stops)
└── unsubscribe(): Void

Only one active subscriber to SDK events can exist in the app.


Subscribing to events​

public func subscribe(callback: @escaping (Event) -> Void): Void

Subscribes to and receives SDK events in the app. When an SDK event occurs, it invokes callback and passes an instance of Event (or its subclass).

Example:

AltcraftSDK.shared.eventSDKFunctions.subscribe { event in
// handle event
}

All events provided by the SDK are instances of Event or its subclasses:

  • Event — general event (information, successful requests);
  • Error — error event;
  • RetryError — error event for a request that the SDK will automatically retry.

Each event contains:

  • function — the name of the function that produced the event;
  • eventCode — internal SDK event code (see “SDK events”);
  • eventMessage — event message;
  • eventValue — arbitrary payload [String: Any?]? added to some events;
  • date — event timestamp.
SDK events classes
open class Event: Hashable {
public let id = UUID()
public let function: String
public let message: String?
public let eventCode: Int?
public let value: [String: Any?]?
public let date: Date
}

open class ErrorEvent: Event {
public override init(
function: String,
message: String? = nil,
eventCode: Int? = nil,
value: [String: Any?]? = nil,
date: Date = Date()
) {
super.init(
function: function,
message: message,
eventCode: eventCode,
value: value,
date: date
)
}
}

public class RetryEvent: ErrorEvent {
public override init(
function: String,
message: String? = nil,
eventCode: Int? = 0,
value: [String: Any?]? = nil,
date: Date = Date()
) {
super.init(
function: function,
message: message,
eventCode: eventCode,
value: value,
date: date
)
}
}
Example contents of a successful push subscription event
├─ function: processResponse
├─ eventCode: 230
├─ message: "successful request: push/subscribe"
├─ value
│ ├─ http code: 200
│ └─ response
│ ├─ error: 0
│ ├─ errorText: ""
│ └─ profile
│ ├─ id: // profile ID
│ ├─ status: subscribed
│ ├─ isTest: false
│ └─ subscription
│ ├─ subscriptionId: // push token of the subscription
│ ├─ hashId: // hash ID
│ ├─ provider: "ios-apns"
│ ├─ status: subscribed
│ ├─ fields
│ │ ├─ _ad_id: // google service ad id
│ │ ├─ _ad_track: false
│ │ ├─ _app_id: "AltcraftMobile"
│ │ ├─ _app_iid: "1.0.0"
│ │ ├─ _app_ver: {"raw":"1.0.0","ver":[1,0]}
│ │ ├─ _device_model: "iPhone14,7"
│ │ ├─ _device_name: "iPhone"
│ │ ├─ _device_type: "mob"
│ │ ├─ _os: "Android"
│ │ ├─ _os_language: "ru"
│ │ ├─ _os_tz: "+0300"
│ │ └─ _os_ver: {"raw":"18.6.2","ver":"[\"18.0\", \"6.0\", \"2.0\"]"}
│ └─ cats
│ └─ [ { name: "cats_1", title: "cats_1", steady: false, active: false } ]
└─ date: Tue Aug 12 15:49:20 GMT+03:00 2025

Unsubscribing from events​

public func unsubscribe(): Void

Unsubscribes from SDK events. It stops event delivery while keeping the callback assigned.

Example:

// unsubscribe from SDK events
AltcraftSDK.shared.eventSDKFunctions.unsubscribe()

List of all SDK events​

Event list
CodeDescription
200SDK configuration is installed
201push provider set
203received Altcraft push notification
204push is posted
205SDK data has been cleared
206SDK background task is registered
207SDK background task completed
208SDK background task is registered
209SDK background task completed
230subscribe request succeeded
231token update request succeeded
232unsuspend request succeeded
233status request succeeded
234push event delivered successfully
235mobile event delivered successfully
401the configuration is not set
402userTag is null. It is impossible to identify the user
403error in CoreData. Re-initialize the SDK
404failed to load model from framework
405App Group name was not set
406forcing a push token update is not possible: the operation is not supported for APNs
407the CoreData entity name is not valid
408entity not found by ID
409SDK initialization timeout has expire
422unsuspend request data is nil
423profile request data is nil
430subscribe request failed
431token update request failed
432unsuspend request failed
433status request failed
434push event delivery failed
435mobile event delivery failed
450invalid userInfo format
451uid in the push data is null; it is impossible to send a push event to the server
452could not download image data from URL
453'media' key is missing or URL string is invalid
454'buttons' key is missing or is invalid
455invalid button identifier (see event value)
456out of range for identifier (see event value)
457unknown button identifier (see event value)
471invalid apiUrl value — empty or null
472invalid resource token value — resource token is empty
473invalid provider. Available — ios-apns, ios-firebase, ios-huawei
474non-JSON object has been provided
475invalid values: not all values are primitives
476unsupported subscription type. Available: EmailSubscription, SmsSubscription, PushSubscription, CcDataSubscription
501config data is nil
502current token is nil
503userTag is null. It is impossible to identify the user
504notification permission denied
505SDK background task expired
520subscribe request data is nil
521update request data is nil
524push event request data is nil
525mobile event request data is nil
529common data is nil
530subscribe request failed (retryable)
531token update request failed (retryable)
534push event delivery failed (retryable)
535mobile event delivery failed (retryable)
540JWT token is nil
541JWT parsing error
542invalid matching
543auth data is nil
561failed to create request
562failed processing the response
571invalid request URL
572invalid response format
573invalid push subscribe request data
574invalid push event request data

Additional functions​


Functional profile field updates​

public func actionField(key: String) -> ActionFieldBuilder

A helper for the functional profile field update process. Supports:

    .set(value)
.unset(value)
.incr(value)
.add(value)
.delete(value)
.upsert(value)

Example:

// "_fname" — field to update
// .set("Andrey") — sets the field to "Andrey"
AltcraftSDK.shared.pushSubscriptionFunctions.pushSubscribe(
profileFields: AltcraftSDK.shared.pushSubscriptionFunctions.actionField(key: "_fname").set(value: "Andrey")
)

Clearing SDK data​

public func clear(completion: (() -> Void)? = nil): Void

Clears SDK data and cancels all pending background tasks. Removes CoreData records and clears UserDefaults.

Example:

// Full SDK reset (cache/database/settings), then call completion
AltcraftSDK.shared. clear(completion: (() -> Void)? = nil)

The function accepts an optional completion called after cleanup and task cancellation.


Manual Push Event Registration​

Note

Use these only if you implement your own notification handling and do not pass them to Altcraft SDK. By default, Altcraft SDK handles notifications automatically; these methods are needed only for custom client-side processing.

// PublicPushEventFunctions

AltcraftSDK
// Singleton, SDK entry point
└── public static let shared: AltcraftSDK
// Manual sending of push events (delivery / open)
└── public let pushEventFunctions: PublicPushEventFunctions
// Record Altcraft push delivery (fires a delivery event)
├── public func deliveryEvent(from request: UNNotificationRequest): Void
// Record Altcraft push open (fires an open event)
└── public func openEvent(from request: UNNotificationRequest): Void

Manual event registration functions:

  • func deliveryEvent() — registers a delivery event;
  • func openEvent() — registers an open event.

public func deliveryEvent(from request: UNNotificationRequest): Void

Registers a delivery event in the Altcraft platform. Pass UNNotificationRequest in the request parameter.

Example:

// Record Altcraft push delivery (fires a delivery event)
AltcraftSDK.shared.pushEventFunctions.deliveryEvent(from: UNNotificationRequest)

public func openEvent(from request: UNNotificationRequest): Void

Registers an open event in the Altcraft platform. Pass UNNotificationRequest in the request parameter.

// Record Altcraft push open (fires an open event)
AltcraftSDK.shared.pushEventFunctions.openEvent(from: UNNotificationRequest)

Last updated on Sep 11, 2024
Previous
Quick Start
Next
SDK Configuration
  • Working with subscription statuses
    • Changing subscription status
    • Requesting subscription status
  • Provider push token management
    • Example of registering providers
  • Managing Push Notifications
    • Push Notification Initialization
    • Passing Push Notifications to the SDK
    • Receiving Foreground Notifications in the App (Optional)
    • Handling Notification Click Events in the App (Optional)
  • Mobile events
    • Subscription interface implementations
  • Receiving SDK events in the app
    • Subscribing to events
    • Unsubscribing from events
    • List of all SDK events
  • Additional functions
    • Functional profile field updates
    • Clearing SDK data
    • Manual Push Event Registration
© 2015 - 2025 Altcraft, LLC. All rights reserved.