From 23b7bc1e4705234eab0cb19476d477d8c26d88d0 Mon Sep 17 00:00:00 2001 From: Frank Schmitt Date: Tue, 17 Mar 2020 16:09:08 -0700 Subject: [PATCH] Apptentive IOS SDK 5.2.9 --- .travis.yml | 8 + .../Apptentive.xcodeproj/project.pbxproj | 60 ++-- .../xcschemes/Apptentive.xcscheme | 28 +- Apptentive/Apptentive/Apptentive.h | 2 +- Apptentive/Apptentive/Apptentive.m | 30 +- Apptentive/Apptentive/ApptentiveStyleSheet.m | 22 +- .../ApptentiveHUDViewController.m | 3 - .../ApptentiveUnreadMessagesBadgeView.m | 6 +- .../ApptentiveInteractionNavigateToLink.m | 39 ++- .../ApptentiveInteractionAppStoreController.m | 22 +- .../Engagement/Model/ApptentiveAppRelease.m | 4 + .../Engagement/Model/ApptentiveConversation.h | 2 +- .../Engagement/Model/ApptentiveConversation.m | 34 ++- .../Model/ApptentiveConversationManager.m | 16 +- .../Model/ApptentiveConversationMetadata.h | 2 +- .../Model/ApptentiveConversationMetadata.m | 3 +- .../Engagement/Model/ApptentiveCount.m | 4 + .../Engagement/Model/ApptentiveCustomData.m | 3 +- .../Engagement/Model/ApptentiveDevice.m | 8 +- .../Engagement/Model/ApptentiveEngagement.m | 10 +- .../Model/ApptentiveEngagementManifest.m | 33 +-- .../Engagement/Model/ApptentiveInteraction.h | 2 +- .../Engagement/Model/ApptentiveInteraction.m | 13 +- .../Engagement/Model/ApptentivePerson.h | 2 +- .../Engagement/Model/ApptentivePerson.m | 22 +- .../Engagement/Model/ApptentiveSDK.m | 4 + .../Engagement/Model/ApptentiveVersion.m | 4 + .../Model/Targeting/ApptentiveAndClause.m | 2 +- .../Model/Targeting/ApptentiveClause.h | 2 + .../Model/Targeting/ApptentiveClause.m | 19 ++ .../Targeting/ApptentiveComparisonClause.m | 3 +- .../Model/Targeting/ApptentiveInvocations.m | 11 +- .../Model/Targeting/ApptentiveNotClause.m | 4 +- .../Model/Targeting/ApptentiveOrClause.m | 2 +- .../Model/Targeting/ApptentiveTarget.m | 2 +- .../Model/Targeting/ApptentiveTargets.m | 3 +- Apptentive/Apptentive/Info.plist | 2 +- .../ApptentiveAboutViewController.m | 5 +- .../ApptentiveAttachmentController.m | 7 +- .../ApptentiveMessageCenterViewController.m | 28 +- .../Message Center/Model/ApptentiveMessage.m | 9 +- .../Model/ApptentiveMessageManager.m | 6 +- .../Model/ApptentiveMessageStore.m | 4 +- .../Apptentive/Misc/ApptentiveArchiver.h | 20 ++ .../Apptentive/Misc/ApptentiveArchiver.m | 44 +++ .../Misc/ApptentiveLogMonitorSession.h | 2 +- .../Misc/ApptentiveLogMonitorSession.m | 14 +- .../Apptentive/Misc/ApptentiveURLOpener.h | 19 ++ .../Apptentive/Misc/ApptentiveURLOpener.m | 27 ++ .../Apptentive/Misc/ApptentiveUnarchiver.h | 23 ++ .../Apptentive/Misc/ApptentiveUnarchiver.m | 71 +++++ .../Apptentive/Model/ApptentiveLegacyEvent.m | 10 +- .../Model/ApptentiveLegacyMessage.m | 4 +- .../Model/ApptentiveLegacySurveyResponse.m | 8 +- .../Persistence/ApptentiveBackend.m | 40 ++- .../ApptentiveSurveyCollectionViewLayout.m | 4 - .../ApptentiveSurveyViewController.m | 8 - .../Views/ApptentiveSurveyCollectionView.m | 2 - .../ApptentiveConversationMigrationTests.m | 270 ++++++++++++++---- .../ApptentiveConversationTests.m | 16 +- .../ApptentiveEngagementTests.swift | 36 ++- .../ApptentiveStyleSheetTests.m | 4 +- Apptentive/ApptentiveTests/Info.plist | 2 +- .../data/configuration-v1.archive | Bin 0 -> 1066 bytes .../ApptentiveTests/data/conversation-v1.meta | Bin 0 -> 1064 bytes .../ApptentiveTests/data/manifest-v2.archive | Bin 0 -> 36762 bytes CHANGELOG.md | 9 + Example/Podfile.lock | 6 +- apptentive-ios.podspec | 2 +- 69 files changed, 834 insertions(+), 302 deletions(-) create mode 100644 .travis.yml create mode 100644 Apptentive/Apptentive/Misc/ApptentiveArchiver.h create mode 100644 Apptentive/Apptentive/Misc/ApptentiveArchiver.m create mode 100644 Apptentive/Apptentive/Misc/ApptentiveURLOpener.h create mode 100644 Apptentive/Apptentive/Misc/ApptentiveURLOpener.m create mode 100644 Apptentive/Apptentive/Misc/ApptentiveUnarchiver.h create mode 100644 Apptentive/Apptentive/Misc/ApptentiveUnarchiver.m create mode 100644 Apptentive/ApptentiveTests/data/configuration-v1.archive create mode 100644 Apptentive/ApptentiveTests/data/conversation-v1.meta create mode 100644 Apptentive/ApptentiveTests/data/manifest-v2.archive diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..2bd3b1e4a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: objective-c +osx_image: xcode11 +xcode_project: Apptentive/Apptentive.xcodeproj +xcode_scheme: Apptentive +xcode_destination: platform=iOS Simulator,OS=13.0,name=iPhone 11 +notifications: + slack: + secure: "dg4pLEaCPh1xcb7c6nKEAbyG0YKxkiKk3LrPknLEvTY1wleD/+yG3yLqhNPkm9wirqlxSn5Hh0A/ukm8gvMsb98v/IuslXCLmBYE5jJ0OC41+Ou0UIYcIRREbCsoH5EVwetPGbpC9gA89yrtO1JO/6a9UEBRDibRjHe7ZPj6q70=" diff --git a/Apptentive/Apptentive.xcodeproj/project.pbxproj b/Apptentive/Apptentive.xcodeproj/project.pbxproj index 0604c5aa4..cb5ecb62c 100644 --- a/Apptentive/Apptentive.xcodeproj/project.pbxproj +++ b/Apptentive/Apptentive.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 0100626A24198DDE00A6C89B /* manifest-v2.archive in Resources */ = {isa = PBXBuildFile; fileRef = 0100626824198DDD00A6C89B /* manifest-v2.archive */; }; + 0100626B24198DDE00A6C89B /* conversation-v1.meta in Resources */ = {isa = PBXBuildFile; fileRef = 0100626924198DDD00A6C89B /* conversation-v1.meta */; }; + 01006271241995FF00A6C89B /* configuration-v1.archive in Resources */ = {isa = PBXBuildFile; fileRef = 01006270241995FF00A6C89B /* configuration-v1.archive */; }; 010FE33B203E2D900021C246 /* CriteriaDescriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 010FE33A203E2D900021C246 /* CriteriaDescriptionTests.swift */; }; 010FE346203E4C810021C246 /* ApptentiveIndentPrinter.h in Headers */ = {isa = PBXBuildFile; fileRef = 010FE344203E4C810021C246 /* ApptentiveIndentPrinter.h */; }; 010FE347203E4C810021C246 /* ApptentiveIndentPrinter.m in Sources */ = {isa = PBXBuildFile; fileRef = 010FE345203E4C810021C246 /* ApptentiveIndentPrinter.m */; }; @@ -309,7 +312,6 @@ 01A2D1FF1E490A9700C2103A /* ApptentiveSurveySubmitButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 01A2D0F71E490A9700C2103A /* ApptentiveSurveySubmitButton.m */; }; 01A2D2401E4946D600C2103A /* ApptentiveConnectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01A2D2021E4946D500C2103A /* ApptentiveConnectTests.m */; }; 01A2D2441E4946D600C2103A /* ApptentiveMetricsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01A2D2061E4946D500C2103A /* ApptentiveMetricsTests.m */; }; - 01A2D2451E4946D600C2103A /* ApptentiveMigrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01A2D2071E4946D500C2103A /* ApptentiveMigrationTests.m */; }; 01A2D2461E4946D600C2103A /* ApptentiveConversationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01A2D2081E4946D500C2103A /* ApptentiveConversationTests.m */; }; 01A2D2471E4946D600C2103A /* ApptentiveStyleSheetTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01A2D2091E4946D500C2103A /* ApptentiveStyleSheetTests.m */; }; 01A2D2481E4946D600C2103A /* ApptentiveSurveyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01A2D20A1E4946D500C2103A /* ApptentiveSurveyTests.m */; }; @@ -361,6 +363,12 @@ 01A50EC51FC5FFB00058C06C /* ApptentiveInvocations.m in Sources */ = {isa = PBXBuildFile; fileRef = 01A50EC31FC5FFB00058C06C /* ApptentiveInvocations.m */; }; 01AA89B81F18177E00FB59AB /* ApptentiveAppInstall.h in Headers */ = {isa = PBXBuildFile; fileRef = 01AA89B61F18177E00FB59AB /* ApptentiveAppInstall.h */; }; 01AA89B91F18177E00FB59AB /* ApptentiveAppInstall.m in Sources */ = {isa = PBXBuildFile; fileRef = 01AA89B71F18177E00FB59AB /* ApptentiveAppInstall.m */; }; + 01B905572411C4C200E2A663 /* ApptentiveUnarchiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 01B905552411C4C200E2A663 /* ApptentiveUnarchiver.h */; }; + 01B905582411C4C200E2A663 /* ApptentiveUnarchiver.m in Sources */ = {isa = PBXBuildFile; fileRef = 01B905562411C4C200E2A663 /* ApptentiveUnarchiver.m */; }; + 01B9055B2411C83600E2A663 /* ApptentiveArchiver.h in Headers */ = {isa = PBXBuildFile; fileRef = 01B905592411C83600E2A663 /* ApptentiveArchiver.h */; }; + 01B9055C2411C83600E2A663 /* ApptentiveArchiver.m in Sources */ = {isa = PBXBuildFile; fileRef = 01B9055A2411C83600E2A663 /* ApptentiveArchiver.m */; }; + 01B9057E2416C52B00E2A663 /* ApptentiveURLOpener.h in Headers */ = {isa = PBXBuildFile; fileRef = 01B9057C2416C52B00E2A663 /* ApptentiveURLOpener.h */; }; + 01B9057F2416C52B00E2A663 /* ApptentiveURLOpener.m in Sources */ = {isa = PBXBuildFile; fileRef = 01B9057D2416C52B00E2A663 /* ApptentiveURLOpener.m */; }; 01E04F9E1E819CD300D7E849 /* ApptentiveMessageManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 01E04F9C1E819CD300D7E849 /* ApptentiveMessageManager.h */; }; 01E04F9F1E819CD300D7E849 /* ApptentiveMessageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 01E04F9D1E819CD300D7E849 /* ApptentiveMessageManager.m */; }; 01E8E2361E6E096D00786738 /* ApptentiveInteractionAppleRatingDialogController.h in Headers */ = {isa = PBXBuildFile; fileRef = 01E8E2341E6E096D00786738 /* ApptentiveInteractionAppleRatingDialogController.h */; }; @@ -440,6 +448,9 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 0100626824198DDD00A6C89B /* manifest-v2.archive */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "manifest-v2.archive"; sourceTree = ""; }; + 0100626924198DDD00A6C89B /* conversation-v1.meta */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "conversation-v1.meta"; sourceTree = ""; }; + 01006270241995FF00A6C89B /* configuration-v1.archive */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "configuration-v1.archive"; sourceTree = ""; }; 010FE33A203E2D900021C246 /* CriteriaDescriptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CriteriaDescriptionTests.swift; sourceTree = ""; }; 010FE344203E4C810021C246 /* ApptentiveIndentPrinter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ApptentiveIndentPrinter.h; sourceTree = ""; }; 010FE345203E4C810021C246 /* ApptentiveIndentPrinter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ApptentiveIndentPrinter.m; sourceTree = ""; }; @@ -819,6 +830,12 @@ 01A50EC31FC5FFB00058C06C /* ApptentiveInvocations.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ApptentiveInvocations.m; sourceTree = ""; }; 01AA89B61F18177E00FB59AB /* ApptentiveAppInstall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ApptentiveAppInstall.h; sourceTree = ""; }; 01AA89B71F18177E00FB59AB /* ApptentiveAppInstall.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ApptentiveAppInstall.m; sourceTree = ""; }; + 01B905552411C4C200E2A663 /* ApptentiveUnarchiver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ApptentiveUnarchiver.h; sourceTree = ""; }; + 01B905562411C4C200E2A663 /* ApptentiveUnarchiver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ApptentiveUnarchiver.m; sourceTree = ""; }; + 01B905592411C83600E2A663 /* ApptentiveArchiver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ApptentiveArchiver.h; sourceTree = ""; }; + 01B9055A2411C83600E2A663 /* ApptentiveArchiver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ApptentiveArchiver.m; sourceTree = ""; }; + 01B9057C2416C52B00E2A663 /* ApptentiveURLOpener.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ApptentiveURLOpener.h; sourceTree = ""; }; + 01B9057D2416C52B00E2A663 /* ApptentiveURLOpener.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ApptentiveURLOpener.m; sourceTree = ""; }; 01E04F9C1E819CD300D7E849 /* ApptentiveMessageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ApptentiveMessageManager.h; sourceTree = ""; }; 01E04F9D1E819CD300D7E849 /* ApptentiveMessageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ApptentiveMessageManager.m; sourceTree = ""; }; 01E8E2341E6E096D00786738 /* ApptentiveInteractionAppleRatingDialogController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ApptentiveInteractionAppleRatingDialogController.h; sourceTree = ""; }; @@ -1488,6 +1505,12 @@ EF4EAB94203F8B99003318C9 /* ApptentiveLogFileWriteTask.m */, EF4EAB9E2040A194003318C9 /* ApptentiveFileUtilities.h */, EF4EAB9F2040A194003318C9 /* ApptentiveFileUtilities.m */, + 01B905552411C4C200E2A663 /* ApptentiveUnarchiver.h */, + 01B905562411C4C200E2A663 /* ApptentiveUnarchiver.m */, + 01B905592411C83600E2A663 /* ApptentiveArchiver.h */, + 01B9055A2411C83600E2A663 /* ApptentiveArchiver.m */, + 01B9057C2416C52B00E2A663 /* ApptentiveURLOpener.h */, + 01B9057D2416C52B00E2A663 /* ApptentiveURLOpener.m */, ); path = Misc; sourceTree = ""; @@ -1634,6 +1657,9 @@ children = ( 018E1CE121936B6600E58F33 /* conversation-4.archive */, 018E1CE221936B6600E58F33 /* conversation-5.archive */, + 0100626924198DDD00A6C89B /* conversation-v1.meta */, + 0100626824198DDD00A6C89B /* manifest-v2.archive */, + 01006270241995FF00A6C89B /* configuration-v1.archive */, EFF4D2A51EC37F0A00FD4EFE /* containers */, 01A2D2161E4946D500C2103A /* criteria */, 01A2D2101E4946D500C2103A /* ATDataModelv1.sqlite */, @@ -1797,6 +1823,7 @@ EF8EE3AB1EBBA5D00033E7A1 /* ApptentiveJWT.h in Headers */, 01A2D1D81E490A9700C2103A /* ApptentiveDataManager.h in Headers */, 01A2D0FF1E490A9700C2103A /* ApptentiveNetworkImageIconView.h in Headers */, + 01B9055B2411C83600E2A663 /* ApptentiveArchiver.h in Headers */, 0174772C1EA92BED00A0A949 /* ApptentiveSurveyResponsePayload.h in Headers */, 01A2D1F01E490A9700C2103A /* ApptentiveSurveyGreetingView.h in Headers */, 01A2D17A1E490A9700C2103A /* ApptentiveAttachmentController.h in Headers */, @@ -1830,7 +1857,9 @@ 01A2D1A81E490A9700C2103A /* NSDictionary+Apptentive.h in Headers */, 01AA89B81F18177E00FB59AB /* ApptentiveAppInstall.h in Headers */, 01A2D1B21E490A9700C2103A /* ApptentiveLegacyEvent.h in Headers */, + 01B9057E2416C52B00E2A663 /* ApptentiveURLOpener.h in Headers */, 01A2D1941E490A9700C2103A /* ApptentiveMessageCenterMessageCell.h in Headers */, + 01B905572411C4C200E2A663 /* ApptentiveUnarchiver.h in Headers */, EFC97A9E1F56303300BCC461 /* UIAlertController+Apptentive.h in Headers */, 01F1DFF41E5BBFB3009AB3D2 /* ApptentiveConversationManager.h in Headers */, 0174772A1EA92BED00A0A949 /* ApptentivePayload.h in Headers */, @@ -1959,10 +1988,9 @@ }; buildConfigurationList = 01A2CF8B1E49062700C2103A /* Build configuration list for PBXProject "Apptentive" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( - English, en, ar, da, @@ -1983,6 +2011,7 @@ tr, "zh-Hans", "zh-Hant", + Base, ); mainGroup = 01A2CF871E49062700C2103A; productRefGroup = 01A2CF921E49062700C2103A /* Products */; @@ -2073,6 +2102,7 @@ 018E1CE421936B6600E58F33 /* conversation-5.archive in Resources */, 01A2D25B1E4946D600C2103A /* testOperatorContains.json in Resources */, 01A2D24E1E4946D600C2103A /* ATDataModelv3.sqlite in Resources */, + 0100626A24198DDE00A6C89B /* manifest-v2.archive in Resources */, 01A2D26A1E4946D600C2103A /* testJsonDiffing.1.new.json in Resources */, 01A2D2621E4946D600C2103A /* testOperatorNot.json in Resources */, 01A2D2601E4946D600C2103A /* testOperatorLessThan.json in Resources */, @@ -2080,10 +2110,12 @@ 01A2D2511E4946D600C2103A /* ATDataModelv6.sqlite in Resources */, 01A2D25D1E4946D600C2103A /* testOperatorExists.json in Resources */, 01A2D2561E4946D600C2103A /* testCornerCasesThatShouldBeTrue.json in Resources */, + 0100626B24198DDE00A6C89B /* conversation-v1.meta in Resources */, 01A2D2661E4946D600C2103A /* testWhitespaceTrimming.json in Resources */, 010FE34E2040C8810021C246 /* testOperatorStringNotEquals.json in Resources */, 01A2D2651E4946D600C2103A /* testV7Criteria.json in Resources */, 01A2D2611E4946D600C2103A /* testOperatorLessThanOrEqual.json in Resources */, + 01006271241995FF00A6C89B /* configuration-v1.archive in Resources */, 01A2D2571E4946D600C2103A /* testDefaultValues.json in Resources */, 01A2D2631E4946D600C2103A /* testOperatorStartsWith.json in Resources */, 01A2D25C1E4946D600C2103A /* testOperatorEndsWith.json in Resources */, @@ -2176,6 +2208,7 @@ 01A2D1F71E490A9700C2103A /* ApptentiveSurveyQuestionBackgroundView.m in Sources */, EFC97A9F1F56303300BCC461 /* UIAlertController+Apptentive.m in Sources */, 01A2D1D71E490A9700C2103A /* ApptentiveBackend.m in Sources */, + 01B9055C2411C83600E2A663 /* ApptentiveArchiver.m in Sources */, 01A2D1A91E490A9700C2103A /* NSDictionary+Apptentive.m in Sources */, 0140D60B1E83553C007B5130 /* ApptentiveLegacyFileAttachment.m in Sources */, 01A2D1951E490A9700C2103A /* ApptentiveMessageCenterMessageCell.m in Sources */, @@ -2221,6 +2254,7 @@ 01A2D1C01E490A9700C2103A /* ATDataModel v1 to v2.xcmappingmodel in Sources */, 01A2D1061E490A9700C2103A /* ApptentiveUnreadMessagesBadgeView.m in Sources */, 01A2D11A1E490A9700C2103A /* ApptentiveInteractionSurveyController.m in Sources */, + 01B905582411C4C200E2A663 /* ApptentiveUnarchiver.m in Sources */, 0134EB1B1EA9930200DA4925 /* ApptentiveDevicePayload.m in Sources */, 01A2D1241E490A9700C2103A /* ApptentiveCustomData.m in Sources */, 0178C56C1FC4A5A000DABF39 /* ApptentiveClause.m in Sources */, @@ -2239,6 +2273,7 @@ 01A2D1AB1E490A9700C2103A /* ApptentiveAppConfiguration.m in Sources */, 01A2D1D11E490A9700C2103A /* ApptentiveSerialRequest.m in Sources */, 0140D5FB1E833103007B5130 /* ApptentiveMessage.m in Sources */, + 01B9057F2416C52B00E2A663 /* ApptentiveURLOpener.m in Sources */, 01A2D1ED1E490A9700C2103A /* ApptentiveSurveyChoiceCell.m in Sources */, 018FAFE01FC4A9C6007C52FE /* ApptentiveAndClause.m in Sources */, 01A2D1FF1E490A9700C2103A /* ApptentiveSurveySubmitButton.m in Sources */, @@ -2271,7 +2306,6 @@ 018E1CE021936B1400E58F33 /* ApptentiveEngagementTests.swift in Sources */, 0174772F1EA92D7D00A0A949 /* PayloadTests.swift in Sources */, 01201AD31FC637BE00EB3593 /* CodePointAndInteractionTests.m in Sources */, - 01A2D2451E4946D600C2103A /* ApptentiveMigrationTests.m in Sources */, 017E54ED1F3B860E00EA9F81 /* ApptentiveJSONSerializationTests.m in Sources */, 01626EBC1EB7E5B90064E73F /* ApptentiveEncryptionTests.m in Sources */, 01A2D29A1E4963A500C2103A /* ATDataModel v2 to v3.xcmappingmodel in Sources */, @@ -2371,7 +2405,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 28; + CURRENT_PROJECT_VERSION = 37; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -2385,7 +2419,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.2; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "-ObjC"; @@ -2429,7 +2463,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 28; + CURRENT_PROJECT_VERSION = 37; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -2441,7 +2475,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.2; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; OTHER_LDFLAGS = "-ObjC"; SDKROOT = iphoneos; @@ -2461,14 +2495,12 @@ DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 86WML2UN43; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 28; + DYLIB_CURRENT_VERSION = 37; DYLIB_INSTALL_NAME_BASE = "@rpath"; GCC_PREFIX_HEADER = "Apptentive/Misc/ApptentiveConnect-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = "APPTENTIVE_DEBUG=1"; - GCC_TREAT_WARNINGS_AS_ERRORS = YES; INFOPLIST_FILE = Apptentive/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.apptentive.Apptentive; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2483,13 +2515,11 @@ DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 86WML2UN43; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 28; + DYLIB_CURRENT_VERSION = 37; DYLIB_INSTALL_NAME_BASE = "@rpath"; GCC_PREFIX_HEADER = "Apptentive/Misc/ApptentiveConnect-Prefix.pch"; - GCC_TREAT_WARNINGS_AS_ERRORS = YES; INFOPLIST_FILE = Apptentive/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.apptentive.Apptentive; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2504,7 +2534,6 @@ DEVELOPMENT_TEAM = 86WML2UN43; GCC_PREPROCESSOR_DEFINITIONS = "APPTENTIVE_DEBUG=1"; INFOPLIST_FILE = ApptentiveTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.apptentive.ApptentiveTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2520,7 +2549,6 @@ CLANG_ENABLE_MODULES = YES; DEVELOPMENT_TEAM = 86WML2UN43; INFOPLIST_FILE = ApptentiveTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.apptentive.ApptentiveTests; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Apptentive/Apptentive.xcodeproj/xcshareddata/xcschemes/Apptentive.xcscheme b/Apptentive/Apptentive.xcodeproj/xcshareddata/xcschemes/Apptentive.xcscheme index e081f214d..2d5e5f756 100644 --- a/Apptentive/Apptentive.xcodeproj/xcshareddata/xcschemes/Apptentive.xcscheme +++ b/Apptentive/Apptentive.xcodeproj/xcshareddata/xcschemes/Apptentive.xcscheme @@ -26,8 +26,17 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - codeCoverageEnabled = "YES" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> + + + + @@ -40,22 +49,13 @@ - - - - - - - - +@interface ApptentiveLegacyConversation : NSObject /** diff --git a/Apptentive/Apptentive/Engagement/Model/ApptentiveConversation.m b/Apptentive/Apptentive/Engagement/Model/ApptentiveConversation.m index 9db88b9af..9ff8508d5 100644 --- a/Apptentive/Apptentive/Engagement/Model/ApptentiveConversation.m +++ b/Apptentive/Apptentive/Engagement/Model/ApptentiveConversation.m @@ -15,6 +15,7 @@ #import "ApptentiveSDK.h" #import "ApptentiveUtilities.h" #import "ApptentiveVersion.h" +#import "ApptentiveUnarchiver.h" NS_ASSUME_NONNULL_BEGIN @@ -87,6 +88,10 @@ @interface ApptentiveConversation () @implementation ApptentiveConversation ++ (BOOL)supportsSecureCoding { + return YES; +} + - (instancetype)initWithState:(ApptentiveConversationState)state { self = [super init]; if (self) { @@ -122,8 +127,10 @@ - (nullable instancetype)initWithCoder:(NSCoder *)coder { _identifier = [coder decodeObjectOfClass:[NSString class] forKey:IdentifierKey]; _localIdentifier = [coder decodeObjectOfClass:[NSString class] forKey:LocalIdentifierKey] ?: [[NSUUID UUID] UUIDString]; _directoryName = [coder decodeObjectOfClass:[NSString class] forKey:DirectoryNameKey]; - _lastSentDevice = [coder decodeObjectOfClass:[NSDictionary class] forKey:LastSentDeviceKey]; - _lastSentPerson = [coder decodeObjectOfClass:[NSDictionary class] forKey:LastSentPersonKey]; + + NSSet *allowedClasses = [NSSet setWithArray:@[[NSDictionary class], [NSString class]]]; + _lastSentDevice = [coder decodeObjectOfClasses:allowedClasses forKey:LastSentDeviceKey]; + _lastSentPerson = [coder decodeObjectOfClasses:allowedClasses forKey:LastSentPersonKey]; } return self; } @@ -302,7 +309,7 @@ - (instancetype)initAndMigrate { [NSKeyedUnarchiver setClass:[ApptentiveLegacyConversation class] forClassName:@"ApptentiveConversation"]; [NSKeyedUnarchiver setClass:[ApptentiveLegacyConversation class] forClassName:@"ATConversation"]; - ApptentiveLegacyConversation *legacyConversation = (ApptentiveLegacyConversation *)[NSKeyedUnarchiver unarchiveObjectWithData:legacyConversationData]; + ApptentiveLegacyConversation *legacyConversation = [ApptentiveUnarchiver unarchivedObjectOfClass:[ApptentiveLegacyConversation class] fromData:legacyConversationData]; [NSKeyedUnarchiver setClass:[self class] forClassName:@"ApptentiveConversation"]; @@ -329,7 +336,8 @@ - (instancetype)initAndMigrate { NSData *lastSentPersondata = [[NSUserDefaults standardUserDefaults] dataForKey:ATPersonLastUpdateValuePreferenceKey]; if (lastSentPersondata != nil) { - NSDictionary *person = [NSKeyedUnarchiver unarchiveObjectWithData:lastSentPersondata]; + NSSet *allowedClasses = [NSSet setWithArray:@[[NSDictionary class], [NSString class]]]; + NSDictionary *person = [ApptentiveUnarchiver unarchivedObjectOfClasses:allowedClasses fromData:lastSentPersondata]; if ([person isKindOfClass:[NSDictionary class]]) { _lastSentPerson = person[@"person"]; } else { @@ -487,6 +495,10 @@ - (id)mutableCopy { @implementation ApptentiveLegacyConversation ++ (BOOL)supportsSecureCoding { + return YES; +} + + (void)load { [NSKeyedUnarchiver setClass:self forClassName:@"ATConversation"]; } @@ -495,9 +507,9 @@ - (nullable instancetype)initWithCoder:(NSCoder *)coder { self = [super init]; if (self) { - _token = [coder decodeObjectForKey:@"token"]; - _personID = [coder decodeObjectForKey:@"personID"]; - _deviceID = [coder decodeObjectForKey:@"deviceID"]; + _token = [coder decodeObjectOfClass:[NSString class] forKey:@"token"]; + _personID = [coder decodeObjectOfClass:[NSString class] forKey:@"personID"]; + _deviceID = [coder decodeObjectOfClass:[NSString class] forKey:@"deviceID"]; } return self; @@ -543,6 +555,14 @@ @implementation ApptentiveMutableConversation @dynamic directoryName; @dynamic sessionIdentifier; ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (nullable instancetype)initWithCoder:(NSCoder *)coder { + return [super initWithCoder:coder]; +} + - (void)setToken:(NSString *)token conversationID:(NSString *)conversationID personID:(NSString *)personID deviceID:(NSString *)deviceID { [self setConversationIdentifier:conversationID JWT:token]; self.person.identifier = personID; diff --git a/Apptentive/Apptentive/Engagement/Model/ApptentiveConversationManager.m b/Apptentive/Apptentive/Engagement/Model/ApptentiveConversationManager.m index f7b969d44..5c73f2914 100644 --- a/Apptentive/Apptentive/Engagement/Model/ApptentiveConversationManager.m +++ b/Apptentive/Apptentive/Engagement/Model/ApptentiveConversationManager.m @@ -38,6 +38,8 @@ #import "Apptentive_Private.h" #import "NSData+Encryption.h" #import "ApptentiveDispatchQueue.h" +#import "ApptentiveArchiver.h" +#import "ApptentiveUnarchiver.h" NS_ASSUME_NONNULL_BEGIN @@ -259,7 +261,7 @@ - (nullable ApptentiveConversation *)loadConversationFromMetadataItem:(Apptentiv ApptentiveLogVerbose(ApptentiveLogTagConversation, @"Conversation decrypted (took %g ms).", decryptionStopWatch.elapsedMilliseconds); } - ApptentiveConversation *conversation = [NSKeyedUnarchiver unarchiveObjectWithData:conversationData]; + ApptentiveConversation *conversation = [ApptentiveUnarchiver unarchivedObjectOfClass:[ApptentiveConversation class] fromData:conversationData]; if (conversation == nil) { ApptentiveLogError(ApptentiveLogTagConversation, @"Unable to load conversation from archive."); return nil; @@ -457,7 +459,7 @@ - (ApptentiveConversationMetadata *)resolveMetadata { ApptentiveConversationMetadata *metadata = nil; if ([ApptentiveFileUtilities fileExistsAtPath:metadataPath]) { - metadata = [NSKeyedUnarchiver unarchiveObjectWithFile:metadataPath]; + metadata = [ApptentiveUnarchiver unarchivedObjectOfClass:[ApptentiveConversationMetadata class] fromFile:metadataPath]; if (metadata) { // TODO: dispatch debug event return metadata; @@ -470,7 +472,7 @@ - (ApptentiveConversationMetadata *)resolveMetadata { } - (BOOL)saveMetadata { - return [NSKeyedArchiver archiveRootObject:self.conversationMetadata toFile:self.metadataPath]; + return [ApptentiveArchiver archiveRootObject:self.conversationMetadata toFile:self.metadataPath]; } #pragma mark - Login/Logout @@ -904,7 +906,7 @@ - (BOOL)saveConversation:(ApptentiveConversation *)conversation { return NO; } - NSData *conversationData = [NSKeyedArchiver archivedDataWithRootObject:conversation]; + NSData *conversationData = [ApptentiveArchiver archivedDataWithRootObject:conversation]; ApptentiveAssertNotNil(conversationData, @"Conversation data serialization failed"); @@ -955,7 +957,7 @@ - (void)loadEngagementManfiest { if ([[NSFileManager defaultManager] fileExistsAtPath:self.manifestPath]) { ApptentiveLogDebug(ApptentiveLogTagConversation, @"Loading cached engagment manifest from %@.", self.manifestPath); @try { - _manifest = [NSKeyedUnarchiver unarchiveObjectWithFile:self.manifestPath]; + _manifest = [ApptentiveUnarchiver unarchivedObjectOfClass:[ApptentiveEngagementManifest class] fromFile:self.manifestPath]; [self notifyEngagementManifestUpdate]; } @catch (NSException *exc) { @@ -984,7 +986,7 @@ - (void)notifyEngagementManifestUpdate { - (BOOL)saveManifest { ApptentiveAssertOperationQueue(self.operationQueue); - return [NSKeyedArchiver archiveRootObject:_manifest toFile:self.manifestPath]; + return [ApptentiveArchiver archiveRootObject:_manifest toFile:self.manifestPath]; } #pragma mark - Private @@ -1103,7 +1105,7 @@ - (void)setLocalEngagementManifestURL:(NSURL *)localEngagementManifestURL { _localEngagementManifestURL = localEngagementManifestURL; if (localEngagementManifestURL == nil) { - _manifest = [NSKeyedUnarchiver unarchiveObjectWithFile:self.manifestPath]; + _manifest = [ApptentiveUnarchiver unarchivedObjectOfClass:[ApptentiveEngagementManifest class] fromFile:self.manifestPath]; if ([self.manifest.expiry timeIntervalSinceNow] <= 0) { [self fetchEngagementManifest]; diff --git a/Apptentive/Apptentive/Engagement/Model/ApptentiveConversationMetadata.h b/Apptentive/Apptentive/Engagement/Model/ApptentiveConversationMetadata.h index b9d581fe4..26332e0b4 100644 --- a/Apptentive/Apptentive/Engagement/Model/ApptentiveConversationMetadata.h +++ b/Apptentive/Apptentive/Engagement/Model/ApptentiveConversationMetadata.h @@ -17,7 +17,7 @@ typedef BOOL (^ApptentiveConversationMetadataItemFilter)(ApptentiveConversationM @interface ApptentiveConversationMetadata : NSObject -@property (strong, nonatomic) NSMutableArray *items; +@property (readonly, strong, nonatomic) NSMutableArray *items; - (nullable ApptentiveConversationMetadataItem *)findItemFilter:(ApptentiveConversationMetadataItemFilter)filter; - (void)addItem:(ApptentiveConversationMetadataItem *)item; diff --git a/Apptentive/Apptentive/Engagement/Model/ApptentiveConversationMetadata.m b/Apptentive/Apptentive/Engagement/Model/ApptentiveConversationMetadata.m index 2382d6c53..4aa65d823 100644 --- a/Apptentive/Apptentive/Engagement/Model/ApptentiveConversationMetadata.m +++ b/Apptentive/Apptentive/Engagement/Model/ApptentiveConversationMetadata.m @@ -39,7 +39,8 @@ - (nullable instancetype)initWithCoder:(NSCoder *)coder { self = [super init]; if (self) { - _items = [coder decodeObjectOfClass:[NSMutableArray class] forKey:ItemsKey]; + NSSet *allowedClasses = [NSSet setWithArray:@[[NSMutableArray class], [ApptentiveConversationMetadataItem class]]]; + _items = [coder decodeObjectOfClasses:allowedClasses forKey:ItemsKey]; [self checkConsistency]; } diff --git a/Apptentive/Apptentive/Engagement/Model/ApptentiveCount.m b/Apptentive/Apptentive/Engagement/Model/ApptentiveCount.m index 42f64cdf3..38cc7956d 100644 --- a/Apptentive/Apptentive/Engagement/Model/ApptentiveCount.m +++ b/Apptentive/Apptentive/Engagement/Model/ApptentiveCount.m @@ -18,6 +18,10 @@ @implementation ApptentiveCount ++ (BOOL)supportsSecureCoding { + return YES; +} + - (instancetype)init { return [self initWithTotalCount:0 versionCount:0 buildCount:0 lastInvoked:nil]; } diff --git a/Apptentive/Apptentive/Engagement/Model/ApptentiveCustomData.m b/Apptentive/Apptentive/Engagement/Model/ApptentiveCustomData.m index 97ac22682..c2efe485b 100644 --- a/Apptentive/Apptentive/Engagement/Model/ApptentiveCustomData.m +++ b/Apptentive/Apptentive/Engagement/Model/ApptentiveCustomData.m @@ -47,7 +47,8 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { - _mutableCustomData = [aDecoder decodeObjectOfClass:[NSMutableDictionary class] forKey:CustomDataKey]; + NSSet *allowedClasses = [NSSet setWithArray:@[[NSDictionary class], [NSString class]]]; + _mutableCustomData = [aDecoder decodeObjectOfClasses:allowedClasses forKey:CustomDataKey]; _identifier = [aDecoder decodeObjectOfClass:[NSString class] forKey:IdentifierKey]; } diff --git a/Apptentive/Apptentive/Engagement/Model/ApptentiveDevice.m b/Apptentive/Apptentive/Engagement/Model/ApptentiveDevice.m index 05eead3fc..29845d4e5 100644 --- a/Apptentive/Apptentive/Engagement/Model/ApptentiveDevice.m +++ b/Apptentive/Apptentive/Engagement/Model/ApptentiveDevice.m @@ -51,6 +51,10 @@ @implementation ApptentiveDevice ++ (BOOL)supportsSecureCoding { + return YES; +} + + (void)setIntegrationConfiguration:(NSDictionary *)integrationConfiguration { _currentIntegrationConfiguration = integrationConfiguration; } @@ -174,7 +178,9 @@ - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { _localeCountryCode = [aDecoder decodeObjectOfClass:[NSString class] forKey:LocaleCountryCodeKey]; _localeLanguageCode = [aDecoder decodeObjectOfClass:[NSString class] forKey:LocaleLanguageCodeKey]; _UTCOffset = [aDecoder decodeIntegerForKey:UTCOffsetKey]; - _integrationConfiguration = [aDecoder decodeObjectOfClass:[NSDictionary class] forKey:IntegrationConfigurationKey]; + + NSSet *allowedClasses = [NSSet setWithArray:@[[NSDictionary class], [NSString class]]]; + _integrationConfiguration = [aDecoder decodeObjectOfClasses:allowedClasses forKey:IntegrationConfigurationKey]; _advertisingIdentifier = [aDecoder decodeObjectOfClass:[NSUUID class] forKey:AdvertisingIdentifierKey]; } diff --git a/Apptentive/Apptentive/Engagement/Model/ApptentiveEngagement.m b/Apptentive/Apptentive/Engagement/Model/ApptentiveEngagement.m index da556b5c6..463557759 100644 --- a/Apptentive/Apptentive/Engagement/Model/ApptentiveEngagement.m +++ b/Apptentive/Apptentive/Engagement/Model/ApptentiveEngagement.m @@ -37,6 +37,10 @@ @interface ApptentiveEngagement () @implementation ApptentiveEngagement ++ (BOOL)supportsSecureCoding { + return YES; +} + - (instancetype)init { self = [super init]; if (self) { @@ -50,8 +54,10 @@ - (instancetype)init { - (nullable instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { - _mutableInteractions = [coder decodeObjectOfClass:[NSMutableDictionary class] forKey:InteractionsKey]; - _mutableCodePoints = [coder decodeObjectOfClass:[NSMutableDictionary class] forKey:CodePointsKey]; + NSSet *classes = [NSSet setWithArray:@[[NSMutableDictionary class], [ApptentiveCount class]]]; + + _mutableInteractions = [coder decodeObjectOfClasses:classes forKey:InteractionsKey]; + _mutableCodePoints = [coder decodeObjectOfClasses:classes forKey:CodePointsKey]; if ([coder containsValueForKey:VersionKey]) { _version = [coder decodeIntegerForKey:VersionKey]; } else { diff --git a/Apptentive/Apptentive/Engagement/Model/ApptentiveEngagementManifest.m b/Apptentive/Apptentive/Engagement/Model/ApptentiveEngagementManifest.m index d98a46837..a931f93b1 100644 --- a/Apptentive/Apptentive/Engagement/Model/ApptentiveEngagementManifest.m +++ b/Apptentive/Apptentive/Engagement/Model/ApptentiveEngagementManifest.m @@ -9,6 +9,8 @@ #import "ApptentiveEngagementManifest.h" #import "ApptentiveInteraction.h" #import "ApptentiveTargets.h" +#import "ApptentiveUnarchiver.h" +#import "ApptentiveInvocations.h" NS_ASSUME_NONNULL_BEGIN @@ -73,31 +75,9 @@ - (instancetype)initWithJSONDictionary:(NSDictionary *)JSONDictionary cacheLifet } - (instancetype)initWithCachePath:(NSString *)cachePath userDefaults:(NSUserDefaults *)userDefaults { - self = [super init]; - - if (self) { - _expiry = [userDefaults objectForKey:ATEngagementCachedInteractionsExpirationPreferenceKey]; - - NSString *cachedTargetsPath = [cachePath stringByAppendingPathComponent:@"cachedtargets.objects"]; - if ([[NSFileManager defaultManager] fileExistsAtPath:cachedTargetsPath]) { - @try { - _targets = [NSKeyedUnarchiver unarchiveObjectWithFile:cachedTargetsPath]; - } @catch (NSException *exception) { - ApptentiveLogWarning(ApptentiveLogTagConversation, @"Unable to unarchive cached targets at path %@ (%@)", cachedTargetsPath, exception); - } - } - - NSString *cachedInteractionsPath = [cachePath stringByAppendingPathComponent:@"cachedinteractionsV2.objects"]; - if ([[NSFileManager defaultManager] fileExistsAtPath:cachedInteractionsPath]) { - @try { - _interactions = [NSKeyedUnarchiver unarchiveObjectWithFile:cachedInteractionsPath]; - } @catch (NSException *exception) { - ApptentiveLogWarning(ApptentiveLogTagConversation, @"Unable to unarchive cached interactions at path %@ (%@)", cachedInteractionsPath, exception); - } - } - } - - return self; + // Don't try to migrate legacy engagement manifests here, rather just download a new one. + // (it's complicated and error prone and hard to test and not really valuable). + return [self init]; } + (void)deleteMigratedDataFromCachePath:(NSString *)cachePath { @@ -122,7 +102,8 @@ - (nullable instancetype)initWithCoder:(NSCoder *)coder { if (self) { _targets = [coder decodeObjectOfClass:[ApptentiveTargets class] forKey:TargetsKey]; - _interactions = [coder decodeObjectOfClass:[NSDictionary class] forKey:InteractionsKey]; + NSSet *allowedClasses = [NSSet setWithArray:@[[NSDictionary class], [ApptentiveInteraction class]]]; + _interactions = [coder decodeObjectOfClasses:allowedClasses forKey:InteractionsKey]; _expiry = [coder decodeObjectOfClass:[NSDate class] forKey:ExpiryKey]; } diff --git a/Apptentive/Apptentive/Engagement/Model/ApptentiveInteraction.h b/Apptentive/Apptentive/Engagement/Model/ApptentiveInteraction.h index d7a09552a..26a49bfe1 100644 --- a/Apptentive/Apptentive/Engagement/Model/ApptentiveInteraction.h +++ b/Apptentive/Apptentive/Engagement/Model/ApptentiveInteraction.h @@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN @class ApptentiveInteractionUsageData; -@interface ApptentiveInteraction : NSObject +@interface ApptentiveInteraction : NSObject @property (copy, nonatomic) NSString *identifier; @property (assign, nonatomic) NSInteger priority; @property (copy, nonatomic) NSString *type; diff --git a/Apptentive/Apptentive/Engagement/Model/ApptentiveInteraction.m b/Apptentive/Apptentive/Engagement/Model/ApptentiveInteraction.m index cf75360dd..2ca7a8981 100644 --- a/Apptentive/Apptentive/Engagement/Model/ApptentiveInteraction.m +++ b/Apptentive/Apptentive/Engagement/Model/ApptentiveInteraction.m @@ -17,6 +17,10 @@ @implementation ApptentiveInteraction ++ (BOOL)supportsSecureCoding { + return YES; +} + + (void)load { [NSKeyedUnarchiver setClass:self forClassName:@"ATInteraction"]; } @@ -61,11 +65,12 @@ - (NSString *)description { - (nullable instancetype)initWithCoder:(NSCoder *)coder { if ((self = [super init])) { - self.identifier = [coder decodeObjectForKey:@"identifier"]; + self.identifier = [coder decodeObjectOfClass:[NSString class] forKey:@"identifier"]; self.priority = [coder decodeIntegerForKey:@"priority"]; - self.type = [coder decodeObjectForKey:@"type"]; - self.configuration = [coder decodeObjectForKey:@"configuration"]; - self.version = [coder decodeObjectForKey:@"version"]; + self.type = [coder decodeObjectOfClass:[NSString class] forKey:@"type"]; + NSSet *allowedClasses = [NSSet setWithArray:@[[NSDictionary class], [NSString class], [NSArray class]]]; + self.configuration = [coder decodeObjectOfClasses:allowedClasses forKey:@"configuration"]; + self.version = [coder decodeObjectOfClass:[NSString class] forKey:@"version"]; } return self; } diff --git a/Apptentive/Apptentive/Engagement/Model/ApptentivePerson.h b/Apptentive/Apptentive/Engagement/Model/ApptentivePerson.h index be594a8a6..2ab5c4edd 100644 --- a/Apptentive/Apptentive/Engagement/Model/ApptentivePerson.h +++ b/Apptentive/Apptentive/Engagement/Model/ApptentivePerson.h @@ -35,7 +35,7 @@ extern NSString *const ATPersonLastUpdateValuePreferenceKey; @end -@interface ApptentiveLegacyPerson : NSObject +@interface ApptentiveLegacyPerson : NSObject @property (copy, nonatomic) NSString *name; @property (copy, nonatomic) NSString *emailAddress; diff --git a/Apptentive/Apptentive/Engagement/Model/ApptentivePerson.m b/Apptentive/Apptentive/Engagement/Model/ApptentivePerson.m index deb2352a7..de20c03e4 100644 --- a/Apptentive/Apptentive/Engagement/Model/ApptentivePerson.m +++ b/Apptentive/Apptentive/Engagement/Model/ApptentivePerson.m @@ -7,6 +7,7 @@ // #import "ApptentivePerson.h" +#import "ApptentiveUnarchiver.h" NS_ASSUME_NONNULL_BEGIN @@ -22,6 +23,10 @@ @implementation ApptentivePerson ++ (BOOL)supportsSecureCoding { + return YES; +} + - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; @@ -52,7 +57,7 @@ - (instancetype)initAndMigrate { [NSKeyedUnarchiver setClass:[ApptentiveLegacyPerson class] forClassName:@"ApptentivePersonInfo"]; [NSKeyedUnarchiver setClass:[ApptentiveLegacyPerson class] forClassName:@"ATPersonInfo"]; - ApptentiveLegacyPerson *person = [NSKeyedUnarchiver unarchiveObjectWithData:personData]; + ApptentiveLegacyPerson *person = [ApptentiveUnarchiver unarchivedObjectOfClass:[ApptentiveLegacyPerson class] fromData:personData]; name = person.name; emailAddress = person.emailAddress; @@ -65,7 +70,8 @@ - (instancetype)initAndMigrate { NSData *data = [[NSUserDefaults standardUserDefaults] dataForKey:ATPersonLastUpdateValuePreferenceKey]; if (data) { - NSDictionary *person = [[NSKeyedUnarchiver unarchiveObjectWithData:data] valueForKey:@"person"]; + NSSet *allowedClasses = [NSSet setWithArray:@[[NSDictionary class], [NSString class]]]; + NSDictionary *person = [[ApptentiveUnarchiver unarchivedObjectOfClasses:allowedClasses fromData:data] valueForKey:@"person"]; if ([person isKindOfClass:[NSDictionary class]]) { customData = person[@"custom_data"]; } @@ -111,17 +117,25 @@ + (NSDictionary *)JSONKeyPathMapping { @implementation ApptentiveLegacyPerson ++ (BOOL)supportsSecureCoding { + return YES; +} + - (nullable instancetype)initWithCoder:(NSCoder *)coder { self = [super init]; if (self) { - self.name = (NSString *)[coder decodeObjectForKey:@"name"]; - self.emailAddress = (NSString *)[coder decodeObjectForKey:@"emailAddress"]; + self.name = (NSString *)[coder decodeObjectOfClass:[NSString class] forKey:@"name"]; + self.emailAddress = (NSString *)[coder decodeObjectOfClass:[NSString class] forKey:@"emailAddress"]; } return self; } +- (void)encodeWithCoder:(NSCoder *)coder { + ApptentiveAssertFail(@"We only ever decode this object, not encode it."); +} + @end @implementation ApptentivePerson (Criteria) diff --git a/Apptentive/Apptentive/Engagement/Model/ApptentiveSDK.m b/Apptentive/Apptentive/Engagement/Model/ApptentiveSDK.m index 5e4ba3fa8..3886609c8 100644 --- a/Apptentive/Apptentive/Engagement/Model/ApptentiveSDK.m +++ b/Apptentive/Apptentive/Engagement/Model/ApptentiveSDK.m @@ -30,6 +30,10 @@ @implementation ApptentiveSDK ++ (BOOL)supportsSecureCoding { + return YES; +} + + (ApptentiveVersion *)SDKVersion { return [[ApptentiveVersion alloc] initWithString:kApptentiveVersionString]; } diff --git a/Apptentive/Apptentive/Engagement/Model/ApptentiveVersion.m b/Apptentive/Apptentive/Engagement/Model/ApptentiveVersion.m index 01d95b3ff..399fa07b2 100644 --- a/Apptentive/Apptentive/Engagement/Model/ApptentiveVersion.m +++ b/Apptentive/Apptentive/Engagement/Model/ApptentiveVersion.m @@ -18,6 +18,10 @@ @implementation ApptentiveVersion ++ (BOOL)supportsSecureCoding { + return YES; +} + - (instancetype)initWithString:(NSString *)versionString { self = [super init]; diff --git a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveAndClause.m b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveAndClause.m index 271379874..0742b416c 100644 --- a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveAndClause.m +++ b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveAndClause.m @@ -124,7 +124,7 @@ - (nullable instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { - _subClauses = [coder decodeObjectForKey:SubClausesKey]; + _subClauses = [coder decodeObjectOfClasses:[[self class] decodingClasses] forKey:SubClausesKey]; } return self; } diff --git a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveClause.h b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveClause.h index c7060f004..e43034dd5 100644 --- a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveClause.h +++ b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveClause.h @@ -15,6 +15,8 @@ NS_ASSUME_NONNULL_BEGIN @interface ApptentiveClause : NSObject ++ (NSSet *)decodingClasses; + - (BOOL)criteriaMetForConversation:(ApptentiveConversation *)conversation; - (BOOL)criteriaMetForConversation:(ApptentiveConversation *)conversation indentPrinter:(ApptentiveIndentPrinter *)indentPrinter; diff --git a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveClause.m b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveClause.m index 5b7fef6e2..592e3ddbc 100644 --- a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveClause.m +++ b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveClause.m @@ -12,6 +12,7 @@ #import "ApptentiveOrClause.h" #import "ApptentiveNotClause.h" #import "ApptentiveIndentPrinter.h" +#import "ApptentiveComparisonClause.h" NS_ASSUME_NONNULL_BEGIN @@ -36,6 +37,24 @@ + (BOOL)supportsSecureCoding { return YES; } ++ (NSSet *)decodingClasses { + static NSSet *_decodingClasses = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _decodingClasses = [NSSet setWithArray:@[ + [NSArray class], + [ApptentiveAndClause class], + [ApptentiveOrClause class], + [ApptentiveNotClause class], + [ApptentiveComparisonClause class], + [ApptentiveFalseClause class] + ]]; + }); + + return _decodingClasses; +} + - (nullable instancetype)initWithCoder:(NSCoder *)coder { return [super init]; diff --git a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveComparisonClause.m b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveComparisonClause.m index 6d2e66a49..7b8daba8c 100644 --- a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveComparisonClause.m +++ b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveComparisonClause.m @@ -148,7 +148,8 @@ - (nullable instancetype)initWithCoder:(NSCoder *)coder self = [super init]; if (self) { _field = [coder decodeObjectOfClass:[NSString class] forKey:FieldKey]; - _comparisons = [coder decodeObjectOfClass:[NSDictionary class] forKey:ComparisonsKey]; + NSSet *allowedClasses = [NSSet setWithArray:@[[NSDictionary class], [NSString class]]]; + _comparisons = [coder decodeObjectOfClasses:allowedClasses forKey:ComparisonsKey]; } return self; } diff --git a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveInvocations.m b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveInvocations.m index 590e410c8..d9423b2bf 100644 --- a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveInvocations.m +++ b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveInvocations.m @@ -9,6 +9,11 @@ #import "ApptentiveInvocations.h" #import "ApptentiveTarget.h" #import "ApptentiveClause.h" +#import "ApptentiveAndClause.h" +#import "ApptentiveOrClause.h" +#import "ApptentiveNotClause.h" +#import "ApptentiveComparisonClause.h" +#import "ApptentiveFalseClause.h" #import "ApptentiveIndentPrinter.h" NS_ASSUME_NONNULL_BEGIN @@ -45,7 +50,11 @@ - (nullable instancetype)initWithCoder:(NSCoder *)coder { self = [super init]; if (self) { - _targets = [coder decodeObjectOfClass:[NSArray class] forKey:TargetsKey]; + NSSet *allowedClasses = [NSSet setWithArray:@[ + [NSArray class], + [ApptentiveTarget class] + ]]; + _targets = [coder decodeObjectOfClasses:allowedClasses forKey:TargetsKey]; } return self; } diff --git a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveNotClause.m b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveNotClause.m index c8d9de97c..7f1bdeb6e 100644 --- a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveNotClause.m +++ b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveNotClause.m @@ -7,8 +7,8 @@ // #import "ApptentiveNotClause.h" -#import "ApptentiveFalseClause.h" #import "ApptentiveAndClause.h" +#import "ApptentiveFalseClause.h" #import "ApptentiveIndentPrinter.h" NS_ASSUME_NONNULL_BEGIN @@ -63,7 +63,7 @@ - (nullable instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { - _subClause = [coder decodeObjectForKey:SubClauseKey]; + _subClause = [coder decodeObjectOfClasses:[[self class] decodingClasses] forKey:SubClauseKey]; } return self; } diff --git a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveOrClause.m b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveOrClause.m index fee857c8c..317bb0872 100644 --- a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveOrClause.m +++ b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveOrClause.m @@ -85,7 +85,7 @@ - (nullable instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { - _subClauses = [coder decodeObjectForKey:SubClausesKey]; + _subClauses = [coder decodeObjectOfClasses:[[self class] decodingClasses] forKey:SubClausesKey]; } return self; } diff --git a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveTarget.m b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveTarget.m index 3ebb242af..76bf3d8a1 100644 --- a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveTarget.m +++ b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveTarget.m @@ -50,7 +50,7 @@ - (nullable instancetype)initWithCoder:(NSCoder *)coder self = [super init]; if (self) { _interactionIdentifier = [coder decodeObjectOfClass:[NSString class] forKey:InteractionIdentifierKey]; - _criteria = [coder decodeObjectForKey:CriteriaKey]; + _criteria = [coder decodeObjectOfClasses:[ApptentiveClause decodingClasses] forKey:CriteriaKey]; } return self; } diff --git a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveTargets.m b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveTargets.m index 5978c16d2..ec864016f 100644 --- a/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveTargets.m +++ b/Apptentive/Apptentive/Engagement/Model/Targeting/ApptentiveTargets.m @@ -49,7 +49,8 @@ - (nullable instancetype)initWithCoder:(NSCoder *)coder { self = [super init]; if (self) { - _invocations = [coder decodeObjectOfClass:[NSDictionary class] forKey:InvocationsKey]; + NSSet *allowedClasses = [NSSet setWithArray:@[[NSDictionary class], [ApptentiveInvocations class]]]; + _invocations = [coder decodeObjectOfClasses:allowedClasses forKey:InvocationsKey]; } return self; } diff --git a/Apptentive/Apptentive/Info.plist b/Apptentive/Apptentive/Info.plist index 597a5dfac..6aa5d60d2 100644 --- a/Apptentive/Apptentive/Info.plist +++ b/Apptentive/Apptentive/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 5.2.8 + 5.2.9 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/Apptentive/Apptentive/Message Center/Controllers/ApptentiveAboutViewController.m b/Apptentive/Apptentive/Message Center/Controllers/ApptentiveAboutViewController.m index b917f0968..e79b7471f 100644 --- a/Apptentive/Apptentive/Message Center/Controllers/ApptentiveAboutViewController.m +++ b/Apptentive/Apptentive/Message Center/Controllers/ApptentiveAboutViewController.m @@ -11,6 +11,7 @@ #import "ApptentiveUtilities.h" #import "Apptentive_Private.h" #import "ApptentiveInteraction.h" +#import "ApptentiveURLOpener.h" NS_ASSUME_NONNULL_BEGIN @@ -65,14 +66,14 @@ - (IBAction)learnMore:(id)sender { NSURLComponents *components = [NSURLComponents componentsWithString:@"http://www.apptentive.com/"]; components.queryItems = @[[[NSURLQueryItem alloc] initWithName:@"source" value:[NSBundle mainBundle].bundleIdentifier]]; - [[UIApplication sharedApplication] openURL:components.URL]; + [ApptentiveURLOpener openURL:components.URL completionHandler:nil]; } - (IBAction)showPrivacy:(id)sender { NSURLComponents *components = [NSURLComponents componentsWithString:@"http://www.apptentive.com/privacy/"]; components.queryItems = @[[[NSURLQueryItem alloc] initWithName:@"source" value:[NSBundle mainBundle].bundleIdentifier]]; - [[UIApplication sharedApplication] openURL:components.URL]; + [ApptentiveURLOpener openURL:components.URL completionHandler:nil]; } - (void)traitCollectionDidChange:(nullable UITraitCollection *)previousTraitCollection { diff --git a/Apptentive/Apptentive/Message Center/Controllers/ApptentiveAttachmentController.m b/Apptentive/Apptentive/Message Center/Controllers/ApptentiveAttachmentController.m index 12b3dc988..1b7c7da98 100644 --- a/Apptentive/Apptentive/Message Center/Controllers/ApptentiveAttachmentController.m +++ b/Apptentive/Apptentive/Message Center/Controllers/ApptentiveAttachmentController.m @@ -14,6 +14,8 @@ #import "ApptentiveMessageCenterViewController.h" #import "Apptentive_Private.h" #import "ApptentiveBackend+Engagement.h" +#import "ApptentiveArchiver.h" +#import "ApptentiveUnarchiver.h" NS_ASSUME_NONNULL_BEGIN @@ -70,7 +72,8 @@ - (void)viewDidLoad { layout.itemSize = [ApptentiveAttachmentCell sizeForScreen:[UIScreen mainScreen] withMargin:marginWithInsets]; [self willChangeValueForKey:@"attachments"]; - self.mutableAttachments = [NSKeyedUnarchiver unarchiveObjectWithFile:self.archivePath]; + NSSet *allowedClasses = [NSSet setWithArray:@[[NSArray class], [UIImage class]]]; + self.mutableAttachments = [ApptentiveUnarchiver unarchivedObjectOfClasses:allowedClasses fromFile:self.archivePath]; if (![self.mutableAttachments isKindOfClass:[NSMutableArray class]]) { self.mutableAttachments = [NSMutableArray array]; @@ -86,7 +89,7 @@ - (void)viewDidLoad { } - (void)saveDraft { - [NSKeyedArchiver archiveRootObject:self.mutableAttachments toFile:self.archivePath]; + [ApptentiveArchiver archiveRootObject:self.mutableAttachments toFile:self.archivePath]; } - (nullable UIResponder *)nextResponder { diff --git a/Apptentive/Apptentive/Message Center/Controllers/ApptentiveMessageCenterViewController.m b/Apptentive/Apptentive/Message Center/Controllers/ApptentiveMessageCenterViewController.m index d03ca5d2e..065b52bf7 100644 --- a/Apptentive/Apptentive/Message Center/Controllers/ApptentiveMessageCenterViewController.m +++ b/Apptentive/Apptentive/Message Center/Controllers/ApptentiveMessageCenterViewController.m @@ -465,20 +465,6 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)ce } } -- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath { - return YES; -} - -- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender { - return action == @selector(copy:); -} - -- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender { - if (indexPath) { - [[UIPasteboard generalPasteboard] setValue:[self.viewModel textOfMessageAtIndexPath:indexPath] forPasteboardType:(__bridge NSString *)kUTTypeUTF8PlainText]; - } -} - #pragma mark Scroll view delegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView { @@ -1109,7 +1095,7 @@ - (void)scrollToFooterView:(nullable NSNotification *)notification { CGFloat heightOfVisibleView = fmin(CGRectGetMinY(localKeyboardRect), CGRectGetHeight(self.view.bounds) - toolbarHeight); -#ifdef __IPHONE_11_0 + CGFloat topInset; if (@available(iOS 11.0, *)) { CGFloat homeAreaHeight = self.tableView.safeAreaInsets.bottom - self.tableView.contentInset.bottom; @@ -1117,10 +1103,16 @@ - (void)scrollToFooterView:(nullable NSNotification *)notification { // If keyboard is hidden, save room for the home "button" heightOfVisibleView -= homeAreaHeight; } + + topInset = self.view.safeAreaInsets.top; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + topInset = self.topLayoutGuide.length; +#pragma clang diagnostic pop } -#endif - CGFloat verticalOffsetMaximum = fmax(self.topLayoutGuide.length * -1, self.tableView.contentSize.height - heightOfVisibleView); + CGFloat verticalOffsetMaximum = fmax(topInset * -1, self.tableView.contentSize.height - heightOfVisibleView); verticalOffset = fmin(verticalOffset, verticalOffsetMaximum); CGPoint contentOffset = CGPointMake(0, verticalOffset); @@ -1144,12 +1136,10 @@ - (void)resizeFooterView:(nullable NSNotification *)notification { CGFloat topContentInset = self.tableView.contentInset.top; CGFloat homeAreaInset = 0; -#ifdef __IPHONE_11_0 if (@available(iOS 11.0, *)) { topContentInset = fmax(self.tableView.layoutMargins.top, self.tableView.safeAreaInsets.top); homeAreaInset = fmax(0, self.tableView.safeAreaInsets.bottom - self.tableView.contentInset.bottom); } -#endif // Available space is between the top of the keyboard and the bottom of the navigation bar height = fmin(CGRectGetMinY(localKeyboardRect), CGRectGetHeight(self.view.bounds)) - topContentInset; diff --git a/Apptentive/Apptentive/Message Center/Model/ApptentiveMessage.m b/Apptentive/Apptentive/Message Center/Model/ApptentiveMessage.m index a9864a861..f2aedd6c4 100644 --- a/Apptentive/Apptentive/Message Center/Model/ApptentiveMessage.m +++ b/Apptentive/Apptentive/Message Center/Model/ApptentiveMessage.m @@ -126,12 +126,17 @@ - (nullable instancetype)initWithCoder:(NSCoder *)coder { _identifier = [coder decodeObjectOfClass:[NSString class] forKey:IdentifierKey]; _localIdentifier = [coder decodeObjectOfClass:[NSString class] forKey:LocalIdentifierKey]; _sentDate = [coder decodeObjectOfClass:[NSDate class] forKey:SentDateKey]; - _attachments = [coder decodeObjectOfClass:[ApptentiveAttachment class] forKey:AttachmentsKey]; + + NSSet *allowedAttachmentClasses = [NSSet setWithArray:@[[NSArray class], [ApptentiveAttachment class]]]; + _attachments = [coder decodeObjectOfClasses:allowedAttachmentClasses forKey:AttachmentsKey]; + _sender = [coder decodeObjectOfClass:[ApptentiveMessageSender class] forKey:SenderKey]; _body = [coder decodeObjectOfClass:[NSString class] forKey:BodyKey]; _state = [coder decodeIntegerForKey:StateKey]; _automated = [coder decodeBoolForKey:AutomatedKey]; - _customData = [coder decodeObjectOfClass:[NSDictionary class] forKey:CustomDataKey]; + + NSSet *allowedCustomDataClasses = [NSSet setWithArray:@[[NSDictionary class], [NSString class]]]; + _customData = [coder decodeObjectOfClasses:allowedCustomDataClasses forKey:CustomDataKey]; _inbound = [coder decodeBoolForKey:InboundKey]; } return self; diff --git a/Apptentive/Apptentive/Message Center/Model/ApptentiveMessageManager.m b/Apptentive/Apptentive/Message Center/Model/ApptentiveMessageManager.m index 6d6485345..af0f6e8ab 100644 --- a/Apptentive/Apptentive/Message Center/Model/ApptentiveMessageManager.m +++ b/Apptentive/Apptentive/Message Center/Model/ApptentiveMessageManager.m @@ -20,6 +20,8 @@ #import "ApptentiveUtilities.h" #import "Apptentive_Private.h" #import "ApptentiveDispatchQueue.h" +#import "ApptentiveUnarchiver.h" +#import "ApptentiveArchiver.h" NS_ASSUME_NONNULL_BEGIN @@ -65,7 +67,7 @@ - (instancetype)initWithStoragePath:(NSString *)storagePath client:(ApptentiveCl _operationQueue = operationQueue; _messageIdentifierIndex = [NSMutableDictionary dictionary]; - _messageStore = [NSKeyedUnarchiver unarchiveObjectWithFile:self.messageStorePath] ?: [[ApptentiveMessageStore alloc] init]; + _messageStore = [ApptentiveUnarchiver unarchivedObjectOfClass:[ApptentiveMessageStore class] fromFile:self.messageStorePath] ?: [[ApptentiveMessageStore alloc] init]; _didSkipProfile = [conversation.userInfo[ATMessageCenterDidSkipProfileKey] boolValue]; _draftMessage = conversation.userInfo[ATMessageCenterDraftMessageKey]; @@ -147,7 +149,7 @@ - (NSString *)messageStorePath { } - (BOOL)saveMessageStore { - return [NSKeyedArchiver archiveRootObject:self.messageStore toFile:self.messageStorePath]; + return [ApptentiveArchiver archiveRootObject:self.messageStore toFile:self.messageStorePath]; } - (NSString *)attachmentDirectoryPath { diff --git a/Apptentive/Apptentive/Message Center/Model/ApptentiveMessageStore.m b/Apptentive/Apptentive/Message Center/Model/ApptentiveMessageStore.m index 58c179e63..426d9a293 100644 --- a/Apptentive/Apptentive/Message Center/Model/ApptentiveMessageStore.m +++ b/Apptentive/Apptentive/Message Center/Model/ApptentiveMessageStore.m @@ -7,6 +7,7 @@ // #import "ApptentiveMessageStore.h" +#import "ApptentiveMessage.h" NS_ASSUME_NONNULL_BEGIN @@ -32,7 +33,8 @@ - (instancetype)init { - (nullable instancetype)initWithCoder:(NSCoder *)coder { self = [super init]; if (self) { - _messages = [coder decodeObjectOfClass:[NSMutableArray class] forKey:MessagesKey]; + NSSet *allowedClasses = [NSSet setWithArray:@[[NSArray class], [ApptentiveMessage class]]]; + _messages = [coder decodeObjectOfClasses:allowedClasses forKey:MessagesKey]; _lastMessageIdentifier = [coder decodeObjectOfClass:[NSString class] forKey:LastMessageIdentifierKey]; } return self; diff --git a/Apptentive/Apptentive/Misc/ApptentiveArchiver.h b/Apptentive/Apptentive/Misc/ApptentiveArchiver.h new file mode 100644 index 000000000..1034999b5 --- /dev/null +++ b/Apptentive/Apptentive/Misc/ApptentiveArchiver.h @@ -0,0 +1,20 @@ +// +// ApptentiveArchiver.h +// Apptentive +// +// Created by Frank Schmitt on 3/5/20. +// Copyright © 2020 Apptentive, Inc. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ApptentiveArchiver : NSObject + ++ (BOOL)archiveRootObject:(NSObject *)rootObject toFile:(NSString *)path; ++ (NSData *)archivedDataWithRootObject:(NSObject *)rootObject; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apptentive/Apptentive/Misc/ApptentiveArchiver.m b/Apptentive/Apptentive/Misc/ApptentiveArchiver.m new file mode 100644 index 000000000..b8cb33e4a --- /dev/null +++ b/Apptentive/Apptentive/Misc/ApptentiveArchiver.m @@ -0,0 +1,44 @@ +// +// ApptentiveArchiver.m +// Apptentive +// +// Created by Frank Schmitt on 3/5/20. +// Copyright © 2020 Apptentive, Inc. All rights reserved. +// + +#import "ApptentiveArchiver.h" + +@implementation ApptentiveArchiver + ++ (BOOL)archiveRootObject:(NSObject *)rootObject toFile:(NSString *)path { + NSData *result = [self archivedDataWithRootObject:rootObject]; + + if (result != nil) { + return [result writeToFile:path atomically:YES]; + } else { + return NO; + } +} + ++ (NSData *)archivedDataWithRootObject:(NSObject *)rootObject { + NSError *error = nil; + NSData *result = nil; + + if (@available(iOS 11.0, *)) { + result = [NSKeyedArchiver archivedDataWithRootObject:rootObject requiringSecureCoding:YES error:&error]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + result = [NSKeyedArchiver archivedDataWithRootObject:rootObject]; +#pragma clang diagnostic pop + } + + if (result == nil) { + ApptentiveAssertFail(@"Unable to archive object: %@", error); + ApptentiveLogError(ApptentiveLogTagUtility, @"Unable to archive object: %@", error); + } + + return result; +} + +@end diff --git a/Apptentive/Apptentive/Misc/ApptentiveLogMonitorSession.h b/Apptentive/Apptentive/Misc/ApptentiveLogMonitorSession.h index d5bb7f332..b84ec0874 100644 --- a/Apptentive/Apptentive/Misc/ApptentiveLogMonitorSession.h +++ b/Apptentive/Apptentive/Misc/ApptentiveLogMonitorSession.h @@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN extern NSNotificationName const ApptentiveLogMonitorSessionDidStart; extern NSNotificationName const ApptentiveLogMonitorSessionDidStop; -@interface ApptentiveLogMonitorSession : NSObject +@interface ApptentiveLogMonitorSession : NSObject /** Email recipients for the log email */ @property (nonatomic, strong) NSArray *emailRecipients; diff --git a/Apptentive/Apptentive/Misc/ApptentiveLogMonitorSession.m b/Apptentive/Apptentive/Misc/ApptentiveLogMonitorSession.m index e8fdc3511..4b269d639 100644 --- a/Apptentive/Apptentive/Misc/ApptentiveLogMonitorSession.m +++ b/Apptentive/Apptentive/Misc/ApptentiveLogMonitorSession.m @@ -13,6 +13,8 @@ #import "ApptentiveFileUtilities.h" #import "ApptentiveJWT.h" #import "ApptentiveSafeCollections.h" +#import "ApptentiveArchiver.h" +#import "ApptentiveUnarchiver.h" NSNotificationName const ApptentiveLogMonitorSessionDidStart = @"ApptentiveLogMonitorSessionDidStart"; NSNotificationName const ApptentiveLogMonitorSessionDidStop = @"ApptentiveLogMonitorSessionDidStop"; @@ -23,7 +25,7 @@ extern NSString *ApptentiveLocalizedString(NSString *key, NSString *_Nullable comment); -@interface ApptentiveLogMonitorSession () +@interface ApptentiveLogMonitorSession () @property (nonatomic, strong) UIWindow *mailComposeControllerWindow; @property (nonatomic, assign) ApptentiveLogLevel oldLogLevel; @@ -32,6 +34,10 @@ @interface ApptentiveLogMonitorSession () + +NS_ASSUME_NONNULL_BEGIN + +@interface ApptentiveURLOpener : NSObject + ++ (void)openURL:(NSURL *)url completionHandler:(void (^ __nullable)(BOOL success))completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apptentive/Apptentive/Misc/ApptentiveURLOpener.m b/Apptentive/Apptentive/Misc/ApptentiveURLOpener.m new file mode 100644 index 000000000..6281554e4 --- /dev/null +++ b/Apptentive/Apptentive/Misc/ApptentiveURLOpener.m @@ -0,0 +1,27 @@ +// +// ApptentiveURLOpener.m +// Apptentive +// +// Created by Frank Schmitt on 3/9/20. +// Copyright © 2020 Apptentive, Inc. All rights reserved. +// + +#import "ApptentiveURLOpener.h" + +@implementation ApptentiveURLOpener + ++ (void)openURL:(NSURL *)url completionHandler:(void (^ __nullable)(BOOL success))completion { + if (@available(iOS 10.0, *)) { + [UIApplication.sharedApplication openURL:url options:@{} completionHandler:completion]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + BOOL result = [UIApplication.sharedApplication openURL:url]; +#pragma clang diagnostic pop + if (completion) { + completion(result); + } + } +} + +@end diff --git a/Apptentive/Apptentive/Misc/ApptentiveUnarchiver.h b/Apptentive/Apptentive/Misc/ApptentiveUnarchiver.h new file mode 100644 index 000000000..19be205cd --- /dev/null +++ b/Apptentive/Apptentive/Misc/ApptentiveUnarchiver.h @@ -0,0 +1,23 @@ +// +// ApptentiveUnarchiver.h +// Apptentive +// +// Created by Frank Schmitt on 3/5/20. +// Copyright © 2020 Apptentive, Inc. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ApptentiveUnarchiver : NSObject + ++ (id)unarchivedObjectOfClass:(Class)klass fromData:(NSData *)data; ++ (nullable id)unarchivedObjectOfClass:(Class)klass fromFile:(NSString *)path; + ++ (id)unarchivedObjectOfClasses:(NSSet*)classes fromData:(NSData *)data; ++ (nullable id)unarchivedObjectOfClasses:(NSSet*)classes fromFile:(NSString *)path; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Apptentive/Apptentive/Misc/ApptentiveUnarchiver.m b/Apptentive/Apptentive/Misc/ApptentiveUnarchiver.m new file mode 100644 index 000000000..50ec0be17 --- /dev/null +++ b/Apptentive/Apptentive/Misc/ApptentiveUnarchiver.m @@ -0,0 +1,71 @@ +// +// ApptentiveUnarchiver.m +// Apptentive +// +// Created by Frank Schmitt on 3/5/20. +// Copyright © 2020 Apptentive, Inc. All rights reserved. +// + +#import "ApptentiveUnarchiver.h" + +@implementation ApptentiveUnarchiver + ++ (id)unarchivedObjectOfClass:(Class)klass fromData:(NSData *)data { + return [self unarchivedObjectOfClasses:[NSSet setWithObject:klass] fromData:data]; +} + ++ (nullable id)unarchivedObjectOfClass:(Class)klass fromFile:(NSString *)path { + return [self unarchivedObjectOfClasses:[NSSet setWithObject:klass] fromFile:path]; +} + ++ (id)unarchivedObjectOfClasses:(NSSet*)classes fromData:(NSData *)data { + NSError *error = nil; + id result = nil; + + if (@available(iOS 11.0, *)) { + @try { + result = [NSKeyedUnarchiver unarchivedObjectOfClasses:classes fromData:data error:&error]; + } @catch (NSException *exception) { + ApptentiveAssertFail(@"Exception raised while unarchiving object: %@", exception); + ApptentiveLogCrit(ApptentiveLogTagUtility, @"Exception raised while unarchiving object: %@", exception); + return nil; + } + } else { + result = [self legacyUnarchiveObjectWithData:data]; + } + + if (result == nil) { + ApptentiveAssertFail(@"Unable to unarchive object: %@", error); + ApptentiveLogCrit(ApptentiveLogTagUtility, @"Unable to unarchive object: %@", error); + } + + return result; +} + ++ (id)legacyUnarchiveObjectWithData:(NSData *)data { + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + @try { + NSData *result = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + + return result; + } @catch (NSException *exception) { + ApptentiveAssertFail(@"Exception raised while unarchiving object: %@", exception); + ApptentiveLogCrit(ApptentiveLogTagUtility, @"Exception raised while unarchiving object: %@", exception); + return nil; + } + #pragma clang diagnostic pop +} + ++ (nullable id)unarchivedObjectOfClasses:(NSSet *)classes fromFile:(nonnull NSString *)path { + NSData *data = [NSData dataWithContentsOfFile:path]; + + if (data) { + return [self unarchivedObjectOfClasses:classes fromData:data]; + } else { + ApptentiveLogWarning(@"File %@ was not found", path); + return nil; + } +} + +@end diff --git a/Apptentive/Apptentive/Model/ApptentiveLegacyEvent.m b/Apptentive/Apptentive/Model/ApptentiveLegacyEvent.m index 5872a834a..53d435562 100644 --- a/Apptentive/Apptentive/Model/ApptentiveLegacyEvent.m +++ b/Apptentive/Apptentive/Model/ApptentiveLegacyEvent.m @@ -12,6 +12,8 @@ #import "ApptentiveLegacyEvent.h" #import "ApptentiveSerialRequest.h" #import "Apptentive_Private.h" +#import "ApptentiveUnarchiver.h" +#import "ApptentiveArchiver.h" NS_ASSUME_NONNULL_BEGIN @@ -43,7 +45,8 @@ + (void)enqueueUnsentEventsInContext:(NSManagedObjectContext *)context forConver // Add custom data, interaction identifier, and extended data if (event.dictionaryData != nil) { - NSDictionary *dictionaryData = [NSKeyedUnarchiver unarchiveObjectWithData:event.dictionaryData]; + NSSet *allowedClasses = [NSSet setWithArray:@[[NSDictionary class], [NSArray class]]]; + NSDictionary *dictionaryData = [ApptentiveUnarchiver unarchivedObjectOfClasses:allowedClasses fromData:event.dictionaryData]; payload.customData = dictionaryData[@"custom_data"]; @@ -129,7 +132,8 @@ - (NSDictionary *)dictionaryForCurrentData { } else { NSDictionary *result = nil; @try { - result = [NSKeyedUnarchiver unarchiveObjectWithData:self.dictionaryData]; + NSSet *allowedClasses = [NSSet setWithArray:@[[NSDictionary class], [NSString class]]]; + result = [ApptentiveUnarchiver unarchivedObjectOfClasses:allowedClasses fromData:self.dictionaryData]; } @catch (NSException *exception) { ApptentiveLogError(ApptentiveLogTagInteractions, @"Unable to unarchive event: %@", exception); } @@ -141,7 +145,7 @@ - (NSData *)dataForDictionary:(NSDictionary *)dictionary { if (dictionary == nil) { return nil; } else { - return [NSKeyedArchiver archivedDataWithRootObject:dictionary]; + return [ApptentiveArchiver archivedDataWithRootObject:dictionary]; } } diff --git a/Apptentive/Apptentive/Model/ApptentiveLegacyMessage.m b/Apptentive/Apptentive/Model/ApptentiveLegacyMessage.m index 71e14da7a..638a415f6 100644 --- a/Apptentive/Apptentive/Model/ApptentiveLegacyMessage.m +++ b/Apptentive/Apptentive/Model/ApptentiveLegacyMessage.m @@ -18,6 +18,7 @@ #import "ApptentivePerson.h" #import "ApptentiveSerialRequest.h" #import "Apptentive_Private.h" +#import "ApptentiveUnarchiver.h" NS_ASSUME_NONNULL_BEGIN @@ -85,7 +86,8 @@ + (BOOL)enqueueUnsentMessagesInContext:(NSManagedObjectContext *)context forConv NSDictionary *customData = @{}; if (legacyMessage.customData) { - customData = [NSKeyedUnarchiver unarchiveObjectWithData:legacyMessage.customData]; + NSSet *allowedClasses = [NSSet setWithArray:@[[NSDictionary class], [NSString class]]]; + customData = [ApptentiveUnarchiver unarchivedObjectOfClasses:allowedClasses fromData:legacyMessage.customData]; }; ApptentiveMessage *message = [[ApptentiveMessage alloc] initWithBody:legacyMessage.body attachments:attachments automated:legacyMessage.automated.boolValue customData:customData creationDate:[NSDate dateWithTimeIntervalSince1970:legacyMessage.clientCreationTime.doubleValue]]; diff --git a/Apptentive/Apptentive/Model/ApptentiveLegacySurveyResponse.m b/Apptentive/Apptentive/Model/ApptentiveLegacySurveyResponse.m index 4bc2cbc6c..08068732e 100644 --- a/Apptentive/Apptentive/Model/ApptentiveLegacySurveyResponse.m +++ b/Apptentive/Apptentive/Model/ApptentiveLegacySurveyResponse.m @@ -11,6 +11,7 @@ #import "ApptentiveSerialRequest.h" #import "ApptentiveSurveyResponsePayload.h" #import "Apptentive_Private.h" +#import "ApptentiveUnarchiver.h" NS_ASSUME_NONNULL_BEGIN @@ -80,11 +81,8 @@ - (NSDictionary *)dictionaryForAnswers { return @{}; } else { NSDictionary *result = nil; - @try { - result = [NSKeyedUnarchiver unarchiveObjectWithData:self.answersData]; - } @catch (NSException *exception) { - ApptentiveLogError(ApptentiveLogTagInteractions, @"Unable to unarchive answers data: %@", exception); - } + NSSet *allowedClasses = [NSSet setWithArray:@[[NSDictionary class], [NSArray class], [NSString class]]]; + result = [ApptentiveUnarchiver unarchivedObjectOfClasses:allowedClasses fromData:self.answersData]; return result; } } diff --git a/Apptentive/Apptentive/Persistence/ApptentiveBackend.m b/Apptentive/Apptentive/Persistence/ApptentiveBackend.m index 367fef891..426e6c131 100644 --- a/Apptentive/Apptentive/Persistence/ApptentiveBackend.m +++ b/Apptentive/Apptentive/Persistence/ApptentiveBackend.m @@ -36,6 +36,8 @@ #import "ApptentiveLegacySurveyResponse.h" #import "ApptentiveApptimize.h" +#import "ApptentiveArchiver.h" +#import "ApptentiveUnarchiver.h" @import CoreTelephony; @@ -146,16 +148,30 @@ - (void)updateAndMonitorDeviceValues { __weak ApptentiveBackend *weakSelf = self; if ([CTTelephonyNetworkInfo class]) { _telephonyNetworkInfo = [[CTTelephonyNetworkInfo alloc] init]; - ApptentiveDevice.carrierName = _telephonyNetworkInfo.subscriberCellularProvider.carrierName; - - _telephonyNetworkInfo.subscriberCellularProviderDidUpdateNotifier = ^(CTCarrier *_Nonnull carrier) { - ApptentiveBackend *strongSelf = weakSelf; - ApptentiveDevice.carrierName = carrier.carrierName; - ApptentiveLogDebug(@"Carrier changed to %@. Updating device.", ApptentiveDevice.carrierName); - [strongSelf.operationQueue dispatchAsync:^{ - [strongSelf scheduleDeviceUpdate]; // Must happen on our queue - }]; - }; + if (@available(iOS 12.0, *)) { + ApptentiveDevice.carrierName = [[_telephonyNetworkInfo.serviceSubscriberCellularProviders.allValues valueForKeyPath:@"carrierName"] componentsJoinedByString:@"|"]; + _telephonyNetworkInfo.serviceSubscriberCellularProvidersDidUpdateNotifier = ^(NSString *_Nonnull carrier) { + ApptentiveBackend *strongSelf = weakSelf; + ApptentiveDevice.carrierName = strongSelf->_telephonyNetworkInfo.serviceSubscriberCellularProviders[carrier].carrierName; + ApptentiveLogDebug(@"Carrier changed to %@. Updating device.", ApptentiveDevice.carrierName); + [strongSelf.operationQueue dispatchAsync:^{ + [strongSelf scheduleDeviceUpdate]; // Must happen on our queue + }]; + }; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + ApptentiveDevice.carrierName = _telephonyNetworkInfo.subscriberCellularProvider.carrierName; + _telephonyNetworkInfo.subscriberCellularProviderDidUpdateNotifier = ^(CTCarrier *_Nonnull carrier) { + ApptentiveBackend *strongSelf = weakSelf; + ApptentiveDevice.carrierName = carrier.carrierName; + ApptentiveLogDebug(@"Carrier changed to %@. Updating device.", ApptentiveDevice.carrierName); + [strongSelf.operationQueue dispatchAsync:^{ + [strongSelf scheduleDeviceUpdate]; // Must happen on our queue + }]; + }; +#pragma clang diagnostic pop + } } ApptentiveDevice.contentSizeCategory = [UIApplication sharedApplication].preferredContentSizeCategory; @@ -350,7 +366,7 @@ - (void)setUpCoreData { - (void)loadConfiguration { ApptentiveAssertOperationQueue(self.operationQueue); if ([[NSFileManager defaultManager] fileExistsAtPath:[self configurationPath]]) { - self->_configuration = [NSKeyedUnarchiver unarchiveObjectWithFile:[self configurationPath]]; + self->_configuration = [ApptentiveUnarchiver unarchivedObjectOfClass:[ApptentiveAppConfiguration class] fromFile:[self configurationPath]]; } else if ([[NSUserDefaults standardUserDefaults] objectForKey:@"ATConfigurationSDKVersionKey"]) { self->_configuration = [[ApptentiveAppConfiguration alloc] initWithUserDefaults:[NSUserDefaults standardUserDefaults]]; if ([self saveConfiguration]) { @@ -443,7 +459,7 @@ - (void)processConfigurationResponse:(NSDictionary *)configurationResponse cache - (BOOL)saveConfiguration { ApptentiveAssertOperationQueue(self.operationQueue); - return [NSKeyedArchiver archiveRootObject:self.configuration toFile:[self configurationPath]]; + return [ApptentiveArchiver archiveRootObject:self.configuration toFile:[self configurationPath]]; } - (void)updateNetworkingForCurrentNetworkStatus { diff --git a/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyCollectionViewLayout.m b/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyCollectionViewLayout.m index 20db61627..ced23e4b7 100644 --- a/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyCollectionViewLayout.m +++ b/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyCollectionViewLayout.m @@ -32,12 +32,10 @@ - (CGSize)collectionViewContentSize { UIEdgeInsets contentInset = self.collectionView.contentInset; UIEdgeInsets adjustedContentInset = self.collectionView.contentInset; -#ifdef __IPHONE_11_0 if (@available(iOS 11.0, *)) { contentInset = self.collectionView.safeAreaInsets; adjustedContentInset = self.collectionView.adjustedContentInset; } -#endif if (self.shouldExpand) { superSize.height = fmax(superSize.height, CGRectGetHeight(self.collectionView.bounds) - contentInset.top - contentInset.bottom); } else if (adjustedContentInset.bottom > contentInset.bottom || adjustedContentInset.top > contentInset.top) { @@ -53,12 +51,10 @@ - (nullable UICollectionViewLayoutAttributes *)layoutAttributesForDecorationView NSInteger numberOfItems = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:section]; UIEdgeInsets sectionInset = self.sectionInset; -#ifdef __IPHONE_11_0 if (@available(iOS 11.0, *)) { sectionInset.left += self.collectionView.safeAreaInsets.left; sectionInset.right += self.collectionView.safeAreaInsets.right; } -#endif UICollectionViewLayoutAttributes *attributesForFirstItem = nil; UICollectionViewLayoutAttributes *attributesForLastItem = nil; diff --git a/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewController.m b/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewController.m index 87778a291..c75b979f4 100644 --- a/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewController.m +++ b/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewController.m @@ -64,11 +64,9 @@ @implementation ApptentiveSurveyViewController - (void)viewDidLoad { [super viewDidLoad]; -#ifdef __IPHONE_11_0 if (@available(iOS 11.0, *)) { ((UICollectionViewFlowLayout *)self.collectionViewLayout).sectionInsetReference = UICollectionViewFlowLayoutSectionInsetFromSafeArea; } -#endif self.collectionView.allowsMultipleSelection = YES; [self.collectionViewLayout registerClass:[ApptentiveSurveyQuestionBackgroundView class] forDecorationViewOfKind:@"QuestionBackground"]; @@ -445,12 +443,10 @@ - (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICol - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { UIEdgeInsets sectionInset = ((UICollectionViewFlowLayout *)collectionViewLayout).sectionInset; -#ifdef __IPHONE_11_0 if (@available(iOS 11.0, *)) { sectionInset.left += self.view.safeAreaInsets.left; sectionInset.right += self.view.safeAreaInsets.right; } -#endif CGSize itemSize = CGSizeMake(collectionView.bounds.size.width - sectionInset.left - sectionInset.right, 44.0); @@ -523,12 +519,10 @@ - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollection - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section { UIEdgeInsets sectionInset = ((UICollectionViewFlowLayout *)self.collectionViewLayout).sectionInset; -#ifdef __IPHONE_11_0 if (@available(iOS 11.0, *)) { sectionInset.left += self.view.safeAreaInsets.left; sectionInset.right += self.view.safeAreaInsets.right; } -#endif CGFloat headerWidth = CGRectGetWidth(collectionView.bounds) - sectionInset.left - sectionInset.right; CGFloat labelWidth = headerWidth - QUESTION_HORIZONTAL_MARGIN; @@ -549,12 +543,10 @@ - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollection - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section { UIEdgeInsets sectionInset = ((UICollectionViewFlowLayout *)collectionViewLayout).sectionInset; -#ifdef __IPHONE_11_0 if (@available(iOS 11.0, *)) { sectionInset.left += self.view.safeAreaInsets.left; sectionInset.right += self.view.safeAreaInsets.right; } -#endif CGSize result = CGSizeMake(collectionView.bounds.size.width - sectionInset.left - sectionInset.right, 12.0); diff --git a/Apptentive/Apptentive/Surveys/Views/ApptentiveSurveyCollectionView.m b/Apptentive/Apptentive/Surveys/Views/ApptentiveSurveyCollectionView.m index b76aea550..d30b029b5 100644 --- a/Apptentive/Apptentive/Surveys/Views/ApptentiveSurveyCollectionView.m +++ b/Apptentive/Apptentive/Surveys/Views/ApptentiveSurveyCollectionView.m @@ -102,11 +102,9 @@ - (void)layoutSubviews { [super layoutSubviews]; UIEdgeInsets contentInset = self.contentInset; -#ifdef __IPHONE_11_0 if (@available(iOS 11.0, *)) { contentInset = self.safeAreaInsets; } -#endif CGFloat top = [self.collectionViewLayout collectionViewContentSize].height - CGRectGetHeight(self.collectionFooterView.bounds); if (((ApptentiveSurveyCollectionViewLayout *)self.collectionViewLayout).shouldExpand) { diff --git a/Apptentive/ApptentiveTests/ApptentiveConversationMigrationTests.m b/Apptentive/ApptentiveTests/ApptentiveConversationMigrationTests.m index 5e67440b5..9a8d2c02f 100644 --- a/Apptentive/ApptentiveTests/ApptentiveConversationMigrationTests.m +++ b/Apptentive/ApptentiveTests/ApptentiveConversationMigrationTests.m @@ -15,15 +15,28 @@ #import "ApptentiveDevice.h" #import "ApptentiveEngagement.h" #import "ApptentivePerson.h" +#import "ApptentiveEngagementManifest.h" +#import "ApptentiveLegacyEvent.h" +#import "ApptentiveLegacyMessage.h" +#import "ApptentiveLegacySurveyResponse.h" #import "ApptentiveDataManager.h" +#import "ApptentiveInteraction.h" +#import "ApptentiveUnarchiver.h" +#import "ApptentiveTargets.h" +#import "ApptentiveConversationMetadata.h" +#import "ApptentiveAppConfiguration.h" -//static inline NSDate *dateFromString(NSString *date) { -// NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; -// formatter.dateFormat = @"MM/dd/yyyy HH:mm:ss aa"; -// return [formatter dateFromString:date]; -//} +static inline NSDate *dateFromString(NSString *date) { + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + NSLocale *enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; + [formatter setLocale:enUSPOSIXLocale]; + [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"]; + [formatter setCalendar:[NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]]; + + return [formatter dateFromString:date]; +} @interface ApptentiveConversationMigrationTests : XCTestCase @@ -43,57 +56,200 @@ - (void)tearDown { [super tearDown]; } -// This is not working in the travis environment. -//- (void)testConversationMigration { -// [ApptentiveAppDataContainer pushDataContainerWithName:@"3.5.0"]; -// -// ApptentiveConversation *conversation = [[ApptentiveConversation alloc] initAndMigrate]; -// XCTAssertNotNil(conversation); -// XCTAssertEqualObjects(conversation.legacyToken, @"1c496320bd0dbca0aad7e774f4eb3ec595f620e1df7a4afc9da37e5536bd5851"); -// XCTAssertEqualObjects(conversation.person.identifier, @"59124f8d09e3da650e000037"); -// XCTAssertEqualObjects(conversation.device.identifier, @"59124f8d09e3da650e000035"); -// -// XCTAssertEqualObjects(conversation.appRelease.timeAtInstallTotal, dateFromString(@"04/01/2017 12:00:00 PM")); -// XCTAssertEqualObjects(conversation.appRelease.timeAtInstallVersion, dateFromString(@"04/02/2017 12:00:00 PM")); -// XCTAssertEqualObjects(conversation.appRelease.timeAtInstallBuild, dateFromString(@"04/02/2017 12:00:00 PM")); -// -// -// XCTAssertNotNil(conversation.SDK); -// -// XCTAssertNotNil(conversation.engagement); -// XCTAssertEqualObjects(conversation.engagement.interactions[@"interaction_1"].lastInvoked, dateFromString(@"03/01/2017 12:00:00 PM")); -// XCTAssertEqualObjects(conversation.engagement.interactions[@"interaction_2"].lastInvoked, dateFromString(@"03/02/2017 12:00:00 PM")); -// XCTAssertEqual(conversation.engagement.interactions[@"interaction_1"].buildCount, 1); -// XCTAssertEqual(conversation.engagement.interactions[@"interaction_2"].buildCount, 2); -// XCTAssertEqual(conversation.engagement.interactions[@"interaction_1"].totalCount, 3); -// XCTAssertEqual(conversation.engagement.interactions[@"interaction_2"].totalCount, 4); -// XCTAssertEqual(conversation.engagement.interactions[@"interaction_1"].versionCount, 5); -// XCTAssertEqual(conversation.engagement.interactions[@"interaction_2"].versionCount, 6); -// -// NSDictionary *expectedPersonData = @{ -// @"string": @"String Test", -// @"number": @22, -// @"boolean1": @NO, -// @"boolean2": @YES -// }; -// -// XCTAssertEqualObjects(conversation.person.name, @"Testy McTesterson"); -// XCTAssertEqualObjects(conversation.person.emailAddress, @"test@apptentive.com"); -// XCTAssertEqualObjects(conversation.person.customData, expectedPersonData); -// -// NSDictionary *expectedDeviceData = @{ -// @"string": @"Test String", -// @"number": @42, -// @"boolean1": @YES, -// @"boolean2": @NO -// }; -// XCTAssertEqualObjects(conversation.device.customData, expectedDeviceData); -// XCTAssertEqualObjects(conversation.device.integrationConfiguration, @{ @"apptentive_push": @{@"token": @"abcdef123456"} }); -// -// XCTAssertEqualObjects([conversation.engagement.codePoints[@"local#app#event_1"] lastInvoked], dateFromString(@"02/01/2017 12:00:00 PM")); -// XCTAssertEqual([conversation.engagement.codePoints[@"local#app#event_1"] buildCount], 1); -// XCTAssertEqual([conversation.engagement.codePoints[@"local#app#event_1"] versionCount], 2); -// XCTAssertEqual([conversation.engagement.codePoints[@"local#app#event_1"] totalCount], 3); -//} +- (void)testConversationMigration { + [ApptentiveAppDataContainer pushDataContainerWithName:@"3.5.0"]; + + ApptentiveConversation *conversation = [[ApptentiveConversation alloc] initAndMigrate]; + XCTAssertNotNil(conversation); + XCTAssertEqualObjects(conversation.legacyToken, @"1c496320bd0dbca0aad7e774f4eb3ec595f620e1df7a4afc9da37e5536bd5851"); + XCTAssertEqualObjects(conversation.person.identifier, @"59124f8d09e3da650e000037"); + XCTAssertEqualObjects(conversation.device.identifier, @"59124f8d09e3da650e000035"); + + XCTAssertEqualObjects(conversation.appRelease.timeAtInstallTotal, dateFromString(@"2017-04-01T12:00:00-0700")); + XCTAssertEqualObjects(conversation.appRelease.timeAtInstallVersion, dateFromString(@"2017-04-02T12:00:00-0700")); + XCTAssertEqualObjects(conversation.appRelease.timeAtInstallBuild, dateFromString(@"2017-04-02T12:00:00-0700")); + + + XCTAssertNotNil(conversation.SDK); + + XCTAssertNotNil(conversation.engagement); + XCTAssertEqualObjects(conversation.engagement.interactions[@"interaction_1"].lastInvoked, dateFromString(@"2017-03-01T13:00:00-0700")); + XCTAssertEqualObjects(conversation.engagement.interactions[@"interaction_2"].lastInvoked, dateFromString(@"2017-03-02T13:00:00-0700")); + XCTAssertEqual(conversation.engagement.interactions[@"interaction_1"].buildCount, 1); + XCTAssertEqual(conversation.engagement.interactions[@"interaction_2"].buildCount, 2); + XCTAssertEqual(conversation.engagement.interactions[@"interaction_1"].totalCount, 3); + XCTAssertEqual(conversation.engagement.interactions[@"interaction_2"].totalCount, 4); + XCTAssertEqual(conversation.engagement.interactions[@"interaction_1"].versionCount, 5); + XCTAssertEqual(conversation.engagement.interactions[@"interaction_2"].versionCount, 6); + + NSDictionary *expectedPersonData = @{ + @"string": @"String Test", + @"number": @22, + @"boolean1": @NO, + @"boolean2": @YES + }; + + XCTAssertEqualObjects(conversation.person.name, @"Testy McTesterson"); + XCTAssertEqualObjects(conversation.person.emailAddress, @"test@apptentive.com"); + XCTAssertEqualObjects(conversation.person.customData, expectedPersonData); + + NSDictionary *expectedDeviceData = @{ + @"string": @"Test String", + @"number": @42, + @"boolean1": @YES, + @"boolean2": @NO + }; + XCTAssertEqualObjects(conversation.device.customData, expectedDeviceData); + XCTAssertEqualObjects(conversation.device.integrationConfiguration, @{ @"apptentive_push": @{@"token": @"abcdef123456"} }); + + XCTAssertEqualObjects([conversation.engagement.codePoints[@"local#app#event_1"] lastInvoked], dateFromString(@"2017-02-01T13:00:00-0700")); + XCTAssertEqual([conversation.engagement.codePoints[@"local#app#event_1"] buildCount], 1); + XCTAssertEqual([conversation.engagement.codePoints[@"local#app#event_1"] versionCount], 2); + XCTAssertEqual([conversation.engagement.codePoints[@"local#app#event_1"] totalCount], 3); +} + +- (void)testEnqueueUnsentEvents { + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *bundlePath = [bundle pathForResource:@"3.5.0" ofType:@"xcappdata"]; + if (![[NSFileManager defaultManager] fileExistsAtPath:bundlePath isDirectory:NULL]) { + XCTFail(@"App data doesn't exist: 3.5.0"); + } + + NSString *supportDirectoryPath = [bundlePath stringByAppendingString:@"/AppData/Library/Application Support/com.apptentive.feedback"]; + + ApptentiveDataManager *dataManager = [[ApptentiveDataManager alloc] initWithModelName:@"ATDataModel" inBundle:bundle storagePath:supportDirectoryPath]; + NSManagedObjectContext *context = dataManager.managedObjectContext; + + NSArray *beforeResults = [context executeFetchRequest:[NSFetchRequest fetchRequestWithEntityName:@"QueuedRequest"] error:NULL]; + + ApptentiveConversation *conversation = [[ApptentiveConversation alloc] initAndMigrate]; + + [ApptentiveLegacyEvent enqueueUnsentEventsInContext:context forConversation:conversation]; + + NSArray *afterResults = [context executeFetchRequest:[NSFetchRequest fetchRequestWithEntityName:@"QueuedRequest"] error:NULL]; + + XCTAssertGreaterThan(afterResults.count, beforeResults.count); +} + +- (void)testEnqueueUnsentMessages { + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *bundlePath = [bundle pathForResource:@"3.5.0" ofType:@"xcappdata"]; + if (![[NSFileManager defaultManager] fileExistsAtPath:bundlePath isDirectory:NULL]) { + XCTFail(@"App data doesn't exist: 3.5.0"); + } + + NSString *supportDirectoryPath = [bundlePath stringByAppendingString:@"/AppData/Library/Application Support/com.apptentive.feedback"]; + + ApptentiveDataManager *dataManager = [[ApptentiveDataManager alloc] initWithModelName:@"ATDataModel" inBundle:bundle storagePath:supportDirectoryPath]; + NSManagedObjectContext *context = dataManager.managedObjectContext; + + NSArray *beforeResults = [context executeFetchRequest:[NSFetchRequest fetchRequestWithEntityName:@"QueuedRequest"] error:NULL]; + + ApptentiveConversation *conversation = [[ApptentiveConversation alloc] initAndMigrate]; + + NSString *oldAttachmentPath = [supportDirectoryPath stringByAppendingString:@"/attachments"]; + + [ApptentiveLegacyMessage enqueueUnsentMessagesInContext:context forConversation:conversation oldAttachmentPath:oldAttachmentPath newAttachmentPath:supportDirectoryPath]; + + NSArray *afterResults = [context executeFetchRequest:[NSFetchRequest fetchRequestWithEntityName:@"QueuedRequest"] error:NULL]; + + XCTAssertGreaterThan(afterResults.count, beforeResults.count); +} + +- (void)testEnqueueUnsentSurveyResponses { + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *bundlePath = [bundle pathForResource:@"3.5.0" ofType:@"xcappdata"]; + if (![[NSFileManager defaultManager] fileExistsAtPath:bundlePath isDirectory:NULL]) { + XCTFail(@"App data doesn't exist: 3.5.0"); + } + + NSString *supportDirectoryPath = [bundlePath stringByAppendingString:@"/AppData/Library/Application Support/com.apptentive.feedback"]; + + ApptentiveDataManager *dataManager = [[ApptentiveDataManager alloc] initWithModelName:@"ATDataModel" inBundle:bundle storagePath:supportDirectoryPath]; + NSManagedObjectContext *context = dataManager.managedObjectContext; + + NSArray *beforeResults = [context executeFetchRequest:[NSFetchRequest fetchRequestWithEntityName:@"QueuedRequest"] error:NULL]; + + ApptentiveConversation *conversation = [[ApptentiveConversation alloc] initAndMigrate]; + + [ApptentiveLegacySurveyResponse enqueueUnsentSurveyResponsesInContext:context forConversation:conversation]; + + NSArray *afterResults = [context executeFetchRequest:[NSFetchRequest fetchRequestWithEntityName:@"QueuedRequest"] error:NULL]; + + XCTAssertGreaterThan(afterResults.count, beforeResults.count); +} + +- (void)testMigratingFrom5ToNew { + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *conversationPath = [bundle pathForResource:@"conversation-5" ofType:@"archive"]; + if (![[NSFileManager defaultManager] fileExistsAtPath:conversationPath isDirectory:NULL]) { + XCTFail(@"Conversation data doesn't exist: conversation-5.archive"); + } + + ApptentiveConversation *conversation = [ApptentiveUnarchiver unarchivedObjectOfClass:[ApptentiveConversation class] fromFile:conversationPath]; + + XCTAssertNotNil(conversation); + XCTAssertNotNil(conversation.appRelease); + XCTAssertNotNil(conversation.SDK); + XCTAssertNotNil(conversation.person); + XCTAssertNotNil(conversation.device); + XCTAssertNotNil(conversation.engagement); +} + +- (void)testMigratingFrom4ToNew { + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *conversationPath = [bundle pathForResource:@"conversation-4" ofType:@"archive"]; + if (![[NSFileManager defaultManager] fileExistsAtPath:conversationPath isDirectory:NULL]) { + XCTFail(@"Conversation data doesn't exist: conversation-4.archive"); + } + + ApptentiveConversation *conversation = [ApptentiveUnarchiver unarchivedObjectOfClass:[ApptentiveConversation class] fromFile:conversationPath]; + + XCTAssertNotNil(conversation); + XCTAssertNotNil(conversation.appRelease); + XCTAssertNotNil(conversation.SDK); + XCTAssertNotNil(conversation.person); + XCTAssertNotNil(conversation.device); + XCTAssertNotNil(conversation.engagement); +} + +- (void)testMigratingEngagmentManifestFrom5ToNew { + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *manifestPath = [bundle pathForResource:@"manifest-v2" ofType:@"archive"]; + if (![[NSFileManager defaultManager] fileExistsAtPath:manifestPath isDirectory:NULL]) { + XCTFail(@"Conversation data doesn't exist: manifest-v2.archive"); + } + + ApptentiveEngagementManifest *manifest = [ApptentiveUnarchiver unarchivedObjectOfClass:[ApptentiveEngagementManifest class] fromFile:manifestPath]; + + XCTAssertNotNil(manifest); + XCTAssertNotEqual(manifest.targets.invocations.count, 0); + XCTAssertNotEqual(manifest.interactions.count, 0); +} + +- (void)testMigratingMetadataFrom5ToNew { + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *metadataPath = [bundle pathForResource:@"conversation-v1" ofType:@"meta"]; + if (![[NSFileManager defaultManager] fileExistsAtPath:metadataPath isDirectory:NULL]) { + XCTFail(@"Conversation data doesn't exist: conversation-v1.meta"); + } + + ApptentiveConversationMetadata *metadata = [ApptentiveUnarchiver unarchivedObjectOfClass:[ApptentiveConversationMetadata class] fromFile:metadataPath]; + + XCTAssertNotNil(metadata); + XCTAssertNotEqual(metadata.items.count, 0); +} + +- (void)testMigratingAppConfigurationFrom5ToNew { + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *configurationPath = [bundle pathForResource:@"configuration-v1" ofType:@"archive"]; + if (![[NSFileManager defaultManager] fileExistsAtPath:configurationPath isDirectory:NULL]) { + XCTFail(@"Conversation data doesn't exist: configuration-v1.archive"); + } + + ApptentiveAppConfiguration *configuration = [ApptentiveUnarchiver unarchivedObjectOfClass:[ApptentiveAppConfiguration class] fromFile:configurationPath]; + + XCTAssertNotNil(configuration); +} @end diff --git a/Apptentive/ApptentiveTests/ApptentiveConversationTests.m b/Apptentive/ApptentiveTests/ApptentiveConversationTests.m index 79a3797a8..c067516df 100644 --- a/Apptentive/ApptentiveTests/ApptentiveConversationTests.m +++ b/Apptentive/ApptentiveTests/ApptentiveConversationTests.m @@ -15,6 +15,8 @@ #import "ApptentiveSDK.h" #import "ApptentiveVersion.h" #import +#import "ApptentiveArchiver.h" +#import "ApptentiveUnarchiver.h" @interface ApptentiveConversationTests : XCTestCase @@ -160,7 +162,8 @@ - (void)testDevice { XCTAssertNotNil(self.conversation.device.localeRaw); XCTAssertNotNil(self.conversation.device.localeLanguageCode); XCTAssertNotNil(self.conversation.device.localeCountryCode); - XCTAssertEqualObjects(self.conversation.device.OSName, @"iOS"); + XCTAssert([self.conversation.device.OSName hasPrefix:@"i"]); + XCTAssert([self.conversation.device.OSName hasSuffix:@"OS"]); XCTAssertEqual(self.conversation.device.UUID.UUIDString.length, (NSUInteger)36); XCTAssertEqual(self.conversation.device.customData.count, (NSUInteger)0); @@ -184,7 +187,8 @@ - (void)testDevice { XCTAssertNotNil(self.conversation.device.localeRaw); XCTAssertNotNil(self.conversation.device.localeLanguageCode); XCTAssertNotNil(self.conversation.device.localeCountryCode); - XCTAssertEqualObjects(self.conversation.device.OSName, @"iOS"); + XCTAssert([self.conversation.device.OSName hasPrefix:@"i"]); + XCTAssert([self.conversation.device.OSName hasSuffix:@"OS"]); XCTAssertEqual(self.conversation.device.UUID.UUIDString.length, (NSUInteger)36); [self.conversation.device removeCustomValueWithKey:@"foo"]; @@ -263,6 +267,8 @@ - (void)testEngagement { - (void)testNSCoding { ApptentiveMutableConversation *mutableConversation = [self.conversation mutableCopy]; + + NSString *OSName = mutableConversation.device.OSName; // Make some changes to the default values [mutableConversation setToken:@"DEF456" conversationID:@"ABC123" personID:@"GHI789" deviceID:@"JKL101"]; @@ -291,9 +297,9 @@ - (void)testNSCoding { NSString *path = [NSTemporaryDirectory() stringByAppendingString:@"conversation.archive"]; - [NSKeyedArchiver archiveRootObject:mutableConversation toFile:path]; + [ApptentiveArchiver archiveRootObject:mutableConversation toFile:path]; - ApptentiveConversation *conversation = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; + ApptentiveConversation *conversation = [ApptentiveUnarchiver unarchivedObjectOfClass:[ApptentiveConversation class] fromFile:path]; XCTAssertEqualObjects(conversation.userInfo[@"bar"], @"foo"); @@ -310,7 +316,7 @@ - (void)testNSCoding { XCTAssertNotNil(conversation.device.localeRaw); XCTAssertNotNil(conversation.device.localeLanguageCode); XCTAssertNotNil(conversation.device.localeCountryCode); - XCTAssertEqualObjects(conversation.device.OSName, @"iOS"); + XCTAssertEqualObjects(conversation.device.OSName, OSName); // This is "iOS" on new systems, "iPhone OS" on iOS 9 XCTAssertEqual(conversation.device.UUID.UUIDString.length, (NSUInteger)36); XCTAssertEqualObjects(conversation.device.customData[@"foo"], @"bar"); XCTAssertEqualObjects(conversation.device.customData[@"five"], @5); diff --git a/Apptentive/ApptentiveTests/ApptentiveEngagementTests.swift b/Apptentive/ApptentiveTests/ApptentiveEngagementTests.swift index 14ca17afe..7fd3b91aa 100644 --- a/Apptentive/ApptentiveTests/ApptentiveEngagementTests.swift +++ b/Apptentive/ApptentiveTests/ApptentiveEngagementTests.swift @@ -94,9 +94,21 @@ class ApptentiveEngagementTests: XCTestCase { Bundle(for: ApptentiveEngagementTests.self).url(forResource: "conversation-4", withExtension:"archive") // Open a 4.0.0 archive with slash/pound/percent event names - guard let fourOhUrl = Bundle(for: ApptentiveEngagementTests.self).url(forResource: "conversation-4", withExtension:"archive"), let fourOhConversation = NSKeyedUnarchiver.unarchiveObject(withFile: fourOhUrl.path) as? ApptentiveConversation else { - XCTFail("Can't open 4.0 conversation archive") - return + guard let fourOhUrl = Bundle(for: ApptentiveEngagementTests.self).url(forResource: "conversation-4", withExtension:"archive"), + let fourOhData = try? Data(contentsOf: fourOhUrl) else { + return XCTFail("Can't open 4.0 conversation archive") + } + + let maybeFourOhConversation: ApptentiveConversation? + + if #available(iOS 11.0, *) { + maybeFourOhConversation = try? NSKeyedUnarchiver.unarchivedObject(ofClass: ApptentiveConversation.self, from: fourOhData) + } else { + maybeFourOhConversation = NSKeyedUnarchiver.unarchiveObject(with: fourOhData) as? ApptentiveConversation + } + + guard let fourOhConversation = maybeFourOhConversation else { + return XCTFail("Can't unarchive 4.0 conversation archive") } let fourOhEngagement = fourOhConversation.engagement @@ -108,11 +120,21 @@ class ApptentiveEngagementTests: XCTestCase { XCTAssertEqual(fourOhEngagement.codePoints["local#app#go%25daddy"]?.totalCount, 1) XCTAssertEqual(fourOhEngagement.codePoints["local#app#go%2520daddy"]?.totalCount, 1) + guard let fiveTwoUrl = Bundle(for: ApptentiveEngagementTests.self).url(forResource: "conversation-5", withExtension:"archive"), + let fiveTwoData = try? Data(contentsOf: fiveTwoUrl) else { + return XCTFail("Can't open 5.2 conversation archive") + } + + let maybeFiveTwoConversation: ApptentiveConversation? + + if #available(iOS 11.0, *) { + maybeFiveTwoConversation = try? NSKeyedUnarchiver.unarchivedObject(ofClass: ApptentiveConversation.self, from: fiveTwoData) + } else { + maybeFiveTwoConversation = NSKeyedUnarchiver.unarchiveObject(with: fiveTwoData) as? ApptentiveConversation + } - // Open a 5.2.2 archive with slash/pound/percent event names - guard let fiveTwoUrl = Bundle(for: ApptentiveEngagementTests.self).url(forResource: "conversation-5", withExtension:"archive"), let fiveTwoConversation = NSKeyedUnarchiver.unarchiveObject(withFile: fiveTwoUrl.path) as? ApptentiveConversation else { - XCTFail("Can't open 5.2 conversation archive") - return + guard let fiveTwoConversation = maybeFiveTwoConversation else { + return XCTFail("Can't unarchive 5.2 conversation archive") } let fiveTwoEngagement = fiveTwoConversation.engagement diff --git a/Apptentive/ApptentiveTests/ApptentiveStyleSheetTests.m b/Apptentive/ApptentiveTests/ApptentiveStyleSheetTests.m index 8ad275b8c..33137d1a0 100644 --- a/Apptentive/ApptentiveTests/ApptentiveStyleSheetTests.m +++ b/Apptentive/ApptentiveTests/ApptentiveStyleSheetTests.m @@ -28,13 +28,11 @@ - (void)setUp { - (void)testUIAppearanceDefaults { [UITableView appearance].separatorColor = [UIColor redColor]; - [UITableView appearanceWhenContainedIn:[ApptentiveNavigationController class], nil].backgroundColor = [UIColor greenColor]; + [UITableView appearanceWhenContainedInInstancesOfClasses:@[[ApptentiveNavigationController class]]].backgroundColor = UIColor.greenColor; if (@available(iOS 13.0, *)) { -#ifdef __IPHONE_13_0 XCTAssertEqualObjects([self.styleSheet colorForStyle:ApptentiveColorSeparator], [UIColor separatorColor]); XCTAssertEqualObjects([self.styleSheet colorForStyle:ApptentiveColorCollectionBackground], [UIColor systemGroupedBackgroundColor]); -#endif } else { XCTAssertEqualObjects([self.styleSheet colorForStyle:ApptentiveColorSeparator], [UIColor redColor]); XCTAssertEqualObjects([self.styleSheet colorForStyle:ApptentiveColorCollectionBackground], [UIColor greenColor]); diff --git a/Apptentive/ApptentiveTests/Info.plist b/Apptentive/ApptentiveTests/Info.plist index 6d7f69650..7bc2025f8 100644 --- a/Apptentive/ApptentiveTests/Info.plist +++ b/Apptentive/ApptentiveTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 5.2.8 + 5.2.9 CFBundleVersion 1 diff --git a/Apptentive/ApptentiveTests/data/configuration-v1.archive b/Apptentive/ApptentiveTests/data/configuration-v1.archive new file mode 100644 index 0000000000000000000000000000000000000000..55ebb92c8c8d0ef31d0a8f325ec35a181debec78 GIT binary patch literal 1066 zcma)5&2JM&6rVSn1RHQz0=N*B66z&FduvOAeP`q4Fq3PEagg1Q0ay1Gx)ec$83 zv?Dhc=P&8%!5t^2Tw>VNTB7Sd;~PtwX%iuI^+VIMZ7HW@Ng)9f%&kpZ5|UL}gxb|Y zon*qd$wrwtR06-bG;0)w2aRf>nj=Gl78#}kXRCRm-y9sy zStMT|17p^xWS?vZShQ%mKF91XfTnU>4XFY0Rd zf8;XTNjWVk-%*znUarj04QiEI&*ssk*#+)-K?o>@_M>i;LTQvo=g<_IK?d^Ccjy+n zjqad_=rQ^W06IZ~UN{A(p%2oKfgJQh9tL0tif{#1p$Z%VXuvxB3S005p22f?3IF1D zd>oJ9alDFcT*nQ(j=#m<;~((P_!oQ+Kfr(D7x-`dN&%%qNhqpvNXaWh%DiGJHN|eW O217HVax1oi;=Ts!DNvdK literal 0 HcmV?d00001 diff --git a/Apptentive/ApptentiveTests/data/conversation-v1.meta b/Apptentive/ApptentiveTests/data/conversation-v1.meta new file mode 100644 index 0000000000000000000000000000000000000000..67bdf79fbd9daf061c5db132419fb7e35617dc6a GIT binary patch literal 1064 zcma)*OOM-B6vvP4KBhC2@w8A%I}Ff3Sye{0o5H`S|m*3(JpWo_Y4U z=Wpg48TZg}zCE|N^w9Z-8+L?7VQa3@%$SZ5h6zq$$>Ke7Y2~7>)v|NlPc9@xa_;*_ zFD*TG`SHaoS681{d-AEL&#zu@9)>88tuA@V^xUbE5vB9U7IGtd&qhIe&vuYzj8K=n z`XEvDOvCvbt0}GeYsfW&i9fC1M3Y7s84*e#Ih8CVXOh#&*#t~K3doEuHHTZ${!nApV_h;C zEw&teAdb~o8S3@^teLs7zGt*{X1vDk#A@8hX>os|*EL%m$+2D^j#VkGkvdb}@3d2P zP-e72hgRz%rwuG2>kR02u*;3zZM&`?clccvNhRJojCxMtpjlO{+@@Xj4-O`qfoX)d z%6+Nil?zfU8jZIV*4AqNdU2yFRA`0on~WH@PvvY!!ja6PEB{0%Kk5|t<|<>#!lPO%x$fv9!5sbL1GXXlTX=P_8+O1 zR-0X=I9KSBYyUpU-C{b$e=uEgS*xXXM3y-JSl_(-NR^SR;2MSmyLJ4Hp zh9=ww1bwh!2tK?HZ@`=I7JL97!WZx*`~W|~Pw)%;3ctbca3B7FKQRm=ur;iVIruyH z`}jxrXZTn6*Z9x)eFBJO;v#X4xIx?`wuu@M5qF5UiFaYPaO@<)tMfPy#Cv}M%*;!o literal 0 HcmV?d00001 diff --git a/Apptentive/ApptentiveTests/data/manifest-v2.archive b/Apptentive/ApptentiveTests/data/manifest-v2.archive new file mode 100644 index 0000000000000000000000000000000000000000..424110828ecfb2abc61a4d7eb8060a8ac7c902dc GIT binary patch literal 36762 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 MTgU(5|GyFZzX~bmY5)KL literal 0 HcmV?d00001 diff --git a/CHANGELOG.md b/CHANGELOG.md index 56725fcbf..276fc7eb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 2020-03-13 - v5.2.9 + +#### Improvements + +* Convert to full use of secure archiving/unarchiving. +* Convert use of other deprecated APIs to use newest APIs where available. +* Guard use of deprecated code with `@available` checks, and suppress warnings for newer deployment targets. +* Warnings no longer automatically block compilation. + # 2019-11-18 - v5.2.8 #### Improvements diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 7b492ac12..7ea9122fa 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - apptentive-ios (5.2.8) + - apptentive-ios (5.2.9) DEPENDENCIES: - apptentive-ios (from `..`) @@ -9,8 +9,8 @@ EXTERNAL SOURCES: :path: ".." SPEC CHECKSUMS: - apptentive-ios: b6d36cb5ef582b9e5fee5104ad909c736d6bff04 + apptentive-ios: edabd25be9824f9de3fc9bd9f043048b2bb67415 PODFILE CHECKSUM: 89d2b5f4683b04482e89df6d46b268cc9ed1ef79 -COCOAPODS: 1.8.4 +COCOAPODS: 1.9.0 diff --git a/apptentive-ios.podspec b/apptentive-ios.podspec index b44cc5b9d..55c5f67cc 100644 --- a/apptentive-ios.podspec +++ b/apptentive-ios.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = 'apptentive-ios' s.module_name = 'Apptentive' - s.version = '5.2.8' + s.version = '5.2.9' s.license = 'BSD' s.summary = 'Apptentive Customer Communications SDK.' s.homepage = 'https://www.apptentive.com/'