Troubleshoot Issues on iOS Apps

App submission errors can occur when you're publishing your apps to App Store Connect. These errors are accompanied by an error message from Xcode or during Apple's app review.

This error can occur even if you aren't using MobilePush Location Messaging. If you're not using the SDK's location messaging functionality, your users aren't prompted to provide location permissions. This error occurs as the SDK is built with references to CoreLocation.framework. During app review, the automated binary scanning detects these references and requires purpose strings to be attached.

If you're not using Location Messaging, add a purpose string to your app’s Info.plist file as a placeholder, as shown in the following example.

To ensure the proper configuration of your Apple Push Notification Service (APNS) server and delivery of push notifications for different types of app builds (development or production), keep the following points in mind.

APNS Server Selection

Irrespective of the chosen .p8 key or .p12 certificate, Engagement attempts to contact the Sandbox APNS server when you check the Development radio button, and the Production APNS server when you check the Production radio button.

For Development Apps (Apps Built Using Development Certificates)

To send notifications only to those devices with apps on Debug configuration, choose the Development radio button in the MobilePush Administration page.

For Production Apps (Apps Built Using Production or Distribution Certificates)

To send notifications only to those devices with apps on Release configuration, select the Production radio button in the MobilePush Administration page.

If you encounter issues receiving messages in your app, consider these troubleshooting steps:

The MarketingCloudSDK.framework uses extensive internal logging to record actions performed by the SDK for informational and diagnostic purposes.

General, default-level logging is always enabled. Additionally, the SDK writes error and fault-level logs when conditions occur that must be recorded.

Enable logging using the following call:

When using SDK versions older than version 8.x, enable logging after SDK configuration.

Enable logging with CustomLogOutputter:

Enable logging with a standard log output and a log filter. For more filtering options, refer to Xcode autocompletion.

Clear previously set filtering options.

Query the state of debug-level logging using this call.

The SDK sends all logging output to Apple’s unified logging system. You can review this information using Xcode’s Devices and Simulators window or the macOS Console application. When SDK debug logging is enabled, the SDK uses the OS_LOG_TYPE_DEBUG value. Make sure to disable logging in your application for release builds to the App Store.

For more information about unified logging, see Logging on Apple Developer.

To test whether your app can receive a push directly from the Apple Push Notification Service (APNS), follow these steps.

  1. Get the push token from the SDK. For testing and troubleshooting purposes, retrieve your device token from a running app by calling sfmc_deviceToken() and send the result to yourself via email, alert, or another method.

  2. Trigger the APNS API directly from the command line if using a .p8 authentication key.

    The following script points to the development APNS service by default. To trigger push from the APNS production server, change the endPoint to https://api.push.apple.com in the following script.

  3. If using a .p12 certificate (legacy), trigger the APNS API directly from the command line.

    If your device receives the push notification, you can assume that you've set up your application correctly. The setup includes creating a .p8 authentication key or .p12 certificate, building the app with the correct bundle ID, and using the appropriate provisioning profiles.

Note the environment (development or production APNS) in which the push was successfully delivered while sending a test push. On the Engagement Administration page, point to the same environment under Sending Services > iOS Sending and send a push notification. Check if your device received the push notification.

Ensuring the selection of the correct APNS environment is crucial for the SDK to trigger push notifications to the appropriate APNS server. If you select the Development radio button, Engagement contacts the Sandbox APNS server. If you select the Production radio button, Engagement contacts the Production APNS server.

To retrieve a all of the information the SDK has and its current state, implement getSDKState() as shown in the following example.

The SDK outputs a JSON string as shown in the following example.

Testing your app while connected to a corporate network can prevent your test device from receiving messages, especially if your IT team doesn't configure port accessibility. Ensure that your IT team unblocks the following TCP ports to facilitate communication between your test device and the APNS servers for MobilePush functionality.

TCP PortDescription
5223Used by devices to communicate to the APNS servers
2195Used to send notifications to the APNS servers
2196Used by the APNS feedback service
443Used as a fallback service for Wi-Fi devices when those devices can't communicate with the APNS service on port 5223

