diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1a43b9cb..497e2180 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,35 +13,42 @@ jobs: - uses: AckeeCZ/load-xcode-version@1.1.0 - name: iOS tests run: set -o pipefail && xcodebuild test -scheme ACKategories -resultBundlePath Tests-iOS.xcresult -sdk iphonesimulator -destination "platform=iOS Simulator,name=$IOS_DEVICE,OS=latest" ONLY_ACTIVE_ARCH=YES | xcpretty - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: Tests-iOS.xcresult path: Tests-iOS.xcresult + - name: iOS PushNotifications tests + run: set -o pipefail && xcodebuild test -scheme PushNotifications -resultBundlePath Tests-PushNotifications-iOS.xcresult -sdk iphonesimulator -destination "platform=iOS Simulator,name=$IOS_DEVICE,OS=latest" ONLY_ACTIVE_ARCH=YES | xcpretty + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: Tests-PushNotifications-iOS.xcresult + path: Tests-PushNotifications-iOS.xcresult - name: iOS responder tests run: set -o pipefail && xcodebuild test -scheme ACKategories -resultBundlePath Tests-iOS-Responder.xcresult -sdk iphonesimulator -destination "platform=iOS Simulator,name=$IOS_DEVICE,OS=latest" ONLY_ACTIVE_ARCH=YES | xcpretty - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: Tests-iOS-Responder.xcresult path: Tests-iOS-Responder.xcresult - name: macOS tests run: set -o pipefail && xcodebuild test -scheme ACKategories -resultBundlePath Tests-macOS.xcresult -destination 'platform=OS X,arch=x86_64' | xcpretty - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: Tests-macOS.xcresult path: Tests-macOS.xcresult - name: watchOS tests run: set -o pipefail && xcodebuild test -scheme ACKategories -resultBundlePath Tests-watchOS.xcresult -sdk watchsimulator -destination "platform=watchOS Simulator,name=Apple Watch Ultra 2 (49mm),OS=latest" ONLY_ACTIVE_ARCH=YES | xcpretty - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: Tests-watchOS.xcresult path: Tests-watchOS.xcresult - name: tvOS tests run: set -o pipefail && xcodebuild test -scheme ACKategories -resultBundlePath Tests-tvOS.xcresult -sdk appletvsimulator -destination "platform=tvOS Simulator,name=Apple TV 4K (3rd generation),OS=latest" ONLY_ACTIVE_ARCH=YES | xcpretty - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: Tests-tvOS.xcresult diff --git a/ACKategories.xcodeproj/project.pbxproj b/ACKategories.xcodeproj/project.pbxproj index cedabe85..07c6e7c5 100644 --- a/ACKategories.xcodeproj/project.pbxproj +++ b/ACKategories.xcodeproj/project.pbxproj @@ -8,20 +8,6 @@ /* Begin PBXBuildFile section */ 690BCB8D2B3DB62400EDA6F8 /* Networking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 693A925D2B3DB290008B3DC3 /* Networking.framework */; }; - 691A1FCF2B45931800B52E56 /* ACKategories.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; }; - 691A1FD52B45932A00B52E56 /* FirebaseFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 691A1FD42B45932A00B52E56 /* FirebaseFetcher.swift */; }; - 691A1FE42B4593EB00B52E56 /* GoogleAppMeasurement.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FD82B4593EB00B52E56 /* GoogleAppMeasurement.xcframework */; }; - 691A1FE62B4593EB00B52E56 /* FirebaseCoreInternal.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FD92B4593EB00B52E56 /* FirebaseCoreInternal.xcframework */; }; - 691A1FE82B4593EB00B52E56 /* FirebaseAnalytics.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDA2B4593EB00B52E56 /* FirebaseAnalytics.xcframework */; }; - 691A1FEA2B4593EB00B52E56 /* FBLPromises.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDB2B4593EB00B52E56 /* FBLPromises.xcframework */; }; - 691A1FEC2B4593EB00B52E56 /* FirebaseRemoteConfig.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDC2B4593EB00B52E56 /* FirebaseRemoteConfig.xcframework */; }; - 691A1FEE2B4593EB00B52E56 /* GoogleUtilities.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDD2B4593EB00B52E56 /* GoogleUtilities.xcframework */; }; - 691A1FF02B4593EB00B52E56 /* nanopb.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDE2B4593EB00B52E56 /* nanopb.xcframework */; }; - 691A1FF22B4593EB00B52E56 /* FirebaseInstallations.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDF2B4593EB00B52E56 /* FirebaseInstallations.xcframework */; }; - 691A1FF42B4593EB00B52E56 /* GoogleAppMeasurementIdentitySupport.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FE02B4593EB00B52E56 /* GoogleAppMeasurementIdentitySupport.xcframework */; }; - 691A1FF62B4593EC00B52E56 /* FirebaseABTesting.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FE12B4593EB00B52E56 /* FirebaseABTesting.xcframework */; }; - 691A1FF82B4593EC00B52E56 /* FirebaseCore.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FE22B4593EB00B52E56 /* FirebaseCore.xcframework */; }; - 691A1FFA2B4593EC00B52E56 /* FirebaseSharedSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FE32B4593EB00B52E56 /* FirebaseSharedSwift.xcframework */; }; 691B9AD92AFD1E5C008AE7BD /* ACKategories.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; }; 691B9ADA2AFD1E5C008AE7BD /* ACKategories.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 6922C77D2AFD1C1A00519CDF /* UINavigationControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6922C77C2AFD1C1A00519CDF /* UINavigationControllerTests.swift */; }; @@ -129,6 +115,8 @@ 69ACD71C2AFD13480021127B /* IntRandomTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6B22AFD114600C8E8D9 /* IntRandomTests.swift */; }; 69ACD71D2AFD13480021127B /* StringRandomTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6B32AFD114600C8E8D9 /* StringRandomTests.swift */; }; 69C6BE902B0BC80E008A4ECF /* UIWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6A82AFD114600C8E8D9 /* UIWindow.swift */; }; + 69DF227B2B459D0D0025C555 /* PushManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DF22792B459D0D0025C555 /* PushManager.swift */; }; + 69DF227C2B459D0D0025C555 /* UNNotificationSettingsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DF227A2B459D0D0025C555 /* UNNotificationSettingsExtensions.swift */; }; 69FA5FAD23C868A900B44BCD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 69FA5F9023C868A900B44BCD /* Assets.xcassets */; }; 69FA5FAE23C868A900B44BCD /* UIControlBlocksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69FA5F9323C868A900B44BCD /* UIControlBlocksViewController.swift */; }; 69FA5FAF23C868A900B44BCD /* TitleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69FA5F9523C868A900B44BCD /* TitleViewController.swift */; }; @@ -157,13 +145,6 @@ remoteGlobalIDString = 693A925C2B3DB290008B3DC3; remoteInfo = Networking; }; - 691A1FD12B45931800B52E56 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 69E819DF23C773010054687B /* Project object */; - proxyType = 1; - remoteGlobalIDString = 69ACD6C12AFD130C0021127B; - remoteInfo = ACKategories; - }; 691B9ADB2AFD1E5C008AE7BD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 69E819DF23C773010054687B /* Project object */; @@ -238,21 +219,6 @@ /* Begin PBXFileReference section */ 690BCB8C2B3DB5DE00EDA6F8 /* Networking.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = Networking.xctestplan; path = Tests/NetworkingTests/Networking.xctestplan; sourceTree = SOURCE_ROOT; }; - 691A1FC72B4592BC00B52E56 /* FirebaseFetcher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FirebaseFetcher.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 691A1FD42B45932A00B52E56 /* FirebaseFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FirebaseFetcher.swift; sourceTree = ""; }; - 691A1FD62B45935400B52E56 /* Cartfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile; sourceTree = ""; }; - 691A1FD82B4593EB00B52E56 /* GoogleAppMeasurement.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GoogleAppMeasurement.xcframework; path = Carthage/Build/GoogleAppMeasurement.xcframework; sourceTree = ""; }; - 691A1FD92B4593EB00B52E56 /* FirebaseCoreInternal.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseCoreInternal.xcframework; path = Carthage/Build/FirebaseCoreInternal.xcframework; sourceTree = ""; }; - 691A1FDA2B4593EB00B52E56 /* FirebaseAnalytics.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseAnalytics.xcframework; path = Carthage/Build/FirebaseAnalytics.xcframework; sourceTree = ""; }; - 691A1FDB2B4593EB00B52E56 /* FBLPromises.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FBLPromises.xcframework; path = Carthage/Build/FBLPromises.xcframework; sourceTree = ""; }; - 691A1FDC2B4593EB00B52E56 /* FirebaseRemoteConfig.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseRemoteConfig.xcframework; path = Carthage/Build/FirebaseRemoteConfig.xcframework; sourceTree = ""; }; - 691A1FDD2B4593EB00B52E56 /* GoogleUtilities.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GoogleUtilities.xcframework; path = Carthage/Build/GoogleUtilities.xcframework; sourceTree = ""; }; - 691A1FDE2B4593EB00B52E56 /* nanopb.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = nanopb.xcframework; path = Carthage/Build/nanopb.xcframework; sourceTree = ""; }; - 691A1FDF2B4593EB00B52E56 /* FirebaseInstallations.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseInstallations.xcframework; path = Carthage/Build/FirebaseInstallations.xcframework; sourceTree = ""; }; - 691A1FE02B4593EB00B52E56 /* GoogleAppMeasurementIdentitySupport.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GoogleAppMeasurementIdentitySupport.xcframework; path = Carthage/Build/GoogleAppMeasurementIdentitySupport.xcframework; sourceTree = ""; }; - 691A1FE12B4593EB00B52E56 /* FirebaseABTesting.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseABTesting.xcframework; path = Carthage/Build/FirebaseABTesting.xcframework; sourceTree = ""; }; - 691A1FE22B4593EB00B52E56 /* FirebaseCore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseCore.xcframework; path = Carthage/Build/FirebaseCore.xcframework; sourceTree = ""; }; - 691A1FE32B4593EB00B52E56 /* FirebaseSharedSwift.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseSharedSwift.xcframework; path = Carthage/Build/FirebaseSharedSwift.xcframework; sourceTree = ""; }; 6922C77C2AFD1C1A00519CDF /* UINavigationControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UINavigationControllerTests.swift; sourceTree = ""; }; 6922C77E2AFD1C3300519CDF /* ACKategoriesResponder.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = ACKategoriesResponder.xctestplan; sourceTree = ""; }; 6922C77F2AFD1C5000519CDF /* ACKategories.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = ACKategories.xctestplan; sourceTree = ""; }; @@ -293,6 +259,9 @@ 698376CE2B3DA81200CD9E89 /* GradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = ""; }; 69ACD6C22AFD130C0021127B /* ACKategories.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ACKategories.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 69ACD6C92AFD130D0021127B /* ACKategoriesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ACKategoriesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 69DF225A2B459CC50025C555 /* PushNotifications.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PushNotifications.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 69DF22792B459D0D0025C555 /* PushManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushManager.swift; sourceTree = ""; }; + 69DF227A2B459D0D0025C555 /* UNNotificationSettingsExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UNNotificationSettingsExtensions.swift; sourceTree = ""; }; 69E0A5F52AFD10BE00C8E8D9 /* UISearchBarExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISearchBarExtensions.swift; sourceTree = ""; }; 69E0A5F72AFD10BE00C8E8D9 /* UserDefault.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefault.swift; sourceTree = ""; }; 69E0A5F82AFD10BE00C8E8D9 /* ArrayExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrayExtensions.swift; sourceTree = ""; }; @@ -384,26 +353,6 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 691A1FC42B4592BC00B52E56 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 691A1FFA2B4593EC00B52E56 /* FirebaseSharedSwift.xcframework in Frameworks */, - 691A1FE42B4593EB00B52E56 /* GoogleAppMeasurement.xcframework in Frameworks */, - 691A1FF82B4593EC00B52E56 /* FirebaseCore.xcframework in Frameworks */, - 691A1FF42B4593EB00B52E56 /* GoogleAppMeasurementIdentitySupport.xcframework in Frameworks */, - 691A1FE82B4593EB00B52E56 /* FirebaseAnalytics.xcframework in Frameworks */, - 691A1FEE2B4593EB00B52E56 /* GoogleUtilities.xcframework in Frameworks */, - 691A1FCF2B45931800B52E56 /* ACKategories.framework in Frameworks */, - 691A1FEA2B4593EB00B52E56 /* FBLPromises.xcframework in Frameworks */, - 691A1FEC2B4593EB00B52E56 /* FirebaseRemoteConfig.xcframework in Frameworks */, - 691A1FF62B4593EC00B52E56 /* FirebaseABTesting.xcframework in Frameworks */, - 691A1FE62B4593EB00B52E56 /* FirebaseCoreInternal.xcframework in Frameworks */, - 691A1FF02B4593EB00B52E56 /* nanopb.xcframework in Frameworks */, - 691A1FF22B4593EB00B52E56 /* FirebaseInstallations.xcframework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 693A925A2B3DB290008B3DC3 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -461,36 +410,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 69DF22572B459CC50025C555 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 691A1FCE2B45930400B52E56 /* FirebaseFetcher */ = { - isa = PBXGroup; - children = ( - 691A1FD42B45932A00B52E56 /* FirebaseFetcher.swift */, - ); - path = FirebaseFetcher; - sourceTree = ""; - }; - 691B9AD82AFD1E5C008AE7BD /* Frameworks */ = { - isa = PBXGroup; - children = ( - 691A1FDB2B4593EB00B52E56 /* FBLPromises.xcframework */, - 691A1FE12B4593EB00B52E56 /* FirebaseABTesting.xcframework */, - 691A1FDA2B4593EB00B52E56 /* FirebaseAnalytics.xcframework */, - 691A1FE22B4593EB00B52E56 /* FirebaseCore.xcframework */, - 691A1FD92B4593EB00B52E56 /* FirebaseCoreInternal.xcframework */, - 691A1FDF2B4593EB00B52E56 /* FirebaseInstallations.xcframework */, - 691A1FDC2B4593EB00B52E56 /* FirebaseRemoteConfig.xcframework */, - 691A1FE32B4593EB00B52E56 /* FirebaseSharedSwift.xcframework */, - 691A1FD82B4593EB00B52E56 /* GoogleAppMeasurement.xcframework */, - 691A1FE02B4593EB00B52E56 /* GoogleAppMeasurementIdentitySupport.xcframework */, - 691A1FDD2B4593EB00B52E56 /* GoogleUtilities.xcframework */, - 691A1FDE2B4593EB00B52E56 /* nanopb.xcframework */, - ); - name = Frameworks; - sourceTree = ""; - }; 693A92722B3DB2AA008B3DC3 /* Networking */ = { isa = PBXGroup; children = ( @@ -625,13 +554,22 @@ path = Theme; sourceTree = ""; }; + 69DF22782B459D0D0025C555 /* PushNotifications */ = { + isa = PBXGroup; + children = ( + 69DF22792B459D0D0025C555 /* PushManager.swift */, + 69DF227A2B459D0D0025C555 /* UNNotificationSettingsExtensions.swift */, + ); + path = PushNotifications; + sourceTree = ""; + }; 69E0A5F32AFD10BE00C8E8D9 /* Sources */ = { isa = PBXGroup; children = ( - 691A1FCE2B45930400B52E56 /* FirebaseFetcher */, 69E0A5F42AFD10BE00C8E8D9 /* ACKategories */, 693A92982B3DB394008B3DC3 /* ACKategoriesTesting */, 693A92722B3DB2AA008B3DC3 /* Networking */, + 69DF22782B459D0D0025C555 /* PushNotifications */, ); path = Sources; sourceTree = ""; @@ -787,13 +725,11 @@ 69E819DE23C773010054687B = { isa = PBXGroup; children = ( - 691A1FD62B45935400B52E56 /* Cartfile */, 697B023227DB65B50082F4AC /* CHANGELOG.md */, 695096D923C7908B00E8F457 /* ACKategoriesExample */, 69E819EB23C773240054687B /* Products */, 69E0A5F32AFD10BE00C8E8D9 /* Sources */, 69E0A69B2AFD114600C8E8D9 /* Tests */, - 691B9AD82AFD1E5C008AE7BD /* Frameworks */, ); sourceTree = ""; }; @@ -807,7 +743,7 @@ 693A925D2B3DB290008B3DC3 /* Networking.framework */, 693A92642B3DB290008B3DC3 /* NetworkingTests.xctest */, 693A92912B3DB388008B3DC3 /* ACKategoriesTesting.framework */, - 691A1FC72B4592BC00B52E56 /* FirebaseFetcher.framework */, + 69DF225A2B459CC50025C555 /* PushNotifications.framework */, ); name = Products; sourceTree = ""; @@ -913,28 +849,28 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - 691A1FC22B4592BC00B52E56 /* Headers */ = { + 693A92582B3DB290008B3DC3 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 693A92582B3DB290008B3DC3 /* Headers */ = { + 693A928C2B3DB388008B3DC3 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 693A928C2B3DB388008B3DC3 /* Headers */ = { + 69ACD6BD2AFD130C0021127B /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 69ACD6BD2AFD130C0021127B /* Headers */ = { + 69DF22552B459CC50025C555 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( @@ -944,25 +880,6 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - 691A1FC62B4592BC00B52E56 /* FirebaseFetcher */ = { - isa = PBXNativeTarget; - buildConfigurationList = 691A1FCD2B4592BC00B52E56 /* Build configuration list for PBXNativeTarget "FirebaseFetcher" */; - buildPhases = ( - 691A1FC22B4592BC00B52E56 /* Headers */, - 691A1FC32B4592BC00B52E56 /* Sources */, - 691A1FC42B4592BC00B52E56 /* Frameworks */, - 691A1FC52B4592BC00B52E56 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 691A1FD22B45931800B52E56 /* PBXTargetDependency */, - ); - name = FirebaseFetcher; - productName = FirebaseFetcher; - productReference = 691A1FC72B4592BC00B52E56 /* FirebaseFetcher.framework */; - productType = "com.apple.product-type.framework"; - }; 693A925C2B3DB290008B3DC3 /* Networking */ = { isa = PBXNativeTarget; buildConfigurationList = 693A92702B3DB290008B3DC3 /* Build configuration list for PBXNativeTarget "Networking" */; @@ -1097,6 +1014,24 @@ productReference = 69ACD6C92AFD130D0021127B /* ACKategoriesTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 69DF22592B459CC50025C555 /* PushNotifications */ = { + isa = PBXNativeTarget; + buildConfigurationList = 69DF226D2B459CC50025C555 /* Build configuration list for PBXNativeTarget "PushNotifications" */; + buildPhases = ( + 69DF22552B459CC50025C555 /* Headers */, + 69DF22562B459CC50025C555 /* Sources */, + 69DF22572B459CC50025C555 /* Frameworks */, + 69DF22582B459CC50025C555 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PushNotifications; + productName = PushNotifications; + productReference = 69DF225A2B459CC50025C555 /* PushNotifications.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -1107,10 +1042,6 @@ LastSwiftUpdateCheck = 1510; LastUpgradeCheck = 1500; TargetAttributes = { - 691A1FC62B4592BC00B52E56 = { - CreatedOnToolsVersion = 15.1; - LastSwiftMigration = 1510; - }; 693A925C2B3DB290008B3DC3 = { CreatedOnToolsVersion = 15.1; }; @@ -1134,6 +1065,9 @@ 69ACD6C82AFD130D0021127B = { CreatedOnToolsVersion = 15.0.1; }; + 69DF22592B459CC50025C555 = { + CreatedOnToolsVersion = 15.1; + }; }; }; buildConfigurationList = 69E819E223C773010054687B /* Build configuration list for PBXProject "ACKategories" */; @@ -1158,19 +1092,12 @@ 693A925C2B3DB290008B3DC3 /* Networking */, 693A92632B3DB290008B3DC3 /* NetworkingTests */, 693A92902B3DB388008B3DC3 /* ACKategoriesTesting */, - 691A1FC62B4592BC00B52E56 /* FirebaseFetcher */, + 69DF22592B459CC50025C555 /* PushNotifications */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 691A1FC52B4592BC00B52E56 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 693A925B2B3DB290008B3DC3 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1222,17 +1149,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 691A1FC32B4592BC00B52E56 /* Sources */ = { - isa = PBXSourcesBuildPhase; + 69DF22582B459CC50025C555 /* Resources */ = { + isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 691A1FD52B45932A00B52E56 /* FirebaseFetcher.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ 693A92592B3DB290008B3DC3 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1397,6 +1323,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 69DF22562B459CC50025C555 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 69DF227C2B459D0D0025C555 /* UNNotificationSettingsExtensions.swift in Sources */, + 69DF227B2B459D0D0025C555 /* PushManager.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -1405,11 +1340,6 @@ target = 693A925C2B3DB290008B3DC3 /* Networking */; targetProxy = 690BCB8F2B3DB62400EDA6F8 /* PBXContainerItemProxy */; }; - 691A1FD22B45931800B52E56 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 69ACD6C12AFD130C0021127B /* ACKategories */; - targetProxy = 691A1FD12B45931800B52E56 /* PBXContainerItemProxy */; - }; 691B9ADC2AFD1E5C008AE7BD /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 69ACD6C12AFD130C0021127B /* ACKategories */; @@ -1453,128 +1383,6 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - 691A1FCB2B4592BC00B52E56 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.FirebaseFetcher; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = macosx; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; - SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 691A1FCC2B4592BC00B52E56 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - ENABLE_NS_ASSERTIONS = NO; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.FirebaseFetcher; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = macosx; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; - SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; 693A926C2B3DB290008B3DC3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2326,6 +2134,121 @@ }; name = Release; }; + 69DF22692B459CC50025C555 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.PushNotifications; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 69DF226A2B459CC50025C555 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.PushNotifications; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; 69E819E323C773010054687B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2416,15 +2339,6 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 691A1FCD2B4592BC00B52E56 /* Build configuration list for PBXNativeTarget "FirebaseFetcher" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 691A1FCB2B4592BC00B52E56 /* Debug */, - 691A1FCC2B4592BC00B52E56 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 693A92702B3DB290008B3DC3 /* Build configuration list for PBXNativeTarget "Networking" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -2488,6 +2402,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 69DF226D2B459CC50025C555 /* Build configuration list for PBXNativeTarget "PushNotifications" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 69DF22692B459CC50025C555 /* Debug */, + 69DF226A2B459CC50025C555 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 69E819E223C773010054687B /* Build configuration list for PBXProject "ACKategories" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/ACKategories.xcodeproj/xcshareddata/xcschemes/PushNotifications.xcscheme b/ACKategories.xcodeproj/xcshareddata/xcschemes/PushNotifications.xcscheme new file mode 100644 index 00000000..124585d1 --- /dev/null +++ b/ACKategories.xcodeproj/xcshareddata/xcschemes/PushNotifications.xcscheme @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Package.swift b/Package.swift index affb0c91..d03ecffc 100644 --- a/Package.swift +++ b/Package.swift @@ -13,6 +13,7 @@ let package = Package( .library(name: "ACKategories", targets: ["ACKategories"]), .library(name: "ACKategoriesTesting", targets: ["ACKategoriesTesting"]), .library(name: "Networking", targets: ["Networking"]), + .library(name: "PushNotifications", targets: ["PushNotifications"]), ], targets: [ .target(name: "ACKategories"), @@ -38,8 +39,6 @@ let package = Package( "Networking", ] ), - dependencies: [ - ] - ), + .target(name: "PushNotifications"), ] ) diff --git a/Sources/PushNotifications/PushManager.swift b/Sources/PushNotifications/PushManager.swift new file mode 100644 index 00000000..93452514 --- /dev/null +++ b/Sources/PushNotifications/PushManager.swift @@ -0,0 +1,142 @@ +import Foundation +import UserNotifications + +@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *) +public protocol PushManaging { + var actions: PushManagingActions { get } + + var notificationSettings: AsyncStream { get } + var currentNotificationSettings: UNNotificationSettings? { get } +} + +@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *) +public protocol PushManagingActions { + func start() + func requestPermission(options: UNAuthorizationOptions) async + + func addNotificationReceivedHandler(_ handler: @escaping (UNNotification) async -> ()) -> String + func removeNotificationReceivedHandler(id: String) + + #if !os(tvOS) + func addNotificationOpenedHandler(_ handler: @escaping (UNNotificationResponse) async -> ()) -> String + func removeNotificationOpenedHandler(id: String) + #endif +} + +@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *) +public extension PushManaging where Self: PushManagingActions { + var actions: PushManagingActions { self } +} + +@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *) +public final class PushManager: NSObject, PushManaging, PushManagingActions { + public private(set) lazy var notificationSettings = AsyncStream { continuation in + notificationSettingsContinuation = continuation + + if let currentNotificationSettings { + continuation.yield(currentNotificationSettings) + } + } + public private(set) var currentNotificationSettings: UNNotificationSettings? + + public lazy var presentationOptions: (UNNotification) async -> UNNotificationPresentationOptions = { [weak self] notification in + guard let self else { return [] } + return await notificationCenter.notificationSettings().allowedPresentationOptions + } + + public var openSettings: (UNNotification?) -> () = { _ in } + + private let notificationCenter: UNUserNotificationCenter + private var notificationSettingsContinuation: AsyncStream.Continuation? + + private var notificationReceivedHandlers = [String: (UNNotification) async -> ()]() + + #if !os(tvOS) + private var notificationOpenedHandlers = [String: (UNNotificationResponse) async -> ()]() + #endif + + public init( + notificationCenter: UNUserNotificationCenter = .current() + ) { + self.notificationCenter = notificationCenter + super.init() + } + + deinit { + notificationSettingsContinuation?.finish() + } + + public func start() { + notificationCenter.delegate = self + } + + public func requestPermission(options: UNAuthorizationOptions) async { + guard let granted = try? await notificationCenter.requestAuthorization(options: options), + granted + else { return } + + let settings = await notificationCenter.notificationSettings() + currentNotificationSettings = settings + notificationSettingsContinuation?.yield(settings) + } + + public func addNotificationReceivedHandler( + _ handler: @escaping (UNNotification) async -> () + ) -> String { + let id = UUID().uuidString + notificationReceivedHandlers[id] = handler + return id + } + + public func removeNotificationReceivedHandler(id: String) { + notificationReceivedHandlers[id] = nil + } + + #if !os(tvOS) + public func addNotificationOpenedHandler( + _ handler: @escaping (UNNotificationResponse) async -> () + ) -> String { + let id = UUID().uuidString + notificationOpenedHandlers[id] = handler + return id + } + + public func removeNotificationOpenedHandler(id: String) { + notificationOpenedHandlers[id] = nil + } + #endif +} + +@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *) +extension PushManager: UNUserNotificationCenterDelegate { + public func userNotificationCenter( + _ center: UNUserNotificationCenter, + willPresent notification: UNNotification + ) async -> UNNotificationPresentationOptions { + for handler in notificationReceivedHandlers.values { + await handler(notification) + } + + return await presentationOptions(notification) + } + + #if !os(tvOS) + public func userNotificationCenter( + _ center: UNUserNotificationCenter, + didReceive response: UNNotificationResponse + ) async { + for handler in notificationOpenedHandlers.values { + await handler(response) + } + } + #endif + + #if !os(watchOS) && !os(tvOS) + public func userNotificationCenter( + _ center: UNUserNotificationCenter, + openSettingsFor notification: UNNotification? + ) { + openSettings(notification) + } + #endif +} diff --git a/Sources/PushNotifications/UNNotificationSettingsExtensions.swift b/Sources/PushNotifications/UNNotificationSettingsExtensions.swift new file mode 100644 index 00000000..07492ae8 --- /dev/null +++ b/Sources/PushNotifications/UNNotificationSettingsExtensions.swift @@ -0,0 +1,22 @@ +import UserNotifications + +@available(iOS 13.0, macOS 10.15, *) +public extension UNNotificationSettings { + var allowedPresentationOptions: UNNotificationPresentationOptions { + var options = [(UNNotificationSetting, UNNotificationPresentationOptions)]() + + #if !os(tvOS) + options.append((soundSetting, UNNotificationPresentationOptions.sound)) + options.append((alertSetting, .alert)) + #endif + + #if !os(watchOS) + options.append((badgeSetting, .badge)) + #endif + + return .init(options.compactMap { setting, option in + if setting == .enabled { return option } + return nil + }) + } +}