diff --git a/ios/PushDeer-iOS/Notification/Info.plist b/ios/PushDeer-iOS/Notification/Info.plist
new file mode 100644
index 0000000..57421eb
--- /dev/null
+++ b/ios/PushDeer-iOS/Notification/Info.plist
@@ -0,0 +1,13 @@
+
+
+
+
+ NSExtension
+
+ NSExtensionPointIdentifier
+ com.apple.usernotifications.service
+ NSExtensionPrincipalClass
+ $(PRODUCT_MODULE_NAME).NotificationService
+
+
+
diff --git a/ios/PushDeer-iOS/Notification/Notification.entitlements b/ios/PushDeer-iOS/Notification/Notification.entitlements
new file mode 100644
index 0000000..ee95ab7
--- /dev/null
+++ b/ios/PushDeer-iOS/Notification/Notification.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.network.client
+
+
+
diff --git a/ios/PushDeer-iOS/Notification/NotificationService.swift b/ios/PushDeer-iOS/Notification/NotificationService.swift
new file mode 100644
index 0000000..2a84990
--- /dev/null
+++ b/ios/PushDeer-iOS/Notification/NotificationService.swift
@@ -0,0 +1,39 @@
+//
+// NotificationService.swift
+// Notification
+//
+// Created by HEXT on 2022/4/19.
+//
+
+import UserNotifications
+import WidgetKit
+
+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)
+
+ if let bestAttemptContent = bestAttemptContent {
+ // Modify the notification content here...
+ // bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
+
+ contentHandler(bestAttemptContent)
+ }
+
+ // 刷新所有桌面小部件
+ WidgetCenter.shared.reloadAllTimelines()
+ }
+
+ 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/ios/PushDeer-iOS/Podfile b/ios/PushDeer-iOS/Podfile
index c3582aa..2bc32c5 100644
--- a/ios/PushDeer-iOS/Podfile
+++ b/ios/PushDeer-iOS/Podfile
@@ -28,6 +28,11 @@ target 'PushDeerClip' do
end
+target 'PushDeerWidgetExtension' do
+ # Pods for PushDeerWidgetExtension
+ pod 'Moya', '~> 15.0'
+end
+
# define unsupported pods
def unsupported_pods
# macCatalyst 不支持的库
diff --git a/ios/PushDeer-iOS/Podfile.lock b/ios/PushDeer-iOS/Podfile.lock
index 003a15d..fe5d5e2 100644
--- a/ios/PushDeer-iOS/Podfile.lock
+++ b/ios/PushDeer-iOS/Podfile.lock
@@ -47,6 +47,6 @@ SPEC CHECKSUMS:
WechatOpenSDK: 6a4d1436c15b3b5fe2a0bd383f3046010186da44
WoodPeckeriOS: 12ec7f38c695e51cd94a476228888dfe85d9d916
-PODFILE CHECKSUM: 1b349626994062a8291e3db07d3dbf087894c4d2
+PODFILE CHECKSUM: 42e3d8abd976589c1043ff9f9e864c275a490160
COCOAPODS: 1.11.2
diff --git a/ios/PushDeer-iOS/PushDeer.xcodeproj/project.pbxproj b/ios/PushDeer-iOS/PushDeer.xcodeproj/project.pbxproj
index f5eda93..a335ba2 100644
--- a/ios/PushDeer-iOS/PushDeer.xcodeproj/project.pbxproj
+++ b/ios/PushDeer-iOS/PushDeer.xcodeproj/project.pbxproj
@@ -7,7 +7,21 @@
objects = {
/* Begin PBXBuildFile section */
+ 1C63523789DA4965F9F87DB8 /* Pods_PushDeerWidgetExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F8DCAE5BD814D7C0D2F2722 /* Pods_PushDeerWidgetExtension.framework */; };
4812F19BB0BFEFE089BC253E /* Pods_PushDeerClip.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E03C2088F4CD9F4C0848E1A5 /* Pods_PushDeerClip.framework */; };
+ 5206008727CF749E00188431 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5206008627CF749E00188431 /* WidgetKit.framework */; };
+ 5206008927CF749E00188431 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5206008827CF749E00188431 /* SwiftUI.framework */; };
+ 5206008C27CF749E00188431 /* PushDeerWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5206008B27CF749E00188431 /* PushDeerWidget.swift */; };
+ 5206008F27CF749F00188431 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5206008E27CF749F00188431 /* Assets.xcassets */; };
+ 5206009127CF749F00188431 /* PushDeerWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 5206008D27CF749E00188431 /* PushDeerWidget.intentdefinition */; };
+ 5206009227CF749F00188431 /* PushDeerWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 5206008D27CF749E00188431 /* PushDeerWidget.intentdefinition */; };
+ 5206009527CF749F00188431 /* PushDeerWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 5206008527CF749E00188431 /* PushDeerWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ 5206009D27CF74C100188431 /* HttpRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52450F412784943F003652D8 /* HttpRequest.swift */; };
+ 5206009E27CF76BC00188431 /* PushDeerApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52450F3727848243003652D8 /* PushDeerApi.swift */; };
+ 5206009F27CF76C400188431 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52450F3E2784923D003652D8 /* Result.swift */; };
+ 520600A027CF76F900188431 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52450F3A278491F8003652D8 /* AppState.swift */; };
+ 520600A127CF770600188431 /* Env.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243372627A6D86200190D00 /* Env.swift */; };
+ 520600A627D0AE2300188431 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EB90B22778DA4E0048E0ED /* Line.swift */; };
52163EB327773F8400594190 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52163EB227773F8400594190 /* MainView.swift */; };
52163EB52777413B00594190 /* MessageListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52163EB42777413B00594190 /* MessageListView.swift */; };
52163EB72777415F00594190 /* DeviceListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52163EB62777415F00594190 /* DeviceListView.swift */; };
@@ -71,6 +85,8 @@
52B8CF86277E0C12004CB680 /* BaseNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EB90AD2778AFD60048E0ED /* BaseNavigationView.swift */; };
52B8CF87277E0C5C004CB680 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5292F4FC2776BC7A00B9A7BB /* Assets.xcassets */; };
52B8FB8727F36F9D00C29D13 /* BetterSafariView in Frameworks */ = {isa = PBXBuildFile; productRef = 52B8FB8627F36F9D00C29D13 /* BetterSafariView */; };
+ 52C4B434280DC28D009817EA /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C4B433280DC28D009817EA /* NotificationService.swift */; };
+ 52C4B438280DC28D009817EA /* Notification.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 52C4B431280DC28D009817EA /* Notification.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
52E317D9279305BB000B8BB1 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 52E317D7279305BB000B8BB1 /* InfoPlist.strings */; };
52E317DC279305BB000B8BB1 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 52E317DA279305BB000B8BB1 /* Localizable.strings */; };
52E317DF279305BB000B8BB1 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 52E317DD279305BB000B8BB1 /* InfoPlist.strings */; };
@@ -91,6 +107,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
+ 5206009327CF749F00188431 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 5292F4ED2776BC7900B9A7BB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 5206008427CF749E00188431;
+ remoteInfo = PushDeerWidgetExtension;
+ };
52B8CF71277E0B46004CB680 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5292F4ED2776BC7900B9A7BB /* Project object */;
@@ -98,9 +121,28 @@
remoteGlobalIDString = 52B8CF63277E0B44004CB680;
remoteInfo = PushDeerClip;
};
+ 52C4B436280DC28D009817EA /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 5292F4ED2776BC7900B9A7BB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 52C4B430280DC28D009817EA;
+ remoteInfo = Notification;
+ };
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
+ 5206009727CF749F00188431 /* Embed App Extensions */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 13;
+ files = (
+ 5206009527CF749F00188431 /* PushDeerWidgetExtension.appex in Embed App Extensions */,
+ 52C4B438280DC28D009817EA /* Notification.appex in Embed App Extensions */,
+ );
+ name = "Embed App Extensions";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
52B8CF77277E0B46004CB680 /* Embed App Clips */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@@ -115,6 +157,15 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 5206008527CF749E00188431 /* PushDeerWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PushDeerWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
+ 5206008627CF749E00188431 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
+ 5206008827CF749E00188431 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
+ 5206008B27CF749E00188431 /* PushDeerWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushDeerWidget.swift; sourceTree = ""; };
+ 5206008D27CF749E00188431 /* PushDeerWidget.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = PushDeerWidget.intentdefinition; sourceTree = ""; };
+ 5206008E27CF749F00188431 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 5206009027CF749F00188431 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 520600A427D085A100188431 /* PushDeerWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PushDeerWidgetExtension.entitlements; sourceTree = ""; };
+ 520600A527D085C200188431 /* PushDeerWidgetExtension-SelfHosted.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "PushDeerWidgetExtension-SelfHosted.entitlements"; sourceTree = ""; };
52163EB227773F8400594190 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; };
52163EB42777413B00594190 /* MessageListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageListView.swift; sourceTree = ""; };
52163EB62777415F00594190 /* DeviceListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceListView.swift; sourceTree = ""; };
@@ -152,6 +203,10 @@
52B8CF6F277E0B46004CB680 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
52B8CF70277E0B46004CB680 /* PushDeerClip.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PushDeerClip.entitlements; sourceTree = ""; };
52BE373227C236DD004AA630 /* PushDeer-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PushDeer-Bridging-Header.h"; sourceTree = ""; };
+ 52C4B431280DC28D009817EA /* Notification.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Notification.appex; sourceTree = BUILT_PRODUCTS_DIR; };
+ 52C4B433280DC28D009817EA /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; };
+ 52C4B435280DC28D009817EA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 52C4B439280DC28D009817EA /* Notification.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Notification.entitlements; sourceTree = ""; };
52E317D8279305BB000B8BB1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
52E317DB279305BB000B8BB1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; };
52E317DE279305BB000B8BB1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
@@ -169,18 +224,33 @@
52F40D2E277CA05600766C24 /* MessageItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageItemView.swift; sourceTree = ""; };
52FB1FEB27CA9D7300367DE0 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = ""; };
69F56B2711ED98819D474BE3 /* Pods-PushDeerClip.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerClip.debug.xcconfig"; path = "Target Support Files/Pods-PushDeerClip/Pods-PushDeerClip.debug.xcconfig"; sourceTree = ""; };
+ 7F8DCAE5BD814D7C0D2F2722 /* Pods_PushDeerWidgetExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PushDeerWidgetExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8B9D658D778AE868A0E052A8 /* Pods-PushDeer.debug-selfhosted.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeer.debug-selfhosted.xcconfig"; path = "Target Support Files/Pods-PushDeer/Pods-PushDeer.debug-selfhosted.xcconfig"; sourceTree = ""; };
+ 8CF87A70A3872FDE613FD228 /* Pods-PushDeerWidgetExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerWidgetExtension.release.xcconfig"; path = "Target Support Files/Pods-PushDeerWidgetExtension/Pods-PushDeerWidgetExtension.release.xcconfig"; sourceTree = ""; };
9CC775BE0326BF31C6FACF06 /* Pods-PushDeerClip.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerClip.release.xcconfig"; path = "Target Support Files/Pods-PushDeerClip/Pods-PushDeerClip.release.xcconfig"; sourceTree = ""; };
+ B3763489EDE07E6970B0F8E5 /* Pods-PushDeerWidgetExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerWidgetExtension.debug.xcconfig"; path = "Target Support Files/Pods-PushDeerWidgetExtension/Pods-PushDeerWidgetExtension.debug.xcconfig"; sourceTree = ""; };
B796E2A583611F7B6DC34726 /* Pods-PushDeerClip.release-selfhosted.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerClip.release-selfhosted.xcconfig"; path = "Target Support Files/Pods-PushDeerClip/Pods-PushDeerClip.release-selfhosted.xcconfig"; sourceTree = ""; };
CB57B55699850AD493C774D0 /* Pods-PushDeerClip.debug-selfhosted.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerClip.debug-selfhosted.xcconfig"; path = "Target Support Files/Pods-PushDeerClip/Pods-PushDeerClip.debug-selfhosted.xcconfig"; sourceTree = ""; };
CCCE1F6E56B157872E2C755F /* Pods-PushDeer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeer.release.xcconfig"; path = "Target Support Files/Pods-PushDeer/Pods-PushDeer.release.xcconfig"; sourceTree = ""; };
CE3005BD875FC9819A92466C /* Pods-PushDeer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeer.debug.xcconfig"; path = "Target Support Files/Pods-PushDeer/Pods-PushDeer.debug.xcconfig"; sourceTree = ""; };
+ D612A216E0469D1A050C3523 /* Pods-PushDeerWidgetExtension.release-selfhosted.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerWidgetExtension.release-selfhosted.xcconfig"; path = "Target Support Files/Pods-PushDeerWidgetExtension/Pods-PushDeerWidgetExtension.release-selfhosted.xcconfig"; sourceTree = ""; };
E03C2088F4CD9F4C0848E1A5 /* Pods_PushDeerClip.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PushDeerClip.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E380A18349DE4D26071E913E /* Pods_PushDeer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PushDeer.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ E45C830227D3A7C7E965F94B /* Pods-PushDeerWidgetExtension.debug-selfhosted.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeerWidgetExtension.debug-selfhosted.xcconfig"; path = "Target Support Files/Pods-PushDeerWidgetExtension/Pods-PushDeerWidgetExtension.debug-selfhosted.xcconfig"; sourceTree = ""; };
E60A7D4CA1149E1DBC55C672 /* Pods-PushDeer.release-selfhosted.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PushDeer.release-selfhosted.xcconfig"; path = "Target Support Files/Pods-PushDeer/Pods-PushDeer.release-selfhosted.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 5206008227CF749E00188431 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5206008927CF749E00188431 /* SwiftUI.framework in Frameworks */,
+ 5206008727CF749E00188431 /* WidgetKit.framework in Frameworks */,
+ 1C63523789DA4965F9F87DB8 /* Pods_PushDeerWidgetExtension.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
5292F4F22776BC7900B9A7BB /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -201,6 +271,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 52C4B42E280DC28D009817EA /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -215,10 +292,27 @@
E60A7D4CA1149E1DBC55C672 /* Pods-PushDeer.release-selfhosted.xcconfig */,
CB57B55699850AD493C774D0 /* Pods-PushDeerClip.debug-selfhosted.xcconfig */,
B796E2A583611F7B6DC34726 /* Pods-PushDeerClip.release-selfhosted.xcconfig */,
+ B3763489EDE07E6970B0F8E5 /* Pods-PushDeerWidgetExtension.debug.xcconfig */,
+ E45C830227D3A7C7E965F94B /* Pods-PushDeerWidgetExtension.debug-selfhosted.xcconfig */,
+ 8CF87A70A3872FDE613FD228 /* Pods-PushDeerWidgetExtension.release.xcconfig */,
+ D612A216E0469D1A050C3523 /* Pods-PushDeerWidgetExtension.release-selfhosted.xcconfig */,
);
path = Pods;
sourceTree = "";
};
+ 5206008A27CF749E00188431 /* PushDeerWidget */ = {
+ isa = PBXGroup;
+ children = (
+ 5206008B27CF749E00188431 /* PushDeerWidget.swift */,
+ 5206008D27CF749E00188431 /* PushDeerWidget.intentdefinition */,
+ 5206008E27CF749F00188431 /* Assets.xcassets */,
+ 5206009027CF749F00188431 /* Info.plist */,
+ 520600A427D085A100188431 /* PushDeerWidgetExtension.entitlements */,
+ 520600A527D085C200188431 /* PushDeerWidgetExtension-SelfHosted.entitlements */,
+ );
+ path = PushDeerWidget;
+ sourceTree = "";
+ };
52450F362784822C003652D8 /* Service */ = {
isa = PBXGroup;
children = (
@@ -246,6 +340,8 @@
children = (
5292F4F72776BC7900B9A7BB /* PushDeer */,
52B8CF65277E0B44004CB680 /* PushDeerClip */,
+ 5206008A27CF749E00188431 /* PushDeerWidget */,
+ 52C4B432280DC28D009817EA /* Notification */,
5292F4F62776BC7900B9A7BB /* Products */,
17D35B157765D96FC4DA6C39 /* Pods */,
78FEAD2568FB92808C44E85A /* Frameworks */,
@@ -258,6 +354,8 @@
children = (
5292F4F52776BC7900B9A7BB /* PushDeer.app */,
52B8CF64277E0B44004CB680 /* PushDeerClip.app */,
+ 5206008527CF749E00188431 /* PushDeerWidgetExtension.appex */,
+ 52C4B431280DC28D009817EA /* Notification.appex */,
);
name = Products;
sourceTree = "";
@@ -327,6 +425,16 @@
path = "Preview Content";
sourceTree = "";
};
+ 52C4B432280DC28D009817EA /* Notification */ = {
+ isa = PBXGroup;
+ children = (
+ 52C4B439280DC28D009817EA /* Notification.entitlements */,
+ 52C4B433280DC28D009817EA /* NotificationService.swift */,
+ 52C4B435280DC28D009817EA /* Info.plist */,
+ );
+ path = Notification;
+ sourceTree = "";
+ };
52EB90B12778D9F90048E0ED /* Common */ = {
isa = PBXGroup;
children = (
@@ -363,6 +471,9 @@
children = (
E380A18349DE4D26071E913E /* Pods_PushDeer.framework */,
E03C2088F4CD9F4C0848E1A5 /* Pods_PushDeerClip.framework */,
+ 5206008627CF749E00188431 /* WidgetKit.framework */,
+ 5206008827CF749E00188431 /* SwiftUI.framework */,
+ 7F8DCAE5BD814D7C0D2F2722 /* Pods_PushDeerWidgetExtension.framework */,
);
name = Frameworks;
sourceTree = "";
@@ -370,6 +481,26 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
+ 5206008427CF749E00188431 /* PushDeerWidgetExtension */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 5206009C27CF749F00188431 /* Build configuration list for PBXNativeTarget "PushDeerWidgetExtension" */;
+ buildPhases = (
+ 97FE7C51BE9FCE0E275A4CA3 /* [CP] Check Pods Manifest.lock */,
+ 5206008127CF749E00188431 /* Sources */,
+ 5206008227CF749E00188431 /* Frameworks */,
+ 5206008327CF749E00188431 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = PushDeerWidgetExtension;
+ packageProductDependencies = (
+ );
+ productName = PushDeerWidgetExtension;
+ productReference = 5206008527CF749E00188431 /* PushDeerWidgetExtension.appex */;
+ productType = "com.apple.product-type.app-extension";
+ };
5292F4F42776BC7900B9A7BB /* PushDeer */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5292F5032776BC7A00B9A7BB /* Build configuration list for PBXNativeTarget "PushDeer" */;
@@ -380,11 +511,14 @@
5292F4F32776BC7900B9A7BB /* Resources */,
52B8CF77277E0B46004CB680 /* Embed App Clips */,
D5C7FAC44EC37CBCD945E0F2 /* [CP] Embed Pods Frameworks */,
+ 5206009727CF749F00188431 /* Embed App Extensions */,
);
buildRules = (
);
dependencies = (
52B8CF72277E0B46004CB680 /* PBXTargetDependency */,
+ 5206009427CF749F00188431 /* PBXTargetDependency */,
+ 52C4B437280DC28D009817EA /* PBXTargetDependency */,
);
name = PushDeer;
packageProductDependencies = (
@@ -418,6 +552,23 @@
productReference = 52B8CF64277E0B44004CB680 /* PushDeerClip.app */;
productType = "com.apple.product-type.application.on-demand-install-capable";
};
+ 52C4B430280DC28D009817EA /* Notification */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 52C4B43E280DC28D009817EA /* Build configuration list for PBXNativeTarget "Notification" */;
+ buildPhases = (
+ 52C4B42D280DC28D009817EA /* Sources */,
+ 52C4B42E280DC28D009817EA /* Frameworks */,
+ 52C4B42F280DC28D009817EA /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Notification;
+ productName = Notification;
+ productReference = 52C4B431280DC28D009817EA /* Notification.appex */;
+ productType = "com.apple.product-type.app-extension";
+ };
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -425,9 +576,12 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
- LastSwiftUpdateCheck = 1320;
+ LastSwiftUpdateCheck = 1330;
LastUpgradeCheck = 1320;
TargetAttributes = {
+ 5206008427CF749E00188431 = {
+ CreatedOnToolsVersion = 13.2.1;
+ };
5292F4F42776BC7900B9A7BB = {
CreatedOnToolsVersion = 13.2.1;
LastSwiftMigration = 1320;
@@ -436,6 +590,9 @@
CreatedOnToolsVersion = 13.2.1;
LastSwiftMigration = 1320;
};
+ 52C4B430280DC28D009817EA = {
+ CreatedOnToolsVersion = 13.3.1;
+ };
};
};
buildConfigurationList = 5292F4F02776BC7900B9A7BB /* Build configuration list for PBXProject "PushDeer" */;
@@ -458,11 +615,21 @@
targets = (
5292F4F42776BC7900B9A7BB /* PushDeer */,
52B8CF63277E0B44004CB680 /* PushDeerClip */,
+ 5206008427CF749E00188431 /* PushDeerWidgetExtension */,
+ 52C4B430280DC28D009817EA /* Notification */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ 5206008327CF749E00188431 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5206008F27CF749F00188431 /* Assets.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
5292F4F32776BC7900B9A7BB /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -485,6 +652,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 52C4B42F280DC28D009817EA /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@@ -527,6 +701,28 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PushDeerClip/Pods-PushDeerClip-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
+ 97FE7C51BE9FCE0E275A4CA3 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-PushDeerWidgetExtension-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
D5C7FAC44EC37CBCD945E0F2 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -569,6 +765,21 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 5206008127CF749E00188431 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 5206009127CF749F00188431 /* PushDeerWidget.intentdefinition in Sources */,
+ 5206009F27CF76C400188431 /* Result.swift in Sources */,
+ 5206008C27CF749E00188431 /* PushDeerWidget.swift in Sources */,
+ 520600A027CF76F900188431 /* AppState.swift in Sources */,
+ 5206009E27CF76BC00188431 /* PushDeerApi.swift in Sources */,
+ 520600A627D0AE2300188431 /* Line.swift in Sources */,
+ 5206009D27CF74C100188431 /* HttpRequest.swift in Sources */,
+ 520600A127CF770600188431 /* Env.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
5292F4F12776BC7900B9A7BB /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -577,6 +788,7 @@
52FB1FEC27CA9D7300367DE0 /* SafariView.swift in Sources */,
523150DC2778762B00941EDC /* DeviceItemView.swift in Sources */,
523150D9277875FB00941EDC /* DeletableView.swift in Sources */,
+ 5206009227CF749F00188431 /* PushDeerWidget.intentdefinition in Sources */,
52163EBB277741AC00594190 /* SettingsView.swift in Sources */,
5242C872278C8CBB00FDB27E /* EditableText.swift in Sources */,
52163EB327773F8400594190 /* MainView.swift in Sources */,
@@ -649,15 +861,33 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 52C4B42D280DC28D009817EA /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 52C4B434280DC28D009817EA /* NotificationService.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
+ 5206009427CF749F00188431 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 5206008427CF749E00188431 /* PushDeerWidgetExtension */;
+ targetProxy = 5206009327CF749F00188431 /* PBXContainerItemProxy */;
+ };
52B8CF72277E0B46004CB680 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
platformFilter = ios;
target = 52B8CF63277E0B44004CB680 /* PushDeerClip */;
targetProxy = 52B8CF71277E0B46004CB680 /* PBXContainerItemProxy */;
};
+ 52C4B437280DC28D009817EA /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 52C4B430280DC28D009817EA /* Notification */;
+ targetProxy = 52C4B436280DC28D009817EA /* PBXContainerItemProxy */;
+ };
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@@ -691,6 +921,130 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
+ 5206009827CF749F00188431 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = B3763489EDE07E6970B0F8E5 /* Pods-PushDeerWidgetExtension.debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
+ CODE_SIGN_ENTITLEMENTS = PushDeerWidget/PushDeerWidgetExtension.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 4;
+ DEVELOPMENT_TEAM = HUJ6HAE4VU;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = PushDeerWidget/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = PushDeerWidget;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ MARKETING_VERSION = 1.2;
+ PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.app.ios.PushDeerWidget;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SUPPORTS_MACCATALYST = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 5206009927CF749F00188431 /* Debug-SelfHosted */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = E45C830227D3A7C7E965F94B /* Pods-PushDeerWidgetExtension.debug-selfhosted.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
+ CODE_SIGN_ENTITLEMENTS = "PushDeerWidget/PushDeerWidgetExtension-SelfHosted.entitlements";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 4;
+ DEVELOPMENT_TEAM = HUJ6HAE4VU;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = PushDeerWidget/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = PushDeerWidget;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ MARKETING_VERSION = 1.2;
+ PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.self.ios.PushDeerWidget;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SUPPORTS_MACCATALYST = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = "Debug-SelfHosted";
+ };
+ 5206009A27CF749F00188431 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 8CF87A70A3872FDE613FD228 /* Pods-PushDeerWidgetExtension.release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
+ CODE_SIGN_ENTITLEMENTS = PushDeerWidget/PushDeerWidgetExtension.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 4;
+ DEVELOPMENT_TEAM = HUJ6HAE4VU;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = PushDeerWidget/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = PushDeerWidget;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ MARKETING_VERSION = 1.2;
+ PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.app.ios.PushDeerWidget;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SUPPORTS_MACCATALYST = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+ 5206009B27CF749F00188431 /* Release-SelfHosted */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D612A216E0469D1A050C3523 /* Pods-PushDeerWidgetExtension.release-selfhosted.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
+ CODE_SIGN_ENTITLEMENTS = "PushDeerWidget/PushDeerWidgetExtension-SelfHosted.entitlements";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 4;
+ DEVELOPMENT_TEAM = HUJ6HAE4VU;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = PushDeerWidget/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = PushDeerWidget;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ MARKETING_VERSION = 1.2;
+ PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.self.ios.PushDeerWidget;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SUPPORTS_MACCATALYST = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = "Release-SelfHosted";
+ };
524E99E827B3DDF000292396 /* Debug-SelfHosted */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -758,6 +1112,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 8B9D658D778AE868A0E052A8 /* Pods-PushDeer.debug-selfhosted.xcconfig */;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-SH";
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
@@ -901,6 +1256,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = E60A7D4CA1149E1DBC55C672 /* Pods-PushDeer.release-selfhosted.xcconfig */;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-SH";
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
@@ -1102,6 +1458,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = CE3005BD875FC9819A92466C /* Pods-PushDeer.debug.xcconfig */;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
@@ -1143,6 +1500,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = CCCE1F6E56B157872E2C755F /* Pods-PushDeer.release.xcconfig */;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
@@ -1266,9 +1624,132 @@
};
name = Release;
};
+ 52C4B43A280DC28D009817EA /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_ENTITLEMENTS = Notification/Notification.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 4;
+ DEVELOPMENT_TEAM = HUJ6HAE4VU;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = Notification/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = Notification;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ MARKETING_VERSION = 1.2;
+ PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.app.ios.Notification;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SUPPORTS_MACCATALYST = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 52C4B43B280DC28D009817EA /* Debug-SelfHosted */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_ENTITLEMENTS = Notification/Notification.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 4;
+ DEVELOPMENT_TEAM = HUJ6HAE4VU;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = Notification/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = Notification;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ MARKETING_VERSION = 1.2;
+ PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.self.ios.Notification;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SUPPORTS_MACCATALYST = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = "Debug-SelfHosted";
+ };
+ 52C4B43C280DC28D009817EA /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_ENTITLEMENTS = Notification/Notification.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 4;
+ DEVELOPMENT_TEAM = HUJ6HAE4VU;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = Notification/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = Notification;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ MARKETING_VERSION = 1.2;
+ PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.app.ios.Notification;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SUPPORTS_MACCATALYST = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+ 52C4B43D280DC28D009817EA /* Release-SelfHosted */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_ENTITLEMENTS = Notification/Notification.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 4;
+ DEVELOPMENT_TEAM = HUJ6HAE4VU;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = Notification/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = Notification;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@executable_path/../../Frameworks",
+ );
+ MARKETING_VERSION = 1.2;
+ PRODUCT_BUNDLE_IDENTIFIER = com.pushdeer.self.ios.Notification;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SUPPORTS_MACCATALYST = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = "Release-SelfHosted";
+ };
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ 5206009C27CF749F00188431 /* Build configuration list for PBXNativeTarget "PushDeerWidgetExtension" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 5206009827CF749F00188431 /* Debug */,
+ 5206009927CF749F00188431 /* Debug-SelfHosted */,
+ 5206009A27CF749F00188431 /* Release */,
+ 5206009B27CF749F00188431 /* Release-SelfHosted */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
5292F4F02776BC7900B9A7BB /* Build configuration list for PBXProject "PushDeer" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -1302,6 +1783,17 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 52C4B43E280DC28D009817EA /* Build configuration list for PBXNativeTarget "Notification" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 52C4B43A280DC28D009817EA /* Debug */,
+ 52C4B43B280DC28D009817EA /* Debug-SelfHosted */,
+ 52C4B43C280DC28D009817EA /* Release */,
+ 52C4B43D280DC28D009817EA /* Release-SelfHosted */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
diff --git a/ios/PushDeer-iOS/PushDeer/Env.swift b/ios/PushDeer-iOS/PushDeer/Env.swift
index 414a088..920d034 100644
--- a/ios/PushDeer-iOS/PushDeer/Env.swift
+++ b/ios/PushDeer-iOS/PushDeer/Env.swift
@@ -27,5 +27,13 @@ struct Env {
static let wxUniversalLink = "https://vip.pushdeer.com/app/"
/// PushDeer 官网地址
static let officialWebsite = "https://www.pushdeer.com"
+ /// 共享组名, 使用它来访问 App Clip Widget 共享数据
+ static let appGroupId: String = {
+ #if SELFHOSTED
+ return "group.com.pushdeer.self.ios"
+ #else
+ return "group.com.pushdeer.app.ios"
+ #endif
+ }()
}
diff --git a/ios/PushDeer-iOS/PushDeer/Model/MessageModel.swift b/ios/PushDeer-iOS/PushDeer/Model/MessageModel.swift
index 4ceaecb..23b4f4a 100644
--- a/ios/PushDeer-iOS/PushDeer/Model/MessageModel.swift
+++ b/ios/PushDeer-iOS/PushDeer/Model/MessageModel.swift
@@ -51,6 +51,19 @@ extension MessageModel {
static let _viewContext = PersistenceController.shared.container.viewContext
static let _fetchRequest = MessageModel.fetchRequest()
+ /// 删除本地持久化的所有消息
+ static func deleteAll() throws -> Void {
+ // let fetchRequest: NSFetchRequest = MessageModel.fetchRequest()
+ // let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
+ // try _viewContext.execute(deleteRequest)
+ let fetchRequest = MessageModel.fetchRequest()
+ let models = try _viewContext.fetch(fetchRequest)
+ models.forEach { model in
+ _viewContext.delete(model)
+ }
+ try _viewContext.save()
+ }
+
/// 持久化保存和更新
static func saveAndUpdate(messageItems: [MessageItem]) throws -> Void {
try messageItems.forEach(saveAndUpdate)
diff --git a/ios/PushDeer-iOS/PushDeer/PushDeer-SelfHosted.entitlements b/ios/PushDeer-iOS/PushDeer/PushDeer-SelfHosted.entitlements
index 64fe498..b5c7e16 100644
--- a/ios/PushDeer-iOS/PushDeer/PushDeer-SelfHosted.entitlements
+++ b/ios/PushDeer-iOS/PushDeer/PushDeer-SelfHosted.entitlements
@@ -14,6 +14,10 @@
com.apple.security.app-sandbox
+ com.apple.security.application-groups
+
+ group.com.pushdeer.self.ios
+
com.apple.security.network.client
com.apple.security.personal-information.photos-library
diff --git a/ios/PushDeer-iOS/PushDeer/PushDeer.entitlements b/ios/PushDeer-iOS/PushDeer/PushDeer.entitlements
index 9cc65b4..46f1c07 100644
--- a/ios/PushDeer-iOS/PushDeer/PushDeer.entitlements
+++ b/ios/PushDeer-iOS/PushDeer/PushDeer.entitlements
@@ -15,6 +15,10 @@
com.apple.security.app-sandbox
+ com.apple.security.application-groups
+
+ group.com.pushdeer.app.ios
+
com.apple.security.network.client
com.apple.security.personal-information.photos-library
diff --git a/ios/PushDeer-iOS/PushDeer/PushDeerApp.swift b/ios/PushDeer-iOS/PushDeer/PushDeerApp.swift
index dab7bc5..51e214c 100644
--- a/ios/PushDeer-iOS/PushDeer/PushDeerApp.swift
+++ b/ios/PushDeer-iOS/PushDeer/PushDeerApp.swift
@@ -6,6 +6,7 @@
//
import SwiftUI
+import WidgetKit
@main
struct PushDeerApp: App {
@@ -39,6 +40,10 @@ struct PushDeerApp: App {
}
}
}
+ .onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification), perform: { _ in
+ // APP将要进入非活跃状态时, 刷新小部件
+ WidgetCenter.shared.reloadAllTimelines()
+ })
.environmentObject(store)
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
diff --git a/ios/PushDeer-iOS/PushDeer/Service/AppState.swift b/ios/PushDeer-iOS/PushDeer/Service/AppState.swift
index 5d33992..96e5dc2 100644
--- a/ios/PushDeer-iOS/PushDeer/Service/AppState.swift
+++ b/ios/PushDeer-iOS/PushDeer/Service/AppState.swift
@@ -10,9 +10,9 @@ import AuthenticationServices
class AppState: ObservableObject {
/// 账号 token
- @Published var token : String {
+ @Published var token : String = "" {
didSet {
- UserDefaults.standard.set(token, forKey: "PushDeer_token")
+ getUserDefaults().set(token, forKey: "PushDeer_token")
}
}
/// 设备列表
@@ -22,9 +22,9 @@ class AppState: ObservableObject {
/// 消息列表
// @Published var messages: [MessageItem] = []
/// 选中的 tab 下标
- @Published var tabSelectedIndex: Int {
+ @Published var tabSelectedIndex: Int = 2 {
didSet {
- UserDefaults.standard.set(tabSelectedIndex, forKey: "PushDeer_tabSelectedIndex")
+ getUserDefaults().set(tabSelectedIndex, forKey: "PushDeer_tabSelectedIndex")
}
}
/// 设备推送 token
@@ -32,28 +32,28 @@ class AppState: ObservableObject {
/// 用户信息
@Published var userInfo: UserInfoContent?
/// 是否显示测试发推送的 UI
- @Published var isShowTestPush: Bool {
+ @Published var isShowTestPush: Bool = true {
didSet {
- UserDefaults.standard.set(isShowTestPush, forKey: "PushDeer_isShowTestPush")
+ getUserDefaults().set(isShowTestPush, forKey: "PushDeer_isShowTestPush")
}
}
/// 是否使用内置浏览器打开链接
- @Published var isUseBuiltInBrowser: Bool {
+ @Published var isUseBuiltInBrowser: Bool = true {
didSet {
- UserDefaults.standard.set(isUseBuiltInBrowser, forKey: "PushDeer_isUseBuiltInBrowser")
+ getUserDefaults().set(isUseBuiltInBrowser, forKey: "PushDeer_isUseBuiltInBrowser")
}
}
/// MarkDown BaseURL
@Published var markDownBaseURL: String? {
didSet {
- UserDefaults.standard.set(markDownBaseURL, forKey: "PushDeer_markDownBaseURL")
+ getUserDefaults().set(markDownBaseURL, forKey: "PushDeer_markDownBaseURL")
}
}
/// API endpoint
- @Published var api_endpoint : String {
+ @Published var api_endpoint : String = "" {
didSet {
- UserDefaults.standard.set(api_endpoint, forKey: "PushDeer_api_endpoint")
+ getUserDefaults().set(api_endpoint, forKey: "PushDeer_api_endpoint")
}
}
@@ -68,12 +68,26 @@ class AppState: ObservableObject {
static let shared = AppState()
private init() {
- let _token = UserDefaults.standard.string(forKey: "PushDeer_token")
- let _tabSelectedIndex = UserDefaults.standard.integer(forKey: "PushDeer_tabSelectedIndex")
- let _isShowTestPush = UserDefaults.standard.object(forKey: "PushDeer_isShowTestPush")
- let _isUseBuiltInBrowser = UserDefaults.standard.object(forKey: "PushDeer_isUseBuiltInBrowser")
- let _markDownBaseURL = UserDefaults.standard.string(forKey: "PushDeer_markDownBaseURL")
- let _api_endpoint = UserDefaults.standard.string(forKey: "PushDeer_api_endpoint")
+ reloadUserDefaults()
+ moveOldUserDefaults()
+ }
+
+ func getUserDefaults() -> UserDefaults {
+ let ud = UserDefaults(suiteName: Env.appGroupId)
+ if let ud = ud {
+ return ud
+ } else {
+ return UserDefaults.standard
+ }
+ }
+
+ func reloadUserDefaults() -> Void {
+ let _token = getUserDefaults().string(forKey: "PushDeer_token")
+ let _tabSelectedIndex = getUserDefaults().integer(forKey: "PushDeer_tabSelectedIndex")
+ let _isShowTestPush = getUserDefaults().object(forKey: "PushDeer_isShowTestPush")
+ let _isUseBuiltInBrowser = getUserDefaults().object(forKey: "PushDeer_isUseBuiltInBrowser")
+ let _markDownBaseURL = getUserDefaults().string(forKey: "PushDeer_markDownBaseURL")
+ let _api_endpoint = getUserDefaults().string(forKey: "PushDeer_api_endpoint")
token = _token ?? ""
tabSelectedIndex = _tabSelectedIndex
isShowTestPush = _isShowTestPush as? Bool ?? true
@@ -82,6 +96,35 @@ class AppState: ObservableObject {
api_endpoint = _api_endpoint ?? ""
}
+ /// 迁移老版本数据, 老版本不是存在共享组中, 需要迁移到共享组
+ func moveOldUserDefaults() -> Void {
+ let oldUserDefaults = UserDefaults.standard
+ if let _token = oldUserDefaults.string(forKey: "PushDeer_token") {
+ oldUserDefaults.removeObject(forKey: "PushDeer_token")
+ token = _token
+ }
+ if let _tabSelectedIndex = oldUserDefaults.object(forKey: "PushDeer_tabSelectedIndex") as? Int {
+ oldUserDefaults.removeObject(forKey: "PushDeer_tabSelectedIndex")
+ tabSelectedIndex = _tabSelectedIndex
+ }
+ if let _isShowTestPush = oldUserDefaults.object(forKey: "PushDeer_isShowTestPush") as? Bool {
+ oldUserDefaults.removeObject(forKey: "PushDeer_isShowTestPush")
+ isShowTestPush = _isShowTestPush
+ }
+ if let _isUseBuiltInBrowser = oldUserDefaults.object(forKey: "PushDeer_isUseBuiltInBrowser") as? Bool {
+ oldUserDefaults.removeObject(forKey: "PushDeer_isUseBuiltInBrowser")
+ isUseBuiltInBrowser = _isUseBuiltInBrowser
+ }
+ if let _markDownBaseURL = oldUserDefaults.string(forKey: "PushDeer_markDownBaseURL") {
+ oldUserDefaults.removeObject(forKey: "PushDeer_markDownBaseURL")
+ markDownBaseURL = _markDownBaseURL
+ }
+ if let _api_endpoint = oldUserDefaults.string(forKey: "PushDeer_api_endpoint") {
+ oldUserDefaults.removeObject(forKey: "PushDeer_api_endpoint")
+ api_endpoint = _api_endpoint
+ }
+ }
+
func appleIdLogin(_ result: Result) async throws -> TokenContent {
switch result {
case let .success(authorization):
diff --git a/ios/PushDeer-iOS/PushDeer/Service/HttpRequest.swift b/ios/PushDeer-iOS/PushDeer/Service/HttpRequest.swift
index 4c0149d..1e91793 100644
--- a/ios/PushDeer-iOS/PushDeer/Service/HttpRequest.swift
+++ b/ios/PushDeer-iOS/PushDeer/Service/HttpRequest.swift
@@ -135,4 +135,8 @@ struct HttpRequest {
static func rmMessage(id: Int) async throws -> ActionContent {
return try await request(.rmMessage(token: AppState.shared.token, id: id), resultType: ActionContent.self)
}
+
+ static func rmAllMessage() async throws -> ActionContent {
+ return try await request(.rmAllMessage(token: AppState.shared.token), resultType: ActionContent.self)
+ }
}
diff --git a/ios/PushDeer-iOS/PushDeer/Service/PushDeerApi.swift b/ios/PushDeer-iOS/PushDeer/Service/PushDeerApi.swift
index 01541af..652f6b4 100644
--- a/ios/PushDeer-iOS/PushDeer/Service/PushDeerApi.swift
+++ b/ios/PushDeer-iOS/PushDeer/Service/PushDeerApi.swift
@@ -40,6 +40,7 @@ enum PushDeerApi {
case getMessages(token: String, limit: Int)
case rmMessage(token: String, id: Int)
+ case rmAllMessage(token: String)
}
@@ -91,6 +92,8 @@ extension PushDeerApi: TargetType {
return "/message/list"
case .rmMessage:
return "/message/remove"
+ case .rmAllMessage:
+ return "/message/clean"
}
}
var method: Moya.Method {
@@ -139,6 +142,8 @@ extension PushDeerApi: TargetType {
return .requestParameters(parameters: ["token": token, "limit": limit],encoding: URLEncoding.queryString)
case let .rmMessage(token, id):
return .requestParameters(parameters: ["token": token, "id": id],encoding: URLEncoding.queryString)
+ case let .rmAllMessage(token):
+ return .requestParameters(parameters: ["token": token],encoding: URLEncoding.queryString)
}
}
diff --git a/ios/PushDeer-iOS/PushDeer/View/MessageListView.swift b/ios/PushDeer-iOS/PushDeer/View/MessageListView.swift
index ef0c80a..a11c664 100644
--- a/ios/PushDeer-iOS/PushDeer/View/MessageListView.swift
+++ b/ios/PushDeer-iOS/PushDeer/View/MessageListView.swift
@@ -15,6 +15,17 @@ struct MessageListView: View {
@FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \MessageModel.created_at, ascending: false)], animation: .default)
private var messages: FetchedResults
+ @State private var showRemoveAllMessageView: Bool = false
+ @State private var lastDeleteTime: TimeInterval = 0
+
+ func recordDeleteTime() -> Void {
+ let currentDeleteTime = Date.timeIntervalSinceReferenceDate
+ if currentDeleteTime - lastDeleteTime < 60 {
+ showRemoveAllMessageView = true
+ }
+ lastDeleteTime = currentDeleteTime
+ }
+
var body: some View {
BaseNavigationView(title: "消息") {
ScrollView {
@@ -28,6 +39,7 @@ struct MessageListView: View {
viewContext.delete(messageItem)
try? viewContext.save()
HToast.showSuccess(NSLocalizedString("已删除", comment: "删除设备/Key/消息时提示"))
+ recordDeleteTime()
Task {
do {
_ = try await HttpRequest.rmMessage(id: Int(id))
@@ -40,6 +52,14 @@ struct MessageListView: View {
Spacer(minLength: 30)
}
}
+ .overlay(
+ Group {
+ if (showRemoveAllMessageView) {
+ RemoveAllMessageView{showRemoveAllMessageView = false}
+ }
+ },
+ alignment: .bottom
+ )
.navigationBarItems(trailing: Button(action: {
withAnimation(.easeOut) {
store.isShowTestPush = !store.isShowTestPush
@@ -115,8 +135,51 @@ struct TestPushView: View {
}
}
+struct RemoveAllMessageView: View {
+ /// 取消按钮点击的回调
+ let closeAction : () -> ()
+ var body: some View {
+ VStack(alignment: .center, spacing: 0) {
+ HLine()
+ .stroke(Color("borderColor"))
+ .frame(height: 1)
+ HStack(spacing: 12) {
+ Button(NSLocalizedString("清除全部消息", comment: "")) {
+ Task {
+ do {
+ try MessageModel.deleteAll()
+ _ = try await HttpRequest.rmAllMessage()
+ HToast.showSuccess(NSLocalizedString("已清空", comment: ""))
+ self.closeAction()
+ } catch {
+ HToast.showError(error.localizedDescription)
+ }
+ }
+ }
+ .font(.system(size: 20))
+ .padding(.horizontal)
+ .frame( height: 42)
+ .overlay(RoundedRectangle(cornerRadius: 4).stroke())
+ .foregroundColor(Color.accentColor)
+
+ Button(NSLocalizedString("取消", comment: "")) {
+ self.closeAction()
+ }
+ .font(.system(size: 20))
+ .padding(.horizontal)
+ .frame( height: 42)
+ .overlay(RoundedRectangle(cornerRadius: 4).stroke())
+ .foregroundColor(Color.accentColor)
+ }
+ .padding()
+ }
+ .background(Color("backgroundColor").opacity(0.9))
+ }
+}
+
struct MessageView_Previews: PreviewProvider {
static var previews: some View {
MessageListView()
+ .environmentObject(AppState.shared)
}
}
diff --git a/ios/PushDeer-iOS/PushDeer/View/SettingsView.swift b/ios/PushDeer-iOS/PushDeer/View/SettingsView.swift
index e18730e..1c3b0ea 100644
--- a/ios/PushDeer-iOS/PushDeer/View/SettingsView.swift
+++ b/ios/PushDeer-iOS/PushDeer/View/SettingsView.swift
@@ -21,6 +21,7 @@ struct SettingsView: View {
VStack {
SettingsItemView(title: NSLocalizedString("登录为", comment: "") + " " + userName(), button: NSLocalizedString("退出", comment: "退出登录按钮上的文字")) {
store.token = ""
+ HToast.showText(NSLocalizedString("退出", comment: "退出登录按钮上的文字"))
}
.padding(EdgeInsets(top: 18, leading: 20, bottom: 0, trailing: 20))
diff --git a/ios/PushDeer-iOS/PushDeerClip/PushDeerClip-SelfHosted.entitlements b/ios/PushDeer-iOS/PushDeerClip/PushDeerClip-SelfHosted.entitlements
index bc419bf..a8f59a6 100644
--- a/ios/PushDeer-iOS/PushDeerClip/PushDeerClip-SelfHosted.entitlements
+++ b/ios/PushDeer-iOS/PushDeerClip/PushDeerClip-SelfHosted.entitlements
@@ -16,5 +16,9 @@
$(AppIdentifierPrefix)com.pushdeer.self.ios
+ com.apple.security.application-groups
+
+ group.com.pushdeer.self.ios
+
diff --git a/ios/PushDeer-iOS/PushDeerClip/PushDeerClip.entitlements b/ios/PushDeer-iOS/PushDeerClip/PushDeerClip.entitlements
index a92507e..e9fb37d 100644
--- a/ios/PushDeer-iOS/PushDeerClip/PushDeerClip.entitlements
+++ b/ios/PushDeer-iOS/PushDeerClip/PushDeerClip.entitlements
@@ -16,5 +16,9 @@
$(AppIdentifierPrefix)com.pushdeer.app.ios
+ com.apple.security.application-groups
+
+ group.com.pushdeer.app.ios
+
diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000..f21a778
--- /dev/null
+++ b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.537",
+ "green" : "0.278",
+ "red" : "0.231"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.702",
+ "green" : "0.365",
+ "red" : "0.302"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..9221b9b
--- /dev/null
+++ b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "83.5x83.5"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Contents.json b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/Contents.json b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/Contents.json b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/Contents.json
new file mode 100644
index 0000000..d12cc1a
--- /dev/null
+++ b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "filename" : "deer.gray.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "deer.gray@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/deer.gray.png b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/deer.gray.png
new file mode 100644
index 0000000..308d2d5
Binary files /dev/null and b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/deer.gray.png differ
diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/deer.gray@2x.png b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/deer.gray@2x.png
new file mode 100644
index 0000000..775bd52
Binary files /dev/null and b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/Images/deer.gray.imageset/deer.gray@2x.png differ
diff --git a/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json
new file mode 100644
index 0000000..eb87897
--- /dev/null
+++ b/ios/PushDeer-iOS/PushDeerWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/ios/PushDeer-iOS/PushDeerWidget/Info.plist b/ios/PushDeer-iOS/PushDeerWidget/Info.plist
new file mode 100644
index 0000000..d4e598e
--- /dev/null
+++ b/ios/PushDeer-iOS/PushDeerWidget/Info.plist
@@ -0,0 +1,16 @@
+
+
+
+
+ NSAppTransportSecurity
+
+ NSAllowsArbitraryLoads
+
+
+ NSExtension
+
+ NSExtensionPointIdentifier
+ com.apple.widgetkit-extension
+
+
+
diff --git a/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidget.intentdefinition b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidget.intentdefinition
new file mode 100644
index 0000000..bdb4045
--- /dev/null
+++ b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidget.intentdefinition
@@ -0,0 +1,59 @@
+
+
+
+
+ INEnums
+
+ INIntentDefinitionModelVersion
+ 1.2
+ INIntentDefinitionNamespace
+ 88xZPY
+ INIntentDefinitionSystemVersion
+ 20A294
+ INIntentDefinitionToolsBuildVersion
+ 12A6144
+ INIntentDefinitionToolsVersion
+ 12.0
+ INIntents
+
+
+ INIntentCategory
+ information
+ INIntentDescriptionID
+ tVvJ9c
+ INIntentEligibleForWidgets
+
+ INIntentIneligibleForSuggestions
+
+ INIntentName
+ Configuration
+ INIntentResponse
+
+ INIntentResponseCodes
+
+
+ INIntentResponseCodeName
+ success
+ INIntentResponseCodeSuccess
+
+
+
+ INIntentResponseCodeName
+ failure
+
+
+
+ INIntentTitle
+ Configuration
+ INIntentTitleID
+ gpCwrM
+ INIntentType
+ Custom
+ INIntentVerb
+ View
+
+
+ INTypes
+
+
+
diff --git a/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidget.swift b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidget.swift
new file mode 100644
index 0000000..1a33d85
--- /dev/null
+++ b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidget.swift
@@ -0,0 +1,179 @@
+//
+// PushDeerWidget.swift
+// PushDeerWidget
+//
+// Created by HEXT on 2022/3/2.
+//
+
+import WidgetKit
+import SwiftUI
+import Intents
+
+struct Provider: IntentTimelineProvider {
+
+ func placeholder(in context: Context) -> SimpleEntry {
+ SimpleEntry(date: Date(), configuration: ConfigurationIntent())
+ }
+
+ func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
+ let entry = SimpleEntry(date: Date(), configuration: configuration)
+ completion(entry)
+ }
+
+ func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline) -> ()) {
+ Task {
+ let currentDate = Date()
+ let entryDate = Calendar.current.date(byAdding: .minute, value: 1, to: currentDate)!
+ var entries: [SimpleEntry] = []
+ var entry = SimpleEntry(date: entryDate, configuration: configuration)
+ AppState.shared.reloadUserDefaults()
+ print("token", AppState.shared.token)
+ do {
+ let messages = try await HttpRequest.getMessages().messages
+ entry.messages = handleList(messages, context: context)
+ } catch {
+
+ }
+ entries.append(entry)
+ let timeline = Timeline(entries: entries, policy: .atEnd)
+ completion(timeline)
+ }
+ }
+
+ func handleList(_ origList: [MessageItem], context: Context) -> [MessageItem] {
+ var list = origList
+ var limit = 0
+ switch context.family {
+ case .systemSmall, .systemMedium:
+ limit = 4
+ default:
+ limit = 10
+ }
+ if list.count <= limit + 1 {
+ return list;
+ }
+ list = list.prefix(limit) + []
+ list.append(
+ MessageItem(id: -1, uid: "", text: "+其它\(origList.count - limit)条", desp: "", type: "text", pushkey_name: "", created_at: "")
+ )
+ return list;
+ }
+}
+
+struct SimpleEntry: TimelineEntry {
+ let date: Date
+ let configuration: ConfigurationIntent
+ var messages: [MessageItem] = placeholderList
+}
+
+struct PushDeerWidgetEntryView : View {
+ var entry: Provider.Entry
+
+ var body: some View {
+ HContentView {
+ if AppState.shared.token.isEmpty {
+ Text("未登录")
+ } else if entry.messages.isEmpty {
+ Text("无消息")
+ } else {
+ VStack(alignment: .leading, spacing: 5) {
+ ForEach(entry.messages) {
+ if entry.messages.first?.id != $0.id {
+ Divider()
+ }
+ if $0.id == -1 {
+ HText(text: $0.text)
+ .foregroundColor(.accentColor)
+ } else {
+ HText(text: $0.text)
+ }
+ }
+ .font(.system(size: 15))
+ Spacer(minLength: 0)
+ }
+ .padding(.top, 5)
+ .padding()
+ .accentColor(Color("AccentColor"))
+ }
+ }
+ }
+}
+
+struct HContentView: View {
+ /// 页面主体View
+ @ViewBuilder let contentView: Content
+
+ @Environment(\.colorScheme) private var colorScheme
+
+ var body: some View {
+ ZStack {
+ // VStack HStack Spacer 组合起来撑到最大
+ VStack {
+ HStack {
+ Spacer()
+ }
+ Spacer()
+ }
+ contentView
+ }
+ .background(
+ Image("deer.gray")
+ .opacity(colorScheme == .dark ? 0.4 : 1),
+ alignment: .topTrailing
+ )
+ }
+}
+
+struct HText: View {
+ let text: String
+ var body: some View {
+ if #available(iOSApplicationExtension 15.0, *) {
+ Group {
+ if #available(iOSApplicationExtension 15.0, *) {
+ Text(try! AttributedString(markdown: text))
+ }
+ }
+ } else {
+ Text(verbatim: text)
+ }
+ }
+}
+
+@main
+struct PushDeerWidget: Widget {
+ let kind: String = "PushDeerWidget"
+
+ var body: some WidgetConfiguration {
+ IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
+ PushDeerWidgetEntryView(entry: entry)
+ }
+ .configurationDisplayName("最近的PushDeer消息")
+ .description("这个小部件可以快捷展示你最近收到的消息.")
+ }
+}
+
+struct PushDeerWidget_Previews: PreviewProvider {
+ static var previews: some View {
+ AppState.shared.token = "1"
+ return Group {
+ PushDeerWidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))
+ .previewContext(WidgetPreviewContext(family: .systemSmall))
+ PushDeerWidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))
+ .previewContext(WidgetPreviewContext(family: .systemMedium))
+ PushDeerWidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))
+ .previewContext(WidgetPreviewContext(family: .systemLarge))
+ if #available(iOSApplicationExtension 15.0, *) {
+ PushDeerWidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))
+ .previewContext(WidgetPreviewContext(family: .systemExtraLarge))
+ }
+ }
+ }
+}
+
+let placeholderList = [
+ MessageItem(id: 1, uid: "", text: "展示最新通知", desp: "", type: "text", pushkey_name: "", created_at: ""),
+ MessageItem(id: 2, uid: "", text: "方便快速查看", desp: "", type: "text", pushkey_name: "", created_at: ""),
+ MessageItem(id: 3, uid: "", text: "保持消息同步", desp: "", type: "text", pushkey_name: "", created_at: ""),
+ MessageItem(id: 4, uid: "", text: "自动即时刷新", desp: "", type: "text", pushkey_name: "", created_at: ""),
+ MessageItem(id: -1, uid: "", text: "+其它\(8)条", desp: "", type: "text", pushkey_name: "", created_at: ""),
+]
diff --git a/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidgetExtension-SelfHosted.entitlements b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidgetExtension-SelfHosted.entitlements
new file mode 100644
index 0000000..51fcac8
--- /dev/null
+++ b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidgetExtension-SelfHosted.entitlements
@@ -0,0 +1,14 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.application-groups
+
+ group.com.pushdeer.self.ios
+
+ com.apple.security.network.client
+
+
+
diff --git a/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidgetExtension.entitlements b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidgetExtension.entitlements
new file mode 100644
index 0000000..c8bd608
--- /dev/null
+++ b/ios/PushDeer-iOS/PushDeerWidget/PushDeerWidgetExtension.entitlements
@@ -0,0 +1,14 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.application-groups
+
+ group.com.pushdeer.app.ios
+
+ com.apple.security.network.client
+
+
+