Push Notifications
Learn how to implement push notifications in EasyAppSwiftUI
EasyAppSwiftUI uses OneSignal as the underlying push notification service. Even the free version of OneSignal generously provides unlimited push notifications.
It's recommended to test on a real device, as simulators may not receive notifications properly.

OneSignal Integration
1. Create OneSignal Project
To integrate OneSignal, you need to create a OneSignal project first. You can visit the OneSignal website to register an account and create a project. OneSignal's documentation is very detailed. You can refer to the official documentation to create the project.

2. Upload p8 Certificate

Uploading the p8 certificate is the most critical step. Please follow the official documentation to upload the p8 certificate.
3. Configure Xcode Push Capabilities
The EasyAppSwiftUI client template has already been configured for push notifications according to the iOS setup, so you don't need to handle this step. However, a critical step is setting up your own App Groups identifier instead of using the template's default one.

How to modify:
- First, uncheck the App Groups identifier configured in the template.
- Then click the + button to add your own App Groups identifier. Please refer to the OneSignal documentation for this step.
Everything else remains unchanged.
4. EasyAppNotifyKit Integration
EasyAppNotifyKit is EasyAppSwiftUI's push integration library, internally implementing push functionality based on OneSignal's SDK. Since this library is a private library of the EasyApp template, if you're not an EasyApp customer, you won't be able to see this library.
Recommended Approach
EasyApp has integrated EasyAppNotifyKit using a local SPM dependency approach, eliminating the need to manually configure Xcode's Github account. All code is included in the project's Packages folder.
Using Local SPM Dependency
Since Xcode's built-in git tool often has issues pulling private libraries, we've adopted a local SPM dependency approach to resolve this problem.
Project Structure:
How to Update Code:
If you need to modify EasyAppNotifyKit code to meet your business requirements, you can directly edit the source code in the EasyAppSwiftUI/Packages/EasyAppNotifyKit folder.
Old Method: Pull via Github Account (Deprecated)
Deprecated
This method is no longer recommended. Due to frequent issues with Xcode pulling private libraries, we have switched to the local SPM dependency approach. The following content is for reference only.
Step 1: Join FastEasyApp Organization
First ensure your Github account belongs to the FastEasyApp organization. If you've already purchased EasyApp but haven't joined the organization yet, please contact us:
Step 2: Login to Github Account in Xcode
Open Xcode settings:

Add Github account:

Enter your Github account and Token. To create a Token, click the Create a Token on Github button and follow the documentation:

After successfully linking your Github account, you can pull EasyAppNotifyKit.
5. Configure EasyAppNotifyKit
Configure your OneSignal
EasyApp enables push notifications by default. You can configure whether to enable it and the OneSignal app id here.
enum Constants {
enum OneSignal {
/// is enabledNotification
static let isEnabledNotification = true
/// The app id for the OneSignal service
static let appId = " your one signal app id"
}
}How to get your OneSignal app id:


