Skip to content

Commit

Permalink
Merge pull request #322 from Countly/location-fix
Browse files Browse the repository at this point in the history
Send an empty location if location is disabled or location consent is not given
  • Loading branch information
turtledreams committed Jul 29, 2024
2 parents 6669d68 + 21ac26f commit f038df8
Show file tree
Hide file tree
Showing 4 changed files with 282 additions and 7 deletions.
24 changes: 17 additions & 7 deletions Countly.m
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ - (void)startWithConfig:(CountlyConfig *)config
CountlyFeedbacks.sharedInstance.disableAskingForEachAppVersion = config.starRatingDisableAskingForEachAppVersion;
CountlyFeedbacks.sharedInstance.ratingCompletionForAutoAsk = config.starRatingCompletion;
[CountlyFeedbacks.sharedInstance checkForStarRatingAutoAsk];
#endif

if(config.disableLocation)
{
Expand All @@ -195,20 +196,14 @@ - (void)startWithConfig:(CountlyConfig *)config
{
[CountlyLocationManager.sharedInstance updateLocation:config.location city:config.city ISOCountryCode:config.ISOCountryCode IP:config.IP];
}
#endif

if (!CountlyCommon.sharedInstance.manualSessionHandling)
[CountlyConnectionManager.sharedInstance beginSession];

[CountlyCommon.sharedInstance recordOrientation];

//NOTE: If there is no consent for sessions, location info and attribution should be sent separately, as they cannot be sent with begin_session request.
if (!CountlyConsentManager.sharedInstance.consentForSessions)
{
[CountlyLocationManager.sharedInstance sendLocationInfo];
[CountlyConnectionManager.sharedInstance sendAttribution];
}


#if (TARGET_OS_IOS || TARGET_OS_OSX)
#ifndef COUNTLY_EXCLUDE_PUSHNOTIFICATIONS
if ([config.features containsObject:CLYPushNotifications])
Expand Down Expand Up @@ -291,6 +286,21 @@ - (void)startWithConfig:(CountlyConfig *)config
else if (config.requiresConsent)
[CountlyConsentManager.sharedInstance sendConsents];

if (!CountlyConsentManager.sharedInstance.consentForSessions)
{
//Send an empty location if location is disabled or location consent is not given, without checking for location consent.
if (!CountlyConsentManager.sharedInstance.consentForLocation || CountlyLocationManager.sharedInstance.isLocationInfoDisabled)
{
[CountlyConnectionManager.sharedInstance sendLocationInfo];
}
else
{
[CountlyLocationManager.sharedInstance sendLocationInfo];
}
[CountlyConnectionManager.sharedInstance sendAttribution];
}


if (config.campaignType && config.campaignData)
[self recordDirectAttributionWithCampaignType:config.campaignType andCampaignData:config.campaignData];

Expand Down
4 changes: 4 additions & 0 deletions Countly.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
39924ED02BEBD0D400139F91 /* CountlyCrashesConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 39924ECF2BEBD0D400139F91 /* CountlyCrashesConfig.m */; };
39924ED62BEBD20F00139F91 /* CountlyCrashData.m in Sources */ = {isa = PBXBuildFile; fileRef = 39924ED52BEBD20F00139F91 /* CountlyCrashData.m */; };
39924ED82BEBD22100139F91 /* CountlyCrashData.h in Headers */ = {isa = PBXBuildFile; fileRef = 39924ED72BEBD22100139F91 /* CountlyCrashData.h */; settings = {ATTRIBUTES = (Public, ); }; };
399B46502C52813700AD384E /* CountlyLocationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 399B464F2C52813700AD384E /* CountlyLocationTests.swift */; };
3B20A9872245225A00E3D7AE /* Countly.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B20A9852245225A00E3D7AE /* Countly.h */; settings = {ATTRIBUTES = (Public, ); }; };
3B20A9B22245228700E3D7AE /* CountlyConnectionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B20A98D2245228300E3D7AE /* CountlyConnectionManager.h */; };
3B20A9B32245228700E3D7AE /* CountlyNotificationService.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B20A98E2245228300E3D7AE /* CountlyNotificationService.m */; };
Expand Down Expand Up @@ -117,6 +118,7 @@
39924ECF2BEBD0D400139F91 /* CountlyCrashesConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CountlyCrashesConfig.m; sourceTree = "<group>"; };
39924ED52BEBD20F00139F91 /* CountlyCrashData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CountlyCrashData.m; sourceTree = "<group>"; };
39924ED72BEBD22100139F91 /* CountlyCrashData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CountlyCrashData.h; sourceTree = "<group>"; };
399B464F2C52813700AD384E /* CountlyLocationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountlyLocationTests.swift; sourceTree = "<group>"; };
3B20A9822245225A00E3D7AE /* Countly.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Countly.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3B20A9852245225A00E3D7AE /* Countly.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Countly.h; sourceTree = "<group>"; };
3B20A9862245225A00E3D7AE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -190,6 +192,7 @@
3972EDDA2C08A38D00EB9D3E /* CountlyEventStruct.swift */,
3966DBCE2C11EE270002ED97 /* CountlyDeviceIDTests.swift */,
3964A3E62C2AF8E90091E677 /* CountlySegmentationTests.swift */,
399B464F2C52813700AD384E /* CountlyLocationTests.swift */,
);
path = CountlyTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -418,6 +421,7 @@
buildActionMask = 2147483647;
files = (
1A5C4C972B35B0850032EE1F /* CountlyTests.swift in Sources */,
399B46502C52813700AD384E /* CountlyLocationTests.swift in Sources */,
1A50D7052B3C5AA3009C6938 /* CountlyBaseTestCase.swift in Sources */,
3979E47D2C0760E900FA1CA4 /* CountlyUserProfileTests.swift in Sources */,
968426812BF2302C007B303E /* CountlyConnectionManagerTests.swift in Sources */,
Expand Down
2 changes: 2 additions & 0 deletions CountlyConsentManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,8 @@ - (void)setConsentForLocation:(BOOL)consentForLocation
else
{
CLY_LOG_D(@"Consent for Location is cancelled.");

[CountlyConnectionManager.sharedInstance sendLocationInfo];
}
}

