Skip to content

Commit fdc6ba0

Browse files
authored
chore: [SDK-4324] add Capacitor SPM example demo project (#4)
1 parent d90866e commit fdc6ba0

157 files changed

Lines changed: 6715 additions & 623 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Package.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,25 @@
33
import PackageDescription
44

55
let package = Package(
6-
name: "OneSignalCapacitorPlugin",
6+
name: "OnesignalCapacitorPlugin",
77
platforms: [.iOS(.v14)],
88
products: [
99
.library(
10-
name: "OneSignalCapacitorPlugin",
11-
targets: ["OneSignalCapacitorPlugin"]
10+
name: "OnesignalCapacitorPlugin",
11+
targets: ["OnesignalCapacitorPlugin"]
1212
)
1313
],
1414
dependencies: [
15-
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "7.0.0"),
16-
.package(url: "https://github.com/nicklama/onesignal-xcframework-spm", from: "5.0.0")
15+
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", "7.0.0"..<"9.0.0"),
16+
.package(url: "https://github.com/OneSignal/OneSignal-XCFramework", from: "5.0.0")
1717
],
1818
targets: [
1919
.target(
20-
name: "OneSignalCapacitorPlugin",
20+
name: "OnesignalCapacitorPlugin",
2121
dependencies: [
2222
.product(name: "Capacitor", package: "capacitor-swift-pm"),
2323
.product(name: "Cordova", package: "capacitor-swift-pm"),
24-
.product(name: "OneSignalFramework", package: "onesignal-xcframework-spm")
24+
.product(name: "OneSignalFramework", package: "OneSignal-XCFramework")
2525
],
2626
path: "ios/Sources/OneSignalCapacitorPlugin"
2727
)

examples/build.md

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,6 @@ All OneSignal SDK interactions and state are managed through a single `useOneSig
170170

171171
`HomeScreen` calls `useOneSignal()` and passes state/callbacks down to section components as props.
172172

173-
### Repository Pattern
174-
175-
`OneSignalRepository` (`src/repositories/OneSignalRepository.ts`) wraps all OneSignal SDK calls with `Capacitor.isNativePlatform()` guards, providing a safe abstraction that no-ops on web. It also delegates notification sending and user fetching to `OneSignalApiService`.
176-
177173
### Services
178174

179175
- `OneSignalApiService` (`src/services/OneSignalApiService.ts`) — Singleton REST client using `fetch` to send notifications via the OneSignal API and fetch user data
@@ -338,7 +334,7 @@ Under the hood, `addEventListener` wraps Capacitor's `this._plugin.addListener(n
338334
The `useOneSignal()` hook centralizes all SDK state and actions:
339335

340336
- **Reactive state** via `useState`: app ID, consent settings, external user ID, push subscription ID, push enabled, notification permission, IAM paused, location shared, aliases, emails, SMS numbers, tags, triggers, loading state
341-
- **Refs** via `useRef`: mount tracking (`mountedRef`), request sequencing (`requestSequenceRef`) to discard stale API responses
337+
- **Refs** via `useRef`: request sequencing (`requestSequenceRef`) to discard stale API responses
342338
- **Effects** via `useEffect`: one-time SDK init + listener registration with full cleanup
343339
- **Memoized callbacks** via `useCallback`: `fetchUserDataFromApi` for API-driven state refresh
344340

@@ -494,19 +490,17 @@ The iOS Xcode project includes extension targets:
494490
- `ios/App/OneSignalNotificationServiceExtension/NotificationService.swift` — forwards to `OneSignalExtension` for rich notification support
495491
- `ios/App/OneSignalNotificationServiceExtension/Info.plist` — extension point `com.apple.usernotifications.service`
496492

497-
### Podfile
493+
### Swift Package Manager
498494

499-
The Podfile includes the NSE target:
495+
The demo uses Swift Package Manager (SPM) instead of CocoaPods. Capacitor manages the App target's plugin dependencies through `ios/App/CapApp-SPM/Package.swift`. The extension targets reference the OneSignal XCFramework Swift package directly:
500496

501-
```ruby
502-
target 'App' do
503-
capacitor_pods
504-
end
497+
- App target → `CapApp-SPM` local package (Capacitor + plugin products, regenerated by `cap sync`)
498+
- `OneSignalNotificationServiceExtension``OneSignalExtension` product from `https://github.com/OneSignal/OneSignal-XCFramework`
499+
- `OneSignalWidgetExtension``OneSignalFramework` product from `https://github.com/OneSignal/OneSignal-XCFramework` (transitively brings in `OneSignalLiveActivities` for Live Activity widgets)
505500

506-
target 'OneSignalNotificationServiceExtension' do
507-
pod 'OneSignalXCFramework', '>= 5.0.0', '< 6.0'
508-
end
509-
```
501+
`ios/debug.xcconfig` is wired up as the App target's Debug base configuration so Capacitor's debug-only behaviors stay enabled.
502+
503+
To migrate an existing CocoaPods-based project to SPM, run `bunx cap spm-migration-assistant` and then add the local `CapApp-SPM` package and any extension dependencies through Xcode's Package Dependencies tab.
510504

511505
---
512506

@@ -525,7 +519,7 @@ end
525519
- `handleApplicationNotifications: false` in `capacitor.config.ts` to let OneSignal handle notifications
526520
- Capacitor setup with push notification entitlement and app groups
527521
- NSE target in Xcode project
528-
- Podspec named `OnesignalCapacitorPlugin` to match Capacitor's derived pod name (Capacitor's `fixName` converts `onesignal-capacitor-plugin``OnesignalCapacitorPlugin`)
522+
- SPM product named `OnesignalCapacitorPlugin` to match Capacitor's derived package name (Capacitor's `fixName` converts `onesignal-capacitor-plugin``OnesignalCapacitorPlugin`)
529523

530524
### Custom Notification Sound
531525

@@ -620,7 +614,13 @@ examples/
620614
├── OneSignalNotificationServiceExtension/ # NSE target
621615
│ ├── NotificationService.swift
622616
│ └── Info.plist
623-
└── Podfile
617+
├── OneSignalWidget/ # Widget + Live Activity target
618+
│ ├── OneSignalWidgetBundle.swift
619+
│ ├── OneSignalWidgetLiveActivity.swift
620+
│ └── Info.plist
621+
└── CapApp-SPM/ # Capacitor-managed SPM package
622+
├── Package.swift # Auto-generated by `cap sync`
623+
└── Sources/CapApp-SPM/CapApp-SPM.swift
624624
```
625625

626626
---
@@ -629,7 +629,6 @@ examples/
629629

630630
- **TypeScript strict mode** on all source files, avoiding `any` and type assertions
631631
- **Central `useOneSignal` hook** manages all SDK state and actions; section components are pure props-driven
632-
- **Repository pattern** wraps SDK calls with `Capacitor.isNativePlatform()` guards
633632
- **Service layer** separates REST API calls, persistence, and logging from UI
634633
- **React + Ionic React** for component-based UI with React Router navigation
635634
- **Separate section components** per feature area for maintainability

examples/demo/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
VITE_ONESIGNAL_APP_ID=your_onesignal_app_id
2+
VITE_ONESIGNAL_API_KEY=your_rest_api_key
3+
VITE_E2E_MODE=false

examples/demo/android/.gitignore

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore
2+
3+
# Built application files
4+
*.apk
5+
*.aar
6+
*.ap_
7+
*.aab
8+
9+
# Files for the ART/Dalvik VM
10+
*.dex
11+
12+
# Java class files
13+
*.class
14+
15+
# Generated files
16+
bin/
17+
gen/
18+
out/
19+
# Uncomment the following line in case you need and you don't have the release build type files in your app
20+
# release/
21+
22+
# Gradle files
23+
.gradle/
24+
build/
25+
26+
# Local configuration file (sdk path, etc)
27+
local.properties
28+
29+
# Proguard folder generated by Eclipse
30+
proguard/
31+
32+
# Log Files
33+
*.log
34+
35+
# Android Studio Navigation editor temp files
36+
.navigation/
37+
38+
# Android Studio captures folder
39+
captures/
40+
41+
# IntelliJ
42+
*.iml
43+
.idea/workspace.xml
44+
.idea/tasks.xml
45+
.idea/gradle.xml
46+
.idea/assetWizardSettings.xml
47+
.idea/dictionaries
48+
.idea/libraries
49+
# Android Studio 3 in .gitignore file.
50+
.idea/caches
51+
.idea/modules.xml
52+
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
53+
.idea/navEditor.xml
54+
55+
# Keystore files
56+
# Uncomment the following lines if you do not want to check your keystore files in.
57+
#*.jks
58+
#*.keystore
59+
60+
# External native build folder generated in Android Studio 2.2 and later
61+
.externalNativeBuild
62+
.cxx/
63+
64+
# Google Services (e.g. APIs or Firebase)
65+
# google-services.json
66+
67+
# Freeline
68+
freeline.py
69+
freeline/
70+
freeline_project_description.json
71+
72+
# fastlane
73+
fastlane/report.xml
74+
fastlane/Preview.html
75+
fastlane/screenshots
76+
fastlane/test_output
77+
fastlane/readme.md
78+
79+
# Version control
80+
vcs.xml
81+
82+
# lint
83+
lint/intermediates/
84+
lint/generated/
85+
lint/outputs/
86+
lint/tmp/
87+
# lint/reports/
88+
89+
# Android Profiling
90+
*.hprof
91+
92+
# Cordova plugins for Capacitor
93+
capacitor-cordova-android-plugins
94+
95+
# Copied web assets
96+
app/src/main/assets/public
97+
98+
# Generated Config files
99+
app/src/main/assets/capacitor.config.json
100+
app/src/main/assets/capacitor.plugins.json
101+
app/src/main/res/xml/config.xml
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/build/*
2+
!/build/.npmkeep
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
plugins {
2+
id("com.android.application")
3+
}
4+
5+
android {
6+
namespace = "com.onesignal.example"
7+
compileSdk = rootProject.extra["compileSdkVersion"] as Int
8+
defaultConfig {
9+
applicationId = "com.onesignal.example"
10+
minSdk = rootProject.extra["minSdkVersion"] as Int
11+
targetSdk = rootProject.extra["targetSdkVersion"] as Int
12+
versionCode = 1
13+
versionName = "1.0"
14+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
15+
androidResources {
16+
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
17+
// Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
18+
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~"
19+
}
20+
}
21+
buildTypes {
22+
release {
23+
isMinifyEnabled = false
24+
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
25+
}
26+
}
27+
}
28+
29+
repositories {
30+
flatDir {
31+
dirs("../capacitor-cordova-android-plugins/src/main/libs", "libs")
32+
}
33+
}
34+
35+
dependencies {
36+
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
37+
implementation(libs.androidx.appcompat)
38+
implementation(libs.androidx.coordinatorlayout)
39+
implementation(libs.androidx.core.splashscreen)
40+
implementation(project(":capacitor-android"))
41+
testImplementation(libs.junit)
42+
androidTestImplementation(libs.androidx.test.junit)
43+
androidTestImplementation(libs.androidx.test.espresso.core)
44+
implementation(project(":capacitor-cordova-android-plugins"))
45+
}
46+
47+
apply(from = "capacitor.build.gradle")
48+
49+
try {
50+
val servicesJSON = file("google-services.json")
51+
if (servicesJSON.readText().isNotEmpty()) {
52+
apply(plugin = "com.google.gms.google-services")
53+
}
54+
} catch (e: Exception) {
55+
logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
56+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
3+
<application
4+
android:allowBackup="true"
5+
android:icon="@mipmap/ic_launcher"
6+
android:label="@string/app_name"
7+
android:roundIcon="@mipmap/ic_launcher_round"
8+
android:supportsRtl="true"
9+
android:theme="@style/AppTheme">
10+
<activity
11+
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation|density"
12+
android:name=".MainActivity"
13+
android:label="@string/title_activity_main"
14+
android:theme="@style/AppTheme.NoActionBarLaunch"
15+
android:launchMode="singleTask"
16+
android:exported="true">
17+
<intent-filter>
18+
<action android:name="android.intent.action.MAIN" />
19+
<category android:name="android.intent.category.LAUNCHER" />
20+
</intent-filter>
21+
</activity>
22+
23+
<provider
24+
android:name="androidx.core.content.FileProvider"
25+
android:authorities="${applicationId}.fileprovider"
26+
android:exported="false"
27+
android:grantUriPermissions="true">
28+
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
29+
</provider>
30+
</application>
31+
32+
<!-- Permissions -->
33+
34+
<uses-permission android:name="android.permission.INTERNET" />
35+
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
36+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
37+
</manifest>

0 commit comments

Comments
 (0)