EasyApp

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.

UnlimitedMobilePush

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.

configureOneSignal

2. Upload p8 Certificate

p8APNS

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.

AppGroups

How to modify:

  1. First, uncheck the App Groups identifier configured in the template.
  2. 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:

  • Follow us on X
  • Join us on Discord
  • Contact our support team via email

Step 2: Login to Github Account in Xcode

Open Xcode settings: xcodeSetting

Add Github account: addGithub

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

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.

EasyAppSwiftUI/Constants/Constants.swift
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:

oneSKeys

oneAppID

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:

EasyAppSwiftUI/App/Developer/SubPages/Notifications/View/NotificationsDemo.swift
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

push1 push2

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.

oneSKeys oneSignalRestAPIKey

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_key

After starting the local development environment, you can use edge functions to send notifications.

npm run dev:functions.env.local

Note: When deploying edge functions, make sure to configure ONESIGNAL_APP_ID and ONESIGNAL_REST_API_KEY in the environment variables. supaKeys

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 icon
  • status: System notification status

If symbolImage and status are not configured, the system will display notifications using the default effect.

How to configure:

  1. In the OneSignal Dashboard, configure the notification's extension parameters.

    additionalData

  2. In Supabase edge functions, configure directly according to the Rest API request parameters.

EasyAppSupabase/supabase/functions/send-onesignal-notification/index.ts
    // 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.

EasyAppSwiftUI/EasyAppSwiftUIApp.swift
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

  1. 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)
  1. When logging out/deleting a user, call the logoutUser method.
PushManager.shared.logoutUser()
  1. After associating the external ID, we can use the send-onesignal-notification edge function and pass the external_ids parameter 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.

EasyAppSwiftUI/EasyAppSwiftUIApp.swift
 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