From 585759048345ab7defdfd9cae3a6c76dc451fe51 Mon Sep 17 00:00:00 2001 From: ijunaid Date: Wed, 11 May 2022 18:53:05 +0500 Subject: [PATCH] Updating underlying SDKs (#66) * Updating underlying SDKs -- Replaced deprecated functions in iOS for present rating widget -- Implement direct attribution for iOS also * Update CHANGELOG.md not relevant for Flutter * updating recordIndirectAttribution with new calls in iOS SDK Co-authored-by: ArtursKadikis --- CHANGELOG.md | 31 +- android/build.gradle | 2 +- ios/Classes/CountlyFlutterPlugin.m | 45 +- ios/Classes/CountlyiOS/CHANGELOG.md | 570 ++++++++++++++++++ ios/Classes/CountlyiOS/Countly-PL.podspec | 43 ++ ios/Classes/CountlyiOS/Countly.h | 54 +- ios/Classes/CountlyiOS/Countly.m | 93 +++ ios/Classes/CountlyiOS/Countly.podspec | 31 + ios/Classes/CountlyiOS/CountlyCommon.m | 2 +- ios/Classes/CountlyiOS/CountlyConfig.h | 24 +- .../CountlyiOS/CountlyConnectionManager.h | 5 +- .../CountlyiOS/CountlyConnectionManager.m | 55 +- .../CountlyiOS/CountlyConsentManager.m | 62 +- ios/Classes/CountlyiOS/CountlyFeedbacks.h | 1 + ios/Classes/CountlyiOS/CountlyFeedbacks.m | 24 + .../CountlyiOS/CountlyPushNotifications.m | 14 +- ios/Classes/CountlyiOS/LICENSE.md | 19 + ios/Classes/CountlyiOS/README.md | 59 ++ ios/Classes/CountlyiOS/SECURITY.md | 3 + .../CountlyiOS/countly_dsym_uploader.sh | 121 ++++ lib/countly_config.dart | 1 - lib/countly_flutter.dart | 4 - 22 files changed, 1182 insertions(+), 81 deletions(-) create mode 100644 ios/Classes/CountlyiOS/CHANGELOG.md create mode 100644 ios/Classes/CountlyiOS/Countly-PL.podspec create mode 100644 ios/Classes/CountlyiOS/Countly.podspec create mode 100644 ios/Classes/CountlyiOS/LICENSE.md create mode 100644 ios/Classes/CountlyiOS/README.md create mode 100644 ios/Classes/CountlyiOS/SECURITY.md create mode 100755 ios/Classes/CountlyiOS/countly_dsym_uploader.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index b31b93b0..8af87011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ ## 21.11.0 +* !! Major breaking change !! Changing device ID without merging will now clear all consent. It has to be given again after this operation. +* !! Major breaking change !! Entering temporary ID mode will now clear all consent. It has to be given again after this operation. +* Added mitigations for potential push notification issue where some apps might be unable to display push notifications in their kill state. * Added 'CountlyConfig' class for init time configurations. +* Added a way to retrieve feedback widget data and manually report them for iOS also +* Added Appear and dismiss callback for nps/survey widgets +* Added an optional 'onFinished' callback to 'getFeedbackWidgetData' method +* Added "getDeviceIDType" method to get current device id type +* Added "recordIndirectAttribution" method +* Added "recordDirectAttribution" method +* Added "setUserLocation" method to set user location +* Added platform information to push actioned events +* Fixed potential deadlock issue in Android. +* Fixed possible SecTrustCopyExceptions leak in iOS +* Fixed bug that occured when recording user profile values. Parameters not provided would be deleted from the server. * Deprecated old init config methods. You should use the config object now. Those methods are: - init - manualSessionHandling @@ -19,22 +33,17 @@ - enableCrashReporting - setCustomCrashSegment - enableApm -* Added a way to retrieve feedback widget data and manually report them for iOS also -* Added Appear and dismiss callback for nps/survey widgets -* Added an optional 'onFinished' callback to 'getFeedbackWidgetData' method -* Added "getDeviceIDType" method to get current device id type -* Added "recordIndirectAttribution" method -* Added "recordDirectAttribution" method (Currently supporterd only for Android) -* Added "setUserLocation" method to set user location * Deprecated "setLocation" method * Deprecated recordAttributionID method * Deprecated enableAttribution method * Deprecated 'askForFeedback' method. Added 'presentRatingWidgetWithID' method that should be used as it's replacement. -* Fixed bug that occured when recording user profile values. Parameters not provided would be deleted from the server. -* Added mitigations for potential push notification issue where some apps might be unable to display push notifications in their kill state. +* Device ID can now be changed when no consent is given +* Push notification now display/use the sent badge number in Android. It's visualization depends on the launcher. +* When recording internal events with 'recordEvent', the respective feature consent will now be checked instead of the 'events' consent. +* Consent changes will now send the whole consent state and not just the "delta" * Updated minimum supported iOS versions to 10.0 -* Updated underlying android SDK to 21.11.0-RC5 -* Updated underlying iOS SDK to 21.11.1 +* Updated underlying android SDK to 21.11.0 +* Updated underlying iOS SDK to 21.11.2 ## 20.11.4 * Moving a push related broadcast receiver declaration to the manifest to comply with 'PendingIntent' checks diff --git a/android/build.gradle b/android/build.gradle index 578ea179..d9f1a0c4 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -34,6 +34,6 @@ android { } dependencies { - implementation 'ly.count.android:sdk:21.11.0-RC5' + implementation 'ly.count.android:sdk:21.11.0' implementation 'com.google.firebase:firebase-messaging:20.2.1' } diff --git a/ios/Classes/CountlyFlutterPlugin.m b/ios/Classes/CountlyFlutterPlugin.m index d38ac181..1d2430f9 100644 --- a/ios/Classes/CountlyFlutterPlugin.m +++ b/ios/Classes/CountlyFlutterPlugin.m @@ -756,7 +756,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { }else if ([@"presentRatingWidgetWithID" isEqualToString:call.method]) { dispatch_async(dispatch_get_main_queue(), ^ { NSString* widgetId = [command objectAtIndex:0]; - [Countly.sharedInstance presentFeedbackWidgetWithID:widgetId completionHandler:^(NSError* error){ + [Countly.sharedInstance presentRatingWidgetWithID:widgetId completionHandler:^(NSError* error){ NSString* errorStr = nil; if (error){ @@ -928,18 +928,27 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { NSException *e = [NSException exceptionWithName:@"Native Exception Crash!" reason:@"Throw Native Exception..." userInfo:nil]; @throw e; }); - }else if([@"recordIndirectAttribution" isEqualToString:call.method]) { + }else if([@"recordDirectAttribution" isEqualToString:call.method]) { + dispatch_async(dispatch_get_main_queue(), ^ { + NSString* campaignType = [command objectAtIndex:0]; + NSString* campaignData = [command objectAtIndex:1]; + if(CountlyCommon.sharedInstance.hasStarted) { + [Countly.sharedInstance recordDirectAttributionWithCampaignType:campaignType andCampaignData:campaignData]; + } + else { + config.campaignType = campaignType; + config.campaignData = campaignData; + } + }); + } + else if([@"recordIndirectAttribution" isEqualToString:call.method]) { dispatch_async(dispatch_get_main_queue(), ^ { NSDictionary* attributionValues = [command objectAtIndex:0]; - NSString* IDFAKey = @"idfa"; - NSString* attributionID = [attributionValues objectForKey:IDFAKey]; - if (attributionID) { - if(CountlyCommon.sharedInstance.hasStarted) { - [Countly.sharedInstance recordAttributionID: attributionID]; - } - else { - config.attributionID = attributionID; - } + if(CountlyCommon.sharedInstance.hasStarted) { + [Countly.sharedInstance recordIndirectAttribution: attributionValues]; + } + else { + config.indirectAttribution = attributionValues; } }); }else if ([@"appLoadingFinished" isEqualToString:call.method]) { @@ -1114,12 +1123,18 @@ - (void)populateConfig:(NSDictionary*)_config if(ipAddress) { config.IP = ipAddress; } + + NSString* campaignType = _config[@"campaignType"]; + if(campaignType) { + config.campaignType = campaignType; + config.campaignData = _config[@"campaignData"];; + } + NSDictionary* attributionValues = _config[@"attributionValues"]; - NSString* IDFAKey = @"idfa"; - NSString* attributionID = [attributionValues objectForKey:IDFAKey]; - if (attributionID) { - config.attributionID = attributionID; + if(attributionValues) { + config.indirectAttribution = attributionValues; } + } @catch(NSException *exception){ COUNTLY_FLUTTER_LOG(@"populateConfig, Unable to parse Config object: %@", exception); diff --git a/ios/Classes/CountlyiOS/CHANGELOG.md b/ios/Classes/CountlyiOS/CHANGELOG.md new file mode 100644 index 00000000..32f36a71 --- /dev/null +++ b/ios/Classes/CountlyiOS/CHANGELOG.md @@ -0,0 +1,570 @@ +## 21.11.2 +- Added direct and indirect attribution +- Added platform info to default segmentation of push action events +- Added `recordRatingWidgetWithID:rating:email:comment:userCanBeContacted:` method to be able to manually record rating widgets +- Added macOS version info to `Countly.xcodeproj` (thanks @ntadej) +- Updated sending consent changes to inlude all current consents state +- Excluded Countly-PL.podspec from SPM manifest (thanks @harrisg) +- Fixed possible SecTrustCopyExceptions leak +- Deprecated `presentFeedbackWidgetWithID:completionHandler:` method + + + +## 21.11.1 +- Fixed a crash when some default user detail properties are set to `NSNull` (thanks @lhunath) +- Updated README.md for minimum supported deployment targets + + + +## 21.11.0 +- Updated minimum supported OS versions as `iOS 10.0`, `tvOS 10.0`, `watchOS 4.0` and `macOS 10.14` +- Updated some deprecated API usage to get rid of warnings +- Added configurable internal limits `maxKeyLength`, `maxValueLength` and `maxSegmentationValues` +- Added `enableOrientationTracking` config for disabling automatic user interface orientation tracking +- Added `setNewHost:` method to be able change the host on the go +- Added `shouldIgnoreTrustCheck` config for self-signed certificates (thanks @centrinvest) +- Created additional `Countly-PL.podspec` for avoiding static framework issue on original `Countly.podspec` (thanks @multinerd) +- Implemented cancelling all consents when device ID is changed without a merge +- Implemented by-passing events consent for reserved internal events +- Discarded consent requirement for changing device ID +- Discarded auto metrics for Apple Watch +- Discarded `customHeaderFieldName` and `customHeaderFieldValue` config properties +- Discarded `setCustomHeaderFieldValue:` method +- Fixed missing nullability specifier on `CountlyCommon.h` +- Fixed missing info level logs on `CountlyFeedbackWidget` class +- Fixed missing info level logs on `CountlyUserDetails` class +- Deprecated `userLoggedIn:` and `userLoggedOut` methods +- Deprecated going back to default system device ID + +- Other various improvements + - Updated HeaderDocs, internal logs, inline notes and pragma marks + - Updated Countly project settings for Xcode 13.1 + - Deleted previously deprecated methods and properties + - Refactored `connectionType` method + + + +## 20.11.3 +- Added optional appear and dismiss callbacks for feedback widget presenting +- Added manually displayed and recorded feedback widgets support +- Fixed HTTP method check for feedback widget requests +- Implemented immediately sending of queued events when a widget event is recorded + + + +## 20.11.2 +- Added configurable internal log levels +- Added internal logs for approximate received and sent data size for requests +- Added numbers and boolean value types for custom user details methods +- Added `clearCrashLogs` method for clearing custom crash logs (breadcrumbs) +- Added `navigationItem`'s title as a view title fallback for view tracking +- Added Mac Catalyst support +- Added selector precaution for `CountlyLoggerDelegate` method call +- Added precautions for nil values in custom user details methods +- Updated request successful check to consider response object +- Updated default `eventSendThreshold` as 100 +- Fixed `UIApplicationState` usage for crashes occured on non-main thread +- Fixed clearing custom crash logs +- Fixed missing frameworks for `ns` subspec in `podspec` file +- Fixed CountlyLoggerDelegate methods optionality +- Fixed view tracking exception view checking +- Fixed adding and removing view tracking exceptions on tvOS +- Fixed cast warnings for an APM method internal log + +- Other various improvements + - Updated HeaderDocs, internal logs, inline notes and pragma marks + - Updated Countly project settings for Xcode 12.4 + + + +## 20.11.1 +- Added `loggerDelegate` initial config property for receiving internal logs on production builds +- Fixed manual view tracking state clean up when view tracking consent is cancelled +- Updated `CountlyFeedbackWidget.h` as public header file in Xcode project file for Carthage +- Added nullability specifiers for block parameters + + + +## 20.11.0 +- Added Surveys and NPS feedback widgets +- Added Swift Package Manager support +- Added `replaceAllAppKeysInQueueWithCurrentAppKey` method to replace all app keys in queue with the current app key +- Added `removeDifferentAppKeysFromQueue` method to remove all different app keys from the queue +- Added `deviceIDType` method to be able to check device ID type +- Added precaution and warning for `nil` crash report case +- Added `consents` initial config property +- Added device type metric +- Updated dismiss button design +- Fixed web view autoresizing mask for legacy feedback widgets +- Fixed a missing `CoreLocation` framework import +- Fixed unnecessary recreation of `NSURLSession` instances +- Fixed dismiss button layout +- Changed interface orientation change event consent from `Events` to `UserDetails` +- Changed remote config consent from `Any` to `RemoteConfig` +- Marked `pushTestMode` initial config property as `_Nullable` + +- Other various improvements + - Refactored picture upload data extraction + - Suppressed an internal log for interface orientation change + - Updated some constant key declarations for common use + - Updated HeaderDocs, internal logs, inline notes and pragma marks + + + +## 20.04.3 +- Deprecated `recordLocation:`, `recordCity:andISOCountryCode:`, `recordIP:` methods +- Added new combined `recordLocation:city:ISOCountryCode:IP:` method for recording location related info +- Deprecated `enableAttribution` initial config flag +- Added `attributionID` initial config property +- Added `recordAttributionID:` method +- Discarded IDFA usage on optional attribution feature +- Discarded `COUNTLY_EXCLUDE_IDFA` preprocessor flag +- Updated `PLCrashReporter` subspec dependency version specifier as `~> 1` + +- Other various improvements + - Updated HeaderDocs, internal logs, inline notes and pragma marks + - Updated some initial config property modifiers as `copy` + - Treated empty string `city`, `ISOCountryCode` and `IP` values as `nil` + - Added warnings for the cases where `city` and `ISOCountryCode` code are not set as a pair + + + +## 20.04.2 +- Implemented overriding default metrics and adding custom ones +- Fixed advertising tracking enabled check + +- Other various improvements + - Improved internal logs for pinned certificate check + - Refactored extra slash check using `hasSuffix:` method + - Renamed some app life cycle observing methods for clarity + + + +## 20.04.1 +- Added Application Performance Monitoring (Phase 1) + - Manual network traces + - Manual custom traces + - Semi-automatic app start time trace + - Automatic app foreground time trace + - Automatic app background time trace + - Consent handling for Application Performance Monitoring +- Added `COUNTLY_EXCLUDE_PUSHNOTIFICATIONS` flag to disable push notifications altogether in order to avoid App Store Connect warnings (thanks @grundleborg) +- Fixed an incorrect internal logging on SDK start +- Fixed location consent order to avoid some legacy Countly Server issue with location info being unavailable even after giving consent +- Improved `UIApplicationWillTerminateNotification` behaviour +- Prevented recording empty string as `city`, `ISOCountryCode` and `IP` for location info +- Applied `alwaysUsePOST` flag to feedback widget check requests +- Applied `alwaysUsePOST` flag to remote config requests + +- Other various improvements + - Deleted some unnecessary imports + - Updated HeaderDocs, internal logs, inline notes and pragma marks + - Added missing frameworks to CocoaPods podspec + - Added ability to override SDK name and version for bridge SDKs + + + +## 20.04 +- Added crash reporting feature for tvOS +- Added crash reporting feature for macOS +- Added crash reporting feature for watchOS +- Added optional crash reporting dependency PLCrashReporter for iOS +- Added UI orientation tracking +- Added crash filtering with regex +- Updated dSYM uploader script for accepting custom dSYM paths +- Updated enableAppleWatch flag default value for independent watchOS apps +- Fixed push notification consent method for macOS targets +- Fixed not appearing rich push notification buttons for some cases +- Discarded OpenGL ES version info in crash reports + +- Other various improvements + - Deleted an unnecessary UIKit import + - Added precaution for possible nil lines in backtrace + - Added precaution for possible nil OS name value + - Replaced scheduledTimerWithTimeInterval call with timerWithTimeInterval (thanks @mt-rpranata) + - Updated architerture method for crash reports + - Updated CocoaPods podspec for core subspec approach + - Updated feature, consent and push test mode specifiers as NSString typedefs + - Updated HeaderDocs, internal logs, inline notes and pragma marks + + + +## 19.08 +- Added temporary device ID mode +- Added support for Carthage +- Added custom URL session configuration support +- Added custom segmentation support on view tracking +- Added ability to change app key on the run +- Added ability to flush queues +- Added `pushTestMode` property and discarded `isTestDevice` property +- Fixed `WCSessionDelegate` interception +- Fixed title and message check in push notification payloads +- Fixed binary image name extraction for crash reports +- Fixed missing delegate forwarding for `userNotificationCenter:openSettingsForNotification:` method +- Fixed in-app alerts on iOS10+ devices when a silent notification with alert key arrives +- Discarded device ID persistency on Keychain +- Discarded OpenUDID device ID option +- Discarded IDFA device ID option +- Discarded zero IDFA fix +- Updated default device ID on tvOS as `identifierForVendor` + +- Other various improvements + - Renamed `forceDeviceIDInitialization` flag as `resetStoredDeviceID` + - Added lightweight generics for segmentation parameters + - Added dSYM upload script to preserved paths in Podspec + - Updated dSYM upload script to support paths with spaces + - Changed request cache policy to `NSURLRequestReloadIgnoringLocalCacheData` + - Added battery level for watchOS 4.0+ + - Added JSON validity check before converting objects + - Deleted unused `kCountlyCRKeyLoadAddress` constant + - Improved internal logging in binary images processing for crash reports + - Added persistency for generated `NSUUID` + - Added precaution to prevent invalid requests from being added to queue + - Discarded null check on request queue + - Discarded all APM related files + - Added length check for view tracking view name + - Added length check for view tracking exceptions + - Updated HeaderDocs, internal logs, inline notes and pragma marks + + + +## 19.02 +- Added push notification support for macOS +- Added provisional push notification permission support for iOS12 +- Added remote config feature +- Added `recordPushNotificationToken` method to be used after device ID changes +- Added `clearPushNotificationToken` method to be used after device ID changes +- Discarded `Push Open` event and replaced it with `Push Action` event +- Fixed push notification token not being sent on some iOS12 devices +- Fixed device ID change request delaying issue by discarding delay altogether +- Fixed internal view controller presenting failure when root view controller is not ready yet +- Fixed `openURL` freeze caused by iOS +- Fixed wrong `kCountlyQSKeyLocationIP` key in location info requests +- Fixed missing app key in feedback widget requests +- Fixed feedback widget dismiss button position + +- Other various improvements + - Discarded separate UIWindow usage for presenting feedback widgets + - Added checksum to feedback widget requests + - Improved internal logging for request queue + + + +## 18.08 +- Added feedback widgets support +- Added limit for number of custom crash logs (100 logs) +- Added limit for each custom crash log length (1000 chars) +- Added support for cancelling timed events +- Added support for recording fatal exceptions manually +- Added `userInfo` to crash report custom property +- Added delay before sending change device ID request (server requirement) +- Renamed `isAutoViewTrackingEnabled` as `isAutoViewTrackingActive` +- Fixed Xcode warnings for `askForNotificationPermission` method +- Fixed `UIAlertController` leak in push notification manager +- Fixed `crashSegmentation` availability for manually recorded crashes +- Fixed `openURL:` call thread as main thread +- Updated minimum supported `macOS` version as `10.10` + +- Other various improvements + - Discarded separate `UIWindow` for presenting `UIAlertControllers` + - Refactored `buildUUID` and `executableName` as properties + - Refactored custom crash log array and date formatter + - Updated HeaderDocs, inline notes, pragma marks + + + +## 18.04 +- Added consent management for GDPR compliance +- Exposed device ID to be used for data export and/or removal requests +- Added precautions for SDK start state to prevent re-starting and early method calls +- Added mutability protection for core functions, configuration properties, events and user details +- Added `COUNTLY_EXCLUDE_IDFA` pre-processor flag to exclude IDFA references +- Added API availability checks and warnings for Apple Watch and Push Notifications +- Renamed `reportView:` method as `recordView:` +- Fixed early ending of `UIBackgroundTask` +- Fixed getting file path form local storage URL (thanks @dsmo) +- Fixed not respecting `doNotShowAlertForNotifications` flag on iOS10+ devices +- Fixed not starting requests queue when `manualSessionHandling` is enabled +- Fixed `block implicitly retains self` warning in Star Rating +- Fixed local variable shadowing warnings +- Fixed Japanese language code for Star Rating dialog + +- Other various improvements + - Refactored all location info into Location Manager + - Refactored `checkForAutoAsk` in Star Rating + - Refactored event recording for consents compatibility + - Refactored Apple Watch matching + - Refactored auto view tracking + - Added top view controller finding method + - Replaced asserts with exceptions + - Deleted unneccessary method declarations in Push Notifications + - Deleted unnecessary reference for `WCSession.defaultSession.delegate` + - Deleted unnecessary `TARGET_OS_OSX` definition + - Standardized `nil` checks + - Renamed and reordered some query string constants + - Updated HeaderDocs, inline notes, pragma marks + - Performed whitespace cleaning + + + +## 18.01 + +- Added `attribution` config +- Added recording city and country for GeoLocation +- Added recording explicit IP address for GeoLocation +- Added disabling GeoLocation +- Updated `recordLocation` method to override inital `location` config +- Fixed reserved key for IP address query string +- Fixed a `CoreTelephony` related crash due to an iOS bug +- Replaced `NSUserDefaults` with `NSCachesDirectory` on tvOS for persistency +- Improved auto dSYM uploader script +- Improved performance on stored request limit execution + +- Other various improvements + - Fixed a placeholder type specifier for `NSNumber` + - Deleted unnecessary `CLYMessaging` definition + - Deleted unnecessary strong ownership qualifiers + - Added Hindu translation for star rating dialog + - Added change log file + - Updated user details and star rating reserved keys as constants + - Updated `OpenGLESVersion` method return type as `NSString` + - Updated time related types as `NSTimeInterval` + - Updated HeaderDocs + + + +## 17.09 + +- Updated for Xcode 9 and iOS 11 +- Added symbolication support for crash reports +- Added Automatic dSYM Uploading script +- Added extension subspec for integrating Rich Push Notifications with CocoaPods +- Added nullability specifiers for better Swift compatibility +- Added 28 new system UIViewController subclass exception for Auto View Tracking +- Added convenience method for recording action event for manually handled notifications +- Added convenience method for recording handled exception with stack trace +- Added precaution for invalid event keys +- Added precaution for corrupt request strings +- Made Zero-IDFA fix optional +- Fixed a view tracking duration problem where duration being reported as timestamp +- Replaced `crashLog` method with `recordCrashLog` and added deprecated warning +- Changed dispatch queue type for opening external URLs + +- Other various improvements + - Added Bengali translation for star rating dialog + - Updated metric, event, view tracking and crash report reserved keys as constants + - Deleted unnecessary gitattributes file + - Deleted duplicate Zero-IDFA const + - Rearranged file imports + - Updated HeaderDocs + - Cleaned whitespace + + + +## 17.05 + +- Added Rich Push Notifications support (attachments and custom action buttons) +- Added manual session handling +- Added URL escaping for custom device ID and other user defined properties +- Added support for accidental extra slash in host +- Added architecture, executable name and load address for crash reporting +- Added IP optional parameter +- Added SDK metadata to all request +- Switched to SHA256 for parameter tampering protection +- Discarded `recordUserDetails` method and combined it with `save` method +- Improved `AutoViewTracking` active duration calculation +- Improved Countly payload check in notification dictionary +- Fixed inner event timestamp for 32 bit devices +- Fixed token cleaning when user's permission changes +- Fixed checksum calculation for `zero-IDFA` fix case +- Fixed OS version metric for `tvOS` +- Fixed double `suspend` method call when user kills the app using App Switcher +- Fixed a compiler warning for `macOS` targets +- Fixed `AutoViewTracking` for `macOS` targets +- Fixed showing of multiple alerts in succession + +- Other various improvements + - Refactored picture upload data preparation from request string using `NSURLComponents` + - Refactored `zero-IDFA` check + - Refactored additional info to be sent with begin session request + - Refactored checksum appending + - Refactored URLSession generation + - Refactored opening external URLs on main thread + - Refactored device model identifier method + - Refactored sending crash report into connection manager + - Replaced `__OPTIMIZED__` flag with `DEBUG` flag for push notification test mode detection + - Replaced boundary method with constant string + - Replaced text based dismiss button with cross dismiss button for star-rating + - Redefined request query string keys as constants + - Redefined push notification reserved keys as constants + - Redefined GET request max length as a constant + - Redefined server input endpoint as a constant + - Redefined push notification test mode values as enum + - Standardized some integer types + - Standardized target checking preprocessor macro usage + - Deleted unnecessary `init` override in push manager + - Deleted unnecessary `updateSessionPeriod` property in connection manager + - Deleted unnecessary `starRatingDismissButtonTitle` config property + - Deleted internal crash test methods + - Added Czech and Latvian localization for star-rating dialog + - Changed example host URL for rebranding compatibility + - Updated handling of notification on `iOS9` and older + - Updated alert key handling in push notification payload + - Updated HeaderDocs + - Cleaned whitespace + + + +## 16.12 + +- Refactored push notifications + - Made integration more easy + - Added iOS10 push notifications handling + - Added convenience method for asking push notifications permission covers all iOS versions + - Renamed feature name from `CLYMessaging` to `CLYPushNotifications` + - Added configuration option `doNotShowAlertForNotifications` to disable push triggered alerts + - Discarded complicated `UIUserNotificationCategory` actions + - Added configuration option `sendPushTokenAlways` to record push token always (for sending silent notification to users without notification permission) + - Discarded App Store URL fetching with `NSURLConnection` +- Discarded iOS7 support and deprecated method calls +- Switched to runtime controlled internal logging instead of preprocessor flag +- Added AutoViewTracking support for tvOS +- Added view controller title and custom titleView support for AutoViewTracking +- Improved AutoViewTracking performance and Swift compatibility +- Refactored suspending for crash reporting +- Switched to async file save for suspending +- Added user login and logout convenience methods +- Added configuration option to enable Apple Watch related features +- Moved archiving of queued request into sync block to prevent a very rare occurring crash +- Refactored unsent session duration +- Added completion callback for automatically displayed star-rating dialog +- Partially fixed but temporarily disabled APM feature until server completely supports it +- Fixed too long exception name in crash reports on iOS10 +- Other various improvements + - Refactored starting method + - Switched to separate window based alert controller displaying for push notifications and star-rating dialogs + - Renamed constant kCountlyStarRatingButtonSize to prevent compile time conflicts + - Renamed server input endpoint variable for white label SDK renamer script compatibility + - Updated star-rating reserved event key + - Added internal log for successful initialization with SDK name and version + - Fixed unused `UIAlertViewAssociatedObjectKey` warning for macOS + - Removed old deviceID zero-IDFA fixer redundant request + - Added internal logging for connection type retrieval exception + - Added exception type info to crash reports + - Fixed duplicate exception adding for AutoViewTracking + - Prevented Countly internal view controllers from being tracked by AutoViewTracking + - Prefixed all category methods to prevent possible conflicts + - Changed timer's runloop mode + - Updated timestamp type specifier (thanks to @scottlaw) + - Changed SDK metadata sending to begin_session only + - Replaced empty string checks with length checks + - Cleared nullability specifiers + - Updated HeaderDocs + - Cleaned whitespace + + + +## 16.06.4 + +- Fixed iOS10 zero-IDFA problem +- Fixed TARGET_OS_OSX warning for iOS10 SDK on Xcode 8. +- Fixed ending of background tasks. +- Added parameter tampering protection. +- Added density metric. +- Added alwaysUsePOST config property for using POST method for all requests regardless of the data size. +- Added timezone. +- Switched to millisecond timestamp. +- Disabled server response dictionary check. +- Other minor improvements like better internal logging, standardization, whitespacing, code cleaning, commenting, pragma marking and HeaderDocing + + + +## 16.06.3 + +- Fixed a persistency related crash +- Improved thread safety of request queue and events +- Added Star-Rating, the simplest form of feedback from users, both automatically and manually. +- Improved event recording performance and safety for APM and Auto View Tracking. +- Added custom HTTP header field support for requests, both on initial configuration and later. +- Standardized internal logging grammar and formatting for easier debugging +- Improved headerdocs grammar and formatting for easier integration and usage +- Fixed some static analyzer warnings + + + +## 16.06.2 + +- Added Star-Rating, the simplest form of feedback from users, both automatically and manually. +- Improved event recording performance and safety for APM and Auto View Tracking. +- Added custom HTTP header field support for requests, both on initial configuration and later. +- Standardized internal logging grammar and formatting for easier debugging +- Improved headerdocs grammar and formatting for easier integration and usage +- Fixed some static analyzer warnings + + + +## 16.06.1 + +- Added support for certificate pinning. +- Added deleting of user details properties on server by setting them as NSNull. +- Implemented switching between GET and POST depending on data size on requests. +- Fixed a URL encoding issue which causes problems for Asian languages and some JSON payloads. +- Fixed custom crash log formatter. + + + +## 16.06 + +- Fixed a problem with changing device ID (for system generated device IDs) +- Added isTestDevice flag to mark test devices for Push Notifications +- Improved Auto View Tracking by ignoring non-visible foundation UIViewController subclasses +- Implemented manually adding exception UIViewController subclasses for Auto View Tracking +- Changed default device ID type for tvOS from IDFA to NSUUID +- Added stored requests limit +- Added optional parameters ISOCountryCode, city and location for advanced segmentation +- Discarded timed events persistency +- Added buildUUID and build number to Crash Reports +- Added SDK name (language-origin-platform) to all requests +- Changed default alert title for push messages +- Other minor improvements like better internal logging, standardization, whitespacing, code cleaning, commenting, pragma marking and HeaderDocing + + + +# 16.02.01 + +- Swithed to POST method for all requests by default +- Fixed some issues with Crash Reporting persistency +- Fixed some issues with CocoaPods v1.0.0 +- Other minor fixes and improvements + + + +## 16.02 + +Completely re-written iOS SDK with watchOS, tvOS & OSX support +- APM +- Manual/Auto ViewTracking +- UserDetails modifiers +- watchOS 2 support +- tvOS support +- Configurable starting +- Custom or system provided (IDFA, IDFV, OpenUDID) device ID +- Changing/merging device ID on runtime +- Persistency without CoreData +- Various performance improvements and minor bugfixes + + + +## 15.06.01 + +Updated CocoaPods spec + + + +## 15.06 + +- Added WatchKit support +- Added CrashReporting support +- Fixed minor problems with Messaging +- Added manually ending sessions on background fetch +- Switched to Ubuntu version numbering diff --git a/ios/Classes/CountlyiOS/Countly-PL.podspec b/ios/Classes/CountlyiOS/Countly-PL.podspec new file mode 100644 index 00000000..8f8c68be --- /dev/null +++ b/ios/Classes/CountlyiOS/Countly-PL.podspec @@ -0,0 +1,43 @@ +Pod::Spec.new do |s| + s.name = 'Countly-PL' + s.version = '21.11.2' + s.license = { :type => 'MIT', :file => 'LICENSE.md' } + s.summary = 'Countly is an innovative, real-time, open source mobile analytics platform.' + s.homepage = 'https://github.com/Countly/countly-sdk-ios' + s.social_media_url = 'https://twitter.com/gocountly' + s.author = {'Countly' => 'hello@count.ly'} + s.source = { :git => 'https://github.com/Countly/countly-sdk-ios.git', :tag => s.version.to_s } + + s.requires_arc = true + s.default_subspecs = 'Core' + s.ios.deployment_target = '10.0' + s.osx.deployment_target = '10.14' + s.watchos.deployment_target = '4.0' + s.tvos.deployment_target = '10.0' + + s.subspec 'Core' do |core| + core.source_files = '*.{h,m}' + core.public_header_files = 'Countly.h', 'CountlyUserDetails.h', 'CountlyConfig.h', 'CountlyFeedbackWidget.h' + core.preserve_path = 'countly_dsym_uploader.sh' + core.ios.frameworks = ['Foundation', 'UIKit', 'UserNotifications', 'CoreLocation', 'WebKit', 'CoreTelephony', 'WatchConnectivity'] + end + + s.subspec 'NotificationService' do |ns| + ns.source_files = 'CountlyNotificationService.{m,h}' + ns.ios.deployment_target = '10.0' + ns.ios.frameworks = ['Foundation', 'UserNotifications'] + end + + s.subspec 'PL' do |pl| + pl.platform = :ios + pl.dependency 'Countly/Core' + pl.dependency 'PLCrashReporter', '~> 1' + + # It is not possible to set static_framework attribute on subspecs. + # So, we have to set it on main spec. + # But it affects the main spec even when this subspec is not used. + # Asked this on CocoaPods GitHub page: https://github.com/CocoaPods/CocoaPods/issues/7355#issuecomment-619261908 + s.static_framework = true + end + +end diff --git a/ios/Classes/CountlyiOS/Countly.h b/ios/Classes/CountlyiOS/Countly.h index efd30442..ed8f5e5e 100644 --- a/ios/Classes/CountlyiOS/Countly.h +++ b/ios/Classes/CountlyiOS/Countly.h @@ -554,7 +554,37 @@ NS_ASSUME_NONNULL_BEGIN * @param widgetID ID of the feedback widget created on Countly Server. * @param completionHandler A completion handler block to be executed when feedback widget is dismissed by user or there is an error. */ -- (void)presentFeedbackWidgetWithID:(NSString *)widgetID completionHandler:(void (^)(NSError * __nullable error))completionHandler; +- (void)presentFeedbackWidgetWithID:(NSString *)widgetID completionHandler:(void (^)(NSError * __nullable error))completionHandler DEPRECATED_MSG_ATTRIBUTE("Use 'presentRatingWidgetWithID:' method instead!"); + +/** + * Presents rating widget with given ID in a WKWebView placed in a UIViewController. + * @discussion First, the availability of the rating widget will be checked asynchronously. + * @discussion If the rating widget with given ID is available, it will be modally presented. + * @discussion Otherwise, @c completionHandler will be executed with an @c NSError. + * @discussion @c completionHandler will also be executed with @c nil when the rating widget is dismissed by user. + * @discussion Calls to this method will be ignored and @c completionHandler will not be executed if: + * @discussion - Consent for @c CLYConsentFeedback is not given, while @c requiresConsent flag is set on initial configuration. + * @discussion - Current device ID is @c CLYTemporaryDeviceID. + * @discussion - @c widgetID is not a non-zero length valid string. + * @discussion This is a legacy method for presenting Rating type feedback widgets only. + * @discussion Passing widget ID's of Survey or NPS type feedback widgets will not work. + * @param widgetID ID of the rating widget created on Countly Server. + * @param completionHandler A completion handler block to be executed when the rating widget is dismissed by user or there is an error. + */ +- (void)presentRatingWidgetWithID:(NSString *)widgetID completionHandler:(void (^)(NSError * __nullable error))completionHandler; + +/** + * Manually records rating widget result with given ID and other info. + * @discussion Calls to this method will be ignored if: + * @discussion - Consent for @c CLYConsentFeedback is not given, while @c requiresConsent flag is set on initial configuration. + * @discussion - @c widgetID is not a non-zero length valid string. + * @param widgetID ID of the rating widget created on Countly Server + * @param rating User's rating + * @param email User's e-mail address (optional) + * @param comment User's comment (optional) + * @param userCanBeContacted User's consent for whether they can be contacted via e-mail or not + */ +- (void)recordRatingWidgetWithID:(NSString *)widgetID rating:(NSInteger)rating email:(NSString * _Nullable)email comment:(NSString * _Nullable)comment userCanBeContacted:(BOOL)userCanBeContacted; /** * Fetches a list of available feedback widgets. @@ -577,11 +607,33 @@ NS_ASSUME_NONNULL_BEGIN * Records attribution ID (IDFA) for campaign attribution. * @discussion This method overrides @c attributionID property specified on initial configuration, and sends an immediate request. * @discussion Also, this attribution ID will be sent with all @c begin_session requests. + * @discussion Calls to this method will be ignored if: + * @discussion - Consent for @c CLYConsentAttribution is not given, while @c requiresConsent flag is set on initial configuration. * @param attributionID Attribution ID (IDFA) */ - (void)recordAttributionID:(NSString *)attributionID; +/** + * Records direct attribution with campaign type and data. + * @discussion Currently supported campaign types are "countly" and "_special_test". + * @discussion Campaign data has to be in `{"cid":"CAMPAIGN_ID", "cuid":"CAMPAIGN_USER_ID"}` format. + * @discussion This method sends an immediate request. + * @discussion Calls to this method will be ignored if: + * @discussion - Consent for @c CLYConsentAttribution is not given, while @c requiresConsent flag is set on initial configuration. + * @param campaignType Campaign Type + * @param campaignData Campaign Data + */ +- (void)recordDirectAttributionWithCampaignType:(NSString *)campaignType andCampaignData:(NSString *)campaignData; +/** + * Records indirect attribution with given key-value pairs. + * @discussion Keys could be a predefined CLYAttributionKey or any non-zero length valid string. + * @discussion This method sends an immediate request. + * @discussion Calls to this method will be ignored if: + * @discussion - Consent for @c CLYConsentAttribution is not given, while @c requiresConsent flag is set on initial configuration. + * @param attribution Attribution key-value pairs + */ +- (void)recordIndirectAttribution:(NSDictionary *)attribution; #pragma mark - Remote Config /** diff --git a/ios/Classes/CountlyiOS/Countly.m b/ios/Classes/CountlyiOS/Countly.m index 9b53436a..a0c19c55 100644 --- a/ios/Classes/CountlyiOS/Countly.m +++ b/ios/Classes/CountlyiOS/Countly.m @@ -183,6 +183,12 @@ - (void)startWithConfig:(CountlyConfig *)config if (config.consents) [self giveConsentForFeatures:config.consents]; + + if (config.campaignType && config.campaignData) + [self recordDirectAttributionWithCampaignType:config.campaignType andCampaignData:config.campaignData]; + + if (config.indirectAttribution) + [self recordIndirectAttribution:config.indirectAttribution]; } @@ -928,6 +934,20 @@ - (void)presentFeedbackWidgetWithID:(NSString *)widgetID completionHandler:(void [CountlyFeedbacks.sharedInstance checkFeedbackWidgetWithID:widgetID completionHandler:completionHandler]; } +- (void)presentRatingWidgetWithID:(NSString *)widgetID completionHandler:(void (^)(NSError * error))completionHandler +{ + CLY_LOG_I(@"%s %@ %@", __FUNCTION__, widgetID, completionHandler); + + [CountlyFeedbacks.sharedInstance checkFeedbackWidgetWithID:widgetID completionHandler:completionHandler]; +} + +- (void)recordRatingWidgetWithID:(NSString *)widgetID rating:(NSInteger)rating email:(NSString * _Nullable)email comment:(NSString * _Nullable)comment userCanBeContacted:(BOOL)userCanBeContacted +{ + CLY_LOG_I(@"%s %@ %ld %@ %@ %d", __FUNCTION__, widgetID, (long)rating, email, comment, userCanBeContacted); + + [CountlyFeedbacks.sharedInstance recordRatingWidgetWithID:widgetID rating:rating email:email comment:comment userCanBeContacted:userCanBeContacted]; +} + - (void)getFeedbackWidgets:(void (^)(NSArray *feedbackWidgets, NSError * error))completionHandler { CLY_LOG_I(@"%s %@", __FUNCTION__, completionHandler); @@ -953,7 +973,80 @@ - (void)recordAttributionID:(NSString *)attributionID [CountlyConnectionManager.sharedInstance sendAttribution]; } +- (void)recordDirectAttributionWithCampaignType:(NSString *)campaignType andCampaignData:(NSString *)campaignData +{ + CLY_LOG_I(@"%s %@ %@", __FUNCTION__, campaignType, campaignData); + + if (!CountlyConsentManager.sharedInstance.consentForAttribution) + return; + + if (!campaignType.length) + { + CLY_LOG_E(@"campaignType must be non-zero length valid string. Method execution will be aborted!"); + return; + } + + if (!campaignData.length) + { + CLY_LOG_E(@"campaignData must be non-zero length valid string. Method execution will be aborted!"); + return; + } + + if ([campaignType isEqualToString:@"_special_test"]) + { + [CountlyConnectionManager.sharedInstance sendAttributionData:campaignData]; + return; + } + + if (![campaignType isEqualToString:@"countly"]) + { + CLY_LOG_W(@"Recording direct attribution with a type other than 'countly' is currently not supported. Method execution will be aborted!"); + return; + } + + NSError* error = nil; + NSDictionary* campaignDataDictionary = [NSJSONSerialization JSONObjectWithData:[campaignData cly_dataUTF8] options:0 error:&error]; + if (error) + { + CLY_LOG_E(@"Campaign data is not in expected format. Method execution will be aborted!"); + return; + } + + NSString* campaignID = campaignDataDictionary[@"cid"]; + if (!campaignID.length) + { + CLY_LOG_E(@"Campaign ID must be non-zero length valid string. Method execution will be aborted!"); + return; + } + + NSString* campaignUserID = campaignDataDictionary[@"cuid"]; + if (!campaignUserID.length) + { + CLY_LOG_W(@"Campaign User ID must be non-zero length valid string. It will be ignored!"); + } + + [CountlyConnectionManager.sharedInstance sendDirectAttributionWithCampaignID:campaignID andCampaignUserID:campaignUserID]; +} + +- (void)recordIndirectAttribution:(NSDictionary *)attribution +{ + CLY_LOG_I(@"%s %@", __FUNCTION__, attribution); + if (!CountlyConsentManager.sharedInstance.consentForAttribution) + return; + + NSMutableDictionary* filtered = attribution.mutableCopy; + [attribution enumerateKeysAndObjectsUsingBlock:^(NSString * key, NSString * value, BOOL * stop) + { + if (!value.length) + [filtered removeObjectForKey:key]; + }]; + + NSDictionary* truncated = [filtered cly_truncated:@"Indirect attribution"]; + NSDictionary* limited = [truncated cly_limited:@"Indirect attribution"]; + + [CountlyConnectionManager.sharedInstance sendIndirectAttribution:limited]; +} #pragma mark - Remote Config diff --git a/ios/Classes/CountlyiOS/Countly.podspec b/ios/Classes/CountlyiOS/Countly.podspec new file mode 100644 index 00000000..c620b95c --- /dev/null +++ b/ios/Classes/CountlyiOS/Countly.podspec @@ -0,0 +1,31 @@ +Pod::Spec.new do |s| + s.name = 'Countly' + s.version = '21.11.2' + s.license = { :type => 'MIT', :file => 'LICENSE.md' } + s.summary = 'Countly is an innovative, real-time, open source mobile analytics platform.' + s.homepage = 'https://github.com/Countly/countly-sdk-ios' + s.social_media_url = 'https://twitter.com/gocountly' + s.author = {'Countly' => 'hello@count.ly'} + s.source = { :git => 'https://github.com/Countly/countly-sdk-ios.git', :tag => s.version.to_s } + + s.requires_arc = true + s.default_subspecs = 'Core' + s.ios.deployment_target = '10.0' + s.osx.deployment_target = '10.14' + s.watchos.deployment_target = '4.0' + s.tvos.deployment_target = '10.0' + + s.subspec 'Core' do |core| + core.source_files = '*.{h,m}' + core.public_header_files = 'Countly.h', 'CountlyUserDetails.h', 'CountlyConfig.h', 'CountlyFeedbackWidget.h' + core.preserve_path = 'countly_dsym_uploader.sh' + core.ios.frameworks = ['Foundation', 'UIKit', 'UserNotifications', 'CoreLocation', 'WebKit', 'CoreTelephony', 'WatchConnectivity'] + end + + s.subspec 'NotificationService' do |ns| + ns.source_files = 'CountlyNotificationService.{m,h}' + ns.ios.deployment_target = '10.0' + ns.ios.frameworks = ['Foundation', 'UserNotifications'] + end + +end diff --git a/ios/Classes/CountlyiOS/CountlyCommon.m b/ios/Classes/CountlyiOS/CountlyCommon.m index 3ead630c..3985178f 100644 --- a/ios/Classes/CountlyiOS/CountlyCommon.m +++ b/ios/Classes/CountlyiOS/CountlyCommon.m @@ -26,7 +26,7 @@ @interface CountlyCommon () #endif @end -NSString* const kCountlySDKVersion = @"21.11.1"; +NSString* const kCountlySDKVersion = @"21.11.2"; NSString* const kCountlySDKName = @"objc-native-ios"; NSString* const kCountlyErrorDomain = @"ly.count.ErrorDomain"; diff --git a/ios/Classes/CountlyiOS/CountlyConfig.h b/ios/Classes/CountlyiOS/CountlyConfig.h index 3a81a40b..bb40b7ee 100644 --- a/ios/Classes/CountlyiOS/CountlyConfig.h +++ b/ios/Classes/CountlyiOS/CountlyConfig.h @@ -90,6 +90,10 @@ extern CLYMetricKey const CLYMetricKeyLocale; extern CLYMetricKey const CLYMetricKeyHasWatch; extern CLYMetricKey const CLYMetricKeyInstalledWatchApp; +//NOTE: Attribution keys +typedef NSString* CLYAttributionKey NS_EXTENSIBLE_STRING_ENUM; +extern CLYAttributionKey const CLYAttributionKeyIDFA; +extern CLYAttributionKey const CLYAttributionKeyADID; //NOTE: Internal log levels typedef enum : NSUInteger @@ -359,11 +363,29 @@ typedef enum : NSUInteger #pragma mark - /** - * For specifying attribution ID (IDFA) for campaign attribution. + * For specifying attribution ID (IDFA). * @discussion If set, this attribution ID will be sent with all @c begin_session requests. */ @property (nonatomic, copy) NSString* attributionID; +/** + * For specifying direct attribution campaign type. + * @discussion Currently supported campaign types are "countly" and "_special_test". + */ +@property (nonatomic, copy) NSString* campaignType; + +/** + * For specifying direct attribution campaign data. + * @discussion Campaign data has to be in `{"cid":"CAMPAIGN_ID", "cuid":"CAMPAIGN_USER_ID"}` format. + */ +@property (nonatomic, copy) NSString* campaignData; + +/** + * For specifying indirect attribution with given key-value pairs. + * @discussion Keys could be a predefined CLYAttributionKey or any non-zero length valid string. + */ +@property (nonatomic, copy) NSDictionary * indirectAttribution; + /** * @c enableAttribution property is deprecated. Please use @c recordAttributionID method instead. * @discussion Using this property will have no effect. diff --git a/ios/Classes/CountlyiOS/CountlyConnectionManager.h b/ios/Classes/CountlyiOS/CountlyConnectionManager.h index 4f2774c2..b8f3b074 100644 --- a/ios/Classes/CountlyiOS/CountlyConnectionManager.h +++ b/ios/Classes/CountlyiOS/CountlyConnectionManager.h @@ -47,7 +47,10 @@ extern const NSInteger kCountlyGETRequestMaxLength; - (void)sendCrashReport:(NSString *)report immediately:(BOOL)immediately; - (void)sendOldDeviceID:(NSString *)oldDeviceID; - (void)sendAttribution; -- (void)sendConsentChanges:(NSString *)consentChanges; +- (void)sendDirectAttributionWithCampaignID:(NSString *)campaignID andCampaignUserID:(NSString *)campaignUserID; +- (void)sendAttributionData:(NSString *)attributionData; +- (void)sendIndirectAttribution:(NSDictionary *)attribution; +- (void)sendConsents:(NSString *)consents; - (void)sendPerformanceMonitoringTrace:(NSString *)trace; - (void)proceedOnQueue; diff --git a/ios/Classes/CountlyiOS/CountlyConnectionManager.m b/ios/Classes/CountlyiOS/CountlyConnectionManager.m index 6763c3ed..6182dab3 100644 --- a/ios/Classes/CountlyiOS/CountlyConnectionManager.m +++ b/ios/Classes/CountlyiOS/CountlyConnectionManager.m @@ -41,18 +41,26 @@ @interface CountlyConnectionManager () NSString* const kCountlyQSKeyLocationCountry = @"country_code"; NSString* const kCountlyQSKeyLocationIP = @"ip_address"; +NSString* const kCountlyQSKeyAttributionID = @"aid"; +NSString* const kCountlyQSKeyIDFA = @"idfa"; +NSString* const kCountlyQSKeyADID = @"adid"; +NSString* const kCountlyQSKeyCampaignID = @"campaign_id"; +NSString* const kCountlyQSKeyCampaignUser = @"campaign_user"; +NSString* const kCountlyQSKeyAttributionData = @"attribution_data"; + NSString* const kCountlyQSKeyMetrics = @"metrics"; NSString* const kCountlyQSKeyEvents = @"events"; NSString* const kCountlyQSKeyUserDetails = @"user_details"; NSString* const kCountlyQSKeyCrash = @"crash"; NSString* const kCountlyQSKeyChecksum256 = @"checksum256"; -NSString* const kCountlyQSKeyAttributionID = @"aid"; -NSString* const kCountlyQSKeyIDFA = @"idfa"; NSString* const kCountlyQSKeyConsent = @"consent"; NSString* const kCountlyQSKeyAPM = @"apm"; NSString* const kCountlyQSKeyMethod = @"method"; +CLYAttributionKey const CLYAttributionKeyIDFA = kCountlyQSKeyIDFA; +CLYAttributionKey const CLYAttributionKeyADID = kCountlyQSKeyADID; + NSString* const kCountlyUploadBoundary = @"0cae04a8b698d63ff6ea55d168993f21"; NSString* const kCountlyEndpointI = @"/i"; //NOTE: input endpoint @@ -432,10 +440,45 @@ - (void)sendAttribution [self proceedOnQueue]; } -- (void)sendConsentChanges:(NSString *)consentChanges +- (void)sendDirectAttributionWithCampaignID:(NSString *)campaignID andCampaignUserID:(NSString *)campaignUserID +{ + NSMutableString* queryString = [self queryEssentials].mutableCopy; + [queryString appendFormat:@"&%@=%@", kCountlyQSKeyCampaignID, campaignID]; + + if (campaignUserID.length) + { + [queryString appendFormat:@"&%@=%@", kCountlyQSKeyCampaignUser, campaignUserID]; + } + + [CountlyPersistency.sharedInstance addToQueue:queryString.copy]; + + [self proceedOnQueue]; +} + +- (void)sendAttributionData:(NSString *)attributionData +{ + NSMutableString* queryString = [self queryEssentials].mutableCopy; + [queryString appendFormat:@"&%@=%@", kCountlyQSKeyAttributionData, [attributionData cly_URLEscaped]]; + + [CountlyPersistency.sharedInstance addToQueue:queryString.copy]; + + [self proceedOnQueue]; +} + +- (void)sendIndirectAttribution:(NSDictionary *)attribution +{ + NSMutableString* queryString = [self queryEssentials].mutableCopy; + [queryString appendFormat:@"&%@=%@", kCountlyQSKeyAttributionID, [attribution cly_JSONify]]; + + [CountlyPersistency.sharedInstance addToQueue:queryString.copy]; + + [self proceedOnQueue]; +} + +- (void)sendConsents:(NSString *)consents { NSString* queryString = [[self queryEssentials] stringByAppendingFormat:@"&%@=%@", - kCountlyQSKeyConsent, consentChanges]; + kCountlyQSKeyConsent, consents]; [CountlyPersistency.sharedInstance addToQueue:queryString]; @@ -717,7 +760,9 @@ - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticat #if DEBUG if (CountlyCommon.sharedInstance.shouldIgnoreTrustCheck) { - SecTrustSetExceptions(serverTrust, SecTrustCopyExceptions(serverTrust)); + CFDataRef exceptions = SecTrustCopyExceptions(serverTrust); + SecTrustSetExceptions(serverTrust, exceptions); + CFRelease(exceptions); } #endif diff --git a/ios/Classes/CountlyiOS/CountlyConsentManager.m b/ios/Classes/CountlyiOS/CountlyConsentManager.m index 434f280d..cb9c73b4 100644 --- a/ios/Classes/CountlyiOS/CountlyConsentManager.m +++ b/ios/Classes/CountlyiOS/CountlyConsentManager.m @@ -21,10 +21,6 @@ CLYConsent const CLYConsentRemoteConfig = @"remote-config"; -@interface CountlyConsentManager () -@property (nonatomic, strong) NSMutableDictionary* consentChanges; -@end - @implementation CountlyConsentManager @synthesize consentForSessions = _consentForSessions; @@ -57,7 +53,7 @@ - (instancetype)init { if (self = [super init]) { - self.consentChanges = NSMutableDictionary.new; + } return self; @@ -116,7 +112,7 @@ - (void)giveConsentForFeatures:(NSArray *)features if ([features containsObject:CLYConsentRemoteConfig] && !self.consentForRemoteConfig) self.consentForRemoteConfig = YES; - [self sendConsentChanges]; + [self sendConsents]; } @@ -128,17 +124,17 @@ - (void)cancelConsentForAllFeatures - (void)cancelConsentForAllFeaturesWithoutSendingConsentsRequest { - [self cancelConsentForFeatures:[self allFeatures] shouldSkipSendingConsentChanges:YES]; + [self cancelConsentForFeatures:[self allFeatures] shouldSkipSendingConsentsRequest:YES]; } - (void)cancelConsentForFeatures:(NSArray *)features { - [self cancelConsentForFeatures:features shouldSkipSendingConsentChanges:NO]; + [self cancelConsentForFeatures:features shouldSkipSendingConsentsRequest:NO]; } -- (void)cancelConsentForFeatures:(NSArray *)features shouldSkipSendingConsentChanges:(BOOL)shouldSkipSendingConsentChanges +- (void)cancelConsentForFeatures:(NSArray *)features shouldSkipSendingConsentsRequest:(BOOL)shouldSkipSendingConsentsRequest { if (!self.requiresConsent) return; @@ -176,18 +172,28 @@ - (void)cancelConsentForFeatures:(NSArray *)features shouldSkipSendingConsentCha if ([features containsObject:CLYConsentRemoteConfig] && self.consentForRemoteConfig) self.consentForRemoteConfig = NO; - if (!shouldSkipSendingConsentChanges) - [self sendConsentChanges]; + if (!shouldSkipSendingConsentsRequest) + [self sendConsents]; } -- (void)sendConsentChanges +- (void)sendConsents { - if (self.consentChanges.allKeys.count) - { - [CountlyConnectionManager.sharedInstance sendConsentChanges:[self.consentChanges cly_JSONify]]; - [self.consentChanges removeAllObjects]; - } + NSDictionary * consents = + @{ + CLYConsentSessions: @(self.consentForSessions), + CLYConsentEvents: @(self.consentForEvents), + CLYConsentUserDetails: @(self.consentForUserDetails), + CLYConsentCrashReporting: @(self.consentForCrashReporting), + CLYConsentPushNotifications: @(self.consentForPushNotifications), + CLYConsentLocation: @(self.consentForLocation), + CLYConsentViewTracking: @(self.consentForViewTracking), + CLYConsentAttribution: @(self.consentForAttribution), + CLYConsentPerformanceMonitoring: @(self.consentForPerformanceMonitoring), + CLYConsentFeedback: @(self.consentForFeedback), + }; + + [CountlyConnectionManager.sharedInstance sendConsents:[consents cly_JSONify]]; } @@ -235,8 +241,6 @@ - (void)setConsentForSessions:(BOOL)consentForSessions { CLY_LOG_D(@"Consent for Session is cancelled."); } - - self.consentChanges[CLYConsentSessions] = @(consentForSessions); } @@ -255,8 +259,6 @@ - (void)setConsentForEvents:(BOOL)consentForEvents [CountlyConnectionManager.sharedInstance sendEvents]; [CountlyPersistency.sharedInstance clearAllTimedEvents]; } - - self.consentChanges[CLYConsentEvents] = @(consentForEvents); } @@ -274,8 +276,6 @@ - (void)setConsentForUserDetails:(BOOL)consentForUserDetails [CountlyUserDetails.sharedInstance clearUserDetails]; } - - self.consentChanges[CLYConsentUserDetails] = @(consentForUserDetails); } @@ -295,8 +295,6 @@ - (void)setConsentForCrashReporting:(BOOL)consentForCrashReporting [CountlyCrashReporter.sharedInstance stopCrashReporting]; } - - self.consentChanges[CLYConsentCrashReporting] = @(consentForCrashReporting); } @@ -321,8 +319,6 @@ - (void)setConsentForPushNotifications:(BOOL)consentForPushNotifications #endif } #endif - - self.consentChanges[CLYConsentPushNotifications] = @(consentForPushNotifications); } @@ -340,8 +336,6 @@ - (void)setConsentForLocation:(BOOL)consentForLocation { CLY_LOG_D(@"Consent for Location is cancelled."); } - - self.consentChanges[CLYConsentLocation] = @(consentForLocation); } @@ -363,8 +357,6 @@ - (void)setConsentForViewTracking:(BOOL)consentForViewTracking [CountlyViewTracking.sharedInstance stopAutoViewTracking]; } #endif - - self.consentChanges[CLYConsentViewTracking] = @(consentForViewTracking); } @@ -382,8 +374,6 @@ - (void)setConsentForAttribution:(BOOL)consentForAttribution { CLY_LOG_D(@"Consent for Attribution is cancelled."); } - - self.consentChanges[CLYConsentAttribution] = @(consentForAttribution); } @@ -405,8 +395,6 @@ - (void)setConsentForPerformanceMonitoring:(BOOL)consentForPerformanceMonitoring [CountlyPerformanceMonitoring.sharedInstance stopPerformanceMonitoring]; } #endif - - self.consentChanges[CLYConsentPerformanceMonitoring] = @(consentForPerformanceMonitoring); } - (void)setConsentForFeedback:(BOOL)consentForFeedback @@ -425,8 +413,6 @@ - (void)setConsentForFeedback:(BOOL)consentForFeedback CLY_LOG_D(@"Consent for Feedback is cancelled."); } #endif - - self.consentChanges[CLYConsentFeedback] = @(consentForFeedback); } - (void)setConsentForRemoteConfig:(BOOL)consentForRemoteConfig @@ -443,8 +429,6 @@ - (void)setConsentForRemoteConfig:(BOOL)consentForRemoteConfig { CLY_LOG_D(@"Consent for RemoteConfig is cancelled."); } - - self.consentChanges[CLYConsentRemoteConfig] = @(consentForRemoteConfig); } #pragma mark - diff --git a/ios/Classes/CountlyiOS/CountlyFeedbacks.h b/ios/Classes/CountlyiOS/CountlyFeedbacks.h index a243f2da..a411e7ba 100644 --- a/ios/Classes/CountlyiOS/CountlyFeedbacks.h +++ b/ios/Classes/CountlyiOS/CountlyFeedbacks.h @@ -21,6 +21,7 @@ extern NSString* const kCountlyReservedEventStarRating; - (void)showDialog:(void(^)(NSInteger rating))completion; - (void)checkFeedbackWidgetWithID:(NSString *)widgetID completionHandler:(void (^)(NSError * error))completionHandler; +- (void)recordRatingWidgetWithID:(NSString *)widgetID rating:(NSInteger)rating email:(NSString *)email comment:(NSString *)comment userCanBeContacted:(BOOL)userCanBeContacted; - (void)checkForStarRatingAutoAsk; - (void)getFeedbackWidgets:(void (^)(NSArray *feedbackWidgets, NSError *error))completionHandler; diff --git a/ios/Classes/CountlyiOS/CountlyFeedbacks.m b/ios/Classes/CountlyiOS/CountlyFeedbacks.m index f693c974..5c689138 100644 --- a/ios/Classes/CountlyiOS/CountlyFeedbacks.m +++ b/ios/Classes/CountlyiOS/CountlyFeedbacks.m @@ -35,6 +35,9 @@ @interface CountlyFeedbacks () NSString* const kCountlyFBKeyPhone = @"phone"; NSString* const kCountlyFBKeyTablet = @"tablet"; NSString* const kCountlyFBKeyFeedback = @"feedback"; +NSString* const kCountlyFBKeyEmail = @"email"; +NSString* const kCountlyFBKeyComment = @"comment"; +NSString* const kCountlyFBKeyContactMe = @"contactMe"; const CGFloat kCountlyStarRatingButtonSize = 40.0; @@ -373,6 +376,27 @@ - (BOOL)isDeviceTargetedByWidget:(NSDictionary *)widgetInfo return ((isTablet && isTabletTargeted) || (isPhone && isPhoneTargeted)); } +- (void)recordRatingWidgetWithID:(NSString *)widgetID rating:(NSInteger)rating email:(NSString *)email comment:(NSString *)comment userCanBeContacted:(BOOL)userCanBeContacted +{ + if (!CountlyConsentManager.sharedInstance.consentForFeedback) + return; + + if (!widgetID.length) + return; + + NSMutableDictionary* segmentation = NSMutableDictionary.new; + segmentation[kCountlyFBKeyPlatform] = CountlyDeviceInfo.osName; + segmentation[kCountlyFBKeyAppVersion] = CountlyDeviceInfo.appVersion; + segmentation[kCountlyFBKeyRating] = @(rating); + segmentation[kCountlyFBKeyWidgetID] = widgetID; + segmentation[kCountlyFBKeyEmail] = email; + segmentation[kCountlyFBKeyComment] = comment; + segmentation[kCountlyFBKeyContactMe] = @(userCanBeContacted); + + [Countly.sharedInstance recordReservedEvent:kCountlyReservedEventStarRating segmentation:segmentation]; +} + + #pragma mark - Feedbacks (Surveys, NPS) - (void)getFeedbackWidgets:(void (^)(NSArray *feedbackWidgets, NSError *error))completionHandler diff --git a/ios/Classes/CountlyiOS/CountlyPushNotifications.m b/ios/Classes/CountlyiOS/CountlyPushNotifications.m index d0ae67f7..1e4fbbe6 100644 --- a/ios/Classes/CountlyiOS/CountlyPushNotifications.m +++ b/ios/Classes/CountlyiOS/CountlyPushNotifications.m @@ -9,6 +9,10 @@ NSString* const kCountlyReservedEventPushAction = @"[CLY]_push_action"; NSString* const kCountlyTokenError = @"kCountlyTokenError"; +NSString* const kCountlyPNKeyPlatform = @"p"; +NSString* const kCountlyPNKeyiOS = @"i"; +NSString* const kCountlyPNKeymacOS = @"m"; + //NOTE: Push Notification Test Modes CLYPushTestMode const CLYPushTestModeDevelopment = @"CLYPushTestModeDevelopment"; CLYPushTestMode const CLYPushTestModeTestFlightOrAdHoc = @"CLYPushTestModeTestFlightOrAdHoc"; @@ -218,10 +222,18 @@ - (void)recordActionEvent:(NSString *)notificationID buttonIndex:(NSInteger)butt if (!notificationID) return; + NSString* platform = @"unknown"; +#if (TARGET_OS_IOS) + platform = kCountlyPNKeyiOS; +#elif (TARGET_OS_OSX) + platform = kCountlyPNKeymacOS; +#endif + NSDictionary* segmentation = @{ kCountlyPNKeyNotificationID: notificationID, - kCountlyPNKeyActionButtonIndex: @(buttonIndex) + kCountlyPNKeyActionButtonIndex: @(buttonIndex), + kCountlyPNKeyPlatform: platform, }; [Countly.sharedInstance recordReservedEvent:kCountlyReservedEventPushAction segmentation:segmentation]; diff --git a/ios/Classes/CountlyiOS/LICENSE.md b/ios/Classes/CountlyiOS/LICENSE.md new file mode 100644 index 00000000..950c9b1b --- /dev/null +++ b/ios/Classes/CountlyiOS/LICENSE.md @@ -0,0 +1,19 @@ +Copyright (c) 2012, 2020 Countly + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ios/Classes/CountlyiOS/README.md b/ios/Classes/CountlyiOS/README.md new file mode 100644 index 00000000..02a7fffb --- /dev/null +++ b/ios/Classes/CountlyiOS/README.md @@ -0,0 +1,59 @@ +# Countly iOS SDK + +[![Platform](https://img.shields.io/cocoapods/p/Countly.svg?style=flat)](https://support.count.ly/hc/en-us/articles/360037753511-iOS-watchOS-tvOS-macOS#supported-system-versions) +[![GitHub license](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://github.com/Countly/countly-sdk-ios/blob/master/LICENSE.md) +[![GitHub release](https://img.shields.io/github/release/Countly/countly-sdk-ios.svg)](https://github.com/Countly/countly-sdk-ios/releases) +[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Countly.svg)](https://support.count.ly/hc/en-us/articles/360037753511-iOS-watchOS-tvOS-macOS#cocoapods) +[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://support.count.ly/hc/en-us/articles/360037753511-iOS-watchOS-tvOS-macOS#carthage) + +## What is Countly? +[Countly](http://count.ly) is a product analytics solution and innovation enabler that helps teams track product performance and customer journey and behavior across [mobile](https://count.ly/mobile-analytics), [web](http://count.ly/web-analytics), and [desktop](https://count.ly/desktop-analytics) applications. [Ensuring privacy by design](https://count.ly/your-data-your-rules), Countly allows you to innovate and enhance your products to provide personalized and customized customer experiences, and meet key business and revenue goals. + +Track, measure, and take action - all without leaving Countly. + +## About this SDK +This repository includes Countly iOS SDK with watchOS, tvOS & macOS support. +The minimum deployment targets are `iOS 10.0`, `watchOS 4.0`, `tvOS 10.0` , `macOS 10.14`, and it requires `Xcode 13.0+`. + +See [Countly iOS SDK documentation](https://support.count.ly/hc/en-us/articles/360037753511-iOS-watchOS-tvOS-macOS) for integration and details. + +## Sample iOS Application +We also have a useful [sample iOS application](https://github.com/Countly/countly-sample-ios) which demonstrates how to use this SDK in depth. +It includes iOS (both Objective-C and Swift), watchOS, tvOS and macOS sample projects. +Feel free to use them as a reference while you develop your application and also for easily testing your Countly Server. + +![iOS-sample-app](https://count.ly/github/countly-ios-sample-app.png) + +## Security + +Security is very important to us. If you discover any issue regarding security, please disclose the information responsibly by sending an email to security@count.ly and **not by creating a GitHub issue**. + +## Other Countly Resources +This SDK needs one of the following counterpart Countly Server editions to work: + +* [Countly Community Edition](https://github.com/Countly/countly-server) (downloadable from GitHub) +* [Countly Enterprise Edition](https://count.ly/product) + +For more information about Countly Enterprise Edition, please see [comparison of Countly editions](https://count.ly/pricing#compare-editions). + +There are also other [Countly SDK repositories](https://support.count.ly/hc/en-us/articles/360037236571-Downloading-Installing-SDKs) both official and community supported. + +## How can I help you with your efforts? +Glad you asked. We need ideas, feedbacks and constructive comments. +All your suggestions will be taken care with upmost importance. +We are on [Twitter](https://twitter.com/gocountly), [Facebook](https://www.facebook.com/Countly) and [YouTube](https://www.youtube.com/user/GoCountly) if you would like to keep up with our fast progress! + +## Badges +If you like Countly, [why not use one of our badges](https://count.ly/brand-assets) and give a link back to us, so others could know about this wonderful platform? + +Countly - Product Analytics + + Countly - Product Analytics + +Countly - Product Analytics + + Countly - Product Analytics + +## Support +Have any questions? +Visit [Countly Community Area](https://support.count.ly/hc/en-us/community/topics "Countly Community Area") or join our [Slack community](https://slack.count.ly). diff --git a/ios/Classes/CountlyiOS/SECURITY.md b/ios/Classes/CountlyiOS/SECURITY.md new file mode 100644 index 00000000..515d399c --- /dev/null +++ b/ios/Classes/CountlyiOS/SECURITY.md @@ -0,0 +1,3 @@ +# Security Policy + +Security is very important to us. If you discover any issue regarding security, please disclose the information responsibly by sending an email to security@count.ly and not by creating a GitHub issue. diff --git a/ios/Classes/CountlyiOS/countly_dsym_uploader.sh b/ios/Classes/CountlyiOS/countly_dsym_uploader.sh new file mode 100755 index 00000000..bb061973 --- /dev/null +++ b/ios/Classes/CountlyiOS/countly_dsym_uploader.sh @@ -0,0 +1,121 @@ +#!/bin/bash + +# countly_dsym_uploader.sh +# +# This code is provided under the MIT License. +# +# Please visit www.count.ly for more information. + + +# For your target, go to `Build Phases` tab and choose `New Run Script Phase` after clicking plus (+) button. +# Add these two lines: +# +# COUNTLY_DSYM_UPLOADER=$(find $SRCROOT -name "countly_dsym_uploader.sh" | head -n 1) +# sh "$COUNTLY_DSYM_UPLOADER" "https://YOUR_COUNTLY_SERVER" "YOUR_APP_KEY" +# +# or if you're using CocoaPods just add this one line: +# +# sh "$(PODS_ROOT)/Countly/countly_dsym_uploader.sh" "https://YOUR_COUNTLY_SERVER" "YOUR_APP_KEY" +# +# Notes: +# Do not forget to replace YOUR_COUNTLY_SERVER and YOUR_APP_KEY with real values. +# If your project setup and/or CI/CD flow requires a custom path for the generated dSYMs, you can specify it as third argument. + + +# Common functions +countly_log () { echo "[Countly] $1"; } + +countly_fail () { countly_log "$1"; exit 0; } + +countly_usage () +{ + countly_log "You must invoke the script as follows:" + echo " sh \"/path/to/.../countly_dsym_uploader.sh\" \"https://YOUR_COUNTLY_SERVER\" \"YOUR_APP_KEY\" [\"/path/to/.../your.dSYM\"]" +} + + +# Reading arguments +HOST="${1}"; +APPKEY="${2}"; +CUSTOM_DSYM_PATH="${3}" + + +# Pre-checks +if [[ -z $HOST ]]; then + countly_usage + countly_fail "Host not specified!" +fi + +if [[ -z $APPKEY ]]; then + countly_usage + countly_fail "App Key not specified!" +fi + +if [[ -z $CUSTOM_DSYM_PATH ]]; then + if [ ! "${DWARF_DSYM_FOLDER_PATH}" ] || [ ! "${DWARF_DSYM_FILE_NAME}" ]; then + countly_usage + countly_fail "Custom dSYM path not specified and Xcode Environment Variables are missing!" + fi + + DSYM_FOLDER_PATH=${DWARF_DSYM_FOLDER_PATH} + DSYM_FILE_NAME=${DWARF_DSYM_FILE_NAME} +else + DSYM_FOLDER_PATH=$(dirname "${CUSTOM_DSYM_PATH}") + DSYM_FILE_NAME=$(basename "${CUSTOM_DSYM_PATH}") +fi + +DSYM_PATH="${DSYM_FOLDER_PATH}/${DSYM_FILE_NAME}"; +if [[ ! -d $DSYM_PATH ]]; then + countly_fail "dSYM path ${DSYM_PATH} does not exist!" +fi + + +# Extracting Build UUIDs from DSYM using dwarfdump +BUILD_UUIDS=$(xcrun dwarfdump --uuid "${DSYM_PATH}" | awk '{print $2}' | xargs | sed 's/ /,/g') +if [ $? -eq 0 ]; then + countly_log "Extracted Build UUIDs: ${BUILD_UUIDS}" +else + countly_fail "Extracting Build UUIDs failed!" +fi + + +# Creating archive of DSYM folder using zip +DSYM_ZIP_PATH="/tmp/$(date +%s)_${DSYM_FILE_NAME}.zip" +pushd "${DSYM_FOLDER_PATH}" > /dev/null +zip -rq "${DSYM_ZIP_PATH}" "${DSYM_FILE_NAME}" +popd > /dev/null +if [ $? -eq 0 ]; then + countly_log "Created archive at $DSYM_ZIP_PATH" +else + countly_fail "Creating archive failed!" +fi + + +# Preparing for upload +ENDPOINT="/i/crash_symbols/upload_symbol" + +PLATFORM="ios" #This value is common for all iOS/iPadOS/watchOS/tvOS/macOS + +EPN=${EFFECTIVE_PLATFORM_NAME:1} +if [[ -z $EPN ]]; then +EPN="macos" +fi + +QUERY="?platform=${PLATFORM}&epn=${EPN}&app_key=${APPKEY}&build=${BUILD_UUIDS}" +URL="${HOST}${ENDPOINT}${QUERY}" +countly_log "Uploading to ${URL}" + + +# Uploading to server using curl +UPLOAD_RESULT=$(curl -s -F "symbols=@${DSYM_ZIP_PATH}" "${URL}") +if [ $? -eq 0 ] && [ "${UPLOAD_RESULT}" == "{\"result\":\"Success\"}" ]; then + countly_log "dSYM upload succesfully completed." +else + countly_fail "dSYM upload failed! ${UPLOAD_RESULT}" +fi + + +# Removing artifacts +rm "${DSYM_ZIP_PATH}" + +exit 0 diff --git a/lib/countly_config.dart b/lib/countly_config.dart index ff51b74b..f592a97e 100644 --- a/lib/countly_config.dart +++ b/lib/countly_config.dart @@ -229,7 +229,6 @@ class CountlyConfig { } /// Report direct user attribution - /// Currently implemented for Android only. CountlyConfig recordDirectAttribution(String campaignType, String campaignData) { _daCampaignType = campaignType; _daCampaignData = campaignData; diff --git a/lib/countly_flutter.dart b/lib/countly_flutter.dart index 664c0c93..f683a0de 100644 --- a/lib/countly_flutter.dart +++ b/lib/countly_flutter.dart @@ -1765,7 +1765,6 @@ class Countly { } /// set direct attribution Id for campaign attribution reporting. - /// Currently implemented for Android only. static Future recordDirectAttribution(String campaignType, String campaignData) async { if (!_isInitialized) { String message = '"initWithConfig" must be called before "recordDirectAttribution"'; @@ -1773,9 +1772,6 @@ class Countly { return message; } log('Calling recordDirectAttribution: [$campaignType] with campaignData: [$campaignData]'); - if (!Platform.isAndroid) { - return 'recordDirectAttribution : To be implemented'; - } if (campaignType.isEmpty) { String error = 'recordDirectAttribution, campaignId cannot be empty'; log(error);