Expand Down
259 changes: 259 additions & 0 deletions CountlyTests/CountlyLocationTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
//
// CountlyLocationTests.swift
// CountlyTests
//
// Created by Muhammad Junaid Akram on 25/07/2024.
// Copyright © 2024 Countly. All rights reserved.
//

import XCTest
@testable import Countly

// M:Manual Sessions enabled
// A:Automatic sessions enabled
// H:Hybrid Sessions enabled
// CR:Consent Required
// CNR:Consent not Required
// CG:Consent given (All)
// CNG:Consent not given (All)
// CGS:Consent given for session
// CGL:Consent givent for location
// LD:Location Disable
// L: Location Provided

class CountlyLocationTests: CountlyBaseTestCase {

func testDummy() {
}

func testLocationInit_CNR_A() throws {
let config = createBaseConfig()
Countly.sharedInstance().start(with: config);

// get request queue
guard let queuedRequests = CountlyPersistency.sharedInstance().value(forKey: "queuedRequests") as? [String] else {
fatalError("Failed to get queuedRequests from CountlyPersistency")
}
XCTAssertTrue(queuedRequests[0].contains("begin_session=1"), "Begin session failed.")
XCTAssertFalse(queuedRequests[0].contains("location="), "Location should not be send in this scenario")
}

func testLocationInit_CR_CNG_A() throws {
let config = createBaseConfig()
config.requiresConsent = true
Countly.sharedInstance().start(with: config);

// get request queue
guard let queuedRequests = CountlyPersistency.sharedInstance().value(forKey: "queuedRequests") as? [String] else {
fatalError("Failed to get queuedRequests from CountlyPersistency")
}

XCTAssertFalse(queuedRequests[0].contains("begin_session=1"), "Begin session should not start session consent is not given.")
XCTAssertTrue(queuedRequests[1].contains("location="), "Individual location request should send in this scenario")
}

func testLocationInit_CR_CG_A() throws {
let config = createBaseConfig()
config.requiresConsent = true
config.enableAllConsents = true
Countly.sharedInstance().start(with: config);

// get request queue
guard let queuedRequests = CountlyPersistency.sharedInstance().value(forKey: "queuedRequests") as? [String] else {
fatalError("Failed to get queuedRequests from CountlyPersistency")
}

XCTAssertTrue(queuedRequests[0].contains("begin_session=1"), "Begin session failed.")
XCTAssertFalse(queuedRequests[0].contains("location="), "Location should not be send in this scenario")
}

func testLocationInit_CR_CGS_A() throws {
let config = createBaseConfig()
config.requiresConsent = true
config.consents = [CLYConsent.sessions];
Countly.sharedInstance().start(with: config);

// get request queue
guard let queuedRequests = CountlyPersistency.sharedInstance().value(forKey: "queuedRequests") as? [String] else {
fatalError("Failed to get queuedRequests from CountlyPersistency")
}

XCTAssertTrue(queuedRequests[0].contains("begin_session=1"), "Begin session failed.")
XCTAssertTrue(queuedRequests[0].contains("location="), "Location should send in this scenario")
}

func testLocationInit_CR_CGL_A() throws {
let config = createBaseConfig()
config.requiresConsent = true
config.consents = [CLYConsent.location];
Countly.sharedInstance().start(with: config);

// get request queue
guard let queuedRequests = CountlyPersistency.sharedInstance().value(forKey: "queuedRequests") as? [String] else {
fatalError("Failed to get queuedRequests from CountlyPersistency")
}

XCTAssertTrue(queuedRequests[0].contains("consent="), "Only consent request should send in this scenario")
}

func testLocationInit_CR_CGLS_A() throws {
let config = createBaseConfig()
config.requiresConsent = true
config.consents = [CLYConsent.location, CLYConsent.sessions];
Countly.sharedInstance().start(with: config);

// get request queue
guard let queuedRequests = CountlyPersistency.sharedInstance().value(forKey: "queuedRequests") as? [String] else {
fatalError("Failed to get queuedRequests from CountlyPersistency")
}

XCTAssertTrue(queuedRequests[0].contains("begin_session=1"), "Begin session failed.")
XCTAssertFalse(queuedRequests[0].contains("location="), "Location should not be send in this scenario")
}

func testLocationInit_CNR_A_L() throws {
let config = createBaseConfig()
config.location = CLLocationCoordinate2D(latitude:35.6895, longitude: 139.6917)
config.city = "Tokyo"
config.isoCountryCode = "JP"
config.ip = "255.255.255.255"
Countly.sharedInstance().start(with: config);

// get request queue
guard let queuedRequests = CountlyPersistency.sharedInstance().value(forKey: "queuedRequests") as? [String] else {
fatalError("Failed to get queuedRequests from CountlyPersistency")
}
XCTAssertTrue(queuedRequests[0].contains("begin_session=1"), "Begin session failed.")

let parsedRequest = parseQueryString(queuedRequests[0])

XCTAssertTrue((parsedRequest["location"] as! String) == "35.689500,139.691700", "Begin session should contains provided location")
XCTAssertTrue((parsedRequest["city"] as! String) == "Tokyo", "Begin session should contains provided city")
XCTAssertTrue((parsedRequest["country_code"] as! String) == "JP", "Begin session should contains provided country code")
XCTAssertTrue((parsedRequest["ip_address"] as! String) == "255.255.255.255", "Begin session should contains provided IP address")
}

func testLocationInit_CNR_M() throws {
let config = createBaseConfig()
config.manualSessionHandling = true
Countly.sharedInstance().start(with: config);

Countly.sharedInstance().beginSession()

// get request queue
guard let queuedRequests = CountlyPersistency.sharedInstance().value(forKey: "queuedRequests") as? [String] else {
fatalError("Failed to get queuedRequests from CountlyPersistency")
}
XCTAssertTrue(queuedRequests[0].contains("begin_session=1"), "Begin session failed.")
XCTAssertFalse(queuedRequests[0].contains("location="), "Location should not be send in this scenario")
}

func testLocationInit_CR_CNG_M() throws {
let config = createBaseConfig()
config.manualSessionHandling = true
config.requiresConsent = true
Countly.sharedInstance().start(with: config);
Countly.sharedInstance().beginSession()

// get request queue
guard let queuedRequests = CountlyPersistency.sharedInstance().value(forKey: "queuedRequests") as? [String] else {
fatalError("Failed to get queuedRequests from CountlyPersistency")
}

XCTAssertFalse(queuedRequests[0].contains("begin_session=1"), "Begin session should not start session consent is not given.")
XCTAssertTrue(queuedRequests[1].contains("location="), "Individual location request should send in this scenario")
}

func testLocationInit_CR_CG_M() throws {
let config = createBaseConfig()
config.manualSessionHandling = true
config.requiresConsent = true
config.enableAllConsents = true
Countly.sharedInstance().start(with: config);
Countly.sharedInstance().beginSession()

// get request queue
guard let queuedRequests = CountlyPersistency.sharedInstance().value(forKey: "queuedRequests") as? [String] else {
fatalError("Failed to get queuedRequests from CountlyPersistency")
}

XCTAssertTrue(queuedRequests[1].contains("begin_session=1"), "Begin session failed.")
XCTAssertFalse(queuedRequests[1].contains("location="), "Location should not be send in this scenario")
}

func testLocationInit_CR_CGS_M() throws {
let config = createBaseConfig()
config.manualSessionHandling = true
config.requiresConsent = true
config.consents = [CLYConsent.sessions];
Countly.sharedInstance().start(with: config);
Countly.sharedInstance().beginSession()

// get request queue
guard let queuedRequests = CountlyPersistency.sharedInstance().value(forKey: "queuedRequests") as? [String] else {
fatalError("Failed to get queuedRequests from CountlyPersistency")
}

XCTAssertTrue(queuedRequests[1].contains("begin_session=1"), "Begin session failed.")
XCTAssertTrue(queuedRequests[1].contains("location="), "Location should send in this scenario")
}

func testLocationInit_CR_CGL_M() throws {
let config = createBaseConfig()
config.manualSessionHandling = true
config.requiresConsent = true
config.consents = [CLYConsent.location];
Countly.sharedInstance().start(with: config);
Countly.sharedInstance().beginSession()

// get request queue
guard let queuedRequests = CountlyPersistency.sharedInstance().value(forKey: "queuedRequests") as? [String] else {
fatalError("Failed to get queuedRequests from CountlyPersistency")
}

XCTAssertTrue(queuedRequests[0].contains("consent="), "Only consent request should send in this scenario")
}

func testLocationInit_CR_CGLS_M() throws {
let config = createBaseConfig()
config.manualSessionHandling = true
config.requiresConsent = true
config.consents = [CLYConsent.location, CLYConsent.sessions];
Countly.sharedInstance().start(with: config);
Countly.sharedInstance().beginSession()

// get request queue
guard let queuedRequests = CountlyPersistency.sharedInstance().value(forKey: "queuedRequests") as? [String] else {
fatalError("Failed to get queuedRequests from CountlyPersistency")
}

XCTAssertTrue(queuedRequests[1].contains("begin_session=1"), "Begin session failed.")
XCTAssertFalse(queuedRequests[1].contains("location="), "Location should not be send in this scenario")
}

func testLocationInit_CNR_M_L() throws {
let config = createBaseConfig()
config.manualSessionHandling = true
config.location = CLLocationCoordinate2D(latitude:35.6895, longitude: 139.6917)
config.city = "Tokyo"
config.isoCountryCode = "JP"
config.ip = "255.255.255.255"
Countly.sharedInstance().start(with: config);
Countly.sharedInstance().beginSession()

// get request queue
guard let queuedRequests = CountlyPersistency.sharedInstance().value(forKey: "queuedRequests") as? [String] else {
fatalError("Failed to get queuedRequests from CountlyPersistency")
}
XCTAssertTrue(queuedRequests[0].contains("begin_session=1"), "Begin session failed.")

let parsedRequest = parseQueryString(queuedRequests[0])

XCTAssertTrue((parsedRequest["location"] as! String) == "35.689500,139.691700", "Begin session should contains provided location")
XCTAssertTrue((parsedRequest["city"] as! String) == "Tokyo", "Begin session should contains provided city")
XCTAssertTrue((parsedRequest["country_code"] as! String) == "JP", "Begin session should contains provided country code")
XCTAssertTrue((parsedRequest["ip_address"] as! String) == "255.255.255.255", "Begin session should contains provided IP address")
}

}

0 comments on commit f038df8

Please sign in to comment.