Refer to the following articles when testing your app, or when troubleshooting why your devices aren't receiving messages or are receiving them sporadically.

The MobilePush SDK can coexist and work in the same app as other push vendor SDKs. However, we recommend checking with your other SDK vendors to ensure they also support a multi-push provider implementation. The following sections provide considerations for multiple push SDKs to successfully work together.

MPP implementations often affect the following functionalities, hence preventing the SDK from behaving as expected.

  • Receiving Device Tokens
  • Receiving Push Messages

Without device tokens, the SDK doesn't register the device properly and Engagement is unable to send push notifications to the device. The SDK expects you to pass push notifications to the setNotificationRequest method. Doing so makes the SDK aware of the notifications and allows it to handle them accordingly (for example, tracking and URL handling).

Apple has specific delegate methods that the consuming application must implement to register with APNS and receive push notifications. Implementing multiple push providers can affect the previously mentioned functionality since other vendors can provide wrapper methods for registration and receiving the notifications. Consuming applications often listen to the wrapper methods instead of actual Apple-provided delegate methods. An example of a common issue is when registration with Apple is done for one push provider without setting the deviceToken for other push providers.

Method Swizzling is the process of changing the implementation of an existing selector at runtime. If Method Swizzling is enabled, other push providers automatically intercept all the application delegate methods, which differ from the normal flow in setting up the deviceToken and notification userinfo.

Method Swizzling can confuse MobilePush SDK users about how and where to set the SDK's required API methods because another provider is changing the implementation without their knowledge.

To determine if non-MobilePush SDK providers use Swizzling, check to see whether they don't use the following methods.

MobilePush SDK users can handle MPP implementations with and without Swizzling enabled.

If Swizzling is enabled, implement the respective push provider’s delegate methods and set the following to the MobilePush SDK.

Configuration With Another Push Provider

Configure the SDK along with the other Push provider.

Handling DeviceToken

API: setDeviceToken(apnsToken)

Handling Notifications

When Swizzling is enabled in the other push provider, respective delegate methods are intercepted. For example, considering Firebase as the other push provider, when a push notification is received from Firebase, the payload received in the UNUserNotificationCenterDelegate's didReceive notification method is altered to receive a MessagingMessageInfo object. This payload alteration results in the message not being reported when passed to the SDK, as the payload expected by the SDK doesn't match.

API:

  • setNotificationUserInfo(userInfo)
  • setNotificationRequest(response.notification.request)

Notification messages from other providers are displayed in the device’s notification center. However, any action on the notification message from the SDK (for example, URL handling and reporting) doesn't work.

To disable Swizzling, refer to the other push provider’s documentation. When Swizzling is disabled in the other push provider, the default AppDelegate methods are called.

Implement AppDelegate Methods

Common areas for poor implementation can include device registration, geolocation, and more.

This isn’t an exhaustive list.

Registration

You must only make one call to a push SDK to register for push notifications. Otherwise, a single push notification can trigger multiple notification banners, alerts, or sounds. The MobilePush SDK gives the app developer the power to register.

Notification Settings

An app can invoke requestAuthorizationWithOptions and registerForRemoteNotifications multiple times. However, only the settings from the last call are used, as each successive call overwrites the previous settings.

Badging

There’s no way to guarantee the value of a badge.

Custom Payload Keys

If your implementation must distinguish between two notification providers, use custom keys or other payload-specific data to ensure that your app calls the correct SDK handler that supports multiple notification handlers.

Passing a third party’s notification to setNotificationRequest or setNotificationUserInfo is essentially a no-op call. The SDK only emits logs indicating the origin of the notification wasn’t from Engagement.

Geolocation

If you implement multiple SDKs that use location-enabled services, use only one SDK’s location enablement. Using more than one leads to unknown and unsupportable consequences. For example, the methods used by the other providers to interact with iOS CoreLocation services and enable location services are likely to affect each provider.

An app can monitor a limited number of geofences at any given time. This number depends on iOS version, device type, and other considerations. With multiple implementations competing for a limited resource, the user experience can suffer. Additionally, permissions needed to use location-enabled SDKs can overlap or conflict.