Paste it into your project.
At this point, you've configured OneSignal's push notification functionality.
Next, you need to familiarize yourself with the usage of EasyAppNotifyKit.
Using EasyAppNotifyKit
EasyAppNotifyKit abstracts the push notification interface, allowing you to freely switch between different push service providers. You just need to implement the PushServiceProtocol protocol.
public struct PushServiceConfiguration {
let appId: String
let launchOptions: [UIApplication.LaunchOptionsKey: Any]?
public init(appId: String, launchOptions: [UIApplication.LaunchOptionsKey: Any]?) {
self.appId = appId
self.launchOptions = launchOptions
}
}
public protocol PushServiceProtocol {
/// Initialize the push service
/// - Parameter configuration: The configuration to initialize the push service
func initialize(with configuration: PushServiceConfiguration)
/// Request permissions
func requestPermissions()
/// Get permissions status
/// - Returns: The permissions status
func getPermissionsStatus() -> UNAuthorizationStatus
/// Login user
/// - Parameter userId: The user id to login
func loginUser(with userId: String)
/// Logout user
func logoutUser()
}If the protocol doesn't meet your current needs, you can extend it freely.
PushManager is a singleton that manages the current push service. You can configure and initialize the current push service through PushManager.
let oneSignalService = OneSignalService()
PushManager.shared.configure(with: oneSignalService)
let configuration: PushServiceConfiguration = PushServiceConfiguration(
appId: Constants.OneSignal.appId,
launchOptions: launchOptions
)
PushManager.shared.initialize(with: configuration)Request Permissions
Apple encourages users to actively request permissions when push notifications are needed. You can call the requestPermissions method to request permissions.
PushManager.shared.requestPermissions()For a better user experience, EasyAppNotifyKit provides three views to help you manage push notification permissions.
NotificationsPermissions()
NotificationGrantedView()
NotificationDeniedView()NotificationsPermissions view is used to request permissions.
NotificationGrantedView view is used to display when push notifications are authorized.
NotificationDeniedView view is used to display when push notifications are not authorized.
You can use these views freely or create custom views.
The EasyAppSwift client provides a Demo page specifically for testing notification permissions, located at:
import EasyAppNotifyKit
import SwiftUI
struct NotificationsDemo: View {
@State private var showNotificationStatus = false
@State private var showDeepLinkTest = false
var body: some View {
ScrollView {
VStack(spacing: 20) {
Button {
showNotificationStatus = true
} label: {
Label("dev_show_notification_status", systemImage: "bell")
}
.buttonStyle(.borderedProminent)
Text("dev_notifications_status_hint")
.font(.caption)
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
.padding(.horizontal)
Divider()
// OneSignal test suite from EasyAppNotifyKit
OSNotificationTestsView()
.background(Color(.systemBackground))
.clipShape(RoundedRectangle(cornerRadius: 12))
// Backend OneSignal payload test harness
NavigationLink {
BackendSentNotificationTestView()
} label: {
Label(
"dev_backend_push_test", systemImage: "paperplane.fill")
}
.buttonStyle(.bordered)
// Manual open the deep link target page for quick test
Button {
showDeepLinkTest = true
} label: {
Label(
"dev_open_deep_link_test_page",
systemImage: "arrow.right.circle")
}
.buttonStyle(.bordered)
}
.padding(.bottom)
}
.navigationTitle("dev_notifications_demo_title")
.padding(.top, 40)
.sheet(isPresented: $showNotificationStatus) {
// Use the new NotificationStatusContainer that automatically determines which view to show
NotificationStatusContainer()
}
.sheet(isPresented: $showDeepLinkTest) {
OSDeepLinkTargetTestView()
}
}
}Sending Notifications
Using OneSignal Dashboard to Send Notifications

Using Supabase Edge Functions to Send Notifications (Recommended)
EasyApp provides server-side capability to send notifications, which gives you more flexibility by controlling notification sending through code. Combined with your business requirements, you can achieve granular push targeting. OneSignal supports very rich custom fields, allowing you to implement notifications targeting specific users/batch users, certain regions, certain time periods, and other events. For details, please refer to the OneSignal documentation.
Supabase provides the send-onesignal-notification edge function, which calls OneSignal's REST API to send notifications. Therefore, you need to configure OneSignal's REST API Key and App ID in the EasyAppSupabase project.
Here's how to get OneSignal's REST API Key and App ID:
Login to your OneSignal account, go to the Dashboard, then click Settings.

Copy the App ID and create a new REST API key.
Next, configure the App ID and REST API Key in the EasyAppSupabase project. In the project root directory, create a .env.local file and configure the following content:
# Configure App ID
export ONESIGNAL_APP_ID=your_onesignal_app_id
# Configure REST API Key
export ONESIGNAL_REST_API_KEY=your_onesignal_rest_api_keyAfter starting the local development environment, you can use edge functions to send notifications.
npm run dev:functions.env.localNote: When deploying edge functions, make sure to configure ONESIGNAL_APP_ID and ONESIGNAL_REST_API_KEY in the environment variables.

The EasyAppSwift client provides a dedicated page for testing notifications. You can test notification reception here. The file is located at EasyAppSwiftUI/App/Developer/SubPages/Notifications/View/BackendSentNotificationTest.swift.
In-App Notification Display
EasyApp supports in-app message display. This means when your app is being used/in the foreground, we can customize the notification display effect. This functionality is already built into the EasyAppSwift client.
In AppDelegate, the addForegroundLifecycleListener event is monitored. When a notification arrives, the onWillDisplay event is triggered, where we can customize the message display effect. However, we need to configure some extension parameters to distinguish system notifications.
Besides the required parameters title and body, we need to add the additionalData parameter, which is a dictionary for storing notification extension parameters. We define symbolImage and status parameters to distinguish system notifications.
symbolImage: System notification iconstatus: System notification status
If symbolImage and status are not configured, the system will display notifications using the default effect.
How to configure:
-
In the OneSignal Dashboard, configure the notification's extension parameters.

