diff --git a/NotificationService/Info.plist b/NotificationService/Info.plist
new file mode 100644
index 0000000..57421eb
--- /dev/null
+++ b/NotificationService/Info.plist
@@ -0,0 +1,13 @@
+
+
+
+
+ NSExtension
+
+ NSExtensionPointIdentifier
+ com.apple.usernotifications.service
+ NSExtensionPrincipalClass
+ $(PRODUCT_MODULE_NAME).NotificationService
+
+
+
diff --git a/NotificationService/NotificationService.entitlements b/NotificationService/NotificationService.entitlements
new file mode 100644
index 0000000..8fe801a
--- /dev/null
+++ b/NotificationService/NotificationService.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.application-groups
+
+ group.ntfy
+
+
+
diff --git a/NotificationService/NotificationService.swift b/NotificationService/NotificationService.swift
new file mode 100644
index 0000000..3a9f305
--- /dev/null
+++ b/NotificationService/NotificationService.swift
@@ -0,0 +1,55 @@
+//
+// NotificationService.swift
+// NotificationService
+//
+// Created by Andrew Cope on 2/7/22.
+//
+
+import UserNotifications
+
+class NotificationService: UNNotificationServiceExtension {
+
+ var contentHandler: ((UNNotificationContent) -> Void)?
+ var bestAttemptContent: UNMutableNotificationContent?
+
+ override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
+ self.contentHandler = contentHandler
+ bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
+ print("RECEIVED NOTIFICATION")
+ if let bestAttemptContent = bestAttemptContent {
+ // Modify the notification content here...
+ bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
+
+ let userInfo = bestAttemptContent.userInfo
+ print(userInfo)
+
+ if //let notificationId = userInfo["id"] as? Int64,
+ let notificationTopic = userInfo["topic"] as? String,
+ //let notificationTimestamp = userInfo["time"] as? Int64,
+ let notificationTitle = userInfo["title"] as? String,
+ let notificationMessage = userInfo["message"] as? String {
+ print("Attempting to create notification")
+ if let subscription = Database.current.getSubscription(topic: notificationTopic) {
+ let ntfyNotification = NtfyNotification(id: Int64(1), subscriptionId: subscription.id, timestamp: Int64(0), title: notificationTitle, message: notificationMessage)
+ ntfyNotification.save()
+ print("Created notification")
+ }
+ } else {
+ print("ERROR")
+ }
+
+ contentHandler(bestAttemptContent)
+ } else {
+ print("No best content?")
+ }
+ }
+
+ override func serviceExtensionTimeWillExpire() {
+ // Called just before the extension will be terminated by the system.
+ // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
+ if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
+ contentHandler(bestAttemptContent)
+ }
+ }
+
+}
diff --git a/ntfy-ios/App/AppDelegate.swift b/ntfy-ios/App/AppDelegate.swift
index a59d506..9ba4d69 100644
--- a/ntfy-ios/App/AppDelegate.swift
+++ b/ntfy-ios/App/AppDelegate.swift
@@ -82,7 +82,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
print("Attempting to create notification")
if let subscription = Database.current.getSubscription(topic: notificationTopic) {
let ntfyNotification = NtfyNotification(id: Int64(1), subscriptionId: subscription.id, timestamp: Int64(0), title: notificationTitle, message: notificationMessage)
- ntfyNotification.save()
+ //ntfyNotification.save()
print("Created notification")
}
} else {
diff --git a/ntfy-ios/Utils/Database.swift b/ntfy-ios/Utils/Database.swift
index e8056ed..06bbd0a 100644
--- a/ntfy-ios/Utils/Database.swift
+++ b/ntfy-ios/Utils/Database.swift
@@ -33,9 +33,10 @@ class Database {
// Initialize
init() {
do {
- if let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
+ let fileManager = FileManager.default
+ if let path = fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.ntfy") {
// Connect to the database
- db = try Connection("\(path)/ntfy.sh.sqlite3")
+ db = try Connection("\(path.path)/ntfy.sh.sqlite3")
// Initialize Subscriptions table
try db?.run(subscriptions.create(ifNotExists: true) { table in
@@ -75,6 +76,7 @@ class Database {
}
func getSubscription(topic: String) -> NtfySubscription? {
+ print("Getting subscription")
do {
print("Looking for subscription for topic " + topic)
if let subscription = try db?.pluck(subscriptions.filter(subscription_topic == topic)) {
diff --git a/ntfy.sh.entitlements b/ntfy.sh.entitlements
index 903def2..9b23adb 100644
--- a/ntfy.sh.entitlements
+++ b/ntfy.sh.entitlements
@@ -4,5 +4,9 @@
aps-environment
development
+ com.apple.security.application-groups
+
+ group.ntfy
+
diff --git a/ntfy.sh.xcodeproj/project.pbxproj b/ntfy.sh.xcodeproj/project.pbxproj
index 893a4a8..6a4642a 100644
--- a/ntfy.sh.xcodeproj/project.pbxproj
+++ b/ntfy.sh.xcodeproj/project.pbxproj
@@ -7,7 +7,14 @@
objects = {
/* Begin PBXBuildFile section */
+ 800FA49627B19CA0005D05B9 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 800FA49527B19CA0005D05B9 /* NotificationService.swift */; };
+ 800FA49A27B19CA0005D05B9 /* NotificationService.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 800FA49327B19CA0005D05B9 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
8015C3DC2793AB1500E6F001 /* Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8015C3DB2793AB1400E6F001 /* Database.swift */; };
+ 802D626927B1A36E00DDD3AF /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 802D626827B1A36E00DDD3AF /* FirebaseMessaging */; };
+ 802D626B27B1A37400DDD3AF /* SQLite in Frameworks */ = {isa = PBXBuildFile; productRef = 802D626A27B1A37400DDD3AF /* SQLite */; };
+ 802D626C27B1A37700DDD3AF /* Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8015C3DB2793AB1400E6F001 /* Database.swift */; };
+ 802D626D27B1A37900DDD3AF /* NtfyNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80A313F62793B56800F1A639 /* NtfyNotification.swift */; };
+ 802D626E27B1A37C00DDD3AF /* NtfySubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80A313F42793B1CF00F1A639 /* NtfySubscription.swift */; };
80386E802793585B009B0480 /* AppMain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80386E7F2793585B009B0480 /* AppMain.swift */; };
80386E822793585B009B0480 /* SubscriptionsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80386E812793585B009B0480 /* SubscriptionsList.swift */; };
80386E842793585C009B0480 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 80386E832793585C009B0480 /* Assets.xcassets */; };
@@ -25,7 +32,35 @@
80A313FD2793C42000F1A639 /* NotificationRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80A313FC2793C42000F1A639 /* NotificationRow.swift */; };
/* End PBXBuildFile section */
+/* Begin PBXContainerItemProxy section */
+ 800FA49827B19CA0005D05B9 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 80386E742793585B009B0480 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 800FA49227B19CA0005D05B9;
+ remoteInfo = NotificationService;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 800FA49B27B19CA0005D05B9 /* Embed App Extensions */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 13;
+ files = (
+ 800FA49A27B19CA0005D05B9 /* NotificationService.appex in Embed App Extensions */,
+ );
+ name = "Embed App Extensions";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
/* Begin PBXFileReference section */
+ 800FA49327B19CA0005D05B9 /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
+ 800FA49527B19CA0005D05B9 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; };
+ 800FA49727B19CA0005D05B9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 800FA49F27B19D2F005D05B9 /* NotificationService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationService.entitlements; sourceTree = ""; };
8015C3DB2793AB1400E6F001 /* Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Database.swift; sourceTree = ""; };
80386E7C2793585B009B0480 /* ntfy.sh.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ntfy.sh.app; sourceTree = BUILT_PRODUCTS_DIR; };
80386E7F2793585B009B0480 /* AppMain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMain.swift; sourceTree = ""; };
@@ -46,6 +81,15 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 800FA49027B19CA0005D05B9 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 802D626B27B1A37400DDD3AF /* SQLite in Frameworks */,
+ 802D626927B1A36E00DDD3AF /* FirebaseMessaging in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
80386E792793585B009B0480 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -58,6 +102,16 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 800FA49427B19CA0005D05B9 /* NotificationService */ = {
+ isa = PBXGroup;
+ children = (
+ 800FA49F27B19D2F005D05B9 /* NotificationService.entitlements */,
+ 800FA49527B19CA0005D05B9 /* NotificationService.swift */,
+ 800FA49727B19CA0005D05B9 /* Info.plist */,
+ );
+ path = NotificationService;
+ sourceTree = "";
+ };
8015C3DA2793AB0300E6F001 /* Utils */ = {
isa = PBXGroup;
children = (
@@ -66,12 +120,21 @@
path = Utils;
sourceTree = "";
};
+ 802D626327B19FFE00DDD3AF /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
80386E732793585B009B0480 = {
isa = PBXGroup;
children = (
80386EA0279363A2009B0480 /* ntfy.sh.entitlements */,
80386E7E2793585B009B0480 /* ntfy-ios */,
+ 800FA49427B19CA0005D05B9 /* NotificationService */,
80386E7D2793585B009B0480 /* Products */,
+ 802D626327B19FFE00DDD3AF /* Frameworks */,
);
sourceTree = "";
};
@@ -79,6 +142,7 @@
isa = PBXGroup;
children = (
80386E7C2793585B009B0480 /* ntfy.sh.app */,
+ 800FA49327B19CA0005D05B9 /* NotificationService.appex */,
);
name = Products;
sourceTree = "";
@@ -148,6 +212,27 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
+ 800FA49227B19CA0005D05B9 /* NotificationService */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 800FA49E27B19CA0005D05B9 /* Build configuration list for PBXNativeTarget "NotificationService" */;
+ buildPhases = (
+ 800FA48F27B19CA0005D05B9 /* Sources */,
+ 800FA49027B19CA0005D05B9 /* Frameworks */,
+ 800FA49127B19CA0005D05B9 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = NotificationService;
+ packageProductDependencies = (
+ 802D626827B1A36E00DDD3AF /* FirebaseMessaging */,
+ 802D626A27B1A37400DDD3AF /* SQLite */,
+ );
+ productName = NotificationService;
+ productReference = 800FA49327B19CA0005D05B9 /* NotificationService.appex */;
+ productType = "com.apple.product-type.app-extension";
+ };
80386E7B2793585B009B0480 /* ntfy.sh */ = {
isa = PBXNativeTarget;
buildConfigurationList = 80386E8A2793585C009B0480 /* Build configuration list for PBXNativeTarget "ntfy.sh" */;
@@ -155,10 +240,12 @@
80386E782793585B009B0480 /* Sources */,
80386E792793585B009B0480 /* Frameworks */,
80386E7A2793585B009B0480 /* Resources */,
+ 800FA49B27B19CA0005D05B9 /* Embed App Extensions */,
);
buildRules = (
);
dependencies = (
+ 800FA49927B19CA0005D05B9 /* PBXTargetDependency */,
);
name = ntfy.sh;
packageProductDependencies = (
@@ -176,9 +263,12 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
- LastSwiftUpdateCheck = 1310;
+ LastSwiftUpdateCheck = 1320;
LastUpgradeCheck = 1310;
TargetAttributes = {
+ 800FA49227B19CA0005D05B9 = {
+ CreatedOnToolsVersion = 13.2.1;
+ };
80386E7B2793585B009B0480 = {
CreatedOnToolsVersion = 13.1;
};
@@ -202,11 +292,19 @@
projectRoot = "";
targets = (
80386E7B2793585B009B0480 /* ntfy.sh */,
+ 800FA49227B19CA0005D05B9 /* NotificationService */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ 800FA49127B19CA0005D05B9 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
80386E7A2793585B009B0480 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -220,6 +318,17 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 800FA48F27B19CA0005D05B9 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 802D626C27B1A37700DDD3AF /* Database.swift in Sources */,
+ 802D626D27B1A37900DDD3AF /* NtfyNotification.swift in Sources */,
+ 800FA49627B19CA0005D05B9 /* NotificationService.swift in Sources */,
+ 802D626E27B1A37C00DDD3AF /* NtfySubscription.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
80386E782793585B009B0480 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -240,7 +349,69 @@
};
/* End PBXSourcesBuildPhase section */
+/* Begin PBXTargetDependency section */
+ 800FA49927B19CA0005D05B9 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 800FA49227B19CA0005D05B9 /* NotificationService */;
+ targetProxy = 800FA49827B19CA0005D05B9 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
/* Begin XCBuildConfiguration section */
+ 800FA49C27B19CA0005D05B9 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = D8Q555UR28;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = NotificationService/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = NotificationService;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 15.2;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.copephobia.ntfy-ios.NotificationService";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 800FA49D27B19CA0005D05B9 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = D8Q555UR28;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = NotificationService/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = NotificationService;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 15.2;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.copephobia.ntfy-ios.NotificationService";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
80386E882793585C009B0480 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -361,6 +532,7 @@
80386E8B2793585C009B0480 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = ntfy.sh.entitlements;
@@ -392,6 +564,7 @@
80386E8C2793585C009B0480 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = ntfy.shRelease.entitlements;
@@ -423,6 +596,15 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ 800FA49E27B19CA0005D05B9 /* Build configuration list for PBXNativeTarget "NotificationService" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 800FA49C27B19CA0005D05B9 /* Debug */,
+ 800FA49D27B19CA0005D05B9 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
80386E772793585B009B0480 /* Build configuration list for PBXProject "ntfy.sh" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -463,6 +645,16 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
+ 802D626827B1A36E00DDD3AF /* FirebaseMessaging */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 80386E9727935CC9009B0480 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
+ productName = FirebaseMessaging;
+ };
+ 802D626A27B1A37400DDD3AF /* SQLite */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 80386EA72793A7AC009B0480 /* XCRemoteSwiftPackageReference "SQLite.swift" */;
+ productName = SQLite;
+ };
80386E9827935CC9009B0480 /* FirebaseMessaging */ = {
isa = XCSwiftPackageProductDependency;
package = 80386E9727935CC9009B0480 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
diff --git a/ntfy.sh.xcodeproj/xcuserdata/copephobia.xcuserdatad/xcschemes/xcschememanagement.plist b/ntfy.sh.xcodeproj/xcuserdata/copephobia.xcuserdatad/xcschemes/xcschememanagement.plist
index 395e75a..62128a4 100644
--- a/ntfy.sh.xcodeproj/xcuserdata/copephobia.xcuserdatad/xcschemes/xcschememanagement.plist
+++ b/ntfy.sh.xcodeproj/xcuserdata/copephobia.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -4,48 +4,53 @@
SchemeUserState
- Promises (Playground) 1.xcscheme
+ NotificationService.xcscheme_^#shared#^_
- isShown
-
- orderHint
- 3
-
- Promises (Playground) 2.xcscheme
-
- isShown
-
- orderHint
- 4
-
- Promises (Playground).xcscheme
-
- isShown
-
orderHint
2
- SQLite (Playground) 1.xcscheme
+ Promises (Playground) 1.xcscheme
isShown
orderHint
6
- SQLite (Playground) 2.xcscheme
+ Promises (Playground) 2.xcscheme
isShown
orderHint
7
- SQLite (Playground).xcscheme
+ Promises (Playground).xcscheme
isShown
orderHint
5
+ SQLite (Playground) 1.xcscheme
+
+ isShown
+
+ orderHint
+ 3
+
+ SQLite (Playground) 2.xcscheme
+
+ isShown
+
+ orderHint
+ 4
+
+ SQLite (Playground).xcscheme
+
+ isShown
+
+ orderHint
+ 2
+
iPhone.xcscheme_^#shared#^_
orderHint