Feedback

Not all providers are able to detect if a device has been unregistered.

To see how notifications are handled using Firebase as a push provider, see the iOS LearningApp.

iOS Data Protection affects the SDK as described in the following table.

iOS Data Protection LevelSDK Behavior
No protectionSDK works in the foreground and background
Complete until first user authenticationSDK works in the foreground and background after the first unlock
Complete unless openSDK works in the foreground and background after the first unlock
CompleteSDK works only in the foreground after the device is unlocked

The MobilePush SDK requires access to files on the iOS Device file system. Some iOS Data Protection modes can prevent the SDK from accessing the needed files at certain times. During normal operation, the SDK must have access to files while running in the foreground and while running in the background. If the SDK can't access these files due to an iOS Data Protection mode, an error is logged and file access fails.

By default, the SDK sets the file protection type to NSFileProtectionCompleteUntilFirstUserAuthentication. With this file protection type, files are stored in an encrypted format on disk and can't be read from or written to until the user unlocks the device for the first time. As of version 8.0.9, the SDK continues to retain the default protection type (NSFileProtectionCompleteUntilFirstUserAuthentication). However, it also provides the capability to override the file protection type within the consuming application. You can choose different file protection types, such as NSFileProtectionComplete and NSFileProtectionCompleteUnlessOpen, based on your specific needs.

NSSQLiteErrorDomain errors can appear in logs if the FileProtectionType is NSFileProtectionComplete and the application goes to the background.

The following example shows how you can override the file protection type.

For an additional example of overriding the file protection type, see the learning application.

If you configure iOS data protection such that the file system isn’t accessible during the SDK configuration call, the SDK retries the call for up to 5 seconds. This retry period allows time for a user to unlock the device and make the file system accessible. If the user doesn’t unlock the device within 5 seconds, an error object is returned describing the error, and the configuration call fails, returning false. In this case, the SDK isn't configured, and no access to SDK methods must be attempted until the configure method returns true. The error object returns the error code configureDatabaseAccessError.

Certain features of the SDK require access to the file system as the app transitions to the foreground or background. Foreground operations include retrieving messages from Engagement for inbox messaging, location messaging, and sending analytic information back to Engagement. Background operations include sending analytic information back to Engagement. If any of these features are enabled via configuration, then an appropriate iOS Data Protection mode must be selected for them to work correctly.

For iOS applications upgraded from SDK versions 7.x to versions up to 8.0.6, the previous v7.x tags and attributes are retained on the device but not sent to the server. If an application doesn't reset or regenerate tags and attributes, the device sends empty tags and attributes to the system.

The following sections walk through the requirements for merging datasets successfully.

Using SPM, upgrade to the latest version of the MobilePush SDK for iOS and SFMCSDK for iOS.

The merging tool offers two options to merge attributes and tags: automatic merging and manual merging. In some scenarios, you can choose to defer or avoid merging the datasets. The merging tool defaults to an "opted out" state if you don't implement either of the merging methods. In the opted out state, tags and attributes aren't merged from the version 7.x dataset to your current application's dataset.

The automatic merging option attempts to merge old data into the current dataset, with the current data taking precedence over data within the version 7.x dataset.

The following tables illustrate how automatic merging behaves and how data is merged.

In the following tables, key-value pairs are denoted using : as the separator.

Attributes

Prior DatasetCurrent DatasetMerge Result
AemptyA
emptyAA
A, CAA, C
AA: clearedA: cleared

Tags

Prior DatasetCurrent DatasetMerge Result
SHIRTSemptySHIRTS
emptyPANTSPANTS
SHIRTSPANTSSHIRTS, PANTS
SHIRTSSHIRTS, PANTSSHIRTS, PANTS

The following code snippets show you how to configure the SDK to attempt an automatic merge.

To ensure the completion callback passed into setAutoMergePolicy is set before SDK initialization, place the following code snippets before SDK initialization.

The manual merge option enables you to receive both the prior data and the current data in a callback, providing the opportunity to choose what data ultimately ends up in the final dataset.

