diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1097129b..f8ece5f3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,6 +20,10 @@ jobs: run: gem install bundler - name: Install bundler dependencies run: bundle install + - name: Install yeetd + run: | + wget https://github.com/biscuitehh/yeetd/releases/download/1.0/yeetd-normal.pkg + sudo installer -pkg yeetd-normal.pkg -target / + yeetd & - name: Run unit tests run: bundle exec fastlane unitTestLane - diff --git a/.github/workflows/prepare_release_branch.yml b/.github/workflows/prepare_release_branch.yml new file mode 100644 index 00000000..42ab0345 --- /dev/null +++ b/.github/workflows/prepare_release_branch.yml @@ -0,0 +1,91 @@ +name: Preparation for release + +on: + push: + tags: + - '*' + +jobs: + preparation: + runs-on: macos-14 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Extract version from branch name + run: echo "VERSION=${GITHUB_REF#refs/heads/release/}" >> $GITHUB_ENV + + - name: Check for existing branch + run: | + if git ls-remote --heads origin release/${{ env.VERSION }} | grep release/${{ env.VERSION }}; then + echo "Branch release/${{ env.VERSION }} already exists." + exit 1 + fi + shell: bash + + - name: Bump version + run: ./git-release-branch-create.sh "${{ env.VERSION }}" + + - name: Check if sdkVersion matches VERSION + run: | + SDK_VERSION=$(sed -n 's/^.*sdkVersion = "\(.*\)"/\1/p' SDKVersionProvider/SDKVersionProvider.swift) + if [ "$SDK_VERSION" != "${{ env.VERSION }}" ]; then + echo "SDK version ($SDK_VERSION) does not match the branch version (${{ env.VERSION }})." + exit 1 + fi + shell: bash + + - name: Create Pull Request + id: create-pr + uses: peter-evans/create-pull-request@v6 + with: + commit-message: Update release version to ${{ env.VERSION }} + title: "Release/${{ env.VERSION }}" + body: | + Updates the release version to ${{ env.VERSION }}. + branch: "release/${{ env.VERSION }}" + base: "master" + + - name: Post to a Slack channel + id: slack + uses: slackapi/slack-github-action@v1.25.0 + with: + channel-id: 'C06RXV161RA' + payload: | + { + "text": "iOS Release Branch Notification", + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "🚀 iOS Release Branch Created" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Branch Name:* *`release/${{ env.VERSION }}`*\n*Status:* Success :white_check_mark:" + } + }, + { + "type": "divider" + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { + "type": "plain_text", + "text": "Pull Request" + }, + "url": "${{ steps.create-pr.outputs.pull-request-url }}" + } + ] + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_MOBILE_NOTIFIER_TOKEN }} \ No newline at end of file diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj new file mode 100644 index 00000000..4aedae17 --- /dev/null +++ b/Example/Example.xcodeproj/project.pbxproj @@ -0,0 +1,837 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 0A0DE3482BB8455A00812E73 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0DE3472BB8455A00812E73 /* NotificationService.swift */; }; + 0A0DE34C2BB8455A00812E73 /* MindboxNotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0A0DE3452BB8455A00812E73 /* MindboxNotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 0A4B681A2BBC82B500639BC5 /* ChooseInAppMessagesDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A4B68192BBC82B500639BC5 /* ChooseInAppMessagesDelegate.swift */; }; + 0A4B682A2BBD7D5100639BC5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0A4B68292BBD7D5100639BC5 /* LaunchScreen.storyboard */; }; + 0AD271D02BB9D81E00750279 /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AD271CF2BB9D81D00750279 /* UserNotifications.framework */; }; + 0AD271D22BB9D81E00750279 /* UserNotificationsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AD271D12BB9D81E00750279 /* UserNotificationsUI.framework */; }; + 0AD271D52BB9D81E00750279 /* NotificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AD271D42BB9D81E00750279 /* NotificationViewController.swift */; }; + 0AD271DC2BB9D81E00750279 /* MindboxNotificationContentExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0AD271CE2BB9D81D00750279 /* MindboxNotificationContentExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 0AD271E42BBAAA8800750279 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0AD271E32BBAAA8800750279 /* PrivacyInfo.xcprivacy */; }; + 0AEDBC7A2BB6F8F200EE8722 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AEDBC792BB6F8F200EE8722 /* AppDelegate.swift */; }; + 0AEDBC7C2BB6F8F200EE8722 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AEDBC7B2BB6F8F200EE8722 /* SceneDelegate.swift */; }; + 0AEDBC832BB6F8F400EE8722 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0AEDBC822BB6F8F400EE8722 /* Assets.xcassets */; }; + 0AEDBC922BB6FA4800EE8722 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AEDBC912BB6FA4800EE8722 /* MainView.swift */; }; + 0AEDBC962BB6FE4900EE8722 /* MainViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AEDBC952BB6FE4900EE8722 /* MainViewModel.swift */; }; + 0AEDBC992BB7058B00EE8722 /* ButtonsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AEDBC982BB7058B00EE8722 /* ButtonsView.swift */; }; + 0AEDBC9B2BB70D6E00EE8722 /* SDKDataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AEDBC9A2BB70D6E00EE8722 /* SDKDataView.swift */; }; + A9A0D2B56DA740D1AC14A76E /* Pods_MindboxNotificationContentExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B48D4AD5551A8E0F2B521ED8 /* Pods_MindboxNotificationContentExtension.framework */; }; + EC719A83A7838F46B132825D /* Pods_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D80D2646AFEBEE6F556A82E3 /* Pods_Example.framework */; }; + ECBF861DEC24D002BCCA5C54 /* Pods_MindboxNotificationServiceExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6D0E4C1407865DB9AA01F215 /* Pods_MindboxNotificationServiceExtension.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 0A0DE34A2BB8455A00812E73 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0AEDBC6E2BB6F8F200EE8722 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0A0DE3442BB8455A00812E73; + remoteInfo = MindboxNotificationServiceExtension; + }; + 0AD271DA2BB9D81E00750279 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0AEDBC6E2BB6F8F200EE8722 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0AD271CD2BB9D81D00750279; + remoteInfo = MindboxNotificationContentExtension; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 0A0DE34D2BB8455A00812E73 /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 0A0DE34C2BB8455A00812E73 /* MindboxNotificationServiceExtension.appex in Embed Foundation Extensions */, + 0AD271DC2BB9D81E00750279 /* MindboxNotificationContentExtension.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0A0DE3452BB8455A00812E73 /* MindboxNotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = MindboxNotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 0A0DE3472BB8455A00812E73 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; + 0A0DE3492BB8455A00812E73 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0A0DE3512BB8457100812E73 /* MindboxNotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MindboxNotificationServiceExtension.entitlements; sourceTree = ""; }; + 0A4B68192BBC82B500639BC5 /* ChooseInAppMessagesDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChooseInAppMessagesDelegate.swift; sourceTree = ""; }; + 0A4B68292BBD7D5100639BC5 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + 0AD271CE2BB9D81D00750279 /* MindboxNotificationContentExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = MindboxNotificationContentExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 0AD271CF2BB9D81D00750279 /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; }; + 0AD271D12BB9D81E00750279 /* UserNotificationsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotificationsUI.framework; path = System/Library/Frameworks/UserNotificationsUI.framework; sourceTree = SDKROOT; }; + 0AD271D42BB9D81E00750279 /* NotificationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationViewController.swift; sourceTree = ""; }; + 0AD271D92BB9D81E00750279 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0AD271E02BB9D84800750279 /* MindboxNotificationContentExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MindboxNotificationContentExtension.entitlements; sourceTree = ""; }; + 0AD271E32BBAAA8800750279 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + 0AEDBC762BB6F8F200EE8722 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 0AEDBC792BB6F8F200EE8722 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 0AEDBC7B2BB6F8F200EE8722 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 0AEDBC822BB6F8F400EE8722 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 0AEDBC872BB6F8F400EE8722 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0AEDBC912BB6FA4800EE8722 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; + 0AEDBC952BB6FE4900EE8722 /* MainViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewModel.swift; sourceTree = ""; }; + 0AEDBC982BB7058B00EE8722 /* ButtonsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonsView.swift; sourceTree = ""; }; + 0AEDBC9A2BB70D6E00EE8722 /* SDKDataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKDataView.swift; sourceTree = ""; }; + 0AEDBC9C2BB7177A00EE8722 /* Example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Example.entitlements; sourceTree = ""; }; + 240E950B149EDBB0CAC016A0 /* Pods-Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.debug.xcconfig"; path = "Target Support Files/Pods-Example/Pods-Example.debug.xcconfig"; sourceTree = ""; }; + 296B8C4BB0F741F658A9E34E /* Pods-MindboxNotificationContentExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MindboxNotificationContentExtension.debug.xcconfig"; path = "Target Support Files/Pods-MindboxNotificationContentExtension/Pods-MindboxNotificationContentExtension.debug.xcconfig"; sourceTree = ""; }; + 6D0E4C1407865DB9AA01F215 /* Pods_MindboxNotificationServiceExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MindboxNotificationServiceExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8800E015E929FCDAB94398CB /* Pods-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.release.xcconfig"; path = "Target Support Files/Pods-Example/Pods-Example.release.xcconfig"; sourceTree = ""; }; + 8FC128008EAD005E4940F3B6 /* Pods-MindboxNotificationServiceExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MindboxNotificationServiceExtension.release.xcconfig"; path = "Target Support Files/Pods-MindboxNotificationServiceExtension/Pods-MindboxNotificationServiceExtension.release.xcconfig"; sourceTree = ""; }; + 97056FE3D936C1A512C53307 /* Pods-MindboxNotificationContentExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MindboxNotificationContentExtension.release.xcconfig"; path = "Target Support Files/Pods-MindboxNotificationContentExtension/Pods-MindboxNotificationContentExtension.release.xcconfig"; sourceTree = ""; }; + AFAF38BA3FE3FE7CDC56ADB5 /* Pods-MindboxNotificationServiceExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MindboxNotificationServiceExtension.debug.xcconfig"; path = "Target Support Files/Pods-MindboxNotificationServiceExtension/Pods-MindboxNotificationServiceExtension.debug.xcconfig"; sourceTree = ""; }; + B48D4AD5551A8E0F2B521ED8 /* Pods_MindboxNotificationContentExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MindboxNotificationContentExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D80D2646AFEBEE6F556A82E3 /* Pods_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0A0DE3422BB8455A00812E73 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ECBF861DEC24D002BCCA5C54 /* Pods_MindboxNotificationServiceExtension.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0AD271CB2BB9D81D00750279 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0AD271D22BB9D81E00750279 /* UserNotificationsUI.framework in Frameworks */, + 0AD271D02BB9D81E00750279 /* UserNotifications.framework in Frameworks */, + A9A0D2B56DA740D1AC14A76E /* Pods_MindboxNotificationContentExtension.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0AEDBC732BB6F8F200EE8722 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + EC719A83A7838F46B132825D /* Pods_Example.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0A0DE3462BB8455A00812E73 /* MindboxNotificationServiceExtension */ = { + isa = PBXGroup; + children = ( + 0A0DE3512BB8457100812E73 /* MindboxNotificationServiceExtension.entitlements */, + 0A0DE3472BB8455A00812E73 /* NotificationService.swift */, + 0A0DE3492BB8455A00812E73 /* Info.plist */, + ); + path = MindboxNotificationServiceExtension; + sourceTree = ""; + }; + 0AD271D32BB9D81E00750279 /* MindboxNotificationContentExtension */ = { + isa = PBXGroup; + children = ( + 0AD271E02BB9D84800750279 /* MindboxNotificationContentExtension.entitlements */, + 0AD271D42BB9D81E00750279 /* NotificationViewController.swift */, + 0AD271D92BB9D81E00750279 /* Info.plist */, + ); + path = MindboxNotificationContentExtension; + sourceTree = ""; + }; + 0AEDBC6D2BB6F8F200EE8722 = { + isa = PBXGroup; + children = ( + 0AEDBC782BB6F8F200EE8722 /* Example */, + 0A0DE3462BB8455A00812E73 /* MindboxNotificationServiceExtension */, + 0AD271D32BB9D81E00750279 /* MindboxNotificationContentExtension */, + 0AEDBC772BB6F8F200EE8722 /* Products */, + D07217A390B79F6F292D4AFC /* Pods */, + 52C896E46FFA4E326FC946A8 /* Frameworks */, + ); + sourceTree = ""; + }; + 0AEDBC772BB6F8F200EE8722 /* Products */ = { + isa = PBXGroup; + children = ( + 0AEDBC762BB6F8F200EE8722 /* Example.app */, + 0A0DE3452BB8455A00812E73 /* MindboxNotificationServiceExtension.appex */, + 0AD271CE2BB9D81D00750279 /* MindboxNotificationContentExtension.appex */, + ); + name = Products; + sourceTree = ""; + }; + 0AEDBC782BB6F8F200EE8722 /* Example */ = { + isa = PBXGroup; + children = ( + 0AEDBC9C2BB7177A00EE8722 /* Example.entitlements */, + 0AEDBC792BB6F8F200EE8722 /* AppDelegate.swift */, + 0AD271E32BBAAA8800750279 /* PrivacyInfo.xcprivacy */, + 0AEDBC7B2BB6F8F200EE8722 /* SceneDelegate.swift */, + 0AEDBC822BB6F8F400EE8722 /* Assets.xcassets */, + 0A4B68292BBD7D5100639BC5 /* LaunchScreen.storyboard */, + 0AEDBC872BB6F8F400EE8722 /* Info.plist */, + 0AEDBC9E2BB71FD700EE8722 /* ViewModel */, + 0AEDBC9D2BB71FC600EE8722 /* View */, + ); + path = Example; + sourceTree = ""; + }; + 0AEDBC972BB7050F00EE8722 /* CustomVIews */ = { + isa = PBXGroup; + children = ( + 0AEDBC982BB7058B00EE8722 /* ButtonsView.swift */, + 0AEDBC9A2BB70D6E00EE8722 /* SDKDataView.swift */, + ); + path = CustomVIews; + sourceTree = ""; + }; + 0AEDBC9D2BB71FC600EE8722 /* View */ = { + isa = PBXGroup; + children = ( + 0AEDBC972BB7050F00EE8722 /* CustomVIews */, + 0AEDBC912BB6FA4800EE8722 /* MainView.swift */, + ); + path = View; + sourceTree = ""; + }; + 0AEDBC9E2BB71FD700EE8722 /* ViewModel */ = { + isa = PBXGroup; + children = ( + 0AEDBC952BB6FE4900EE8722 /* MainViewModel.swift */, + 0A4B68192BBC82B500639BC5 /* ChooseInAppMessagesDelegate.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + 52C896E46FFA4E326FC946A8 /* Frameworks */ = { + isa = PBXGroup; + children = ( + D80D2646AFEBEE6F556A82E3 /* Pods_Example.framework */, + 6D0E4C1407865DB9AA01F215 /* Pods_MindboxNotificationServiceExtension.framework */, + 0AD271CF2BB9D81D00750279 /* UserNotifications.framework */, + 0AD271D12BB9D81E00750279 /* UserNotificationsUI.framework */, + B48D4AD5551A8E0F2B521ED8 /* Pods_MindboxNotificationContentExtension.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + D07217A390B79F6F292D4AFC /* Pods */ = { + isa = PBXGroup; + children = ( + 240E950B149EDBB0CAC016A0 /* Pods-Example.debug.xcconfig */, + 8800E015E929FCDAB94398CB /* Pods-Example.release.xcconfig */, + AFAF38BA3FE3FE7CDC56ADB5 /* Pods-MindboxNotificationServiceExtension.debug.xcconfig */, + 8FC128008EAD005E4940F3B6 /* Pods-MindboxNotificationServiceExtension.release.xcconfig */, + 296B8C4BB0F741F658A9E34E /* Pods-MindboxNotificationContentExtension.debug.xcconfig */, + 97056FE3D936C1A512C53307 /* Pods-MindboxNotificationContentExtension.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 0A0DE3442BB8455A00812E73 /* MindboxNotificationServiceExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0A0DE3502BB8455A00812E73 /* Build configuration list for PBXNativeTarget "MindboxNotificationServiceExtension" */; + buildPhases = ( + 4A515FC593E4F8BE59A54487 /* [CP] Check Pods Manifest.lock */, + 0A0DE3412BB8455A00812E73 /* Sources */, + 0A0DE3422BB8455A00812E73 /* Frameworks */, + 0A0DE3432BB8455A00812E73 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MindboxNotificationServiceExtension; + productName = MindboxNotificationServiceExtension; + productReference = 0A0DE3452BB8455A00812E73 /* MindboxNotificationServiceExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; + 0AD271CD2BB9D81D00750279 /* MindboxNotificationContentExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0AD271DF2BB9D81E00750279 /* Build configuration list for PBXNativeTarget "MindboxNotificationContentExtension" */; + buildPhases = ( + B467E7F45CD3917D961596F6 /* [CP] Check Pods Manifest.lock */, + 0AD271CA2BB9D81D00750279 /* Sources */, + 0AD271CB2BB9D81D00750279 /* Frameworks */, + 0AD271CC2BB9D81D00750279 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MindboxNotificationContentExtension; + productName = MindboxNotificationContentExtension; + productReference = 0AD271CE2BB9D81D00750279 /* MindboxNotificationContentExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; + 0AEDBC752BB6F8F200EE8722 /* Example */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0AEDBC8A2BB6F8F400EE8722 /* Build configuration list for PBXNativeTarget "Example" */; + buildPhases = ( + BD4E3170B25EA8DE299726F5 /* [CP] Check Pods Manifest.lock */, + 0AEDBC722BB6F8F200EE8722 /* Sources */, + 0AEDBC732BB6F8F200EE8722 /* Frameworks */, + 0AEDBC742BB6F8F200EE8722 /* Resources */, + AEC74811D64186EB15BEEA5B /* [CP] Embed Pods Frameworks */, + 0A0DE34D2BB8455A00812E73 /* Embed Foundation Extensions */, + ); + buildRules = ( + ); + dependencies = ( + 0A0DE34B2BB8455A00812E73 /* PBXTargetDependency */, + 0AD271DB2BB9D81E00750279 /* PBXTargetDependency */, + ); + name = Example; + productName = Example; + productReference = 0AEDBC762BB6F8F200EE8722 /* Example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0AEDBC6E2BB6F8F200EE8722 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1530; + LastUpgradeCheck = 1530; + TargetAttributes = { + 0A0DE3442BB8455A00812E73 = { + CreatedOnToolsVersion = 15.3; + }; + 0AD271CD2BB9D81D00750279 = { + CreatedOnToolsVersion = 15.3; + }; + 0AEDBC752BB6F8F200EE8722 = { + CreatedOnToolsVersion = 15.3; + }; + }; + }; + buildConfigurationList = 0AEDBC712BB6F8F200EE8722 /* Build configuration list for PBXProject "Example" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 0AEDBC6D2BB6F8F200EE8722; + productRefGroup = 0AEDBC772BB6F8F200EE8722 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0AEDBC752BB6F8F200EE8722 /* Example */, + 0A0DE3442BB8455A00812E73 /* MindboxNotificationServiceExtension */, + 0AD271CD2BB9D81D00750279 /* MindboxNotificationContentExtension */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 0A0DE3432BB8455A00812E73 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0AD271CC2BB9D81D00750279 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0AEDBC742BB6F8F200EE8722 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0A4B682A2BBD7D5100639BC5 /* LaunchScreen.storyboard in Resources */, + 0AD271E42BBAAA8800750279 /* PrivacyInfo.xcprivacy in Resources */, + 0AEDBC832BB6F8F400EE8722 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 4A515FC593E4F8BE59A54487 /* [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-MindboxNotificationServiceExtension-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; + }; + AEC74811D64186EB15BEEA5B /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Example/Pods-Example-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Example/Pods-Example-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example/Pods-Example-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + B467E7F45CD3917D961596F6 /* [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-MindboxNotificationContentExtension-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; + }; + BD4E3170B25EA8DE299726F5 /* [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-Example-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; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 0A0DE3412BB8455A00812E73 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0A0DE3482BB8455A00812E73 /* NotificationService.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0AD271CA2BB9D81D00750279 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0AD271D52BB9D81E00750279 /* NotificationViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0AEDBC722BB6F8F200EE8722 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0AEDBC7A2BB6F8F200EE8722 /* AppDelegate.swift in Sources */, + 0AEDBC992BB7058B00EE8722 /* ButtonsView.swift in Sources */, + 0AEDBC922BB6FA4800EE8722 /* MainView.swift in Sources */, + 0AEDBC962BB6FE4900EE8722 /* MainViewModel.swift in Sources */, + 0AEDBC7C2BB6F8F200EE8722 /* SceneDelegate.swift in Sources */, + 0AEDBC9B2BB70D6E00EE8722 /* SDKDataView.swift in Sources */, + 0A4B681A2BBC82B500639BC5 /* ChooseInAppMessagesDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 0A0DE34B2BB8455A00812E73 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0A0DE3442BB8455A00812E73 /* MindboxNotificationServiceExtension */; + targetProxy = 0A0DE34A2BB8455A00812E73 /* PBXContainerItemProxy */; + }; + 0AD271DB2BB9D81E00750279 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0AD271CD2BB9D81D00750279 /* MindboxNotificationContentExtension */; + targetProxy = 0AD271DA2BB9D81E00750279 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 0A0DE34E2BB8455A00812E73 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AFAF38BA3FE3FE7CDC56ADB5 /* Pods-MindboxNotificationServiceExtension.debug.xcconfig */; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = MindboxNotificationServiceExtension/MindboxNotificationServiceExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = N39VVWZXXP; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MindboxNotificationServiceExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = MindboxNotificationServiceExtension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = cloud.Mindbox.ReleaseExampleIos.MindboxNotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 0A0DE34F2BB8455A00812E73 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8FC128008EAD005E4940F3B6 /* Pods-MindboxNotificationServiceExtension.release.xcconfig */; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = MindboxNotificationServiceExtension/MindboxNotificationServiceExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = N39VVWZXXP; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MindboxNotificationServiceExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = MindboxNotificationServiceExtension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = cloud.Mindbox.ReleaseExampleIos.MindboxNotificationServiceExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 0AD271DD2BB9D81E00750279 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 296B8C4BB0F741F658A9E34E /* Pods-MindboxNotificationContentExtension.debug.xcconfig */; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = MindboxNotificationContentExtension/MindboxNotificationContentExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = N39VVWZXXP; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MindboxNotificationContentExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = MindboxNotificationContentExtension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = cloud.Mindbox.ReleaseExampleIos.MindboxNotificationContentExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 0AD271DE2BB9D81E00750279 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 97056FE3D936C1A512C53307 /* Pods-MindboxNotificationContentExtension.release.xcconfig */; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = MindboxNotificationContentExtension/MindboxNotificationContentExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = N39VVWZXXP; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = MindboxNotificationContentExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = MindboxNotificationContentExtension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = cloud.Mindbox.ReleaseExampleIos.MindboxNotificationContentExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 0AEDBC882BB6F8F400EE8722 /* 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_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 0AEDBC892BB6F8F400EE8722 /* 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_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 0AEDBC8B2BB6F8F400EE8722 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 240E950B149EDBB0CAC016A0 /* Pods-Example.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Example/Example.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = N39VVWZXXP; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Example/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen.storyboard; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = cloud.Mindbox.ReleaseExampleIos; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 0AEDBC8C2BB6F8F400EE8722 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8800E015E929FCDAB94398CB /* Pods-Example.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Example/Example.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = N39VVWZXXP; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Example/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen.storyboard; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = cloud.Mindbox.ReleaseExampleIos; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0A0DE3502BB8455A00812E73 /* Build configuration list for PBXNativeTarget "MindboxNotificationServiceExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0A0DE34E2BB8455A00812E73 /* Debug */, + 0A0DE34F2BB8455A00812E73 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0AD271DF2BB9D81E00750279 /* Build configuration list for PBXNativeTarget "MindboxNotificationContentExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0AD271DD2BB9D81E00750279 /* Debug */, + 0AD271DE2BB9D81E00750279 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0AEDBC712BB6F8F200EE8722 /* Build configuration list for PBXProject "Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0AEDBC882BB6F8F400EE8722 /* Debug */, + 0AEDBC892BB6F8F400EE8722 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0AEDBC8A2BB6F8F400EE8722 /* Build configuration list for PBXNativeTarget "Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0AEDBC8B2BB6F8F400EE8722 /* Debug */, + 0AEDBC8C2BB6F8F400EE8722 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0AEDBC6E2BB6F8F200EE8722 /* Project object */; +} diff --git a/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Example/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme b/Example/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme new file mode 100644 index 00000000..be9443e2 --- /dev/null +++ b/Example/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/Example.xcworkspace/contents.xcworkspacedata b/Example/Example.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..a37cf193 --- /dev/null +++ b/Example/Example.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Example/Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Example/Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/Example/Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Example/Example/AppDelegate.swift b/Example/Example/AppDelegate.swift new file mode 100644 index 00000000..b08cc4e7 --- /dev/null +++ b/Example/Example/AppDelegate.swift @@ -0,0 +1,69 @@ +// +// AppDelegate.swift +// Example +// +// Created by Дмитрий Ерофеев on 29.03.2024. +// + +import Mindbox +import Foundation +import UIKit + +@main +class AppDelegate: MindboxAppDelegate { + + //https://developers.mindbox.ru/docs/ios-sdk-initialization + override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + super.application(application, didFinishLaunchingWithOptions: launchOptions) + + do { + let mindboxSdkConfig = try MBConfiguration( + //To run the application on a physical device you need to change the endpoint + //You should also change the application bundle ID in all targets, more details in the readme + //You can still run the application on the simulator to see In-Apps + endpoint: "Mpush-test.ReleaseExample.IosApp", + domain: "api.mindbox.ru", + subscribeCustomerIfCreated: true, + shouldCreateCustomer: true + ) + Mindbox.shared.initialization(configuration: mindboxSdkConfig) + } catch { + print(error) + } + //https://developers.mindbox.ru/docs/ios-send-push-notifications-appdelegate + registerForRemoteNotifications() + return true + } + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + //https://developers.mindbox.ru/docs/ios-send-push-notifications-appdelegate + func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + completionHandler([.list, .badge, .sound]) + + //https://developers.mindbox.ru/docs/ios-sdk-methods + print("Is mindbox notification: \(Mindbox.shared.isMindboxPush(userInfo: notification.request.content.userInfo))") + print("Notification data: \(String(describing: Mindbox.shared.getMindboxPushData(userInfo: notification.request.content.userInfo)))") + if let uniqueKey = Mindbox.shared.getMindboxPushData(userInfo: notification.request.content.userInfo)?.uniqueKey { + Mindbox.shared.pushClicked(uniqueKey: uniqueKey) + } + } + + //https://developers.mindbox.ru/docs/ios-send-push-notifications-appdelegate + func registerForRemoteNotifications() { + UNUserNotificationCenter.current().delegate = self + DispatchQueue.main.async { + UIApplication.shared.registerForRemoteNotifications() + UNUserNotificationCenter.current().requestAuthorization(options: [ .alert, .sound, .badge]) { granted, error in + print("Permission granted: \(granted)") + if let error = error { + print("NotificationsRequestAuthorization failed with error: \(error.localizedDescription)") + } + Mindbox.shared.notificationsRequestAuthorization(granted: granted) + } + } + } +} + diff --git a/Example/Example/Assets.xcassets/AccentColor.colorset/Contents.json b/Example/Example/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/Example/Example/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/AppIcon1024x1024.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/AppIcon1024x1024.png new file mode 100644 index 00000000..236dea42 Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/AppIcon1024x1024.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d7ab46b9 --- /dev/null +++ b/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "AppIcon1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/Contents.json b/Example/Example/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Example/Example/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/MBBackground.colorset/Contents.json b/Example/Example/Assets.xcassets/MBBackground.colorset/Contents.json new file mode 100644 index 00000000..f9e8ab88 --- /dev/null +++ b/Example/Example/Assets.xcassets/MBBackground.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.165", + "green" : "0.176", + "red" : "0.169" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/MBGreen.colorset/Contents.json b/Example/Example/Assets.xcassets/MBGreen.colorset/Contents.json new file mode 100644 index 00000000..94772acc --- /dev/null +++ b/Example/Example/Assets.xcassets/MBGreen.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.408", + "green" : "0.808", + "red" : "0.404" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.408", + "green" : "0.808", + "red" : "0.404" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/MBText.colorset/Contents.json b/Example/Example/Assets.xcassets/MBText.colorset/Contents.json new file mode 100644 index 00000000..d8907191 --- /dev/null +++ b/Example/Example/Assets.xcassets/MBText.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.000", + "green" : "0.000", + "red" : "0.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/MBView.colorset/Contents.json b/Example/Example/Assets.xcassets/MBView.colorset/Contents.json new file mode 100644 index 00000000..b4f722fa --- /dev/null +++ b/Example/Example/Assets.xcassets/MBView.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.870", + "green" : "0.870", + "red" : "0.870" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.239", + "green" : "0.239", + "red" : "0.239" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/m-green_Main_large.imageset/Contents.json b/Example/Example/Assets.xcassets/m-green_Main_large.imageset/Contents.json new file mode 100644 index 00000000..e636b3a3 --- /dev/null +++ b/Example/Example/Assets.xcassets/m-green_Main_large.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "m-green_Main_large.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "m-green_Main_large 1.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "m-green_Main_large 2.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/m-green_Main_large.imageset/m-green_Main_large 1.png b/Example/Example/Assets.xcassets/m-green_Main_large.imageset/m-green_Main_large 1.png new file mode 100644 index 00000000..c3d1eb30 Binary files /dev/null and b/Example/Example/Assets.xcassets/m-green_Main_large.imageset/m-green_Main_large 1.png differ diff --git a/Example/Example/Assets.xcassets/m-green_Main_large.imageset/m-green_Main_large 2.png b/Example/Example/Assets.xcassets/m-green_Main_large.imageset/m-green_Main_large 2.png new file mode 100644 index 00000000..c3d1eb30 Binary files /dev/null and b/Example/Example/Assets.xcassets/m-green_Main_large.imageset/m-green_Main_large 2.png differ diff --git a/Example/Example/Assets.xcassets/m-green_Main_large.imageset/m-green_Main_large.png b/Example/Example/Assets.xcassets/m-green_Main_large.imageset/m-green_Main_large.png new file mode 100644 index 00000000..c3d1eb30 Binary files /dev/null and b/Example/Example/Assets.xcassets/m-green_Main_large.imageset/m-green_Main_large.png differ diff --git a/Example/Example/Example.entitlements b/Example/Example/Example.entitlements new file mode 100644 index 00000000..900ab9c8 --- /dev/null +++ b/Example/Example/Example.entitlements @@ -0,0 +1,12 @@ + + + + + aps-environment + development + com.apple.security.application-groups + + group.cloud.Mindbox.cloud.Mindbox.ReleaseExampleIos + + + diff --git a/Example/Example/Info.plist b/Example/Example/Info.plist new file mode 100644 index 00000000..866f0578 --- /dev/null +++ b/Example/Example/Info.plist @@ -0,0 +1,29 @@ + + + + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + UIBackgroundModes + + fetch + processing + remote-notification + + + diff --git a/Example/Example/LaunchScreen.storyboard b/Example/Example/LaunchScreen.storyboard new file mode 100644 index 00000000..a0192c22 --- /dev/null +++ b/Example/Example/LaunchScreen.storyboard @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/Example/PrivacyInfo.xcprivacy b/Example/Example/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..01ce4525 --- /dev/null +++ b/Example/Example/PrivacyInfo.xcprivacy @@ -0,0 +1,48 @@ + + + + + NSPrivacyTracking + + NSPrivacyTrackingDomains + + NSPrivacyCollectedDataTypes + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeOtherDiagnosticData + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeDeviceID + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeProductPersonalization + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + + diff --git a/Example/Example/SceneDelegate.swift b/Example/Example/SceneDelegate.swift new file mode 100644 index 00000000..11e0f67a --- /dev/null +++ b/Example/Example/SceneDelegate.swift @@ -0,0 +1,24 @@ +// +// SceneDelegate.swift +// Example +// +// Created by Дмитрий Ерофеев on 29.03.2024. +// + +import UIKit +import SwiftUI + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + guard let windowScene = (scene as? UIWindowScene) else { return } + let window = UIWindow(windowScene: windowScene) + let viewModel = MainViewModel() + window.rootViewController = UIHostingController(rootView: MainView(viewModel: viewModel)) + self.window = window + window.makeKeyAndVisible() + } +} + diff --git a/Example/Example/View/CustomVIews/ButtonsView.swift b/Example/Example/View/CustomVIews/ButtonsView.swift new file mode 100644 index 00000000..117a8374 --- /dev/null +++ b/Example/Example/View/CustomVIews/ButtonsView.swift @@ -0,0 +1,54 @@ +// +// ButtonsView.swift +// Example +// +// Created by Дмитрий Ерофеев on 29.03.2024. +// + +import SwiftUI + +struct ButtonsViewline: View { + + var label: String + var action: () -> () + + var body: some View { + HStack { + Text(label) + .foregroundStyle(.mbText) + Spacer() + Button("Show In-App") { + action() + } + .frame(width: 120) + .frame(height: 30) + .background(Color.mbGreen) + .cornerRadius(10) + .tint(.white) + } + } +} + +struct ButtonsView: View { + + @ObservedObject var viewModel: MainViewModel + + var body: some View { + ZStack { + RoundedRectangle(cornerRadius: 20) + .frame(width: 350) + .frame(height: 110) + .foregroundColor(.mbView) + VStack { + ButtonsViewline(label: "Async operation") { + viewModel.showInAppWithExecuteAsyncOperation() + } + Divider() + ButtonsViewline(label: "Sync operation") { + viewModel.showInAppWithExecuteSyncOperation() + } + } + .frame(width: 310) + } + } +} diff --git a/Example/Example/View/CustomVIews/SDKDataView.swift b/Example/Example/View/CustomVIews/SDKDataView.swift new file mode 100644 index 00000000..0b168457 --- /dev/null +++ b/Example/Example/View/CustomVIews/SDKDataView.swift @@ -0,0 +1,73 @@ +// +// SDKDataView.swift +// Example +// +// Created by Дмитрий Ерофеев on 29.03.2024. +// + +import SwiftUI + +struct TitleDescrptionView: View { + + var title: String + var message: String + + var body: some View { + HStack { + VStack(alignment: .leading) { + Text(title) + .font(.system(size: 9)) + .padding(.bottom, 2) + .lineLimit(1) + .foregroundStyle(.mbText) + Text(message) + .font(.system(size: 13)) + .contextMenu { + Button { + UIPasteboard.general.string = message + } label: { + Label("Copy", systemImage: "doc.on.doc.fill") + } + } + .lineLimit(1) + .foregroundStyle(.mbText) + } + Spacer() + } + } +} + +struct SDKDataView: View { + + @ObservedObject var viewModel: MainViewModel + + var body: some View { + ZStack { + RoundedRectangle(cornerRadius: 20) + .frame(width: 350) + .frame(height: 190) + .foregroundColor(.mbView) + VStack { + HStack { + Text("Data from SDK:") + .foregroundStyle(.mbText) + .padding(.leading, -1) + .padding(.bottom, 5) + Spacer() + } + TitleDescrptionView(title: "SDK version", message: viewModel.SDKVersion) + Divider() + TitleDescrptionView(title: "APNS token", message: viewModel.APNSToken) + Divider() + TitleDescrptionView(title: "Device UUID", message: viewModel.deviceUUID) + } + .frame(width: 310) + } + } +} + +struct SDKDataView_Preview: PreviewProvider { + static var previews: some View { + SDKDataView(viewModel: MainViewModel()) + } +} diff --git a/Example/Example/View/MainView.swift b/Example/Example/View/MainView.swift new file mode 100644 index 00000000..ff8fafd9 --- /dev/null +++ b/Example/Example/View/MainView.swift @@ -0,0 +1,38 @@ +// +// MainView.swift +// Example +// +// Created by Дмитрий Ерофеев on 29.03.2024. +// + +import SwiftUI +import Foundation + +struct MainView: View { + + @ObservedObject var viewModel: MainViewModel + @State private var showingAlert = !UserDefaults.standard.bool(forKey: "ShownAlert") + + var body: some View { + ZStack { + Color.mbBackground.ignoresSafeArea() + VStack { + ButtonsView(viewModel: viewModel) + SDKDataView(viewModel: viewModel) + } + }.onAppear { + viewModel.setupData() + let alertShown = UserDefaults.standard.bool(forKey: "ShownAlert") + if !alertShown { + UserDefaults.standard.set(true, forKey: "ShownAlert") + } + } + .alert("In-App can only be shown once per session", isPresented: $showingAlert) { + Button("OK", role: .cancel) {} + } + } +} + +#Preview { + MainView(viewModel: MainViewModel()) +} diff --git a/Example/Example/ViewModel/ChooseInAppMessagesDelegate.swift b/Example/Example/ViewModel/ChooseInAppMessagesDelegate.swift new file mode 100644 index 00000000..e81d2526 --- /dev/null +++ b/Example/Example/ViewModel/ChooseInAppMessagesDelegate.swift @@ -0,0 +1,45 @@ +// +// ChooseInAppMessagesDelegate.swift +// Example +// +// Created by Дмитрий Ерофеев on 02.04.2024. +// + +import Foundation +import Mindbox + +class ChooseInAppMessagesDelegate: InAppMessagesDelegate { + + private init() {} + static var shared = ChooseInAppMessagesDelegate() + + //https://developers.mindbox.ru/docs/in-app + func inAppMessageTapAction(id: String, url: URL?, payload: String) { + //Here you can add your custom logic + print("inAppMessageTapAction") + print(id) + print(url ?? "") + print(payload) + } + + //https://developers.mindbox.ru/docs/in-app + func inAppMessageDismissed(id: String) { + //Here you can add your custom logic + print("inAppMessageDismissed") + print(id) + } + + func select(chooseInappMessageDelegate: ChooseInappMessageDelegate) { + switch chooseInappMessageDelegate { + case .DefaultInappMessageDelegate: + break + case .InAppMessagesDelegate: + Mindbox.shared.inAppMessagesDelegate = self + } + } +} + +enum ChooseInappMessageDelegate { + case DefaultInappMessageDelegate + case InAppMessagesDelegate +} diff --git a/Example/Example/ViewModel/MainViewModel.swift b/Example/Example/ViewModel/MainViewModel.swift new file mode 100644 index 00000000..88586fc1 --- /dev/null +++ b/Example/Example/ViewModel/MainViewModel.swift @@ -0,0 +1,72 @@ +// +// MainViewModel.swift +// Example +// +// Created by Дмитрий Ерофеев on 29.03.2024. +// + +import Foundation +import Mindbox +import UIKit + +class MainViewModel: ObservableObject { + + @Published var SDKVersion: String = "" + @Published var deviceUUID: String = "" + @Published var APNSToken: String = "" + + //https://developers.mindbox.ru/docs/ios-sdk-methods + func setupData() { + self.SDKVersion = Mindbox.shared.sdkVersion + Mindbox.shared.getDeviceUUID { deviceUUID in + DispatchQueue.main.async { + self.deviceUUID = deviceUUID + } + } + Mindbox.shared.getAPNSToken { APNSToken in + DispatchQueue.main.async { + self.APNSToken = APNSToken + } + } + ChooseInAppMessagesDelegate.shared.select(chooseInappMessageDelegate: .InAppMessagesDelegate) + } + + //https://developers.mindbox.ru/docs/in-app-targeting-by-custom-operation + func showInAppWithExecuteSyncOperation () { + let json = """ + { "viewProduct": + { "product": + { "ids": + { "website": "1" } + } + } + } + """ + Mindbox.shared.executeSyncOperation(operationSystemName: "APIMethodForReleaseExampleIos", json: json) { result in + switch result { + case .success(let success): + Mindbox.logger.log(level: .info, message: "\(success)") + case .failure(let error): + Mindbox.logger.log(level: .error, message: "\(error)") + } + } + } + + //https://developers.mindbox.ru/docs/in-app-targeting-by-custom-operation + func showInAppWithExecuteAsyncOperation () { + let json = """ + { "viewProduct": + { "product": + { "ids": + { "website": "2" } + } + } + } + """ + Mindbox.shared.executeAsyncOperation(operationSystemName: "APIMethodForReleaseExampleIos", json: json) + } +} + + + + diff --git a/Example/MindboxNotificationContentExtension/Info.plist b/Example/MindboxNotificationContentExtension/Info.plist new file mode 100644 index 00000000..b9111b15 --- /dev/null +++ b/Example/MindboxNotificationContentExtension/Info.plist @@ -0,0 +1,22 @@ + + + + + NSExtension + + NSExtensionPrincipalClass + MindboxNotificationContentExtension.NotificationViewController + NSExtensionAttributes + + UNNotificationExtensionUserInteractionEnabled + 1 + UNNotificationExtensionCategory + MindBoxCategoryIdentifier + UNNotificationExtensionInitialContentSizeRatio + 0.0001 + + NSExtensionPointIdentifier + com.apple.usernotifications.content-extension + + + diff --git a/Example/MindboxNotificationContentExtension/MindboxNotificationContentExtension.entitlements b/Example/MindboxNotificationContentExtension/MindboxNotificationContentExtension.entitlements new file mode 100644 index 00000000..ddec1215 --- /dev/null +++ b/Example/MindboxNotificationContentExtension/MindboxNotificationContentExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.cloud.Mindbox.cloud.Mindbox.ReleaseExampleIos + + + diff --git a/Example/MindboxNotificationContentExtension/NotificationViewController.swift b/Example/MindboxNotificationContentExtension/NotificationViewController.swift new file mode 100644 index 00000000..b45dc0ce --- /dev/null +++ b/Example/MindboxNotificationContentExtension/NotificationViewController.swift @@ -0,0 +1,20 @@ +// +// NotificationViewController.swift +// MindboxNotificationContentExtension +// +// Created by Дмитрий Ерофеев on 31.03.2024. +// + +import UIKit +import UserNotifications +import UserNotificationsUI +import MindboxNotifications + +class NotificationViewController: UIViewController, UNNotificationContentExtension { + + lazy var mindboxService = MindboxNotificationService() + + func didReceive(_ notification: UNNotification) { + mindboxService.didReceive(notification: notification, viewController: self, extensionContext: extensionContext) + } +} diff --git a/Example/MindboxNotificationServiceExtension/Info.plist b/Example/MindboxNotificationServiceExtension/Info.plist new file mode 100644 index 00000000..57421ebf --- /dev/null +++ b/Example/MindboxNotificationServiceExtension/Info.plist @@ -0,0 +1,13 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.usernotifications.service + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).NotificationService + + + diff --git a/Example/MindboxNotificationServiceExtension/MindboxNotificationServiceExtension.entitlements b/Example/MindboxNotificationServiceExtension/MindboxNotificationServiceExtension.entitlements new file mode 100644 index 00000000..ddec1215 --- /dev/null +++ b/Example/MindboxNotificationServiceExtension/MindboxNotificationServiceExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.cloud.Mindbox.cloud.Mindbox.ReleaseExampleIos + + + diff --git a/Example/MindboxNotificationServiceExtension/NotificationService.swift b/Example/MindboxNotificationServiceExtension/NotificationService.swift new file mode 100644 index 00000000..2fa604c4 --- /dev/null +++ b/Example/MindboxNotificationServiceExtension/NotificationService.swift @@ -0,0 +1,24 @@ +// +// NotificationService.swift +// MindboxNotificationServiceExtension +// +// Created by Дмитрий Ерофеев on 30.03.2024. +// + +import UserNotifications +import MindboxNotifications + +class NotificationService: UNNotificationServiceExtension { + + lazy var mindboxService = MindboxNotificationService() + + override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { + mindboxService.didReceive(request, withContentHandler: contentHandler) + } + + 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. + mindboxService.serviceExtensionTimeWillExpire() + } +} diff --git a/Example/Podfile b/Example/Podfile new file mode 100644 index 00000000..3f3c6553 --- /dev/null +++ b/Example/Podfile @@ -0,0 +1,13 @@ +use_frameworks! + +target 'Example' do + pod 'Mindbox', '2.8.5' +end + +target 'MindboxNotificationServiceExtension' do + pod 'MindboxNotifications', '2.8.5' +end + +target 'MindboxNotificationContentExtension' do + pod 'MindboxNotifications', '2.8.5' +end \ No newline at end of file diff --git a/Example/Podfile.lock b/Example/Podfile.lock new file mode 100644 index 00000000..0168f173 --- /dev/null +++ b/Example/Podfile.lock @@ -0,0 +1,25 @@ +PODS: + - Mindbox (2.8.5): + - MindboxLogger (= 0.0.6) + - MindboxLogger (0.0.6) + - MindboxNotifications (2.8.5): + - MindboxLogger (= 0.0.6) + +DEPENDENCIES: + - Mindbox (= 2.8.5) + - MindboxNotifications (= 2.8.5) + +SPEC REPOS: + trunk: + - Mindbox + - MindboxLogger + - MindboxNotifications + +SPEC CHECKSUMS: + Mindbox: ff0cf579406343e606ade907e66f75cdc19e8cb6 + MindboxLogger: d59b32ee8ee008c848fe33e5862b74bac3b327bd + MindboxNotifications: f0f687e2305c2d30fd09f2c9a6f821a3ee8e2b66 + +PODFILE CHECKSUM: 1dc0b8379ca779d2bf2710b2fa281ebcfd84a4ec + +COCOAPODS: 1.15.2 diff --git a/Example/README.md b/Example/README.md new file mode 100644 index 00000000..a3fef6a6 --- /dev/null +++ b/Example/README.md @@ -0,0 +1,65 @@ +# Example app with SPM for Mindbox SDK for iOS + +This is an example of SDK [integration](https://developers.mindbox.ru/docs/ios-sdk-integration) + +## Getting started + +### Launching the application +The app has integration via cocoapods, but you can use SPM if you need. +#### Cocoapods: +1. [Clone ios-sdk repository](https://github.com/mindbox-cloud/ios-sdk). +2. Make sure you have CocoaPods installed or install it according to the instructions. +3. Go to `ios-sdk/Example/` +4. Install the pods. + ```ruby + pod update + ``` + Or + ```ruby + pod install + ``` +5. Go to `ios-sdk/Example/Example.xcworkspace`. +6. Run file `Example.xcworkspace`. +#### SPM: +1. To deintegrate cocoapods you can use next comands: + - `sudo gem install cocoapods-deintegrate` + - `pod deintegrate` + - `rm Podfile` + - `rm Podfile.lock` + - `rm Example.xcworkspace` + - `rm -rf Pods` +2. Launch `Example.xcodeproj`. +3. [Read this](https://developers.mindbox.ru/docs/add-ios-sdk) and follow the initialization instructions via SPM. + +Now you can test the in-app on the simulator. +In our admin panel there are already 3 ready-made in-apps that you can look at. +To run the application on a real device and try push notifications, follow the instructions below. + +### Setting up a Example application with your personal account (to run on a real device) + +1. Change [team](https://developers.mindbox.ru/docs/ios-get-keys) and bundle identifiers and App Group name for next targets: + - ExampleApp + - MindboxNotificationServiceExtension + - MindboxNotificationContentExtension +2. [Configure your endpoints](https://developers.mindbox.ru/docs/add-ios-integration). +3. Change domain and endpoints in the `AppDelegate.swift` to yours. + +### SDK functionality testing + +1. To check innap when opening: + - [Read this](https://help.mindbox.ru/docs/in-app-what-is). + - Open app. +2. To check the inapp anywhere in the application: + - [Read this](https://help.mindbox.ru/docs/in-app-location). + - Replace `operationSystemName` in `showInAppWithExecuteSyncOperation` and `showInAppWithExecuteAsyncOperation` in MainViewModel. + - Click to the button `Show in-app` opposite the selected operation. +3. To check push notifications: + - [Read this](https://developers.mindbox.ru/docs/ios-send-push-notifications-advanced) + - Send a notification from your account. +4. To check rich notifications: + - [Read this](https://developers.mindbox.ru/docs/ios-send-push-notifications-advanced) + - Send a notification from your account. + +### Additionally + - Currently the In-App only comes once per session. + - There are comments and links in the ExampleApp code that can help you. diff --git a/Gemfile.lock b/Gemfile.lock index 9d5dcb2b..d660f291 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,9 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.6) + CFPropertyList (3.0.7) + base64 + nkf rexml activesupport (6.1.7.6) concurrent-ruby (~> 1.0, >= 1.0.2) @@ -9,30 +11,31 @@ GEM minitest (>= 5.1) tzinfo (~> 2.0) zeitwerk (~> 2.3) - addressable (2.8.5) + addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) - artifactory (3.0.15) + artifactory (3.0.17) atomos (0.1.3) - aws-eventstream (1.2.0) - aws-partitions (1.825.0) - aws-sdk-core (3.182.0) - aws-eventstream (~> 1, >= 1.0.2) + aws-eventstream (1.3.0) + aws-partitions (1.909.0) + aws-sdk-core (3.191.6) + aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.5) + aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.71.0) - aws-sdk-core (~> 3, >= 3.177.0) + aws-sdk-kms (1.78.0) + aws-sdk-core (~> 3, >= 3.191.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.134.0) - aws-sdk-core (~> 3, >= 3.181.0) + aws-sdk-s3 (1.146.1) + aws-sdk-core (~> 3, >= 3.191.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.6) - aws-sigv4 (1.6.0) + aws-sigv4 (~> 1.8) + aws-sigv4 (1.8.0) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) + base64 (0.2.0) claide (1.1.0) cocoapods (1.13.0) addressable (~> 2.8) @@ -79,14 +82,13 @@ GEM declarative (0.0.20) digest-crc (0.6.5) rake (>= 12.0.0, < 14.0.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) + domain_name (0.6.20240107) dotenv (2.8.1) emoji_regex (3.2.3) escape (0.0.4) ethon (0.16.0) ffi (>= 1.15.0) - excon (0.103.0) + excon (0.110.0) faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) @@ -115,7 +117,7 @@ GEM faraday-retry (1.0.3) faraday_middleware (1.2.0) faraday (~> 1.0) - fastimage (2.2.7) + fastimage (2.3.1) fastlane (2.214.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) @@ -159,9 +161,9 @@ GEM fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.49.0) + google-apis-androidpublisher_v3 (0.54.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-core (0.11.1) + google-apis-core (0.11.3) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -169,28 +171,27 @@ GEM representable (~> 3.0) retriable (>= 2.0, < 4.a) rexml - webrick google-apis-iamcredentials_v1 (0.17.0) google-apis-core (>= 0.11.0, < 2.a) google-apis-playcustomapp_v1 (0.13.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-storage_v1 (0.19.0) - google-apis-core (>= 0.9.0, < 2.a) - google-cloud-core (1.6.0) - google-cloud-env (~> 1.0) + google-apis-storage_v1 (0.31.0) + google-apis-core (>= 0.11.0, < 2.a) + google-cloud-core (1.7.0) + google-cloud-env (>= 1.0, < 3.a) google-cloud-errors (~> 1.0) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) - google-cloud-errors (1.3.1) - google-cloud-storage (1.44.0) + google-cloud-errors (1.4.0) + google-cloud-storage (1.47.0) addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.19.0) + google-apis-storage_v1 (~> 0.31.0) google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.8.0) + googleauth (1.8.1) faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) multi_json (~> 1.11) @@ -203,23 +204,25 @@ GEM i18n (1.14.1) concurrent-ruby (~> 1.0) jmespath (1.6.2) - json (2.6.3) - jwt (2.7.1) + json (2.7.2) + jwt (2.8.1) + base64 mini_magick (4.12.0) mini_mime (1.1.5) minitest (5.20.0) molinillo (0.8.0) multi_json (1.15.0) - multipart-post (2.3.0) + multipart-post (2.4.0) nanaimo (0.3.0) nap (1.1.0) naturally (2.2.1) netrc (0.11.0) + nkf (0.2.0) optparse (0.1.1) os (1.1.4) - plist (3.7.0) + plist (3.7.1) public_suffix (4.0.7) - rake (13.0.6) + rake (13.2.1) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) @@ -231,7 +234,7 @@ GEM ruby2_keywords (0.0.5) rubyzip (2.3.2) security (0.1.3) - signet (0.18.0) + signet (0.19.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) @@ -244,7 +247,7 @@ GEM unicode-display_width (~> 1.1, >= 1.1.1) trailblazer-option (0.1.2) tty-cursor (0.7.1) - tty-screen (0.8.1) + tty-screen (0.8.2) tty-spinner (0.9.3) tty-cursor (~> 0.7) typhoeus (1.4.1) @@ -252,13 +255,9 @@ GEM tzinfo (2.0.6) concurrent-ruby (~> 1.0) uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.8.2) unicode-display_width (1.8.0) - webrick (1.8.1) word_wrap (1.0.0) - xcodeproj (1.23.0) + xcodeproj (1.24.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) diff --git a/Mindbox.podspec b/Mindbox.podspec index caeebb63..38bc11c4 100644 --- a/Mindbox.podspec +++ b/Mindbox.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "Mindbox" - spec.version = "2.9.0-rc" + spec.version = "2.9.0" spec.summary = "SDK for integration with Mindbox" spec.description = "This library allows you to integrate data transfer to Mindbox Marketing Cloud" spec.homepage = "https://github.com/mindbox-cloud/ios-sdk" @@ -11,9 +11,9 @@ Pod::Spec.new do |spec| spec.source_files = "Mindbox/**/*.{swift}", "SDKVersionProvider/**/*.{swift}" spec.exclude_files = "Classes/Exclude" spec.resource_bundles = { - 'Mindbox' => ['Mindbox/**/*.xcassets', 'Mindbox/**/*.xcdatamodeld'] + 'Mindbox' => ['Mindbox/**/*.xcassets', 'Mindbox/**/*.xcdatamodeld', 'Mindbox/**/*.xcprivacy'] } spec.swift_version = "5" - spec.dependency 'MindboxLogger', '0.0.6' + spec.dependency 'MindboxLogger', '0.0.7' end diff --git a/Mindbox.xcodeproj/project.pbxproj b/Mindbox.xcodeproj/project.pbxproj index 8c31cecd..1e9de79c 100644 --- a/Mindbox.xcodeproj/project.pbxproj +++ b/Mindbox.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0A3D045A2BC6803E00E1FC52 /* ImageFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A3D04592BC6803E00E1FC52 /* ImageFormat.swift */; }; 3132DFF625C2A811007FE358 /* TestDependencyProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3132DFF525C2A811007FE358 /* TestDependencyProvider.swift */; }; 313B233A25ADEA0F00A1CB72 /* Mindbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 313B233025ADEA0F00A1CB72 /* Mindbox.framework */; }; 313B233F25ADEA0F00A1CB72 /* MindboxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 313B233E25ADEA0F00A1CB72 /* MindboxTests.swift */; }; @@ -520,6 +521,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 0A3D04592BC6803E00E1FC52 /* ImageFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageFormat.swift; sourceTree = ""; }; 3132DFF525C2A811007FE358 /* TestDependencyProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestDependencyProvider.swift; sourceTree = ""; }; 313B233025ADEA0F00A1CB72 /* Mindbox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Mindbox.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 313B233325ADEA0F00A1CB72 /* Mindbox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Mindbox.h; sourceTree = ""; }; @@ -1720,6 +1722,7 @@ isa = PBXGroup; children = ( 9B24FAB428C751E400F10B5D /* InAppImagesStorage.swift */, + 0A3D04592BC6803E00E1FC52 /* ImageFormat.swift */, ); path = Images; sourceTree = ""; @@ -3193,6 +3196,7 @@ 9BC24E7528F6953D00C2619C /* ConfigResponse.swift in Sources */, F331DD332A84D57800222120 /* VariantImageUrlExtractorService.swift in Sources */, 33072F482664CCBA001F1AB2 /* PoolResponse.swift in Sources */, + 0A3D045A2BC6803E00E1FC52 /* ImageFormat.swift in Sources */, 337C79562656533F0092B580 /* ViewProductRequest.swift in Sources */, 6FDD145F266F7CD900A50C35 /* DiscountResponse.swift in Sources */, 33072F462664CC9A001F1AB2 /* UsedPointOfContactResponse.swift in Sources */, diff --git a/Mindbox/DI/DependencyProvider.swift b/Mindbox/DI/DependencyProvider.swift index be60dd33..0662b0b1 100644 --- a/Mindbox/DI/DependencyProvider.swift +++ b/Mindbox/DI/DependencyProvider.swift @@ -81,7 +81,12 @@ final class DependencyProvider: DependencyContainer { let variantsFilterService = VariantFilterService(layersFilter: layersFilterService, elementsFilter: elementsFilterService, contentPositionFilter: contentPositionFilterService) - inappFilterService = InappsFilterService(variantsFilter: variantsFilterService) + + inappFilterService = InappsFilterService(persistenceStorage: persistenceStorage, + abTestDeviceMixer: abTestDeviceMixer, + variantsFilter: variantsFilterService, + sdkVersionValidator: sdkVersionValidator) + inAppConfigurationDataFacade = InAppConfigurationDataFacade(geoService: geoService, segmentationService: segmentationSevice, targetingChecker: inAppTargetingChecker, @@ -94,10 +99,7 @@ final class DependencyProvider: DependencyContainer { inAppConfigRepository: InAppConfigurationRepository(), inAppConfigurationMapper: InAppConfigutationMapper(inappFilterService: inappFilterService, targetingChecker: inAppTargetingChecker, - persistenceStorage: persistenceStorage, - sdkVersionValidator: sdkVersionValidator, urlExtractorService: urlExtractorService, - abTestDeviceMixer: abTestDeviceMixer, dataFacade: inAppConfigurationDataFacade), logsManager: logsManager), presentationManager: presentationManager, diff --git a/Mindbox/InAppMessages/Images/ImageFormat.swift b/Mindbox/InAppMessages/Images/ImageFormat.swift new file mode 100644 index 00000000..5510dc00 --- /dev/null +++ b/Mindbox/InAppMessages/Images/ImageFormat.swift @@ -0,0 +1,79 @@ +// +// ImageFormat.swift +// Mindbox +// +// Created by Дмитрий Ерофеев on 09.04.2024. +// + +import Foundation +import ImageIO +import UIKit + +enum ImageFormat: String { + case png, jpg, gif + + init?(_ data: Data) { + if let type = ImageFormat.get(from: data) { + self = type + } else { + return nil + } + } +} + +extension ImageFormat { + + private static func get(from data: Data?) -> ImageFormat? { + + guard let firstByte = data?.first else { return nil } + + switch firstByte { + case 0x89: + return .png + case 0xFF: + return .jpg + case 0x47: + return .gif + default: + return nil + } + } + + private static func animatedImage(withGIFData data: Data) -> UIImage? { + + guard let source = CGImageSourceCreateWithData(data as CFData, nil) else { return nil } + + let frameCount = CGImageSourceGetCount(source) + var frames: [UIImage] = [] + var gifDuration = 0.0 + + for i in 0.. UIImage? { + + guard let imageData else { return nil } + let imageFormat = ImageFormat.get(from: imageData) + + switch imageFormat { + case .gif: + return animatedImage(withGIFData: imageData) + default: + return UIImage(data: imageData) + } + } +} diff --git a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift index 2cd66653..438fd03a 100644 --- a/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift +++ b/Mindbox/InAppMessages/InAppConfigurationMapper/InAppConfigutationMapper.swift @@ -17,35 +17,26 @@ protocol InAppConfigurationMapperProtocol { final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { - private let inappFilterService: InappFilterProtocol var targetingChecker: InAppTargetingCheckerProtocol - private let persistenceStorage: PersistenceStorage var filteredInAppsByEvent: [InAppMessageTriggerEvent: [InAppTransitionData]] = [:] - private let sdkVersionValidator: SDKVersionValidator - private let urlExtractorService: VariantImageUrlExtractorServiceProtocol - private let abTestDeviceMixer: ABTestDeviceMixer + var filteredInappsByEventForTargeting: [InAppMessageTriggerEvent: [InAppTransitionData]] = [:] let dataFacade: InAppConfigurationDataFacadeProtocol - private var fullListOfInapps: [InApp] = [] - private var inappsDictForTargeting: [InAppMessageTriggerEvent: [InAppTransitionData]] = [:] + private let inappFilterService: InappFilterProtocol + private let urlExtractorService: VariantImageUrlExtractorServiceProtocol + + private var validInapps: [InApp] = [] private var savedEventForTargeting: ApplicationEvent? - private var shownInnapId: String = "" - private var completionSuccess = false + private var shownInnapId = "" init(inappFilterService: InappFilterProtocol, targetingChecker: InAppTargetingCheckerProtocol, - persistenceStorage: PersistenceStorage, - sdkVersionValidator: SDKVersionValidator, urlExtractorService: VariantImageUrlExtractorServiceProtocol, - abTestDeviceMixer: ABTestDeviceMixer, dataFacade: InAppConfigurationDataFacadeProtocol) { self.inappFilterService = inappFilterService self.targetingChecker = targetingChecker - self.persistenceStorage = persistenceStorage - self.sdkVersionValidator = sdkVersionValidator self.urlExtractorService = urlExtractorService - self.abTestDeviceMixer = abTestDeviceMixer self.dataFacade = dataFacade } @@ -53,16 +44,17 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { func mapConfigResponse(_ event: ApplicationEvent?, _ response: ConfigResponse, _ completion: @escaping (InAppFormData?) -> Void) { - let shownInAppsIds = Set(persistenceStorage.shownInAppsIds ?? []) savedEventForTargeting = event self.targetingChecker.event = nil - fullListOfInapps = inappFilterService.filter(inapps: response.inapps?.elements) - let responseInapps = filterInappsByABTests(response.abtests, responseInapps: fullListOfInapps) - let filteredInapps = filterInappsBySDKVersion(responseInapps, shownInAppsIds: shownInAppsIds) - Logger.common(message: "Shown in-apps ids: [\(shownInAppsIds)]", level: .info, category: .inAppMessages) + + let filteredInapps = inappFilterService.filter(inapps: response.inapps?.elements, abTests: response.abtests) + validInapps = inappFilterService.validInapps targetingChecker.event = event prepareTargetingChecker(for: filteredInapps) + + prepareForRemainingTargeting() + dataFacade.setObservedOperation() if filteredInapps.isEmpty { @@ -92,122 +84,48 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { } } - func sendRemainingInappsTargeting() { - Logger.common(message: "TR | Initiating processing of remaining in-app targeting requests.", level: .debug, category: .inAppMessages) - Logger.common(message: "TR | Full list of in-app messages: \(fullListOfInapps.map { $0.id })", level: .debug, category: .inAppMessages) - Logger.common(message: "TR | Saved event for targeting: \(savedEventForTargeting?.name ?? "None")", level: .debug, category: .inAppMessages) - - self.prepareTargetingChecker(for: fullListOfInapps) - dataFacade.setObservedOperation() - self.dataFacade.fetchDependencies(model: savedEventForTargeting?.model) { - self.filterByInappsEvents(inapps: self.fullListOfInapps, filteredInAppsByEvent: &self.inappsDictForTargeting) - let inappsForTargeting = self.inAppsByEventForTargeting(event: self.savedEventForTargeting, asd: self.inappsDictForTargeting) - var ids = inappsForTargeting.map { $0.inAppId } - if self.completionSuccess && ids.contains(self.shownInnapId) { - ids.removeAll { $0 == self.shownInnapId } - } - - let setIds = Set(ids) - Logger.common(message: "TR | In-apps selected for targeting requests: \(setIds)", level: .debug, category: .inAppMessages) - setIds.forEach { self.dataFacade.trackTargeting(id: $0) } - self.completionSuccess = false - } - } - - func inAppsByEventForTargeting(event: ApplicationEvent?, asd: [InAppMessageTriggerEvent: [InAppTransitionData]]) -> [InAppTransitionData] { - if let event = event { - if let inappsByEvent = asd[.applicationEvent(event)] { - return inappsByEvent - } else { - return [] - } - } else if let inappsByEvent = asd[.start] { - return inappsByEvent - } else { - Logger.common(message: "No inapps available for the event or start.") - return [] - } - } - - func filterInappsBySDKVersion(_ inapps: [InApp]?, shownInAppsIds: Set) -> [InApp] { - guard let inapps = inapps else { - return [] - } - - let filteredInapps = inapps.filter { - sdkVersionValidator.isValid(item: $0.sdkVersion) - && !shownInAppsIds.contains($0.id) - } - - return filteredInapps + func prepareForRemainingTargeting() { + let estimatedInapps = validInapps + prepareTargetingChecker(for: estimatedInapps) } - func filterInappsByABTests(_ abTests: [ABTest]?, responseInapps: [InApp]?) -> [InApp] { - let responseInapps = responseInapps ?? [] - guard let abTests = abTests, !abTests.isEmpty else { - return responseInapps - } - - var result: [InApp] = responseInapps - - for abTest in abTests { - guard let uuid = UUID(uuidString: persistenceStorage.deviceUUID ?? "" ), - let salt = abTest.salt, - let variants = abTest.variants else { - continue - } - - let hashValue = try? abTestDeviceMixer.modulusGuidHash(identifier: uuid, salt: salt) - - guard let hashValue = hashValue else { - continue - } - - Logger.common(message: "[Hash Value]: \(hashValue) for [UUID]: \(persistenceStorage.deviceUUID ?? "nil")") - Logger.common(message: "[AB-test ID]: \(abTest.id)") + func sendRemainingInappsTargeting() { + self.dataFacade.fetchDependencies(model: savedEventForTargeting?.model) { + self.filterByInappsEvents(inapps: self.validInapps, + filteredInAppsByEvent: &self.filteredInappsByEventForTargeting) - var allInappsInVariantsExceptCurrentBranch: [String] = [] + let logMessage = """ + TR | Initiating processing of remaining in-app targeting requests. + Full list of in-app messages: \(self.validInapps.map { $0.id }) + Saved event for targeting: \(self.savedEventForTargeting?.name ?? "None") + """ + Logger.common(message: logMessage, level: .debug, category: .inAppMessages) + + let targetedEventKey: InAppMessageTriggerEvent = self.savedEventForTargeting != nil + ? .applicationEvent(self.savedEventForTargeting!) + : .start - for variant in variants { - if let objects = variant.objects { - for object in objects { - if object.kind == .all { - responseInapps.forEach( { - allInappsInVariantsExceptCurrentBranch.append($0.id) - }) - } else { - allInappsInVariantsExceptCurrentBranch += object.inapps ?? [] - } - } - } + guard let inappsByEvent = self.filteredInappsByEventForTargeting[targetedEventKey] else { + return } - var setInapps = Set(allInappsInVariantsExceptCurrentBranch) - - for variant in variants { - if let modulus = variant.modulus, let objects = variant.objects, let upper = modulus.upper { - let range = modulus.lower.. = Set(self.validInapps.compactMap { inapp -> String? in + guard inapp.id != self.shownInnapId, + inappsByEvent.contains(where: { $0.inAppId == inapp.id }), + self.targetingChecker.check(targeting: inapp.targeting) else { + return nil } + return inapp.id + }) + + Logger.common(message: "TR | In-apps selected for targeting requests: \(preparedForTrackTargetingInapps)", level: .debug, category: .inAppMessages) + + preparedForTrackTargetingInapps.forEach { id in + self.dataFacade.trackTargeting(id: id) } - let currentResult = responseInapps.filter { !setInapps.contains($0.id) } - result = result.filter { currentResult.contains($0) } + self.shownInnapId = "" } - - let ids = result.map { $0.id } - Logger.common(message: "Filtered in-app IDs after AB-filter based on UUID branch: [\(ids.joined(separator: ", "))]") - - return result } private func prepareTargetingChecker(for inapps: [InApp]) { @@ -261,7 +179,7 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { var imageDict: [String: UIImage] = [:] var gotError = false - if let shownInapps = self.persistenceStorage.shownInAppsIds, shownInapps.contains(inapp.inAppId) { + if self.inappFilterService.shownInAppsIds.contains(inapp.inAppId) { continue } @@ -299,9 +217,11 @@ final class InAppConfigutationMapper: InAppConfigurationMapperProtocol { group.notify(queue: .main) { DispatchQueue.main.async { [weak self] in - self?.shownInnapId = formData?.inAppId ?? "" - self?.dataFacade.trackTargeting(id: formData?.inAppId) - self?.completionSuccess = true + if !SessionTemporaryStorage.shared.isPresentingInAppMessage { + self?.shownInnapId = formData?.inAppId ?? "" + self?.dataFacade.trackTargeting(id: formData?.inAppId) + } + completion(formData) } } diff --git a/Mindbox/InAppMessages/InAppConfigurationMapper/Services/ImageDownloadService.swift b/Mindbox/InAppMessages/InAppConfigurationMapper/Services/ImageDownloadService.swift index 28ffce50..d39d4fe1 100644 --- a/Mindbox/InAppMessages/InAppConfigurationMapper/Services/ImageDownloadService.swift +++ b/Mindbox/InAppMessages/InAppConfigurationMapper/Services/ImageDownloadService.swift @@ -37,7 +37,7 @@ class ImageDownloadService: ImageDownloadServiceProtocol { } else if let localURL = localURL { do { let imageData = try Data(contentsOf: localURL) - guard let image = UIImage(data: imageData) else { + guard let image = ImageFormat.getImage(imageData: imageData) else { Logger.common(message: "Inapps image is incorrect. [URL]: \(localURL)", level: .debug, category: .inAppMessages) let error = NSError(domain: "", code: NSURLErrorCannotDecodeContentData, userInfo: nil) completion(.failure(error)) diff --git a/Mindbox/InAppMessages/InAppConfigurationMapper/Services/InappFilterService/InappFilter.swift b/Mindbox/InAppMessages/InAppConfigurationMapper/Services/InappFilterService/InappFilter.swift index ca912c6c..dd43c3ae 100644 --- a/Mindbox/InAppMessages/InAppConfigurationMapper/Services/InappFilterService/InappFilter.swift +++ b/Mindbox/InAppMessages/InAppConfigurationMapper/Services/InappFilterService/InappFilter.swift @@ -10,26 +10,60 @@ import Foundation import MindboxLogger protocol InappFilterProtocol { - func filter(inapps: [InAppDTO]?) -> [InApp] + func filter(inapps: [InAppDTO]?, abTests: [ABTest]?) -> [InApp] + var validInapps: [InApp] { get } + var shownInAppsIds: Set { get } } final class InappsFilterService: InappFilterProtocol { + + var validInapps: [InApp] = [] + var shownInAppsIds: Set = [] + + private let persistenceStorage: PersistenceStorage + private let abTestDeviceMixer: ABTestDeviceMixer private let variantsFilter: VariantFilterProtocol + private let sdkVersionValidator: SDKVersionValidator - init(variantsFilter: VariantFilterProtocol) { + init(persistenceStorage: PersistenceStorage, abTestDeviceMixer: ABTestDeviceMixer, variantsFilter: VariantFilterProtocol, sdkVersionValidator: SDKVersionValidator) { + self.persistenceStorage = persistenceStorage + self.abTestDeviceMixer = abTestDeviceMixer self.variantsFilter = variantsFilter + self.sdkVersionValidator = sdkVersionValidator } - func filter(inapps: [InAppDTO]?) -> [InApp] { + func filter(inapps: [InAppDTO]?, abTests: [ABTest]?) -> [InApp] { guard var inapps = inapps else { Logger.common(message: "Received nil for in-apps. Returning an empty array.", level: .debug, category: .inAppMessages) return [] } inapps = filterInappsBySDKVersion(inapps) - Logger.common(message: "Processing \(inapps.count) in-app(s).", level: .debug, category: .inAppMessages) + let validInapps = filterValidInAppMessages(inapps) + let filteredByABTestInapps = filterInappsByABTests(abTests, responseInapps: validInapps) + let filteredByAlreadyShown = filterInappsByAlreadyShown(filteredByABTestInapps) + return filteredByAlreadyShown + } +} + +// MARK: - Private methods +private extension InappsFilterService { + func filterInappsBySDKVersion(_ inapps: [InAppDTO]) -> [InAppDTO] { + let inapps = inapps + + let filteredInapps = inapps.filter { + let minVersionValid = $0.sdkVersion.min.map { $0 <= Constants.Versions.sdkVersionNumeric } ?? false + let maxVersionValid = $0.sdkVersion.max.map { $0 >= Constants.Versions.sdkVersionNumeric } ?? true + + return minVersionValid && maxVersionValid + } + + return filteredInapps + } + + func filterValidInAppMessages(_ inapps: [InAppDTO]) -> [InApp] { var filteredInapps: [InApp] = [] for inapp in inapps { do { @@ -48,17 +82,84 @@ final class InappsFilterService: InappFilterProtocol { } Logger.common(message: "Filtering process completed. \(filteredInapps.count) valid in-app(s) found.", level: .debug, category: .inAppMessages) + validInapps = filteredInapps return filteredInapps } - func filterInappsBySDKVersion(_ inapps: [InAppDTO]) -> [InAppDTO] { - let inapps = inapps - - let filteredInapps = inapps.filter { - let minVersionValid = $0.sdkVersion.min.map { $0 <= Constants.Versions.sdkVersionNumeric } ?? false - let maxVersionValid = $0.sdkVersion.max.map { $0 >= Constants.Versions.sdkVersionNumeric } ?? true + func filterInappsByABTests(_ abTests: [ABTest]?, responseInapps: [InApp]?) -> [InApp] { + let responseInapps = responseInapps ?? [] + guard let abTests = abTests, !abTests.isEmpty else { + return responseInapps + } + + var result: [InApp] = responseInapps + + for abTest in abTests { + guard let uuid = UUID(uuidString: persistenceStorage.deviceUUID ?? "" ), + let salt = abTest.salt, + let variants = abTest.variants else { + continue + } - return minVersionValid && maxVersionValid + let hashValue = try? abTestDeviceMixer.modulusGuidHash(identifier: uuid, salt: salt) + + guard let hashValue = hashValue else { + continue + } + + Logger.common(message: "[Hash Value]: \(hashValue) for [UUID]: \(persistenceStorage.deviceUUID ?? "nil")") + Logger.common(message: "[AB-test ID]: \(abTest.id)") + + var allInappsInVariantsExceptCurrentBranch: [String] = [] + + for variant in variants { + if let objects = variant.objects { + for object in objects { + if object.kind == .all { + responseInapps.forEach( { + allInappsInVariantsExceptCurrentBranch.append($0.id) + }) + } else { + allInappsInVariantsExceptCurrentBranch += object.inapps ?? [] + } + } + } + } + + var setInapps = Set(allInappsInVariantsExceptCurrentBranch) + + for variant in variants { + if let modulus = variant.modulus, let objects = variant.objects, let upper = modulus.upper { + let range = modulus.lower.. [InApp] { + shownInAppsIds = Set(persistenceStorage.shownInAppsIds ?? []) + Logger.common(message: "Shown in-apps ids: [\(shownInAppsIds)]", level: .info, category: .inAppMessages) + let filteredInapps = inapps.filter { + sdkVersionValidator.isValid(item: $0.sdkVersion) + && !shownInAppsIds.contains($0.id) } return filteredInapps diff --git a/Mindbox/InAppMessages/InAppTargetingChecker/TargetingCheckerTypes/PushEnabledTargetingChecker.swift b/Mindbox/InAppMessages/InAppTargetingChecker/TargetingCheckerTypes/PushEnabledTargetingChecker.swift index 69256924..6ca555f1 100644 --- a/Mindbox/InAppMessages/InAppTargetingChecker/TargetingCheckerTypes/PushEnabledTargetingChecker.swift +++ b/Mindbox/InAppMessages/InAppTargetingChecker/TargetingCheckerTypes/PushEnabledTargetingChecker.swift @@ -7,19 +7,26 @@ // import Foundation +import UserNotifications final class PushEnabledTargetingChecker: InternalTargetingChecker { override func checkInternal(targeting: PushEnabledTargeting) -> Bool { - var pushPermissionBoolean = true - switch SessionTemporaryStorage.shared.pushPermissionStatus { - case .notDetermined, .denied: - pushPermissionBoolean = false - case .authorized, .provisional, .ephemeral: - pushPermissionBoolean = true - @unknown default: - pushPermissionBoolean = true + let lock = DispatchSemaphore(value: 0) + var isNotificationsEnabled = true + + UNUserNotificationCenter.current().getNotificationSettings { settings in + switch settings.authorizationStatus { + case .notDetermined, .denied: + isNotificationsEnabled = false + case .authorized, .provisional, .ephemeral: + isNotificationsEnabled = true + @unknown default: + isNotificationsEnabled = true + } + lock.signal() } - return targeting.value == pushPermissionBoolean + lock.wait() + return targeting.value == isNotificationsEnabled } } diff --git a/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationActionUseCase/PushPermissionActionUseCase.swift b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationActionUseCase/PushPermissionActionUseCase.swift index fe48a3aa..f2a98d9c 100644 --- a/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationActionUseCase/PushPermissionActionUseCase.swift +++ b/Mindbox/InAppMessages/Presentation/Manager/UseCases/PresentationActionUseCase/PushPermissionActionUseCase.swift @@ -29,6 +29,7 @@ final class PushPermissionActionUseCase: PresentationActionUseCaseProtocol { tracker.trackClick(id: id) Logger.common(message: "In-app with push permission | ID: \(id)", level: .debug, category: .inAppMessages) requestOrOpenSettingsForNotifications() + onTap(nil, model.intentPayload) close() } @@ -41,7 +42,6 @@ final class PushPermissionActionUseCase: PresentationActionUseCaseProtocol { case .denied: self.openPushNotificationSettings() case .authorized, .provisional, .ephemeral: - UIPasteboard.general.string = self.model.intentPayload return @unknown default: Logger.common(message: "Encountered an unknown notification authorization status: \(settings.authorizationStatus.description)", level: .debug, category: .inAppMessages) diff --git a/Mindbox/Info.plist b/Mindbox/Info.plist index 2983809b..2c85542f 100644 --- a/Mindbox/Info.plist +++ b/Mindbox/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 5293 + 5351 diff --git a/MindboxLogger.podspec b/MindboxLogger.podspec index f26ca741..10dbe2a1 100644 --- a/MindboxLogger.podspec +++ b/MindboxLogger.podspec @@ -1,17 +1,17 @@ Pod::Spec.new do |spec| spec.name = "MindboxLogger" - spec.version = "0.0.6" + spec.version = "0.0.7" spec.summary = "SDK for utilities to work with Mindbox" spec.description = "-" spec.homepage = "https://github.com/mindbox-cloud/ios-sdk" spec.license = { :type => "CC BY-NC-ND 4.0", :file => "LICENSE" } spec.author = { "Mindbox" => "ios-sdk@mindbox.ru" } spec.platform = :ios, "10.0" - spec.source = { :git => "https://github.com/mindbox-cloud/ios-sdk.git", :tag => spec.version } + spec.source = { :git => "https://github.com/mindbox-cloud/ios-sdk.git", :tag => "#{spec.version}-logger" } spec.source_files = "MindboxLogger/**/*.{swift}", "SDKVersionProvider/**/*.{swift}" spec.exclude_files = "Classes/Exclude" spec.resource_bundles = { - 'MindboxLogger' => ["MindboxLogger/**/*.xcdatamodeld"] + 'MindboxLogger' => ["MindboxLogger/**/*.xcdatamodeld", 'MindboxLogger/**/*.xcprivacy'] } spec.swift_version = "5" diff --git a/MindboxNotifications.podspec b/MindboxNotifications.podspec index fb7c8737..d0cec8d4 100644 --- a/MindboxNotifications.podspec +++ b/MindboxNotifications.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "MindboxNotifications" - spec.version = "2.9.0-rc" + spec.version = "2.9.0" spec.summary = "SDK for integration notifications with Mindbox" spec.description = "This library allows you to integrate notifications and transfer them to Mindbox Marketing Cloud" spec.homepage = "https://github.com/mindbox-cloud/ios-sdk" @@ -10,7 +10,10 @@ Pod::Spec.new do |spec| spec.source = { :git => "https://github.com/mindbox-cloud/ios-sdk.git", :tag => spec.version } spec.source_files = "MindboxNotifications/**/*.{swift}", "SDKVersionProvider/**/*.{swift}" spec.exclude_files = "Classes/Exclude" + spec.resource_bundles = { + 'MindboxNotifications' => ['MindboxNotifications/**/*.xcprivacy'] + } spec.swift_version = "5" - spec.dependency 'MindboxLogger', '0.0.6' + spec.dependency 'MindboxLogger', '0.0.7' end diff --git a/MindboxNotifications/MindboxNotificationService.swift b/MindboxNotifications/MindboxNotificationService.swift index 2a59e20b..d8d38442 100644 --- a/MindboxNotifications/MindboxNotificationService.swift +++ b/MindboxNotifications/MindboxNotificationService.swift @@ -40,10 +40,13 @@ public class MindboxNotificationService: NSObject { public func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) - guard let bestAttemptContent = bestAttemptContent else { return } + guard let bestAttemptContent = bestAttemptContent else { + Logger.common(message: "MindboxNotificationService: Failed to get bestAttemptContent. bestAttemptContent: \(String(describing: bestAttemptContent))", level: .error, category: .notification) + return + } pushDelivered(request) - + Logger.common(message: "Push notification UniqueKey: \(request.identifier)", level: .info, category: .notification) if let imageUrl = parse(request: request)?.withImageURL?.imageUrl, @@ -53,23 +56,26 @@ public class MindboxNotificationService: NSObject { self?.proceedFinalStage(bestAttemptContent) } } else { + Logger.common(message: "MindboxNotificationService: Failed to parse imageUrl", level: .error, category: .notification) proceedFinalStage(bestAttemptContent) } } - + /// Call this method in `didReceive(_ request, withContentHandler)` of your `NotificationService` if you have implemented a custom version of NotificationService. This is necessary as an indicator that the push notification has been delivered to Mindbox services. public func pushDelivered(_ request: UNNotificationRequest) { let utilities = MBUtilitiesFetcher() guard let configuration = utilities.configuration else { - Logger.error(.init(errorType: .internal, description: "Configuration is not set")) + Logger.common(message: "MindboxNotificationService: Failed to get configuration", level: .error, category: .notification) return } + Logger.common(message: "MindboxNotificationService: Successfully received configuration. configuration: \(configuration)", level: .info, category: .notification) let networkService = NetworkService(utilitiesFetcher: utilities, configuration: configuration) let deliveryService = DeliveryService(utilitiesFetcher: utilities, networkService: networkService) - + do { try deliveryService.track(request: request) + Logger.common(message: "MindboxNotificationService: Successfully tracked. request: \(request)", level: .info, category: .notification) } catch { Logger.error(.init(errorType: .unknown, description: error.localizedDescription)) } @@ -81,11 +87,12 @@ public class MindboxNotificationService: NSObject { defer { completion() } guard let self = self, let data = data else { + Logger.common(message: "MindboxNotificationService: Failed to get self or data. self: \(String(describing: self)), data: \(String(describing: data))", level: .error, category: .notification) return } - - Logger.response(data: data, response: response, error: error) + Logger.response(data: data, response: response, error: error) + if let attachment = self.saveImage(data) { self.bestAttemptContent?.attachments = [attachment] } @@ -100,13 +107,17 @@ public class MindboxNotificationService: NSObject { /// Call this method in `serviceExtensionTimeWillExpire()` of `NotificationService` public func serviceExtensionTimeWillExpire() { if let bestAttemptContent = bestAttemptContent { + Logger.common(message: "MindboxNotificationService: Failed to get bestAttemptContent. bestAttemptContent: \(bestAttemptContent)", level: .error, category: .notification) proceedFinalStage(bestAttemptContent) } } private func createContent(for notification: UNNotification, extensionContext: NSExtensionContext?) { let request = notification.request - guard let payload = parse(request: request) else { return } + guard let payload = parse(request: request) else { + Logger.common(message: "MindboxNotificationService: Failed to parse payload. request: \(request)", level: .error, category: .notification) + return + } if let attachment = notification.request.content.attachments.first, attachment.url.startAccessingSecurityScopedResource() { @@ -117,6 +128,7 @@ public class MindboxNotificationService: NSObject { private func createActions(with payload: Payload, context: NSExtensionContext?) { guard let context = context, let buttons = payload.withButton?.buttons else { + Logger.common(message: "MindboxNotificationService: Failed to create actions. payload: \(payload), context: \(String(describing: context)), payload.withButton?.buttons: \(String(describing: payload.withButton?.buttons))", level: .error, category: .notification) return } let actions = buttons.map { button in @@ -140,7 +152,10 @@ public class MindboxNotificationService: NSObject { private func createImageView(with imagePath: String, view: UIView?) { guard let view = view, - let data = FileManager.default.contents(atPath: imagePath) else { return } + let data = FileManager.default.contents(atPath: imagePath) else { + Logger.common(message: "MindboxNotificationService: Failed to create view. imagePath: \(imagePath), view: \(String(describing: view))", level: .error, category: .notification) + return + } let imageView = UIImageView(image: UIImage(data: data)) imageView.contentMode = .scaleAspectFill @@ -157,24 +172,36 @@ public class MindboxNotificationService: NSObject { } private func parse(request: UNNotificationRequest) -> Payload? { - guard let userInfo = getUserInfo(from: request) else { return nil } - guard let data = try? JSONSerialization.data(withJSONObject: userInfo, options: .prettyPrinted) else { return nil } + guard let userInfo = getUserInfo(from: request) else { + Logger.common(message: "MindboxNotificationService: Failed to get userInfo", level: .error, category: .notification) + return nil + } + guard let data = try? JSONSerialization.data(withJSONObject: userInfo, options: .prettyPrinted) else { + Logger.common(message: "MindboxNotificationService: Failed to get data. userInfo: \(userInfo)", level: .error, category: .notification) + return nil + } var payload = Payload() payload.withButton = try? JSONDecoder().decode(Payload.Button.self, from: data) + Logger.common(message: "MindboxNotificationService: payload.withButton: \(String(describing: payload.withButton))", level: .info, category: .notification) + payload.withImageURL = try? JSONDecoder().decode(Payload.ImageURL.self, from: data) - + Logger.common(message: "MindboxNotificationService: payload.withImageURL: \(String(describing: payload.withImageURL))", level: .info, category: .notification) + return payload } private func getUserInfo(from request: UNNotificationRequest) -> [AnyHashable: Any]? { guard let userInfo = (request.content.mutableCopy() as? UNMutableNotificationContent)?.userInfo else { + Logger.common(message: "MindboxNotificationService: Failed to get userInfo", level: .error, category: .notification) return nil } if userInfo.keys.count == 1, let innerUserInfo = userInfo["aps"] as? [AnyHashable: Any] { + Logger.common(message: "MindboxNotificationService: userInfo: \(innerUserInfo), userInfo.keys.count: \(userInfo.keys.count), innerUserInfo: \(innerUserInfo)", level: .info, category: .notification) return innerUserInfo } else { + Logger.common(message: "MindboxNotificationService: userInfo: \(userInfo)", level: .info, category: .notification) return userInfo } } @@ -182,7 +209,7 @@ public class MindboxNotificationService: NSObject { private func saveImage(_ data: Data) -> UNNotificationAttachment? { let name = UUID().uuidString guard let format = ImageFormat(data) else { - Logger.common(message: "Image load failed", level: .error, category: .notification) + Logger.common(message: "MindboxNotificationService: Image load failed, data: \(data)", level: .error, category: .notification) return nil } let url = URL(fileURLWithPath: NSTemporaryDirectory()) @@ -193,6 +220,7 @@ public class MindboxNotificationService: NSObject { try data.write(to: fileURL, options: .atomic) return try UNNotificationAttachment(identifier: name, url: fileURL, options: nil) } catch { + Logger.common(message: "MindboxNotificationService: Failed to save image. data: \(data), name: \(name), url: \(url), directory: \(directory)", level: .error, category: .notification) return nil } } diff --git a/MindboxNotifications/Network/DeliveryService.swift b/MindboxNotifications/Network/DeliveryService.swift index 1749e560..8bdfecc2 100644 --- a/MindboxNotifications/Network/DeliveryService.swift +++ b/MindboxNotifications/Network/DeliveryService.swift @@ -42,9 +42,11 @@ public class DeliveryService { @discardableResult func track(request: UNNotificationRequest) throws -> Bool { guard let decoder = NotificationDecoder(request: request) else { + Logger.common(message: "DeliveryService: Failed to create decoder. request: \(request)", level: .error, category: .notification) return false } guard decoder.isMindboxNotification else { + Logger.common(message: "DeliveryService: Not MindboxNotification. decoder.isMindboxNotification: \(decoder.isMindboxNotification)", level: .error, category: .notification) return false } let payload = try decoder.decode() diff --git a/MindboxNotifications/Network/DeviceModelHelper.swift b/MindboxNotifications/Network/DeviceModelHelper.swift index 13015332..ebb745e8 100644 --- a/MindboxNotifications/Network/DeviceModelHelper.swift +++ b/MindboxNotifications/Network/DeviceModelHelper.swift @@ -8,6 +8,7 @@ import Foundation import UIKit.UIDevice +import MindboxLogger struct DeviceModelHelper { @@ -19,7 +20,9 @@ struct DeviceModelHelper { uname(&systemInfo) let machineMirror = Mirror(reflecting: systemInfo.machine) let identifier = machineMirror.children.reduce("") { identifier, element in - guard let value = element.value as? Int8, value != 0 else { return identifier } + guard let value = element.value as? Int8, value != 0 else { + return identifier + } return identifier + String(UnicodeScalar(UInt8(value))) } return identifier diff --git a/MindboxNotifications/Network/MBConfiguration.swift b/MindboxNotifications/Network/MBConfiguration.swift index 7ee44aab..4dbb8e10 100644 --- a/MindboxNotifications/Network/MBConfiguration.swift +++ b/MindboxNotifications/Network/MBConfiguration.swift @@ -8,6 +8,7 @@ import Foundation +import MindboxLogger public struct MBConfiguration: Codable { public let endpoint: String @@ -27,11 +28,18 @@ public struct MBConfiguration: Codable { } } - guard let url = findeURL else { return nil } + guard let url = findeURL else { + return nil + } + + guard let data = try? Data(contentsOf: url) else { + return nil + } - guard let data = try? Data(contentsOf: url) else { return nil } + guard let configuration = try? decoder.decode(MBConfiguration.self, from: data) else { + return nil + } - guard let configuration = try? decoder.decode(MBConfiguration.self, from: data) else { return nil } self = configuration } @@ -48,17 +56,21 @@ public struct MBConfiguration: Codable { let endpoint = try values.decode(String.self, forKey: .endpoint) let domain = try values.decode(String.self, forKey: .domain) var previousInstallationId: String? + if let value = try? values.decode(String.self, forKey: .previousInstallationId) { if !value.isEmpty { previousInstallationId = value } } + var previousDeviceUUID: String? + if let value = try? values.decode(String.self, forKey: .previousDeviceUUID) { if !value.isEmpty { previousDeviceUUID = value } } + let subscribeCustomerIfCreated = try values.decodeIfPresent(Bool.self, forKey: .subscribeCustomerIfCreated) ?? false self.endpoint = endpoint diff --git a/MindboxNotifications/Network/MBUtilitiesFetcher.swift b/MindboxNotifications/Network/MBUtilitiesFetcher.swift index cca941bb..1ecfd31f 100644 --- a/MindboxNotifications/Network/MBUtilitiesFetcher.swift +++ b/MindboxNotifications/Network/MBUtilitiesFetcher.swift @@ -1,5 +1,5 @@ // -// UtilitiesFetcher.swift +// MBUtilitiesFetcher.swift // MindboxNotifications // // Created by Ihor Kandaurov on 22.06.2021. @@ -7,6 +7,7 @@ // import Foundation +import MindboxLogger #if SWIFT_PACKAGE import SDKVersionProvider #endif @@ -23,10 +24,11 @@ class MBUtilitiesFetcher { var bundle = Bundle(for: MindboxNotificationService.self) return bundle }() - - var applicationGroupIdentifier: String { + + var applicationGroupIdentifier: String? { guard let hostApplicationName = hostApplicationName else { - fatalError("CFBundleShortVersionString not found for host app") + Logger.common(message: "MBUtilitiesFetcher: Failed to get applicationGroupIdentifier. hostApplicationName: \(String(describing: hostApplicationName))", level: .error, category: .notification) + return nil } let identifier = "group.cloud.Mindbox.\(hostApplicationName)" let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: identifier) @@ -34,8 +36,8 @@ class MBUtilitiesFetcher { #if targetEnvironment(simulator) return "" #else - let message = "AppGroup for \(hostApplicationName) not found. Add AppGroup with value: \(identifier)" - fatalError(message) + Logger.common(message: "MBUtilitiesFetcher: Failed to get AppGroup for \(hostApplicationName). identifier: \(identifier))", level: .error, category: .notification) + return nil #endif } return identifier @@ -51,6 +53,7 @@ class MBUtilitiesFetcher { let url = bundle.bundleURL.deletingLastPathComponent().deletingLastPathComponent() if let otherBundle = Bundle(url: url) { bundle = otherBundle + Logger.common(message: "MBUtilitiesFetcher: Successfully prepared bundle. bundle: \(bundle)", level: .debug, category: .notification) } } } @@ -72,7 +75,10 @@ class MBUtilitiesFetcher { } var configuration: MBConfiguration? { - guard let data = userDefaults?.data(forKey: "MBPersistenceStorage-configurationData") else { return nil } + guard let data = userDefaults?.data(forKey: "MBPersistenceStorage-configurationData") else { + Logger.common(message: "MBUtilitiesFetcher: Failed to get data from userDefaults for key 'MBPersistenceStorage-configurationData'", level: .error, category: .notification) + return nil + } return try? JSONDecoder().decode(MBConfiguration.self, from: data) } } diff --git a/MindboxNotifications/Network/Model/Body+/BodyDecoder.swift b/MindboxNotifications/Network/Model/Body+/BodyDecoder.swift index 23a6b20b..d134eb91 100644 --- a/MindboxNotifications/Network/Model/Body+/BodyDecoder.swift +++ b/MindboxNotifications/Network/Model/Body+/BodyDecoder.swift @@ -7,6 +7,7 @@ // import Foundation +import MindboxLogger struct BodyDecoder { @@ -17,9 +18,11 @@ struct BodyDecoder { if let body = try? JSONDecoder().decode(T.self, from: data) { self.body = body } else { + Logger.common(message: "BodyDecoder: Failed to decode JSON. data: \(data)", level: .error, category: .notification) return nil } } else { + Logger.common(message: "BodyDecoder: Failed to decode string. decodable: \(decodable)", level: .error, category: .notification) return nil } } diff --git a/MindboxNotifications/Network/Model/Body+/BodyEncoder.swift b/MindboxNotifications/Network/Model/Body+/BodyEncoder.swift index 8b28539f..18966d69 100644 --- a/MindboxNotifications/Network/Model/Body+/BodyEncoder.swift +++ b/MindboxNotifications/Network/Model/Body+/BodyEncoder.swift @@ -7,6 +7,7 @@ // import Foundation +import MindboxLogger struct BodyEncoder { @@ -17,6 +18,7 @@ struct BodyEncoder { body = String(data: encodedBody, encoding: .utf8) ?? "" } else { body = "" + Logger.common(message: "BodyEncoder: Failed to encode JSON. encodable: \(encodable)", level: .error, category: .notification) } } diff --git a/MindboxNotifications/Network/Model/DeliveryOperation.swift b/MindboxNotifications/Network/Model/DeliveryOperation.swift index 760f9a77..be2350d3 100644 --- a/MindboxNotifications/Network/Model/DeliveryOperation.swift +++ b/MindboxNotifications/Network/Model/DeliveryOperation.swift @@ -7,6 +7,7 @@ // import Foundation +import MindboxLogger class PushDeliveryOperation: Operation { private let event: Event @@ -43,7 +44,10 @@ class PushDeliveryOperation: Operation { } service.sendPushDelivered(event: event) { [weak self] result in - guard let self = self else { return } + guard let self = self else { + Logger.common(message: "PushDeliveryOperation: Failed to get PushDeliveryOperation. self: \(String(describing: self))", level: .error, category: .notification) + return + } switch result { case true: self.onCompleted?(self.event, true) diff --git a/MindboxNotifications/Network/Model/Event.swift b/MindboxNotifications/Network/Model/Event.swift index 80e24d69..7089f188 100644 --- a/MindboxNotifications/Network/Model/Event.swift +++ b/MindboxNotifications/Network/Model/Event.swift @@ -22,7 +22,7 @@ struct Event { return Int64(ms) } - // Время добавляения персистентно в очередь событий + // The time of adding is persistent to the event queue let enqueueTimeStamp: Double let serialNumber: String? diff --git a/MindboxNotifications/Network/Model/ImageFormat.swift b/MindboxNotifications/Network/Model/ImageFormat.swift index a80ab3f2..aedefd5b 100644 --- a/MindboxNotifications/Network/Model/ImageFormat.swift +++ b/MindboxNotifications/Network/Model/ImageFormat.swift @@ -7,6 +7,7 @@ // import Foundation +import MindboxLogger enum ImageFormat: String { case png, jpg, gif @@ -26,7 +27,10 @@ enum ImageFormat: String { extension ImageFormat { static func get(from data: Data) -> ImageFormat? { - guard let firstByte = data.first else { return nil } + guard let firstByte = data.first else { + Logger.common(message: "ImageFormat: Failed to get firstByte", level: .error, category: .notification) + return nil + } switch firstByte { case 0x89: return .png @@ -35,6 +39,7 @@ extension ImageFormat { case 0x47: return .gif default: + Logger.common(message: "ImageFormat: Failed to get image format", level: .error, category: .notification) return nil } } diff --git a/MindboxNotifications/Network/Model/NotificationDecoder.swift b/MindboxNotifications/Network/Model/NotificationDecoder.swift index 8f9a5872..30dbe092 100644 --- a/MindboxNotifications/Network/Model/NotificationDecoder.swift +++ b/MindboxNotifications/Network/Model/NotificationDecoder.swift @@ -8,6 +8,7 @@ import Foundation import UserNotifications +import MindboxLogger struct NotificationDecoder { var isMindboxNotification: Bool { @@ -18,15 +19,17 @@ struct NotificationDecoder { init?(request: UNNotificationRequest) { guard let userInfo = (request.content.mutableCopy() as? UNMutableNotificationContent)?.userInfo else { + Logger.common(message: "NotificationDecoder: Failed to get user info from notification content", level: .fault, category: .notification) return nil } + self.init(userInfo: userInfo) } - + init?(response: UNNotificationResponse) { self.init(request: response.notification.request) } - + init?(userInfo: [AnyHashable: Any]) { if userInfo.keys.count == 1, let innerUserInfo = userInfo["aps"] as? [AnyHashable: Any] { self.userInfo = innerUserInfo @@ -34,7 +37,7 @@ struct NotificationDecoder { self.userInfo = userInfo } } - + func decode() throws -> T { do { let data = try JSONSerialization.data(withJSONObject: userInfo, options: .prettyPrinted) @@ -43,9 +46,11 @@ struct NotificationDecoder { let payload = try decoder.decode(T.self, from: data) return payload } catch { + Logger.common(message: "NotificationDecoder: Failed to decode data into payload. error: \(error)", level: .error, category: .notification) throw error } } catch { + Logger.common(message: "NotificationDecoder: Failed to serialize userInfo into data. error: \(error)", level: .error, category: .notification) throw error } } diff --git a/MindboxNotifications/Network/Model/URLRequestBuilder.swift b/MindboxNotifications/Network/Model/URLRequestBuilder.swift index a2644e8b..91482166 100644 --- a/MindboxNotifications/Network/Model/URLRequestBuilder.swift +++ b/MindboxNotifications/Network/Model/URLRequestBuilder.swift @@ -7,6 +7,7 @@ // import Foundation +import MindboxLogger struct URLRequestBuilder { @@ -20,6 +21,7 @@ struct URLRequestBuilder { let components = makeURLComponents(for: route) guard let url = components.url else { + Logger.common(message: "URLRequestBuilder: Failed to create URL. Components: \(components)", level: .error, category: .notification) throw URLError(.badURL) } diff --git a/MindboxNotifications/Network/NetworkService.swift b/MindboxNotifications/Network/NetworkService.swift index e94f244a..56156159 100644 --- a/MindboxNotifications/Network/NetworkService.swift +++ b/MindboxNotifications/Network/NetworkService.swift @@ -32,6 +32,7 @@ class NetworkService { public func sendPushDelivered(event: Event, completion: @escaping ((Bool) -> Void)) { guard let deviceUUID = configuration.previousDeviceUUID else { completion(false) + Logger.common(message: "NetworkService: Failed to get deviceUUID. configuration.previousDeviceUUID: \(String(describing: configuration.previousDeviceUUID))", level: .error, category: .network) return } diff --git a/MindboxTests/DI/TestDependencyProvider.swift b/MindboxTests/DI/TestDependencyProvider.swift index 38d33acd..fc491e7e 100644 --- a/MindboxTests/DI/TestDependencyProvider.swift +++ b/MindboxTests/DI/TestDependencyProvider.swift @@ -83,7 +83,10 @@ final class TestDependencyProvider: DependencyContainer { let variantsFilterService = VariantFilterService(layersFilter: layersFilterService, elementsFilter: elementsFilterService, contentPositionFilter: contentPositionFilterService) - inappFilterService = InappsFilterService(variantsFilter: variantsFilterService) + inappFilterService = InappsFilterService(persistenceStorage: persistenceStorage, + abTestDeviceMixer: abTestDeviceMixer, + variantsFilter: variantsFilterService, + sdkVersionValidator: sdkVersionValidator) pushValidator = MindboxPushValidator() userVisitManager = UserVisitManager(persistenceStorage: persistenceStorage, sessionManager: sessionManager) } diff --git a/MindboxTests/InApp/Tests/InAppConfigResponseTests/InAppTargetingRequestsTests.swift b/MindboxTests/InApp/Tests/InAppConfigResponseTests/InAppTargetingRequestsTests.swift index 22b72690..6989ddfd 100644 --- a/MindboxTests/InApp/Tests/InAppConfigResponseTests/InAppTargetingRequestsTests.swift +++ b/MindboxTests/InApp/Tests/InAppConfigResponseTests/InAppTargetingRequestsTests.swift @@ -9,7 +9,7 @@ import XCTest @testable import Mindbox -class AInAppTargetingRequestsTests: XCTestCase { +class InAppTargetingRequestsTests: XCTestCase { var container: TestDependencyProvider! @@ -34,10 +34,7 @@ class AInAppTargetingRequestsTests: XCTestCase { mapper = InAppConfigutationMapper(inappFilterService: container.inappFilterService, targetingChecker: targetingChecker, - persistenceStorage: container.persistenceStorage, - sdkVersionValidator: container.sdkVersionValidator, urlExtractorService: container.urlExtractorService, - abTestDeviceMixer: container.abTestDeviceMixer, dataFacade: mockDataFacade) } @@ -77,7 +74,7 @@ class AInAppTargetingRequestsTests: XCTestCase { } wait(for: [expectation], timeout: 1) - targetingContains("2", expectedToShow: true) + targetingContains("2") targetingContains("1") } catch { XCTFail("Some error: \(error)") @@ -142,7 +139,7 @@ class AInAppTargetingRequestsTests: XCTestCase { } wait(for: [expectationForsendRemainingInappsTargeting], timeout: 1) - targetingContains("3", expectedToShow: true) + targetingContains("3") targetingContains("1") targetingContains("4") targetingContains("5") @@ -383,7 +380,7 @@ class AInAppTargetingRequestsTests: XCTestCase { } private func getConfig(name: String) throws -> ConfigResponse { - let bundle = Bundle(for: AInAppTargetingRequestsTests.self) + let bundle = Bundle(for: InAppTargetingRequestsTests.self) let fileURL = bundle.url(forResource: name, withExtension: "json")! let data = try Data(contentsOf: fileURL) return try JSONDecoder().decode(ConfigResponse.self, from: data) diff --git a/MindboxTests/InApp/Tests/InAppConfigurationMapperTests/InappFilterServiceTests/InappFilterServiceTests.swift b/MindboxTests/InApp/Tests/InAppConfigurationMapperTests/InappFilterServiceTests/InappFilterServiceTests.swift index aedba78d..f477d04e 100644 --- a/MindboxTests/InApp/Tests/InAppConfigurationMapperTests/InappFilterServiceTests/InappFilterServiceTests.swift +++ b/MindboxTests/InApp/Tests/InAppConfigurationMapperTests/InappFilterServiceTests/InappFilterServiceTests.swift @@ -34,35 +34,35 @@ final class InappFilterServiceTests: XCTestCase { func test_unknown_type_for_variants() throws { let config = try getConfig(name: "unknownVariantType") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } func test_missingBackgroundSection() throws { let config = try getConfig(name: "missingBackgroundSection") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } func test_emptyLayersSection() throws { let config = try getConfig(name: "emptyLayersSection") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } func test_unknownLayerType() throws { let config = try getConfig(name: "unknownLayerType") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } func test_knownImageUnknownPictureLayerType() throws { let config = try getConfig(name: "knownImageUnknownPictureLayerType") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 2) if let variant = inapps.first?.form.variants.first { @@ -78,63 +78,63 @@ final class InappFilterServiceTests: XCTestCase { func test_unknownActionLayerType() throws { let config = try getConfig(name: "unknownActionLayerType") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } func test_redirectUrlValueNumberInsteadOfString() throws { let config = try getConfig(name: "redirectUrlValueNumberInsteadOfString") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } func test_missingIntentPayloadInActionLayer() throws { let config = try getConfig(name: "missingIntentPayloadInActionLayer") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } func test_missingSourceSection() throws { let config = try getConfig(name: "missingSourceSection") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } func test_emptyVariantsArray() throws { let config = try getConfig(name: "emptyVariantsArray") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } func test_unknownSourceType() throws { let config = try getConfig(name: "unknownSourceType") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } func test_missingValueInSourceLayer() throws { let config = try getConfig(name: "missingValueInSourceLayer") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } func test_missingImageLinkInSourceLayerValue() throws { let config = try getConfig(name: "missingImageLinkInSourceLayerValue") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } func test_missingElementsSection() throws { let config = try getConfig(name: "missingElementsSection") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 2) if let variant = inapps.first?.form.variants.first { @@ -150,7 +150,7 @@ final class InappFilterServiceTests: XCTestCase { func test_invalidCloseButtonColor() throws { let config = try getConfig(name: "invalidCloseButtonColor") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 2) if let variant = inapps.first?.form.variants.first { @@ -174,7 +174,7 @@ final class InappFilterServiceTests: XCTestCase { func test_missingCloseButtonColorLineWidthSize() throws { let config = try getConfig(name: "missingCloseButtonColorLineWidthSize") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 2) if let variant = inapps.first?.form.variants.first { @@ -201,7 +201,7 @@ final class InappFilterServiceTests: XCTestCase { func test_twoCloseButtonsInApp() throws { let config = try getConfig(name: "twoCloseButtonsInApp") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 2) if let variant = inapps.first?.form.variants.first { @@ -226,7 +226,7 @@ final class InappFilterServiceTests: XCTestCase { func test_closeButtonWithOpenButton() throws { let config = try getConfig(name: "closeButtonWithOpenButton") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 2) if let variant = inapps.first?.form.variants.first { @@ -255,7 +255,7 @@ final class InappFilterServiceTests: XCTestCase { func test_unknownSizeKind() throws { let config = try getConfig(name: "unknownSizeKind") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 2) if let variant = inapps.first?.form.variants.first { @@ -286,28 +286,28 @@ final class InappFilterServiceTests: XCTestCase { func test_missingMarginFieldInSection() throws { let config = try getConfig(name: "missingMarginFieldInSection") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } func test_negativeCloseButtonSizeValues() throws { let config = try getConfig(name: "negativeCloseButtonSizeValues") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } func test_closeButtonMarginAboveOne() throws { let config = try getConfig(name: "closeButtonMarginAboveOne") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } func test_closeButtonMarginBelowZero() throws { let config = try getConfig(name: "closeButtonMarginBelowZero") - let inapps = sut.filter(inapps: config.inapps?.elements) + let inapps = sut.filter(inapps: config.inapps?.elements, abTests: config.abtests) XCTAssertEqual(inapps.count, 1) XCTAssertEqual(inapps.first?.id, Constants.defaultID) } diff --git a/SDKVersionProvider/SDKVersionConfig.xcconfig b/SDKVersionProvider/SDKVersionConfig.xcconfig index e46cb9a5..8502e56d 100644 --- a/SDKVersionProvider/SDKVersionConfig.xcconfig +++ b/SDKVersionProvider/SDKVersionConfig.xcconfig @@ -1 +1 @@ -MARKETING_VERSION = 2.9.0-rc +MARKETING_VERSION = 2.9.0 diff --git a/SDKVersionProvider/SDKVersionProvider.swift b/SDKVersionProvider/SDKVersionProvider.swift index 1453b80b..d12410ed 100644 --- a/SDKVersionProvider/SDKVersionProvider.swift +++ b/SDKVersionProvider/SDKVersionProvider.swift @@ -8,5 +8,5 @@ import Foundation public class SDKVersionProvider { - public static let sdkVersion = "2.9.0-rc" + public static let sdkVersion = "2.9.0" } diff --git a/git-release-branch-create.sh b/git-release-branch-create.sh index 95a42a70..1ff343f4 100755 --- a/git-release-branch-create.sh +++ b/git-release-branch-create.sh @@ -52,4 +52,9 @@ git add $notifivation_podspec_file git add $sdkversionprovider_file git commit -m "Bump SDK version to $version" -echo "Branch $branch_name has been created." +git push origin $branch_name + +git tag $branch_name +git push origin $branch_name --tags + +echo "Branch $branch_name has been created and pushed."