SDK: Swift / Obj-C
Prerequisites
Before you can integrate the SDK with your app the following requirements must be met:
An iOS 11+ device (iPhone, iPad, or iPod Touch) to develop and test on, or a simulator that meets the requirements in Testing on a simulator.
A Mac OS device with XCode 14+ (Swift 5.7, if using Swift) installed.
An iOS P12 Push Certificate or P8 Key. If you do not already have these follow our documentation to generate them.
An App ID registered for your bundle id with the
Push NotificationsandApp Groupscapabilities enabled. See Register Your App ID.SDK Key: Obtained from the Pushly Platform in the
Domain Settingspage under theOverview>Details>Domain Informationsection.
Step 1: Import the PushSDK Framework
The PushSDK Framework is compatible with both Swift and Objective-C.
Select the root project and then your primary application target >
GeneralClick
+>Add Other>Add Package DependencyunderFrameworks, Libraries, and Embedded ContentSearch using the Package URL: https://github.com/pushly/push-sdk-ios
Set the Dependency Rule to:
Range of Versionsand1.0.0<2.0.0Click
Add Package

Ensure the
Pushlyoption is checked and clickAdd Package

Finally, add the
Pushlylibrary to the Primary Application Target

The PushSDK Framework is compatible with both Swift and Objective-C.
CocoaPods Link: https://cocoapods.org/pods/Pushly
Add the following to your Podfile:
Then run a pod install inside your terminal, or from CocoaPods.app.
Step 2: Add the Notification Service Extension
The Notification Service Extension is required to enable rich notifications that support images and custom actions.
Within your app's XCode project select File > New > Target. Select Notification Service Extension inside the iOS templates tab and click Next.

Enter "NotificationServiceExtension" for the Product Name and any other configuration details for your app extension and then click Finish but do not click activate on the subsequent dialog.

Click Cancel on the dialoging prompting you to activate the service extension so that you can set the deployment target once the modal is closed.

Make sure to set your deployment target to the same as your primary application target. Unless you have a specific reason not to, you should set the Deployment Target to be iOS 11 which is the lowest supported version of iOS in the PushSDK Framework and in the most recent releases of XCode.
Next, add the Pushly library to your Notification Service Extension:
Click + under Frameworks and Libraries and select the already imported Pushly library.

Now, open the newly created Notification Service Extension and replace the code with the following:
You can ignore dependency errors, such as Module Pushly not found, at this point as they will be resolved during the next build.
Step 3: Add Capabilities to Primary Application Target
The Push Notifications capability should only be added to the primary application target.
Select the root project and then your primary application target > Signing & Capabilities

Click the + Capability and add Push Notifications.
Click the + Capability and add Background Modes.
After adding the Background Modes capability ensure that Remote Notifications are enabled.

Adding Push Notifications writes the aps-environment entitlement into the target's entitlements file — development for debug builds, and production for release, TestFlight, and App Store builds. Xcode manages this value; you should not set it by hand.
If the entitlement is missing, the app cannot register with APNs and registration fails at launch with:
That error means either the Push Notifications capability is not present on this target, or the provisioning profile predates the capability being enabled on your App ID. Confirm the capability is listed, then let Xcode regenerate the profile.
Step 4: Configure the Shared App Group
Your app and its Notification Service Extension run as two separate processes. The shared App Group container is the only storage both can reach, and the PushSDK relies on it for everything that begins in one process and completes in the other.
The same App Group must be declared on both targets. If only one carries it, each process silently falls back to its own private storage and the sharing simply does not happen — with no error raised at the point of failure.
Naming
The SDK derives the identifier from your app's bundle id rather than reading it from configuration, so the two targets cannot disagree about which container to use. It must be exactly:
For an app whose bundle id is com.pushly.example, the App Group is group.com.pushly.example.push. The Notification Service Extension uses the app's bundle id here, not the extension's own.
Add it to the primary application target
With your primary application target selected under Signing & Capabilities, click the + Capability and add App Groups.
Click the + symbol located inside the App Groups section to add a new named container, and name it as above.