You can decide what attributes and tags are set in the current dataset. However, to set attributes and tags accordingly, you must retain the data until the SDK is initialized.

Access to the tags and attributes for versions 7.x and 8.x is provided before SDK initialization.

If you must run the merge tool again, you can attempt the merge multiple times.

Reattempting merges doesn’t roll back the current dataset but enables you to regain access to the data within the old version 7.x dataset.

The following troubleshooting guidance applies only to MobilePush iOS SDK versions up to 8.0.13.

Fatal Keychain Access exceptions occur when an application with an active MobilePush SDK integration tries to access Keychain on a locked device secured with Face ID or passcode-based authentication. iOS Data Protection modes prevent the MobilePush SDK from accessing these files.

The following sections describe common Keychain Access exceptions and provide guidance on resolving them.

The following are common occurrences of the errSecInteractionNotAllowed exception:

  • Fatal Exception: com.salesforce.security.keychainException dictionaryItemFromKeychain: Error attempting to look up keychain item: errSecInteractionNotAllowed
  • Fatal Exception: com.salesforce.security.keychainException setObject:forKey:: Error saving value to the keychain: errSecInteractionNotAllowed.
  • Fatal Exception: com.salesforce.security.keychainException writeToKeychain: Error adding keychain item: errSecInteractionNotAllowed.

To resolve errSecInteractionNotAllowed exceptions, follow these steps.

  • Upgrade to MobilePush iOS SDK version 8.0.8 and SFMC SDK version 1.0.6

  • Before initializing the SDK, set the setKeychainAccessErrorsAreFatal method to false as shown in the following example.

    Doing so logs the exception to the console instead of causing the application to crash.

    By default, the SDK sets setKeychainAccessErrorsAreFatal to true.

You can encounter the following error message that indicates a keychain access issue along with an unknown status code (-34018).

To resolve this issue, enable Keychain Sharing in Xcode's Signing & Capabilities. While enabling Keychain Sharing, you don't need to add identifiers specific to the SDK.

For an example of an ideal MobilePush iOS SDK implementation, review the iOS LearningApp.

Not all pieces of code used in the LearningApp are mandatory. Depending on your app, you can decide which parts to use or replicate and which ones to ignore.

The following troubleshooting guidance applies only to MobilePush iOS SDK versions up to 8.0.13. Starting version 8.1.0, the SDK automatically includes the bundle in applications, eliminating the need for manual addition.

When upgrading to the latest version of the SDK, changes to MarketingCloudSDK.bundle are expected. If you don't copy the right versions, your app can crash with exceptions as the older versions of MarketingCloudSDK.bundle don't have the required resources.

When you don't include the latest MarketingCloudSDK.bundle in your app, you can encounter the following exceptions.

NSInvalidArgumentException

Reason: +entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name 'SFMCEndpointConfigurationEntity'

NSInternalInconsistencyException

Reasons:

  • 'NSFetchRequest could not locate an NSEntityDescription for entity name 'SFMCEventConfigurationEntity''
  • 'Cannot create an NSPersistentStoreCoordinator with a nil model'

To resolve these exceptions, upgrade to the latest version of the SDK and then follow these steps.

  1. Remove the existing MarketingCloudSDK.bundle from Xcode under Build phases > Copy Resources Bundle.
  2. Based on your integration method, add the latest MarketingCloudSDK.bundle.

For every upgrade of the SDK, we recommend pulling MarketingCloudSDK.bundle each time to ensure your app is using the latest bundle.

Silent push notifications are delivered without an alert message or sound, typically to trigger updates to the app UI or background operations. A silent push notification wakes your app from a "Suspended" or "Not Running" state to update content or run certain background tasks without notifying users.

To use silent push notifications to trigger background tasks, you must configure your app to receive notifications even when it's in the background. To do so, navigate to the Signing & Capabilities pane on Xcode, and add the Background Modes capability to the main app target. Also ensure that you select the Remote notifications checkbox.

To handle silent push notifications, the app must implement the application:didReceiveRemoteNotification:fetchCompletionHandler: application delegate method. For more information, see Apple's documentation on Pushing background updates to your app.