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 000000000..55ebb92c8 Binary files /dev/null and b/Apptentive/ApptentiveTests/data/configuration-v1.archive differ diff --git a/Apptentive/ApptentiveTests/data/conversation-v1.meta b/Apptentive/ApptentiveTests/data/conversation-v1.meta new file mode 100644 index 000000000..67bdf79fb Binary files /dev/null and b/Apptentive/ApptentiveTests/data/conversation-v1.meta differ diff --git a/Apptentive/ApptentiveTests/data/manifest-v2.archive b/Apptentive/ApptentiveTests/data/manifest-v2.archive new file mode 100644 index 000000000..424110828 Binary files /dev/null and b/Apptentive/ApptentiveTests/data/manifest-v2.archive differ 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/'