Check the box next to the newly created App Group in the primary application target.
Add it to the Notification Service Extension
Select the root project and then the NotificationServiceExtension > Signing & Capabilities
Click the + Capability and add App Groups.
Check the box next to the same App Group in the NotificationServiceExtension.
Adding the capability writes com.apple.security.application-groups into each target's entitlements file, containing the group identifier. Both files must list it.
With Automatically manage signing enabled, Xcode creates the App Group identifier, enables the App Groups capability on your App ID, and regenerates the provisioning profiles for both targets. If Xcode complains about the new App Group, it is usually still provisioning — refresh the list once it has finished.
If you manage signing manually, creating the identifier is not enough on its own: enable App Groups on the App ID as well, then regenerate and download both profiles.
What breaks without it
The SDK keeps running, but the features that depend on cross-process state stop working:
Notification impressions — recorded by the extension and completed by the app, so a severed container costs every one of them.
Badge counts — the shared count is no longer visible to both processes.
Service-extension attribution — notifications handled by the extension are no longer attributed correctly.
The per-installation event sequence counter is also guarded by a cross-process lock that lives in the container. Without the container the SDK falls back to a best-effort in-process increment, so events still carry a sequence value, but it is no longer coordinated between the two processes.
Confirming it is configured
The SDK reports the state of the shared container once per launch. When it is missing you will see:
When it is configured correctly, the same check reports the resolved identifier at debug level:
Step 5: SDK Initialization
First import the Pushly library in your AppDelegate
Add the following code to the AppDelegate's didFinishLaunchingWithOptions method of your application.
Replace the REPLACE_WITH_SDK_KEY in the setConfiguration method with the SDK Key from the platform settings page.
SwiftUI apps. If your app supplies its delegate with @UIApplicationDelegateAdaptor, place the setConfiguration call in that delegate's didFinishLaunchingWithOptions exactly as shown above — the integration works normally.
Be aware when debugging that SwiftUI installs its own SwiftUI.AppDelegate as UIApplication.shared.delegate and forwards callbacks on to your adapted class. Inspecting UIApplication.shared.delegate will therefore not show your own delegate type. This is expected and does not indicate a broken integration.
Step 6: Test the Implementation
Run your app on a physical iOS device, or on a simulator that meets the requirements in Testing on a simulator, to make sure it builds correctly.
The code you added in the previous step will show the push permission dialog upon app open. This can be customized using SDK methods to control when the dialog shows.
After accepting the dialog, log into the platform and navigate to Notifications > Create Notification and send your first notification, targeting the Native: iOS channel, to your device.
Testing on a simulator
A simulator can register with APNs and receive real remote notifications, so it can be used to test subscription and end-to-end delivery. This requires all of the following:
A Mac with Apple silicon or a T2 chip
macOS 13 or later
An iOS 16 or later simulator
Xcode 14 or later
When those conditions are met the app registers normally and iOS issues a genuine device token. Two things differ from a physical device:
The token is sandbox-only. Notifications must be sent through the APNs sandbox environment. A debug build carries
aps-environment: development, which is what routes sends to sandbox, so this is handled for you.The token is tied to that simulator on that Mac. It is specific to the combination of the simulator and the Mac hardware it runs on, so it is not portable, and erasing the simulator issues a new one.
If any of the requirements above are not met — an Intel Mac without a T2 chip, macOS 12 or earlier, or a simulator older than iOS 16 — registration fails with APNs error 3010 and the SDK logs:
On those setups token registration has to be exercised on a physical device.
Delivering a notification without APNs
xcrun simctl push delivers a notification straight to a booted simulator with no APNs setup at all, and works on any simulator regardless of the requirements above. It is the quickest way to check notification presentation and tap handling.
Save a payload as payload.apns:
Then deliver it to a booted simulator, replacing the bundle id with your own:
For the SDK to recognise the payload as a Pushly notification and process it, the payload must carry all of the following:
aps.alertas a dictionary — a plain string alert is not recognisedpiid— stringnotification_id— integerlanding_url— string
If any of these are missing or the wrong type, iOS still displays the notification but the SDK ignores it, and no SDK callbacks or events fire.
Notifications delivered with simctl push never travel through APNs or Pushly, so they produce no delivery, impression, or click reporting in the platform. Use them to verify presentation and tap handling only.
Next Steps
Once you have confirmed the SDK is working properly you may continue to add additional optional functionality like:
Last updated