-
In Supabase edge functions, configure directly according to the Rest API request parameters.
// Build OneSignal payload (target_channel enforced to 'push')
const onesignalPayload: Notification = {
app_id,
target_channel: "push",
// if you set include_aliases, you should delete included_segments parameter
// included_segments: ["Active Subscriptions"],
include_aliases: {
external_id: external_ids,
},
is_ios: true,
contents,
...(headings ? { headings } : {}),
...(data !== undefined ? { data } : {}),
...rest,
};
const result = await oneSignalClient.createNotification(onesignalPayload);Note here: If you specify the include_aliases parameter, you need to delete the included_segments parameter. included_segments: ["Active Subscriptions"] means sending to all users who have agreed to notifications. include_aliases means you are sending notifications to specific users. In the EasyAppSwiftUI template, when users log in, the template associates the user's userId with OneSignal's externalId, so the externalId here is the user's userId.
For details, check the implementation of the send-onesignal-notification edge function.
class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
if Constants.OneSignal.isEnabledNotification {
OneSignal.Notifications.addForegroundLifecycleListener(self)
}
return true
}
}
extension AppDelegate: OSNotificationClickListener, OSNotificationLifecycleListener {
func onWillDisplay(event: OSNotificationWillDisplayEvent) {
devLogger.info("Foreground notification: \(event.notification.title ?? "No Title")")
event.preventDefault()
guard let notifTitle = event.notification.title,
let notifMessage = event.notification.body,
let additionalData = event.notification.additionalData,
let symbol = additionalData["symbolImage"] as? String
else {
event.notification.display() // Show notification as usual
return
}
var notifHaptics: UINotificationFeedbackGenerator.FeedbackType = .warning
if let size = additionalData["status"] as? String {
if size == "error" {
notifHaptics = .error
} else if size == "success" {
notifHaptics = .success
}
UINotificationFeedbackGenerator().notificationOccurred(notifHaptics)
}
ShowNotification.showInAppNotification(
title: notifTitle,
message: notifMessage,
isSuccess: notifHaptics == .success,
duration: .seconds(seconds: 5),
systemImage: symbol,
handleViewTap: {
self.handleViewTap(additionalData: event.notification.additionalData)
}
)
}
}How to Send Notifications to Specific Users/Batch Users
- First, EasyAppNotifyKit exposes OneSignal's external ID association API. We just need to bind the external ID when the user logs in/registers.
PushManager.shared.loginUser(userId)- When logging out/deleting a user, call the
logoutUsermethod.
PushManager.shared.logoutUser()- After associating the external ID, we can use the
send-onesignal-notificationedge function and pass theexternal_idsparameter to send notifications to specific users/batch users.
external_ids: String array type.
For detailed logic, please refer to the client EasyAppSwiftUI/App/Developer/SubPages/Notifications/View/BackendSentNotificationTest.swift and the edge function EasyAppSupabase/supabase/functions/send-onesignal-notification/index.ts implementations.
How to Navigate to Pages When Clicking Notification Messages
Clicking notification messages to navigate to pages is the most common scenario, and EasyApp already provides a demo.
Similarly, we rely on the additionalData parameter. We specify passing the deeplink parameter, with the deeplink value being: EasyApp://notifykit?screen=osTests
For this logic, please refer to EasyAppSwiftUI/App/Developer/SubPages/Notifications/View/BackendSentNotificationTest.swift.
In AppDelegate, the addClickListener event is monitored. When a notification message is clicked, the onClick event is triggered, where we can handle the logic after clicking the notification message.
EasyApp broadcasts an osNotificationClicked event through NotificationCenter, which we can listen to elsewhere to handle the logic after clicking notification messages.
private func handleViewTap(additionalData: [AnyHashable: Any]? = nil) {
let ad = JSON(additionalData ?? [:])
let deeplink = ad["deeplink"].stringValue
if !deeplink.isEmpty {
// Broadcast click to allow host views to react (e.g., navigate)
NotificationCenter.default.post(
name: EasyAppNotifyKit.Notifications.osNotificationClicked,
object: nil,
userInfo: [
"deeplink": deeplink
]
)
}
}For example, in the demo, we listen to this event. When a notification message is clicked, a test page opens.
var body: some Scene {
WindowGroup {
ContentView()
#if DEBUG
.sheet(isPresented: $showDeepLinkTest) {
OSDeepLinkTargetTestView()
}
.onReceive(
NotificationCenter.default.publisher(
for: EasyAppNotifyKit.Notifications.osNotificationClicked)
) { notification in
let deeplink = notification.userInfo?["deeplink"] as? String
if let deeplink, deeplink == "EasyApp://notifykit?screen=osTests" {
withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) {
showDeepLinkTest = true
}
}
}
#endif
}
}Other cases follow the same pattern. If you have other business logic, you can put business parameters in the additionalData parameter, and when the client receives the parameters, you can handle them according to your business logic.
About App Store submission:
If you don't need push capabilities for now, it's recommended to remove notification-related code from the template. Remove the EasyAppNotifyKit dependency. Delete the entire EasyAppSwiftUI/App/Developer folder to avoid Apple reviewers mistakenly thinking you're using push capabilities.
Last updated on