diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 54e3c4a22..c1d95c987 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -14,6 +14,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 + - name: Build, Analyze, & Test env: scheme: RadarSDK diff --git a/.github/workflows/release-sdk.yml b/.github/workflows/release-sdk.yml index 0c9826734..d59c3739a 100644 --- a/.github/workflows/release-sdk.yml +++ b/.github/workflows/release-sdk.yml @@ -74,12 +74,22 @@ jobs: event-type: update-xcframework client-payload: '{"release": "${{ github.event.release.tag_name }}", "checksum": "${{ steps.checksum_radarsdk.outputs.checksum }}", "url": "${{ github.event.release.html_url }}", "checksum_motion": "${{ steps.checksum_radarsdkmotion.outputs.checksum }}"}' + - name: Check versions is not duplicate + run: | + VERSION=$(pod ipc spec RadarSDK.podspec | jq -r .version) + ! pod trunk info RadarSDK | grep -q $VERSION + echo "RadarSDK version is OK" + VERSION=$(pod ipc spec RadarSDKMotion.podspec | jq -r .version) + ! pod trunk info RadarSDKMotion | grep -q $VERSION + echo "RadarSDKMotion version is OK" + - name: Deploy to Cocoapods run: | set -eo pipefail + gem install cocoapods pod lib lint --allow-warnings RadarSDK.podspec - pod trunk push --allow-warnings RadarSDK.podspec pod lib lint --allow-warnings RadarSDKMotion.podspec + pod trunk push --allow-warnings RadarSDK.podspec pod trunk push --allow-warnings RadarSDKMotion.podspec env: - COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} \ No newline at end of file + COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} diff --git a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..0c67376eb --- /dev/null +++ b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,5 @@ + + + + + diff --git a/Example/Example/AppDelegate.swift b/Example/Example/AppDelegate.swift index b872f92fd..b266b98ad 100644 --- a/Example/Example/AppDelegate.swift +++ b/Example/Example/AppDelegate.swift @@ -131,6 +131,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UIWindowSceneDelegate, UN } } + demoButton(text: "startVerifyServer") { + Radar.startVerifyServer() + } + + demoButton(text: "stopVerifyServer") { + Radar.stopVerifyServer() + } + demoButton(text: "searchPlaces") { // In the Radar dashboard settings // (https://radar.com/dashboard/settings), add this to the chain diff --git a/Example/Example/Package.swift b/Example/Example/Package.swift new file mode 100644 index 000000000..f2f11ba84 --- /dev/null +++ b/Example/Example/Package.swift @@ -0,0 +1,13 @@ +// swift-tools-version:5.8 +import PackageDescription + +let package = Package( + name: "MyProject", + dependencies: [ + .package(url: "https//github.com/apple/swift-nio.git", from: "2.0.0"), + .package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.0.0") + ], + targets: [ + .target(name: "MyProject", dependencies: ["NIO", "NIOSSL"]), + ] +) diff --git a/Package.swift b/Package.swift index 526f226ef..8b2dfd6e1 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ import PackageDescription let package = Package( name: "RadarSDK", platforms: [ - .iOS(.v10) + .iOS(.v12) ], products: [ .library( @@ -16,7 +16,10 @@ let package = Package( targets: ["RadarSDKMotion"] ) ], - dependencies: [], + dependencies: [ + .package(url: "https://github.com/apple/swift-nio.git", from: "2.62.0"), + .package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.25.0") + ], targets: [ .target( name: "RadarSDK", @@ -26,6 +29,10 @@ let package = Package( publicHeadersPath: "Include", cSettings: [ .headerSearchPath(".") + ], + dependencies: [ + .product(name: "NIO", package: "swift-nio"), + .product(name: "NIOHTTP1", package: "swift-nio") ] ), .target( @@ -35,7 +42,7 @@ let package = Package( publicHeadersPath: "Include", cSettings: [ .headerSearchPath(".") - ] + ] ) ] ) diff --git a/RadarSDK.podspec b/RadarSDK.podspec index 91b13e184..c92ecf36e 100644 --- a/RadarSDK.podspec +++ b/RadarSDK.podspec @@ -1,16 +1,17 @@ Pod::Spec.new do |s| s.name = 'RadarSDK' - s.version = '3.19.0' + s.version = '3.19.1' s.summary = 'iOS SDK for Radar, the leading geofencing and location tracking platform' s.homepage = 'https://radar.com' s.author = { 'Radar Labs, Inc.' => 'support@radar.com' } s.platform = :ios s.source = { :git => 'https://github.com/radarlabs/radar-sdk-ios.git', :tag => s.version.to_s } - s.source_files = ["RadarSDK/*.{h,m}", "RadarSDK/Internal/*.{h,m}", "RadarSDK/Include/*.h"] + s.source_files = ["RadarSDK/*.{h,m,swift}", "RadarSDK/Include/*.h"] s.module_name = 'RadarSDK' - s.ios.deployment_target = '10.0' + s.ios.deployment_target = '12.0' s.frameworks = 'CoreLocation' s.requires_arc = true s.license = { :type => 'Apache-2.0' } s.resource_bundles = {'RadarSDK' => ['RadarSDK/PrivacyInfo.xcprivacy']} + s.swift_version = '5.0' end diff --git a/RadarSDK.xcodeproj/project.pbxproj b/RadarSDK.xcodeproj/project.pbxproj index 40cb91daf..97679c973 100644 --- a/RadarSDK.xcodeproj/project.pbxproj +++ b/RadarSDK.xcodeproj/project.pbxproj @@ -69,8 +69,8 @@ 0107AB2F262201FB008AB52F /* RadarUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DD236D0423099B8400EB88F9 /* RadarUtils.m */; }; 0107AB7A2622061A008AB52F /* RadarSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0107A9E82621FFB9008AB52F /* RadarSDK.framework */; }; 0114F058284EFDB700ADA4E4 /* RadarRouteMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0114F057284EFDB700ADA4E4 /* RadarRouteMode.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 015C53AD29B8E8BA004F53A6 /* (null) in Headers */ = {isa = PBXBuildFile; }; - 015C53AE29B8E8BA004F53A6 /* (null) in Sources */ = {isa = PBXBuildFile; }; + 0158673E2CEB3B83007BEAC0 /* RadarVerifyServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0158673D2CEB3B83007BEAC0 /* RadarVerifyServer.swift */; }; + 015867572CEB5071007BEAC0 /* RadarVerifyServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 015867562CEB5071007BEAC0 /* RadarVerifyServer.h */; }; 019514362BD078D90031ABA2 /* RadarVerifiedLocationToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 019514352BD077D10031ABA2 /* RadarVerifiedLocationToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; 019514392BD081630031ABA2 /* RadarVerifiedLocationToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 019514372BD081630031ABA2 /* RadarVerifiedLocationToken.m */; }; 0195143A2BD081630031ABA2 /* RadarVerifiedLocationToken+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 019514382BD081630031ABA2 /* RadarVerifiedLocationToken+Internal.h */; }; @@ -188,6 +188,8 @@ 0107A9E82621FFB9008AB52F /* RadarSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RadarSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0113020E2AE1467800EFC377 /* Network.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Network.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.3.sdk/System/Library/PrivateFrameworks/Network.framework; sourceTree = DEVELOPER_DIR; }; 0114F057284EFDB700ADA4E4 /* RadarRouteMode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RadarRouteMode.h; sourceTree = ""; }; + 0158673D2CEB3B83007BEAC0 /* RadarVerifyServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadarVerifyServer.swift; sourceTree = ""; }; + 015867562CEB5071007BEAC0 /* RadarVerifyServer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RadarVerifyServer.h; sourceTree = ""; }; 019514352BD077D10031ABA2 /* RadarVerifiedLocationToken.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RadarVerifiedLocationToken.h; sourceTree = ""; }; 019514372BD081630031ABA2 /* RadarVerifiedLocationToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RadarVerifiedLocationToken.m; sourceTree = ""; }; 019514382BD081630031ABA2 /* RadarVerifiedLocationToken+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RadarVerifiedLocationToken+Internal.h"; sourceTree = ""; }; @@ -533,6 +535,8 @@ DD236D0423099B8400EB88F9 /* RadarUtils.m */, 82F7FAEC2A65FE030055AA4B /* RadarVerificationManager.h */, 82F7FAED2A65FE030055AA4B /* RadarVerificationManager.m */, + 015867562CEB5071007BEAC0 /* RadarVerifyServer.h */, + 0158673D2CEB3B83007BEAC0 /* RadarVerifyServer.swift */, DD27CB7D235D13F000299FEC /* Models */, 53CCD781275E576B00F79CC8 /* Util */, ); @@ -574,8 +578,8 @@ 96A5A0BA27AD9F41007B960B /* RadarContext+Internal.h */, 5343FFD923E38BA300808D93 /* RadarContext.m */, 96A5A0B627AD9F40007B960B /* RadarCoordinate+Internal.h */, - 96A5A0B927AD9F41007B960B /* RadarEvent+Internal.h */, DD236CBA2308812700EB88F9 /* RadarCoordinate.m */, + 96A5A0B927AD9F41007B960B /* RadarEvent+Internal.h */, DD236CB82308812700EB88F9 /* RadarEvent.m */, 96A5A0AD27AD9F40007B960B /* RadarFraud+Internal.h */, 96FC90F6277379C1000757DF /* RadarFraud.m */, @@ -678,7 +682,6 @@ 96A5A10927AD9F7F007B960B /* RadarContext.h in Headers */, 0107AA1226220049008AB52F /* RadarCollectionAdditions.h in Headers */, E6EEC56E2B20F41A00DD096B /* RadarFileStorage.h in Headers */, - 015C53AD29B8E8BA004F53A6 /* (null) in Headers */, 0107AA1C26220055008AB52F /* RadarPermissionsHelper.h in Headers */, 96A5A11227AD9F7F007B960B /* Radar.h in Headers */, 96A5A10827AD9F7F007B960B /* RadarSegment.h in Headers */, @@ -711,6 +714,7 @@ 96A5A10127AD9F7F007B960B /* RadarRoute.h in Headers */, 96A5A0FE27AD9F7F007B960B /* RadarTrip.h in Headers */, 96A5A10027AD9F7F007B960B /* RadarCoordinate.h in Headers */, + 015867572CEB5071007BEAC0 /* RadarVerifyServer.h in Headers */, 532FC304277A790600989279 /* Radar+Internal.h in Headers */, 96A5A0FB27AD9F7F007B960B /* RadarChain.h in Headers */, ); @@ -767,7 +771,7 @@ TargetAttributes = { 0107A9E72621FFB9008AB52F = { CreatedOnToolsVersion = 12.4; - LastSwiftMigration = 1320; + LastSwiftMigration = 1610; }; 0107AB3826220308008AB52F = { CreatedOnToolsVersion = 12.4; @@ -868,7 +872,6 @@ 8227EF0C2CDAB69B00C47290 /* RadarRouteMode.m in Sources */, 9683FD6527B36C26009EBB6B /* RadarMeta.m in Sources */, 0107AA9B26220153008AB52F /* RadarContext.m in Sources */, - 015C53AE29B8E8BA004F53A6 /* (null) in Sources */, 0107AB23262201EC008AB52F /* RadarSettings.m in Sources */, 0107AAAA26220165008AB52F /* RadarGeofenceGeometry.m in Sources */, 0107AAEC262201A6008AB52F /* RadarTrip.m in Sources */, @@ -897,6 +900,7 @@ 0107AA832622013A008AB52F /* RadarBeacon.m in Sources */, 82F7FAEB2A65FDD50055AA4B /* RadarNotificationHelper.m in Sources */, 0107AAB626220172008AB52F /* RadarPolygonGeometry.m in Sources */, + 0158673E2CEB3B83007BEAC0 /* RadarVerifyServer.swift in Sources */, 0107AB0E262201D5008AB52F /* RadarBeaconManager.m in Sources */, 82D04AC729771BF10036619F /* RadarReplay.m in Sources */, 0107AADA26220195008AB52F /* RadarRouteMatrix.m in Sources */, @@ -937,6 +941,7 @@ 0107A9ED2621FFB9008AB52F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; @@ -947,26 +952,28 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = RadarSDK/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 3.9.6; PRODUCT_BUNDLE_IDENTIFIER = io.radar.sdk; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; - TVOS_DEPLOYMENT_TARGET = 10.0; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Debug; }; 0107A9EE2621FFB9008AB52F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; @@ -977,20 +984,20 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = RadarSDK/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 3.9.6; PRODUCT_BUNDLE_IDENTIFIER = io.radar.sdk; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SKIP_INSTALL = YES; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; - TVOS_DEPLOYMENT_TARGET = 10.0; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Release; }; @@ -1065,8 +1072,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - MARKETING_VERSION = 3.19.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MARKETING_VERSION = 3.19.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -1123,8 +1130,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - MARKETING_VERSION = 3.19.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MARKETING_VERSION = 3.19.1; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_CFLAGS = "-fembed-bitcode"; diff --git a/RadarSDK.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RadarSDK.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000..f5cc1804f --- /dev/null +++ b/RadarSDK.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,51 @@ +{ + "originHash" : "5b93791d19b1fba6dd03f2def460785e85cc17099b7c47acd513cd259380ff0c", + "pins" : [ + { + "identity" : "swift-atomics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-atomics.git", + "state" : { + "revision" : "cd142fd2f64be2100422d658e7411e39489da985", + "version" : "1.2.0" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections.git", + "state" : { + "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7", + "version" : "1.1.4" + } + }, + { + "identity" : "swift-nio", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio.git", + "state" : { + "revision" : "914081701062b11e3bb9e21accc379822621995e", + "version" : "2.76.1" + } + }, + { + "identity" : "swift-nio-ssl", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio-ssl", + "state" : { + "revision" : "c7e95421334b1068490b5d41314a50e70bab23d1", + "version" : "2.29.0" + } + }, + { + "identity" : "swift-system", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-system.git", + "state" : { + "revision" : "c8a44d836fe7913603e246acab7c528c2e780168", + "version" : "1.4.0" + } + } + ], + "version" : 3 +} diff --git a/RadarSDK/Include/Radar.h b/RadarSDK/Include/Radar.h index 27745b9da..96288bb03 100644 --- a/RadarSDK/Include/Radar.h +++ b/RadarSDK/Include/Radar.h @@ -510,6 +510,20 @@ typedef void (^_Nullable RadarLogConversionCompletionHandler)(RadarStatus status */ + (void)stopTrackingVerified NS_SWIFT_NAME(stopTrackingVerified()); +/** + Starts the Radar Verify companion app server. + + @warning Note that you must configure SSL pinning before calling this method. + */ ++ (void)startVerifyServer NS_SWIFT_NAME(startVerifyServer()); + +/** + Stops the Radar Verify companion app server. + + @warning Note that you must configure SSL pinning before calling this method. + */ ++ (void)stopVerifyServer NS_SWIFT_NAME(stopVerifyServer()); + /** Returns the user's last verified location token if still valid, or requests a fresh token if not. diff --git a/RadarSDK/Include/RadarUser.h b/RadarSDK/Include/RadarUser.h index 02a58c2e2..3c981d33a 100644 --- a/RadarSDK/Include/RadarUser.h +++ b/RadarSDK/Include/RadarUser.h @@ -132,8 +132,6 @@ typedef NS_ENUM(NSInteger, RadarActivityType); */ @property (nullable, copy, nonatomic, readonly) NSArray *topChains; -- (NSDictionary *_Nonnull)dictionaryValue; - /** The source of the user's current location. */ @@ -158,4 +156,6 @@ typedef NS_ENUM(NSInteger, RadarActivityType); */ @property (nullable, copy, nonatomic, readonly) RadarFraud *fraud; +- (NSDictionary *_Nonnull)dictionaryValue; + @end diff --git a/RadarSDK/Include/RadarVerifiedLocationToken.h b/RadarSDK/Include/RadarVerifiedLocationToken.h index fd79f0dfa..79a6bb94c 100644 --- a/RadarSDK/Include/RadarVerifiedLocationToken.h +++ b/RadarSDK/Include/RadarVerifiedLocationToken.h @@ -56,6 +56,11 @@ */ @property (nullable, copy, nonatomic, readonly) NSString *_id; +/** + The raw dictionary value of the token. + */ +@property (nullable, copy, nonatomic, readonly) NSDictionary *rawDict; + - (NSDictionary *_Nonnull)dictionaryValue; @end diff --git a/RadarSDK/Radar.m b/RadarSDK/Radar.m index b80d9e8b8..6e3d6d5f8 100644 --- a/RadarSDK/Radar.m +++ b/RadarSDK/Radar.m @@ -23,6 +23,7 @@ #import "RadarReplayBuffer.h" #import "RadarNotificationHelper.h" #import "RadarTripOptions.h" +#import "RadarVerifyServer.h" @interface Radar () @@ -309,6 +310,26 @@ + (void)stopTrackingVerified { [[RadarVerificationManager sharedInstance] stopTrackingVerified]; } ++ (void)startVerifyServer { + [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelInfo type:RadarLogTypeSDKCall message:@"startVerifyServer()"]; + [RadarUtils downloadDataFromURL:[NSURL URLWithString:@"https://s3.us-east-2.amazonaws.com/app.radar-verify.com/mac/c.der"] completionHandler:^(NSData * _Nonnull certData, NSError * _Nonnull error) { + [RadarUtils downloadDataFromURL:[NSURL URLWithString:@"https://s3.us-east-2.amazonaws.com/app.radar-verify.com/mac/id.p12"] completionHandler:^(NSData * _Nonnull identityData, NSError * _Nonnull error) { + if (!certData || !identityData) { + [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelError type:RadarLogTypeSDKCall message:@"Error starting server: Error downloading cert data"]; + return; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [[RadarVerifyServer sharedInstance] startServerWithCertData:certData identityData:identityData]; + }); + }]; + }]; +} + ++ (void)stopVerifyServer { + [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelInfo type:RadarLogTypeSDKCall message:@"stopVerifyServer()"]; + [[RadarVerifyServer sharedInstance] stopServer]; +} + + (void)getVerifiedLocationToken:(RadarTrackVerifiedCompletionHandler)completionHandler { [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelInfo type:RadarLogTypeSDKCall message:@"getVerifiedLocationToken()"]; [[RadarVerificationManager sharedInstance] diff --git a/RadarSDK/RadarLocationManager.m b/RadarSDK/RadarLocationManager.m index 7a4adbc0d..7c1b41c07 100644 --- a/RadarSDK/RadarLocationManager.m +++ b/RadarSDK/RadarLocationManager.m @@ -396,9 +396,7 @@ - (void)updateTracking:(CLLocation *)location fromInitialize:(BOOL)fromInitializ } self.locationManager.desiredAccuracy = desiredAccuracy; - if (@available(iOS 11.0, *)) { - self.lowPowerLocationManager.showsBackgroundLocationIndicator = options.showBlueBar; - } + self.lowPowerLocationManager.showsBackgroundLocationIndicator = options.showBlueBar; BOOL startUpdates = options.showBlueBar || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedAlways; BOOL stopped = [RadarState stopped]; diff --git a/RadarSDK/RadarLog.h b/RadarSDK/RadarLog.h index 6ad9133d8..a033b299c 100644 --- a/RadarSDK/RadarLog.h +++ b/RadarSDK/RadarLog.h @@ -10,7 +10,7 @@ /** Represents a debug log. */ -@interface RadarLog : NSObject +@interface RadarLog : NSObject /** The levels for debug logs. diff --git a/RadarSDK/RadarLog.m b/RadarSDK/RadarLog.m index 0aaff4b9a..3b2d8b462 100644 --- a/RadarSDK/RadarLog.m +++ b/RadarSDK/RadarLog.m @@ -130,4 +130,8 @@ - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeObject:_createdAt forKey:@"createdAt"]; } ++ (BOOL)supportsSecureCoding { + return YES; +} + @end diff --git a/RadarSDK/RadarLogBuffer.m b/RadarSDK/RadarLogBuffer.m index f69459a14..5592b68d5 100644 --- a/RadarSDK/RadarLogBuffer.m +++ b/RadarSDK/RadarLogBuffer.m @@ -111,7 +111,11 @@ - (void)persistLogs { for (NSString *file in files) { NSString *filePath = [self.logFileDir stringByAppendingPathComponent:file]; NSData *fileData = [self.fileHandler readFileAtPath:filePath]; - RadarLog *log = [NSKeyedUnarchiver unarchiveObjectWithData:fileData]; + NSError *error; + RadarLog *log = [NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithObjects:[NSString class], [RadarLog class], [NSDate class], nil] fromData:fileData error:&error]; + if (error) { + NSLog(@"Failed to unarchive log: %@", error); + } if (log && log.message) { [logs addObject:log]; } @@ -122,7 +126,11 @@ - (void)persistLogs { - (void)writeToFileStorage:(NSArray *)logs { for (RadarLog *log in logs) { - NSData *logData = [NSKeyedArchiver archivedDataWithRootObject:log]; + NSError *error; + NSData *logData = [NSKeyedArchiver archivedDataWithRootObject:log requiringSecureCoding:YES error:&error]; + if (error) { + NSLog(@"Failed to archive log: %@", error); + } NSTimeInterval unixTimestamp = [log.createdAt timeIntervalSince1970]; // logs may be created in the same millisecond, so we append a counter to the end of the timestamp to "tiebreak" NSString *unixTimestampString = [NSString stringWithFormat:@"%lld_%04d", (long long)unixTimestamp, fileCounter++]; diff --git a/RadarSDK/RadarReplay.h b/RadarSDK/RadarReplay.h index c67b67742..edf72f3f7 100644 --- a/RadarSDK/RadarReplay.h +++ b/RadarSDK/RadarReplay.h @@ -7,7 +7,7 @@ #import "Radar.h" -@interface RadarReplay : NSObject +@interface RadarReplay : NSObject @property (nonnull, copy, nonatomic, readonly) NSDictionary *replayParams; diff --git a/RadarSDK/RadarReplay.m b/RadarSDK/RadarReplay.m index 7a7d0fb77..b29e99a09 100644 --- a/RadarSDK/RadarReplay.m +++ b/RadarSDK/RadarReplay.m @@ -57,4 +57,8 @@ - (NSUInteger)hash { return [self.replayParams hash]; } ++ (BOOL)supportsSecureCoding { + return YES; +} + @end diff --git a/RadarSDK/RadarReplayBuffer.m b/RadarSDK/RadarReplayBuffer.m index 542cd1fb7..db94c4dd6 100644 --- a/RadarSDK/RadarReplayBuffer.m +++ b/RadarSDK/RadarReplayBuffer.m @@ -50,6 +50,7 @@ - (void)writeNewReplayToBuffer:(NSMutableDictionary *)replayParams { RadarSdkConfiguration *sdkConfiguration = [RadarSettings sdkConfiguration]; if (sdkConfiguration.usePersistence) { NSData *replaysData; + NSError *error; // if buffer length is above 50, remove every fifth replay from the persisted buffer if ([mutableReplayBuffer count] > 50) { @@ -59,12 +60,16 @@ - (void)writeNewReplayToBuffer:(NSMutableDictionary *)replayParams { [prunedBuffer addObject:mutableReplayBuffer[i]]; } } - replaysData = [NSKeyedArchiver archivedDataWithRootObject:prunedBuffer]; + replaysData = [NSKeyedArchiver archivedDataWithRootObject:prunedBuffer requiringSecureCoding:YES error:&error]; } else { - replaysData = [NSKeyedArchiver archivedDataWithRootObject:mutableReplayBuffer]; + replaysData = [NSKeyedArchiver archivedDataWithRootObject:mutableReplayBuffer requiringSecureCoding:YES error:&error]; + } + if (error) { + [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Error archiving replays"]; + return; + } else { + [[NSUserDefaults standardUserDefaults] setObject:replaysData forKey:@"radar-replays"]; } - - [[NSUserDefaults standardUserDefaults] setObject:replaysData forKey:@"radar-replays"]; } } @@ -150,14 +155,26 @@ - (void)removeReplaysFromBuffer:(NSArray *)replays { [mutableReplayBuffer removeObjectsInArray:replays]; // persist the updated buffer - NSData *replaysData = [NSKeyedArchiver archivedDataWithRootObject:mutableReplayBuffer]; - [[NSUserDefaults standardUserDefaults] setObject:replaysData forKey:@"radar-replays"]; + NSError *error; + NSData *replaysData = [NSKeyedArchiver archivedDataWithRootObject:mutableReplayBuffer requiringSecureCoding:YES error:&error]; + if (error) { + [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Error archiving replays"]; + return; + } else { + [[NSUserDefaults standardUserDefaults] setObject:replaysData forKey:@"radar-replays"]; + } } - (void)loadReplaysFromPersistentStore { NSData *replaysData = [[NSUserDefaults standardUserDefaults] objectForKey:@"radar-replays"]; if (replaysData) { - NSArray *replays = [NSKeyedUnarchiver unarchiveObjectWithData:replaysData]; + NSError *error; + NSSet *allowedClasses = [NSSet setWithObjects:[NSArray class], [RadarReplay class], [NSDictionary class], [NSString class], [NSNumber class], nil]; + NSArray *replays = [NSKeyedUnarchiver unarchivedObjectOfClasses:allowedClasses fromData:replaysData error:&error]; + if (error) { + [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Error unarchiving replays"]; + return; + } [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:[NSString stringWithFormat:@"Loaded replays | length = %lu", (unsigned long)[replays count]]]; mutableReplayBuffer = [NSMutableArray arrayWithArray:replays]; } diff --git a/RadarSDK/RadarUtils.h b/RadarSDK/RadarUtils.h index db38af04e..fa880c904 100644 --- a/RadarSDK/RadarUtils.h +++ b/RadarSDK/RadarUtils.h @@ -32,6 +32,7 @@ NS_ASSUME_NONNULL_BEGIN + (NSDictionary *)dictionaryForLocation:(CLLocation *)location; + (NSString *)dictionaryToJson:(NSDictionary *)dict; + (NSDictionary *)extractGeofenceIdAndTimestampFromIdentifier:(NSString *)identifier; ++ (void)downloadDataFromURL:(NSURL *)url completionHandler:(void (^)(NSData *data, NSError *error))completionHandler; + (void)runOnMainThread:(dispatch_block_t)block; @end diff --git a/RadarSDK/RadarUtils.m b/RadarSDK/RadarUtils.m index 11abc6d56..3f67926e5 100644 --- a/RadarSDK/RadarUtils.m +++ b/RadarSDK/RadarUtils.m @@ -45,7 +45,7 @@ + (NSNumber *)timeZoneOffset { } + (NSString *)sdkVersion { - return @"3.19.0"; + return @"3.19.1"; } + (NSString *)deviceId { @@ -176,6 +176,15 @@ + (NSString *)dictionaryToJson:(NSDictionary *)dict { return @{@"geofenceId": geofenceId, @"registeredAt": registeredAt}; } ++ (void)downloadDataFromURL:(NSURL *)url completionHandler:(void (^)(NSData *data, NSError *error))completionHandler { + NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + if (completionHandler) { + completionHandler(data, error); + } + }]; + [dataTask resume]; +} + #pragma mark - threading + (void)runOnMainThread:(dispatch_block_t)block { diff --git a/RadarSDK/RadarVerificationManager.m b/RadarSDK/RadarVerificationManager.m index 2f189a4a7..3a3d6c564 100644 --- a/RadarSDK/RadarVerificationManager.m +++ b/RadarSDK/RadarVerificationManager.m @@ -230,55 +230,50 @@ - (void)startTrackingVerifiedWithInterval:(NSTimeInterval)interval beacons:(BOOL self.startedInterval = interval; self.startedBeacons = beacons; - if (@available(iOS 12.0, *)) { - if (!_monitor) { - _monitor = nw_path_monitor_create(); + if (!_monitor) { + _monitor = nw_path_monitor_create(); - nw_path_monitor_set_queue(_monitor, dispatch_get_main_queue()); + nw_path_monitor_set_queue(_monitor, dispatch_get_main_queue()); - nw_path_monitor_set_update_handler(_monitor, ^(nw_path_t path) { - if (nw_path_get_status(path) == nw_path_status_satisfied) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Network connected"]; - } else { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Network disconnected"]; - } - - NSString *ips = [self getIPs]; - BOOL changed = NO; - - if (!self.lastIPs) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:[NSString stringWithFormat:@"First time getting IPs | ips = %@", ips]]; - changed = NO; - } else if (!ips || [ips isEqualToString:@"error"]) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:[NSString stringWithFormat:@"Error getting IPs | ips = %@", ips]]; - changed = YES; - } else if (![ips isEqualToString:self.lastIPs]) { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:[NSString stringWithFormat:@"IPs changed | ips = %@; lastIPs = %@", ips, self.lastIPs]]; - changed = YES; - } else { - [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"IPs unchanged"]; - } - self.lastIPs = ips; + nw_path_monitor_set_update_handler(_monitor, ^(nw_path_t path) { + if (nw_path_get_status(path) == nw_path_status_satisfied) { + [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Network connected"]; + } else { + [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"Network disconnected"]; + } - if (changed) { - [self callTrackVerified]; - } - }); - - nw_path_monitor_start(_monitor); - } + NSString *ips = [self getIPs]; + BOOL changed = NO; + + if (!self.lastIPs) { + [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:[NSString stringWithFormat:@"First time getting IPs | ips = %@", ips]]; + changed = NO; + } else if (!ips || [ips isEqualToString:@"error"]) { + [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:[NSString stringWithFormat:@"Error getting IPs | ips = %@", ips]]; + changed = YES; + } else if (![ips isEqualToString:self.lastIPs]) { + [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:[NSString stringWithFormat:@"IPs changed | ips = %@; lastIPs = %@", ips, self.lastIPs]]; + changed = YES; + } else { + [[RadarLogger sharedInstance] logWithLevel:RadarLogLevelDebug message:@"IPs unchanged"]; + } + self.lastIPs = ips; + + if (changed) { + [self callTrackVerified]; + } + }); + nw_path_monitor_start(_monitor); } - + [self callTrackVerified]; } - (void)stopTrackingVerified { self.started = NO; - - if (@available(iOS 12.0, *)) { - if (_monitor) { - nw_path_monitor_cancel(_monitor); - } + + if (_monitor) { + nw_path_monitor_cancel(_monitor); } [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(intervalFired) object:nil]; diff --git a/RadarSDK/RadarVerifiedLocationToken+Internal.h b/RadarSDK/RadarVerifiedLocationToken+Internal.h index 216cb664c..d03a8288f 100644 --- a/RadarSDK/RadarVerifiedLocationToken+Internal.h +++ b/RadarSDK/RadarVerifiedLocationToken+Internal.h @@ -17,7 +17,8 @@ expiresIn:(NSTimeInterval)expiresIn passed:(BOOL)passed failureReasons:(NSArray *_Nonnull)failureReasons - _id:(NSString *_Nonnull)_id; + _id:(NSString *_Nonnull)_id + rawDict:(NSDictionary *_Nonnull)rawDict; - (instancetype _Nullable)initWithObject:(id _Nonnull)object; @end diff --git a/RadarSDK/RadarVerifiedLocationToken.m b/RadarSDK/RadarVerifiedLocationToken.m index 594a21f2e..2c10a51f9 100644 --- a/RadarSDK/RadarVerifiedLocationToken.m +++ b/RadarSDK/RadarVerifiedLocationToken.m @@ -20,7 +20,8 @@ - (instancetype _Nullable)initWithUser:(RadarUser *_Nonnull)user expiresIn:(NSTimeInterval)expiresIn passed:(BOOL)passed failureReasons:(NSArray * _Nonnull)failureReasons - _id:(NSString * _Nonnull)_id { + _id:(NSString * _Nonnull)_id + rawDict:(NSDictionary * _Nonnull)rawDict { self = [super init]; if (self) { _user = user; @@ -31,6 +32,7 @@ - (instancetype _Nullable)initWithUser:(RadarUser *_Nonnull)user _passed = passed; _failureReasons = failureReasons; __id = _id; + _rawDict = rawDict; } return self; } @@ -91,21 +93,14 @@ - (instancetype _Nullable)initWithObject:(id _Nonnull)object { } if (user && events && token && expiresAt) { - return [[RadarVerifiedLocationToken alloc] initWithUser:user events:events token:token expiresAt:expiresAt expiresIn:expiresIn passed:passed failureReasons:failureReasons _id:_id]; + return [[RadarVerifiedLocationToken alloc] initWithUser:user events:events token:token expiresAt:expiresAt expiresIn:expiresIn passed:passed failureReasons:failureReasons _id:_id rawDict:dict]; } return nil; } - (NSDictionary *)dictionaryValue { - NSMutableDictionary *dict = [NSMutableDictionary new]; - dict[@"user"] = [self.user dictionaryValue]; - dict[@"events"] = [RadarEvent arrayForEvents:self.events]; - dict[@"token"] = self.token; - dict[@"expiresAt"] = [RadarUtils.isoDateFormatter stringFromDate:self.expiresAt]; - dict[@"expiresIn"] = @(self.expiresIn); - dict[@"passed"] = @(self.passed); - return dict; + return self.rawDict; } @end diff --git a/RadarSDK/RadarVerifyServer.h b/RadarSDK/RadarVerifyServer.h new file mode 100644 index 000000000..d5626b0a1 --- /dev/null +++ b/RadarSDK/RadarVerifyServer.h @@ -0,0 +1,17 @@ +// +// RadarVerifyServer.h +// RadarSDK +// +// Created by Nick Patrick on 11/11/24. +// Copyright © 2024 Radar Labs, Inc. All rights reserved. +// + +#import + +@interface RadarVerifyServer: NSObject + ++ (instancetype)sharedInstance; +- (void)startServerWithCertData:(NSData *)certData identityData:(NSData *)identityData; +- (void)stopServer; + +@end diff --git a/RadarSDK/RadarVerifyServer.swift b/RadarSDK/RadarVerifyServer.swift new file mode 100644 index 000000000..d6478b24f --- /dev/null +++ b/RadarSDK/RadarVerifyServer.swift @@ -0,0 +1,157 @@ +// +// RadarVerifyServer.swift +// RadarSDK +// +// Created by Nick Patrick on 11/11/24. +// Copyright © 2024 Radar Labs, Inc. All rights reserved. +// + +import Foundation +import UserNotifications + +#if canImport(Telegraph) +import Telegraph + +@objc(RadarVerifyServer) class RadarVerifyServer: NSObject, CLLocationManagerDelegate { + @MainActor @objc static let sharedInstance = RadarVerifyServer() + + private var locationManager = CLLocationManager() + private var server: Server? + + override init() { + super.init() + + locationManager.delegate = self + locationManager.requestWhenInUseAuthorization() + locationManager.allowsBackgroundLocationUpdates = true + locationManager.pausesLocationUpdatesAutomatically = false + locationManager.showsBackgroundLocationIndicator = true + locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers + locationManager.distanceFilter = 3000 + } + + private func addHeaders(to response: HTTPResponse) { + response.headers["Access-Control-Allow-Headers"] = "Content-Type, Accept, Authorization, X-Radar-Device-Type, X-Radar-SDK-Version" + response.headers["Access-Control-Allow-Origin"] = "*" + response.headers["Access-Control-Allow-Methods"] = "GET, OPTIONS" + } + + @objc func startServer(withCertData certData: Data, identityData: Data) { + do { + guard let identity = CertificateIdentity(p12Data: identityData), + let caCertificate = Certificate(derData: certData) else { + print("Error starting server: Error parsing cert data") + return + } + + server = Server(identity: identity, caCertificates: [caCertificate]) + + self.server?.route(.OPTIONS, "/v1/verify") { _ in + let response = HTTPResponse(.ok) + self.addHeaders(to: response) + return response + } + + self.server?.route(.GET, "/v1/verify") { request in + let userId = request.params["userId"] + let description = request.params["description"] + + Radar.setUserId(userId) + Radar.setDescription(description) + + var response = HTTPResponse(.internalServerError) + + let semaphore = DispatchSemaphore(value: 0) + + self.showNotification(title: "Location check in progress...") + + Radar.trackVerified { status, token in + if status == .success, let rawDict = token?.rawDict { + do { + let mutableDict = NSMutableDictionary(dictionary: rawDict) + mutableDict["meta"] = ["code": 200] + + let data = try JSONSerialization.data(withJSONObject: mutableDict) + + if let content = String(data: data, encoding: .utf8) { + response = HTTPResponse(.ok, content: content) + } + } catch { + + } + } else if status == .errorUnauthorized { + response = HTTPResponse(.unauthorized) + } else if status == .errorForbidden { + response = HTTPResponse(.forbidden) + } else if status == .errorPaymentRequired { + response = HTTPResponse(.paymentRequired) + } else if status == .errorPermissions { + let content = "{\"meta\":{\"code\":400,\"error\":\"ERROR_PERMISSIONS\"}}" + response = HTTPResponse(.badRequest, content: content) + } else if status == .errorLocation { + let content = "{\"meta\":{\"code\":400,\"error\":\"ERROR_LOCATION\"}}" + response = HTTPResponse(.badRequest, content: content) + } else if status == .errorNetwork { + let content = "{\"meta\":{\"code\":400,\"error\":\"ERROR_NETWORK\"}}" + response = HTTPResponse(.badRequest, content: content) + } + semaphore.signal() + } + + semaphore.wait() + + self.addHeaders(to: response) + return response + } + + self.locationManager.startUpdatingLocation() + try self.server?.start(port: 52516, interface: "127.0.0.1") + } catch { + print("Failed to start server: \(error)") + } + } + + @objc func stopServer() { + self.server?.stop() + self.server = nil + self.locationManager.stopUpdatingLocation() + } + + private func showNotification(title: String? = nil, body: String? = nil) { + let content = UNMutableNotificationContent() + if let title = title { content.title = title } + if let body = body { content.body = body } + content.sound = UNNotificationSound.default + + let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil) + UNUserNotificationCenter.current().add(request, withCompletionHandler: nil) + } + + private func downloadData(from url: URL) async throws -> Data { + var request = URLRequest(url: url) + request.cachePolicy = .reloadIgnoringLocalCacheData + + let (data, response) = try await URLSession.shared.data(for: request) + + guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { + throw URLError(.badServerResponse) + } + + return data + } + +} +#else +@objc(RadarVerifyServer) class RadarVerifyServer: NSObject { + @MainActor @objc static let sharedInstance = RadarVerifyServer() + + @objc func startServer(withCertData certData: Data, identityData: Data) { + print("Error starting server: Missing dependencies") + } + + @objc func stopServer() { + print("Error stopping server: Error parsing cert data") + } + +} +#endif diff --git a/RadarSDKMotion.podspec b/RadarSDKMotion.podspec index e599fc506..08719b872 100644 --- a/RadarSDKMotion.podspec +++ b/RadarSDKMotion.podspec @@ -1,16 +1,15 @@ Pod::Spec.new do |s| - s.name = 'RadarSDKMotion' - s.version = '3.19.0' - s.summary = 'Motion detection plugin for RadarSDK, the leading geofencing and location tracking platform' - s.homepage = 'https://radar.com' - s.author = { 'Radar Labs, Inc.' => 'support@radar.com' } - s.platform = :ios - s.source = { :git => 'https://github.com/radarlabs/radar-sdk-ios.git', :tag => s.version.to_s } - s.source_files = ["RadarSDKMotion/RadarSDKMotion/*.{h,m}", "RadarSDKMotion/RadarSDKMotion/Include/*.h"] - s.module_name = 'RadarSDKMotion' - s.ios.deployment_target = '10.0' - s.frameworks = 'CoreMotion' - s.requires_arc = true - s.license = { :type => 'Apache-2.0' } - end - \ No newline at end of file + s.name = 'RadarSDKMotion' + s.version = '3.19.1' + s.summary = 'Motion detection plugin for RadarSDK, the leading geofencing and location tracking platform' + s.homepage = 'https://radar.com' + s.author = { 'Radar Labs, Inc.' => 'support@radar.com' } + s.platform = :ios + s.source = { :git => 'https://github.com/radarlabs/radar-sdk-ios.git', :tag => s.version.to_s } + s.source_files = ["RadarSDKMotion/RadarSDKMotion/*.{h,m}", "RadarSDKMotion/RadarSDKMotion/Include/*.h"] + s.module_name = 'RadarSDKMotion' + s.ios.deployment_target = '12.0' + s.frameworks = 'CoreMotion' + s.requires_arc = true + s.license = { :type => 'Apache-2.0' } +end diff --git a/RadarSDKMotion/RadarSDKMotion.xcodeproj/project.pbxproj b/RadarSDKMotion/RadarSDKMotion.xcodeproj/project.pbxproj index b6ff455c9..a2bf691ea 100644 --- a/RadarSDKMotion/RadarSDKMotion.xcodeproj/project.pbxproj +++ b/RadarSDKMotion/RadarSDKMotion.xcodeproj/project.pbxproj @@ -289,6 +289,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 3.18.4; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -347,6 +348,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 3.18.4; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -376,7 +378,6 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = RadarLabs.RadarSDKMotion; @@ -407,7 +408,6 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = RadarLabs.RadarSDKMotion; @@ -425,7 +425,6 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 96GHH65B9D; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = RadarLabs.RadarMotionTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -440,7 +439,6 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 96GHH65B9D; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = RadarLabs.RadarMotionTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; diff --git a/RadarSDKTests/RadarSDKTests.m b/RadarSDKTests/RadarSDKTests.m index 83b23cf0f..d55798f1c 100644 --- a/RadarSDKTests/RadarSDKTests.m +++ b/RadarSDKTests/RadarSDKTests.m @@ -20,7 +20,7 @@ #import "RadarTestUtils.h" #import "RadarTripOptions.h" #import "RadarFileStorage.h" - +#import "RadarReplayBuffer.h" @interface RadarSDKTests : XCTestCase @@ -30,6 +30,7 @@ @interface RadarSDKTests : XCTestCase @property (nonatomic, strong) RadarFileStorage *fileSystem; @property (nonatomic, strong) NSString *testFilePath; @property (nonatomic, strong) RadarLogBuffer *logBuffer; +@property (nonatomic, strong) RadarReplayBuffer *replayBuffer; @end @implementation RadarSDKTests @@ -299,7 +300,7 @@ - (void)setUp { self.testFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"testfile"]; [[RadarLogBuffer sharedInstance]clearBuffer]; [[RadarLogBuffer sharedInstance]setPersistentLogFeatureFlag:YES]; - + [[RadarReplayBuffer sharedInstance]clearBuffer]; } - (void)tearDown { @@ -1491,6 +1492,22 @@ - (void)test_RadarLogBuffer_purge { [[RadarLogBuffer sharedInstance]clearBuffer]; } +- (void)test_RadarReplayBuffer_writeAndRead { + RadarSdkConfiguration *sdkConfiguration = [RadarSettings sdkConfiguration]; + sdkConfiguration.usePersistence = true; + [RadarSettings setSdkConfiguration:sdkConfiguration]; + + CLLocation *location = [[CLLocation alloc] initWithLatitude:0.1 longitude:0.1]; + NSMutableDictionary * params = [RadarTestUtils createTrackParamWithLocation:location stopped:YES foreground:YES source:RadarLocationSourceGeofenceEnter replayed:YES beacons:[NSArray arrayWithObject:[RadarBeacon alloc]] verified:YES attestationString:@"attestationString" keyId:@"keyID" attestationError:@"attestationError" encrypted:YES expectedCountryCode:@"CountryCode" expectedStateCode:@"StateCode"]; + + [[RadarReplayBuffer sharedInstance] writeNewReplayToBuffer:params]; + [[RadarReplayBuffer sharedInstance] setValue:NULL forKey:@"mutableReplayBuffer"]; + [[RadarReplayBuffer sharedInstance] loadReplaysFromPersistentStore]; + NSMutableArray *mutableReplayBuffer = [[RadarReplayBuffer sharedInstance] valueForKey:@"mutableReplayBuffer"]; + XCTAssertEqual(mutableReplayBuffer.count, 1); + XCTAssertEqualObjects(mutableReplayBuffer.firstObject.replayParams, params); +} + - (void)test_RadarSdkConfiguration { RadarSdkConfiguration *sdkConfiguration = [[RadarSdkConfiguration alloc] initWithDict:@{ @"logLevel": @"warning", diff --git a/RadarSDKTests/RadarTestUtils.h b/RadarSDKTests/RadarTestUtils.h index 1f78dc911..9dfd0fed2 100644 --- a/RadarSDKTests/RadarTestUtils.h +++ b/RadarSDKTests/RadarTestUtils.h @@ -7,12 +7,38 @@ #import +#import "CLLocation+Radar.h" +#import "RadarLocationManager.h" +#import "RadarSettings.h" +#import "RadarState.h" +#import "RadarUtils.h" +#import "RadarTripOptions.h" +#import "RadarVerificationManager.h" + NS_ASSUME_NONNULL_BEGIN @interface RadarTestUtils : NSObject + (NSDictionary *)jsonDictionaryFromResource:(NSString *)resource; +/** + Construct a track param dict which can be used to test replays from paramters of the track call. + uses the stored values from `RadarSettings`, `RadarUtils`, and `RadarState` + */ ++ (NSMutableDictionary *)createTrackParamWithLocation:(CLLocation *_Nonnull)location + stopped:(BOOL)stopped + foreground:(BOOL)foreground + source:(RadarLocationSource)source + replayed:(BOOL)replayed + beacons:(NSArray *_Nullable)beacons + verified:(BOOL)verified + attestationString:(NSString *_Nullable)attestationString + keyId:(NSString *_Nullable)keyId + attestationError:(NSString *_Nullable)attestationError + encrypted:(BOOL)encrypted + expectedCountryCode:(NSString * _Nullable)expectedCountryCode + expectedStateCode:(NSString * _Nullable)expectedStateCode; + @end NS_ASSUME_NONNULL_END diff --git a/RadarSDKTests/RadarTestutils.m b/RadarSDKTests/RadarTestutils.m index 83d61937c..b725c9956 100644 --- a/RadarSDKTests/RadarTestutils.m +++ b/RadarSDKTests/RadarTestutils.m @@ -18,4 +18,181 @@ + (NSDictionary *)jsonDictionaryFromResource:(NSString *)resource { return jsonDict; } ++ (NSMutableDictionary *)createTrackParamWithLocation:(CLLocation *_Nonnull)location + stopped:(BOOL)stopped + foreground:(BOOL)foreground + source:(RadarLocationSource)source + replayed:(BOOL)replayed + beacons:(NSArray *_Nullable)beacons + verified:(BOOL)verified + attestationString:(NSString *_Nullable)attestationString + keyId:(NSString *_Nullable)keyId + attestationError:(NSString *_Nullable)attestationError + encrypted:(BOOL)encrypted + expectedCountryCode:(NSString * _Nullable)expectedCountryCode + expectedStateCode:(NSString * _Nullable)expectedStateCode{ + NSMutableDictionary *params = [NSMutableDictionary new]; + BOOL anonymous = [RadarSettings anonymousTrackingEnabled]; + params[@"anonymous"] = @(anonymous); + if (anonymous) { + params[@"deviceId"] = @"anonymous"; + params[@"geofenceIds"] = [RadarState geofenceIds]; + params[@"placeId"] = [RadarState placeId]; + params[@"regionIds"] = [RadarState regionIds]; + params[@"beaconIds"] = [RadarState beaconIds]; + } else { + params[@"id"] = [RadarSettings _id]; + params[@"installId"] = [RadarSettings installId]; + params[@"userId"] = [RadarSettings userId]; + params[@"deviceId"] = [RadarUtils deviceId]; + params[@"description"] = [RadarSettings __description]; + params[@"metadata"] = [RadarSettings metadata]; + NSString *sessionId = [RadarSettings sessionId]; + if (sessionId) { + params[@"sessionId"] = sessionId; + } + } + params[@"latitude"] = @(location.coordinate.latitude); + params[@"longitude"] = @(location.coordinate.longitude); + CLLocationAccuracy accuracy = location.horizontalAccuracy; + if (accuracy <= 0) { + accuracy = 1; + } + params[@"accuracy"] = @(accuracy); + params[@"altitude"] = @(location.altitude); + params[@"verticalAccuracy"] = @(location.verticalAccuracy); + params[@"speed"] = @(location.speed); + params[@"speedAccuracy"] = @(location.speedAccuracy); + params[@"course"] = @(location.course); + if (@available(iOS 13.4, *)) { + params[@"courseAccuracy"] = @(location.courseAccuracy); + } + if (location.floor) { + params[@"floorLevel"] = @(location.floor.level); + } + long nowMs = (long)([NSDate date].timeIntervalSince1970 * 1000); + if (!foreground) { + long timeInMs = (long)(location.timestamp.timeIntervalSince1970 * 1000); + params[@"updatedAtMsDiff"] = @(nowMs - timeInMs); + } + params[@"foreground"] = @(foreground); + params[@"stopped"] = @(stopped); + params[@"replayed"] = @(replayed); + params[@"deviceType"] = [RadarUtils deviceType]; + params[@"deviceMake"] = [RadarUtils deviceMake]; + params[@"sdkVersion"] = [RadarUtils sdkVersion]; + params[@"deviceModel"] = [RadarUtils deviceModel]; + params[@"deviceOS"] = [RadarUtils deviceOS]; + params[@"country"] = [RadarUtils country]; + params[@"timeZoneOffset"] = [RadarUtils timeZoneOffset]; + params[@"source"] = [Radar stringForLocationSource:source]; + if ([RadarSettings xPlatform]) { + params[@"xPlatformType"] = [RadarSettings xPlatformSDKType]; + params[@"xPlatformSDKVersion"] = [RadarSettings xPlatformSDKVersion]; + } else { + params[@"xPlatformType"] = @"Native"; + } + NSMutableArray *fraudFailureReasons = [NSMutableArray new]; + if (@available(iOS 15.0, *)) { + CLLocationSourceInformation *sourceInformation = location.sourceInformation; + if (sourceInformation) { + if (sourceInformation.isSimulatedBySoftware) { + params[@"mocked"] = @(YES); + [fraudFailureReasons addObject:@"fraud_mocked_from_mock_provider"]; + } + if (sourceInformation.isProducedByAccessory) { + [fraudFailureReasons addObject:@"fraud_mocked_produced_by_accessory"]; + } + } + } + + RadarTripOptions *tripOptions = Radar.getTripOptions; + + if (tripOptions) { + NSMutableDictionary *tripParams = [NSMutableDictionary new]; + tripParams[@"version"] = @("2"); + [tripParams setValue:tripOptions.externalId forKey:@"externalId"]; + [tripParams setValue:tripOptions.metadata forKey:@"metadata"]; + [tripParams setValue:tripOptions.destinationGeofenceTag forKey:@"destinationGeofenceTag"]; + [tripParams setValue:tripOptions.destinationGeofenceExternalId forKey:@"destinationGeofenceExternalId"]; + [tripParams setValue:[Radar stringForMode:tripOptions.mode] forKey:@"mode"]; + params[@"tripOptions"] = tripParams; + } + + RadarTrackingOptions *options = [Radar getTrackingOptions]; + if (options.syncGeofences) { + params[@"nearbyGeofences"] = @(YES); + } + if (beacons) { + params[@"beacons"] = [RadarBeacon arrayForBeacons:beacons]; + } + NSString *locationAuthorization = [RadarUtils locationAuthorization]; + if (locationAuthorization) { + params[@"locationAuthorization"] = locationAuthorization; + } + NSString *locationAccuracyAuthorization = [RadarUtils locationAccuracyAuthorization]; + if (locationAccuracyAuthorization) { + params[@"locationAccuracyAuthorization"] = locationAccuracyAuthorization; + } + params[@"notificationAuthorization"] = [RadarState notificationPermissionGranted] ? @"true" : @"false"; + + params[@"trackingOptions"] = [options dictionaryValue]; + + BOOL usingRemoteTrackingOptions = RadarSettings.tracking && RadarSettings.remoteTrackingOptions; + params[@"usingRemoteTrackingOptions"] = @(usingRemoteTrackingOptions); + + params[@"verified"] = @(verified); + if (verified) { + params[@"attestationString"] = attestationString; + params[@"keyId"] = keyId; + params[@"attestationError"] = attestationError; + params[@"encrypted"] = @(encrypted); + BOOL jailbroken = [[RadarVerificationManager sharedInstance] isJailbroken]; + params[@"compromised"] = @(jailbroken); + if (jailbroken) { + [fraudFailureReasons addObject:@"fraud_compromised_jailbroken"]; + } + if (expectedCountryCode) { + params[@"expectedCountryCode"] = expectedCountryCode; + } + if (expectedStateCode) { + params[@"expectedStateCode"] = expectedStateCode; + } + } + params[@"appId"] = [[NSBundle mainBundle] bundleIdentifier]; + RadarSdkConfiguration *sdkConfiguration = [RadarSettings sdkConfiguration]; + if (sdkConfiguration.useLocationMetadata) { + NSMutableDictionary *locationMetadata = [NSMutableDictionary new]; + locationMetadata[@"motionActivityData"] = [RadarState lastMotionActivityData]; + locationMetadata[@"heading"] = [RadarState lastHeadingData]; + locationMetadata[@"speed"] = @(location.speed); + locationMetadata[@"speedAccuracy"] = @(location.speedAccuracy); + locationMetadata[@"course"] = @(location.course); + + if (@available(iOS 13.4, *)) { + locationMetadata[@"courseAccuracy"] = @(location.courseAccuracy); + } + + locationMetadata[@"battery"] = @([[UIDevice currentDevice] batteryLevel]); + locationMetadata[@"altitude"] = @(location.altitude); + + if (@available(iOS 15, *)) { + locationMetadata[@"ellipsoidalAltitude"] = @(location.ellipsoidalAltitude); + locationMetadata[@"isProducedByAccessory"] = @([location.sourceInformation isProducedByAccessory]); + locationMetadata[@"isSimulatedBySoftware"] = @([location.sourceInformation isSimulatedBySoftware]); + } + locationMetadata[@"floor"] = @([location.floor level]); + + params[@"locationMetadata"] = locationMetadata; + } + + params[@"fraudFailureReasons"] = fraudFailureReasons; + + // added after API call fail + params[@"replayed"] = @(YES); + params[@"updatedAtMs"] = @(nowMs); + + return params; +} + @end diff --git a/set_version.sh b/set_version.sh new file mode 100755 index 000000000..3179c6644 --- /dev/null +++ b/set_version.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +if [ $# -lt 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +# sed has slightly different syntax on linux vs mac +if [ $(uname -s) = "Darwin" ]; then + alias sed_inplace="sed -E -i ''" +else + alias sed_inplace="sed -E -i" +fi + +version_full=$1 +version="${version_full%%-*}" + +sed_inplace "s/s.version( +)= '(.+)'/s.version\1= '$version_full'/" RadarSDK.podspec +sed_inplace "s/s.version( +)= '(.+)'/s.version\1= '$version_full'/" RadarSDKMotion.podspec + +sed_inplace "s/MARKETING_VERSION = .+;/MARKETING_VERSION = $version;/" RadarSDK.xcodeproj/project.pbxproj +sed_inplace "s/MARKETING_VERSION = .+;/MARKETING_VERSION = $version;/" RadarSDKMotion/RadarSDKMotion.xcodeproj/project.pbxproj + +sed_inplace "s/return @\"[0-9]+\.[0-9]+\.[0-9]+\";/return @\"$version_full\";/" RadarSDK/RadarUtils.m