diff --git a/ntfy.xcodeproj/project.pbxproj b/ntfy.xcodeproj/project.pbxproj
index 5526708..8aff12f 100644
--- a/ntfy.xcodeproj/project.pbxproj
+++ b/ntfy.xcodeproj/project.pbxproj
@@ -538,17 +538,19 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ APP_BASE_URL = "http://192.168.1.7";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = ntfy/ntfy.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_ASSET_PATHS = "\"ntfy/Assets/Preview Content\"";
- DEVELOPMENT_TEAM = YXQ4AMS4B4;
+ DEVELOPMENT_TEAM = MZWHX5Z44T;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ntfy/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ntfy;
+ INFOPLIST_KEY_NSCameraUsageDescription = "We need access to the camera to scan QR codes.";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
@@ -560,7 +562,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.2;
- PRODUCT_BUNDLE_IDENTIFIER = io.heckel.ntfy;
+ PRODUCT_BUNDLE_IDENTIFIER = com.tcaputi.ntfy;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
@@ -572,17 +574,19 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ APP_BASE_URL = "http://192.168.1.7";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = ntfy/ntfy.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_ASSET_PATHS = "\"ntfy/Assets/Preview Content\"";
- DEVELOPMENT_TEAM = YXQ4AMS4B4;
+ DEVELOPMENT_TEAM = MZWHX5Z44T;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ntfy/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ntfy;
+ INFOPLIST_KEY_NSCameraUsageDescription = "We need access to the camera to scan QR codes.";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
@@ -594,7 +598,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.2;
- PRODUCT_BUNDLE_IDENTIFIER = io.heckel.ntfy;
+ PRODUCT_BUNDLE_IDENTIFIER = com.tcaputi.ntfy;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
@@ -605,10 +609,11 @@
9474F1ED282F3FFD00CDE4DD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ APP_BASE_URL = "http://192.168.1.7";
CODE_SIGN_ENTITLEMENTS = ntfyNSE/ntfyNSE.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
- DEVELOPMENT_TEAM = YXQ4AMS4B4;
+ DEVELOPMENT_TEAM = MZWHX5Z44T;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ntfyNSE/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ntfyNSE;
@@ -620,7 +625,7 @@
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.2;
- PRODUCT_BUNDLE_IDENTIFIER = io.heckel.ntfy.ntfyNSE;
+ PRODUCT_BUNDLE_IDENTIFIER = com.tcaputi.ntfy.ntfyNSE;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -632,10 +637,11 @@
9474F1EE282F3FFD00CDE4DD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ APP_BASE_URL = "http://192.168.1.7";
CODE_SIGN_ENTITLEMENTS = ntfyNSE/ntfyNSE.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
- DEVELOPMENT_TEAM = YXQ4AMS4B4;
+ DEVELOPMENT_TEAM = MZWHX5Z44T;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ntfyNSE/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ntfyNSE;
@@ -647,7 +653,7 @@
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.2;
- PRODUCT_BUNDLE_IDENTIFIER = io.heckel.ntfy.ntfyNSE;
+ PRODUCT_BUNDLE_IDENTIFIER = com.tcaputi.ntfy.ntfyNSE;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
diff --git a/ntfy.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ntfy.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index a445f0c..e1ec2d0 100644
--- a/ntfy.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/ntfy.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -23,8 +23,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk",
"state" : {
- "revision" : "cfa854c9c1073c4d1b83b20dfcb1ef7ceb85388b",
- "version" : "9.0.0"
+ "revision" : "7e80c25b51c2ffa238879b07fbfc5baa54bb3050",
+ "version" : "9.6.0"
}
},
{
@@ -32,8 +32,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
- "revision" : "6a3123fab90f3884167990bee9bb30097d99c98c",
- "version" : "9.0.0"
+ "revision" : "c1cfde8067668027b23a42c29d11c246152fe046",
+ "version" : "9.6.0"
}
},
{
@@ -86,8 +86,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/nanopb.git",
"state" : {
- "revision" : "7ee9ef9f627d85cbe1b8c4f49a3ed26eed216c77",
- "version" : "2.30908.0"
+ "revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692",
+ "version" : "2.30909.0"
}
},
{
diff --git a/ntfy.xcodeproj/xcshareddata/xcschemes/ntfy.xcscheme b/ntfy.xcodeproj/xcshareddata/xcschemes/ntfy.xcscheme
new file mode 100644
index 0000000..5967b20
--- /dev/null
+++ b/ntfy.xcodeproj/xcshareddata/xcschemes/ntfy.xcscheme
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ntfy.xcodeproj/xcshareddata/xcschemes/ntfyNSE.xcscheme b/ntfy.xcodeproj/xcshareddata/xcschemes/ntfyNSE.xcscheme
new file mode 100644
index 0000000..7a4f970
--- /dev/null
+++ b/ntfy.xcodeproj/xcshareddata/xcschemes/ntfyNSE.xcscheme
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ntfy/App/AppDelegate.swift b/ntfy/App/AppDelegate.swift
index c0265a5..dcb5551 100644
--- a/ntfy/App/AppDelegate.swift
+++ b/ntfy/App/AppDelegate.swift
@@ -13,11 +13,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, ObservableObject {
// Implements navigation from notifications, see https://stackoverflow.com/a/70731861/1440785
@Published var selectedBaseUrl: String? = nil
- func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
Log.d(tag, "Launching AppDelegate")
-
+
+ FirebaseApp.configure()
+ FirebaseConfiguration.shared.setLoggerLevel(.max)
+
// Register app permissions for push notifications
UNUserNotificationCenter.current().delegate = self
+ Messaging.messaging().delegate = self
+
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
guard success else {
Log.e(self.tag, "Failed to register for local push notifications", error)
@@ -28,13 +33,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, ObservableObject {
// Register too receive remote notifications
application.registerForRemoteNotifications()
-
- // Set self as messaging delegate
- Messaging.messaging().delegate = self
-
- // Register to "~poll" topic
- Messaging.messaging().subscribe(toTopic: pollTopic)
-
+
return true
}
@@ -137,8 +136,8 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
extension AppDelegate: MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
Log.d(tag, "Firebase token received: \(String(describing: fcmToken))")
-
- // We don't actually need the FCM token, since we're just using topics.
- // We still print it so we can see if things were successful.
+
+ // We wait until we have a registration token before subscribing to our pollTopic
+ Messaging.messaging().subscribe(toTopic: pollTopic)
}
}
diff --git a/ntfy/App/AppMain.swift b/ntfy/App/AppMain.swift
index b753e28..1fedf39 100644
--- a/ntfy/App/AppMain.swift
+++ b/ntfy/App/AppMain.swift
@@ -13,11 +13,6 @@ struct AppMain: App {
init() {
Log.d(tag, "Launching ntfy 🥳. Welcome!")
Log.d(tag, "Base URL is \(Config.appBaseUrl), user agent is \(ApiService.userAgent)")
-
- // We must configure Firebase here, and not in the AppDelegate. For some reason
- // configuring it there did not work.
- FirebaseApp.configure()
- FirebaseConfiguration.shared.setLoggerLevel(.max)
}
var body: some Scene {
diff --git a/ntfy/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json b/ntfy/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json
index 04de9d4..f78687a 100644
--- a/ntfy/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/ntfy/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -150,6 +150,66 @@
"scale" : "1x",
"size" : "1024x1024"
},
+ {
+ "filename" : "16.png",
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "16x16"
+ },
+ {
+ "filename" : "32.png",
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "16x16"
+ },
+ {
+ "filename" : "32.png",
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "32x32"
+ },
+ {
+ "filename" : "64.png",
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "32x32"
+ },
+ {
+ "filename" : "128.png",
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "128x128"
+ },
+ {
+ "filename" : "256.png",
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "128x128"
+ },
+ {
+ "filename" : "256.png",
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "256x256"
+ },
+ {
+ "filename" : "512.png",
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "256x256"
+ },
+ {
+ "filename" : "512.png",
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "512x512"
+ },
+ {
+ "filename" : "1024.png",
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "512x512"
+ },
{
"filename" : "48.png",
"idiom" : "watch",
@@ -225,6 +285,13 @@
"size" : "51x51",
"subtype" : "45mm"
},
+ {
+ "idiom" : "watch",
+ "role" : "appLauncher",
+ "scale" : "2x",
+ "size" : "54x54",
+ "subtype" : "49mm"
+ },
{
"filename" : "172.png",
"idiom" : "watch",
@@ -256,71 +323,18 @@
"size" : "117x117",
"subtype" : "45mm"
},
+ {
+ "idiom" : "watch",
+ "role" : "quickLook",
+ "scale" : "2x",
+ "size" : "129x129",
+ "subtype" : "49mm"
+ },
{
"filename" : "1024.png",
"idiom" : "watch-marketing",
"scale" : "1x",
"size" : "1024x1024"
- },
- {
- "filename" : "16.png",
- "idiom" : "mac",
- "scale" : "1x",
- "size" : "16x16"
- },
- {
- "filename" : "32.png",
- "idiom" : "mac",
- "scale" : "2x",
- "size" : "16x16"
- },
- {
- "filename" : "32.png",
- "idiom" : "mac",
- "scale" : "1x",
- "size" : "32x32"
- },
- {
- "filename" : "64.png",
- "idiom" : "mac",
- "scale" : "2x",
- "size" : "32x32"
- },
- {
- "filename" : "128.png",
- "idiom" : "mac",
- "scale" : "1x",
- "size" : "128x128"
- },
- {
- "filename" : "256.png",
- "idiom" : "mac",
- "scale" : "2x",
- "size" : "128x128"
- },
- {
- "filename" : "256.png",
- "idiom" : "mac",
- "scale" : "1x",
- "size" : "256x256"
- },
- {
- "filename" : "512.png",
- "idiom" : "mac",
- "scale" : "2x",
- "size" : "256x256"
- },
- {
- "filename" : "512.png",
- "idiom" : "mac",
- "scale" : "1x",
- "size" : "512x512"
- },
- {
- "filename" : "1024.png",
- "idiom" : "mac",
- "scale" : "2x",
- "size" : "512x512"
}
],
"info" : {
diff --git a/ntfy/Info.plist b/ntfy/Info.plist
index f1f6093..6abed55 100644
--- a/ntfy/Info.plist
+++ b/ntfy/Info.plist
@@ -18,5 +18,7 @@
remote-notification
+ FirebaseAppDelegateProxyEnabled
+
diff --git a/ntfy/Persistence/Store.swift b/ntfy/Persistence/Store.swift
index 03eab1b..7b40d33 100644
--- a/ntfy/Persistence/Store.swift
+++ b/ntfy/Persistence/Store.swift
@@ -7,7 +7,7 @@ import Combine
class Store: ObservableObject {
static let shared = Store()
static let tag = "Store"
- static let appGroup = "group.io.heckel.ntfy" // Must match app group of ntfy = ntfyNSE targets
+ static let appGroup = "group.com.tcaputi.ntfy" // Must match app group of ntfy = ntfyNSE targets
static let modelName = "ntfy" // Must match .xdatamodeld folder
static let prefKeyDefaultBaseUrl = "defaultBaseUrl"
@@ -43,9 +43,13 @@ class Store: ObservableObject {
NotificationCenter.default
.publisher(for: .NSPersistentStoreRemoteChange)
.sink { value in
- Log.d(Store.tag, "Remote change detected, refreshing view", value)
+ // TODO: this could probably broadcast the name of the channel
+ // so that only relevant views can update.
+ Log.d(Store.tag, "Remote change detected, refreshing views", value)
+
DispatchQueue.main.async {
self.hardRefresh()
+ NotificationCenter.default.post(name: .notificationReceived, object: nil)
}
}
.store(in: &cancellables)
@@ -266,3 +270,7 @@ extension Store {
return notification
}
}
+
+extension Foundation.Notification.Name {
+ static let notificationReceived = Foundation.Notification.Name("notificationReceived")
+}
diff --git a/ntfy/Views/NotificationListView.swift b/ntfy/Views/NotificationListView.swift
index 48b3149..4f3815b 100644
--- a/ntfy/Views/NotificationListView.swift
+++ b/ntfy/Views/NotificationListView.swift
@@ -22,15 +22,23 @@ struct NotificationListView: View {
private var subscriptionManager: SubscriptionManager {
return SubscriptionManager(store: store)
}
-
+
var body: some View {
+ let notificationReceived = Foundation.Notification.Name("notificationReceived")
+
if #available(iOS 15.0, *) {
notificationList
.refreshable {
subscriptionManager.poll(subscription)
+ }.onReceive(NotificationCenter.default.publisher(for: notificationReceived)) { _ in
+ // Handle the notification
+ subscriptionManager.poll(subscription)
}
} else {
- notificationList
+ notificationList.onReceive(NotificationCenter.default.publisher(for: notificationReceived)) { _ in
+ // Handle the notification
+ subscriptionManager.poll(subscription)
+ }
}
}
diff --git a/ntfy/Views/SubscriptionListView.swift b/ntfy/Views/SubscriptionListView.swift
index 537b727..5bcd4bc 100644
--- a/ntfy/Views/SubscriptionListView.swift
+++ b/ntfy/Views/SubscriptionListView.swift
@@ -15,6 +15,8 @@ struct SubscriptionListView: View {
}
var body: some View {
+ let notificationReceived = Foundation.Notification.Name("notificationReceived")
+
NavigationView {
if #available(iOS 15.0, *) {
subscriptionList
@@ -22,9 +24,20 @@ struct SubscriptionListView: View {
subscriptions.forEach { subscription in
subscriptionManager.poll(subscription)
}
+ }.onReceive(NotificationCenter.default.publisher(for: notificationReceived)) { _ in
+ // Handle the notification
+ subscriptions.forEach { subscription in
+ subscriptionManager.poll(subscription)
+ }
}
} else {
subscriptionList
+ .onReceive(NotificationCenter.default.publisher(for: notificationReceived)) { _ in
+ // Handle the notification
+ subscriptions.forEach { subscription in
+ subscriptionManager.poll(subscription)
+ }
+ }
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
diff --git a/ntfy/ntfy.entitlements b/ntfy/ntfy.entitlements
index 384fbad..d16d692 100644
--- a/ntfy/ntfy.entitlements
+++ b/ntfy/ntfy.entitlements
@@ -6,7 +6,7 @@
development
com.apple.security.application-groups
- group.io.heckel.ntfy
+ group.com.tcaputi.ntfy
diff --git a/ntfyNSE/ntfyNSE.entitlements b/ntfyNSE/ntfyNSE.entitlements
index 384fbad..d16d692 100644
--- a/ntfyNSE/ntfyNSE.entitlements
+++ b/ntfyNSE/ntfyNSE.entitlements
@@ -6,7 +6,7 @@
development
com.apple.security.application-groups
- group.io.heckel.ntfy
+ group.com.tcaputi.ntfy