Skip to content

Commit fa0d3c5

Browse files
bsneedBrandon Sneed
andauthored
Data Residency Changes (#973)
* Fixed warnings * Added data residency support. * Fixed test Co-authored-by: Brandon Sneed <[email protected]>
1 parent c40da41 commit fa0d3c5

14 files changed

+183
-29
lines changed

Segment.xcodeproj/project.pbxproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@
463463
isa = PBXProject;
464464
attributes = {
465465
LastSwiftUpdateCheck = 0810;
466-
LastUpgradeCheck = 1200;
466+
LastUpgradeCheck = 1220;
467467
ORGANIZATIONNAME = Segment;
468468
TargetAttributes = {
469469
EADEB85A1DECD080005322DA = {
@@ -607,7 +607,7 @@
607607
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
608608
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
609609
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
610-
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
610+
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
611611
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
612612
CLANG_WARN_STRICT_PROTOTYPES = YES;
613613
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -674,7 +674,7 @@
674674
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
675675
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
676676
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
677-
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
677+
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
678678
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
679679
CLANG_WARN_STRICT_PROTOTYPES = YES;
680680
CLANG_WARN_SUSPICIOUS_MOVE = YES;

Segment.xcodeproj/xcshareddata/xcschemes/Segment.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1200"
3+
LastUpgradeVersion = "1220"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

Segment.xcodeproj/xcshareddata/xcschemes/SegmentTests.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1200"
3+
LastUpgradeVersion = "1220"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

Segment.xcodeproj/xcshareddata/xcschemes/SegmentTestsTVOS.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1200"
3+
LastUpgradeVersion = "1220"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

Segment/Classes/SEGAnalyticsConfiguration.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,33 @@ NS_SWIFT_NAME(AnalyticsConfiguration)
5454

5555
/**
5656
* Creates and returns a configuration with default settings and the given write key.
57+
* This will use the API host `https://api.segment.io/v1` as the default.
5758
*
5859
* @param writeKey Your project's write key from segment.io.
5960
*/
6061
+ (_Nonnull instancetype)configurationWithWriteKey:(NSString *_Nonnull)writeKey;
6162

63+
/**
64+
* Creates and returns a configuration with default settings and the given write key.
65+
*
66+
* @param writeKey Your project's write key from segment.io.
67+
* @param defaultAPIHost The default API host to be used if none are supplied from Segment.com
68+
*/
69+
+ (_Nonnull instancetype)configurationWithWriteKey:(NSString *_Nonnull)writeKey defaultAPIHost:(NSURL *_Nullable)defaultAPIHost;
70+
6271
/**
6372
* Your project's write key from segment.io.
6473
*
6574
* @see +configurationWithWriteKey:
6675
*/
6776
@property (nonatomic, copy, readonly, nonnull) NSString *writeKey;
6877

78+
/**
79+
* The API host to be used for network requests to Segment.
80+
* This value can change based on settings obtained from Segment.com.
81+
*/
82+
@property (nonatomic, copy, readonly, nullable) NSURL *apiHost;
83+
6984
/**
7085
* Whether the analytics client should use location services.
7186
* If `YES` and the host app hasn't asked for permission to use location services then the user will be presented with an alert view asking to do so. `NO` by default.

Segment/Classes/SEGAnalyticsConfiguration.m

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#import "SEGAnalytics.h"
1111
#import "SEGMiddleware.h"
1212
#import "SEGCrypto.h"
13+
#import "SEGHTTPClient.h"
14+
#import "SEGUtils.h"
1315
#if TARGET_OS_IPHONE
1416
@import UIKit;
1517
#elif TARGET_OS_OSX
@@ -41,20 +43,39 @@ @interface SEGAnalyticsConfiguration ()
4143
@property (nonatomic, strong, readonly) NSMutableArray *factories;
4244
@property (nonatomic, strong) SEGAnalyticsExperimental *experimental;
4345

46+
- (instancetype)initWithWriteKey:(NSString *)writeKey defaultAPIHost:(NSURL * _Nullable)defaultAPIHost;
47+
4448
@end
4549

4650

4751
@implementation SEGAnalyticsConfiguration
4852

4953
+ (instancetype)configurationWithWriteKey:(NSString *)writeKey
5054
{
51-
return [[SEGAnalyticsConfiguration alloc] initWithWriteKey:writeKey];
55+
return [[SEGAnalyticsConfiguration alloc] initWithWriteKey:writeKey defaultAPIHost:nil];
56+
}
57+
58+
+ (instancetype)configurationWithWriteKey:(NSString *)writeKey defaultAPIHost:(NSURL * _Nullable)defaultAPIHost
59+
{
60+
return [[SEGAnalyticsConfiguration alloc] initWithWriteKey:writeKey defaultAPIHost:defaultAPIHost];
5261
}
5362

54-
- (instancetype)initWithWriteKey:(NSString *)writeKey
63+
- (instancetype)initWithWriteKey:(NSString *)writeKey defaultAPIHost:(NSURL * _Nullable)defaultAPIHost
5564
{
5665
if (self = [self init]) {
5766
self.writeKey = writeKey;
67+
68+
// get the host we have stored
69+
NSString *host = [SEGUtils getAPIHost];
70+
if ([host isEqualToString:kSegmentAPIBaseHost]) {
71+
// we're getting the generic host back. have they
72+
// supplied something other than that?
73+
if (defaultAPIHost && ![host isEqualToString:defaultAPIHost.absoluteString]) {
74+
// we should use the supplied default.
75+
host = defaultAPIHost.absoluteString;
76+
[SEGUtils saveAPIHost:host];
77+
}
78+
}
5879
}
5980
return self;
6081
}
@@ -86,6 +107,11 @@ - (instancetype)init
86107
return self;
87108
}
88109

110+
- (NSURL *)apiHost
111+
{
112+
return [SEGUtils getAPIHostURL];
113+
}
114+
89115
- (void)use:(id<SEGIntegrationFactory>)factory
90116
{
91117
[self.factories addObject:factory];

Segment/Classes/SEGHTTPClient.h

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
@import Foundation;
22
#import "SEGAnalytics.h"
33

4-
// TODO: Make this configurable via SEGAnalyticsConfiguration
5-
// NOTE: `/` at the end kind of screws things up. So don't use it
6-
//#define SEGMENT_API_BASE [NSURL URLWithString:@"https://api-segment-io-5fsaj1xnikhp.runscope.net/v1"]
7-
//#define SEGMENT_CDN_BASE [NSURL URLWithString:@"https://cdn-segment-com-5fsaj1xnikhp.runscope.net/v1"]
8-
#define SEGMENT_API_BASE [NSURL URLWithString:@"https://api.segment.io/v1"]
9-
#define SEGMENT_CDN_BASE [NSURL URLWithString:@"https://cdn-settings.segment.com/v1"]
10-
114
NS_ASSUME_NONNULL_BEGIN
125

6+
extern NSString * const kSegmentAPIBaseHost;
7+
138

149
NS_SWIFT_NAME(HTTPClient)
1510
@interface SEGHTTPClient : NSObject

Segment/Classes/SEGHTTPClient.m

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
#import "SEGHTTPClient.h"
22
#import "NSData+SEGGZIP.h"
33
#import "SEGAnalyticsUtils.h"
4+
#import "SEGUtils.h"
5+
6+
#define SEGMENT_CDN_BASE [NSURL URLWithString:@"https://cdn-settings.segment.com/v1"]
47

58
static const NSUInteger kMaxBatchSize = 475000; // 475KB
69

10+
NSString * const kSegmentAPIBaseHost = @"https://api.segment.io/v1";
11+
712
@implementation SEGHTTPClient
813

914
+ (NSMutableURLRequest * (^)(NSURL *))defaultRequestFactory
@@ -72,7 +77,7 @@ - (nullable NSURLSessionUploadTask *)upload:(NSDictionary *)batch forWriteKey:(N
7277
// batch = SEGCoerceDictionary(batch);
7378
NSURLSession *session = [self sessionForWriteKey:writeKey];
7479

75-
NSURL *url = [SEGMENT_API_BASE URLByAppendingPathComponent:@"batch"];
80+
NSURL *url = [[SEGUtils getAPIHostURL] URLByAppendingPathComponent:@"batch"];
7681
NSMutableURLRequest *request = self.requestFactory(url);
7782

7883
// This is a workaround for an IOS 8.3 bug that causes Content-Type to be incorrectly set

Segment/Classes/SEGSegmentIntegration.m

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ @interface SEGSegmentIntegration ()
4141
@property (nonatomic, assign) SEGAnalyticsConfiguration *configuration;
4242
@property (atomic, copy) NSDictionary *referrer;
4343
@property (nonatomic, copy) NSString *userId;
44-
@property (nonatomic, strong) NSURL *apiURL;
4544
@property (nonatomic, strong) SEGHTTPClient *httpClient;
4645
@property (nonatomic, strong) id<SEGStorage> fileStorage;
4746
@property (nonatomic, strong) id<SEGStorage> userDefaultsStorage;
@@ -69,7 +68,6 @@ - (id)initWithAnalytics:(SEGAnalytics *)analytics httpClient:(SEGHTTPClient *)ht
6968
self.httpClient.httpSessionDelegate = analytics.oneTimeConfiguration.httpSessionDelegate;
7069
self.fileStorage = fileStorage;
7170
self.userDefaultsStorage = userDefaultsStorage;
72-
self.apiURL = [SEGMENT_API_BASE URLByAppendingPathComponent:@"import"];
7371
self.reachability = [SEGReachability reachabilityWithHostname:@"google.com"];
7472
[self.reachability startNotifier];
7573
self.serialQueue = seg_dispatch_queue_create_specific("io.segment.analytics.segmentio", DISPATCH_QUEUE_SERIAL);

Segment/Internal/SEGIntegrationsManager.m

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,12 @@ - (void)setCachedSettings:(NSDictionary *)settings
403403

404404
- (void)updateIntegrationsWithSettings:(NSDictionary *)projectSettings
405405
{
406+
// see if we have a new segment API host and set it.
407+
NSString *apiHost = projectSettings[@"Segment.io"][@"apiHost"];
408+
if (apiHost) {
409+
[SEGUtils saveAPIHost:apiHost];
410+
}
411+
406412
seg_dispatch_specific_sync(_serialQueue, ^{
407413
if (self.initialized) {
408414
return;
@@ -443,8 +449,29 @@ - (void)configureEdgeFunctions:(NSDictionary *)settings
443449
}
444450
}
445451

452+
- (NSDictionary *)defaultSettings
453+
{
454+
return @{
455+
@"integrations" : @{
456+
@"Segment.io" : @{
457+
@"apiKey" : self.configuration.writeKey,
458+
@"apiHost" : [SEGUtils getAPIHost]
459+
},
460+
},
461+
@"plan" : @{@"track" : @{}}
462+
};
463+
}
464+
446465
- (void)refreshSettings
447466
{
467+
// look at our cache immediately, lets try to get things running
468+
// with the last values while we wait to see about any updates.
469+
NSDictionary *previouslyCachedSettings = [self cachedSettings];
470+
if (previouslyCachedSettings && [previouslyCachedSettings count] > 0) {
471+
[self setCachedSettings:previouslyCachedSettings];
472+
[self configureEdgeFunctions:previouslyCachedSettings];
473+
}
474+
448475
seg_dispatch_specific_async(_serialQueue, ^{
449476
if (self.settingsRequest) {
450477
return;
@@ -459,23 +486,24 @@ - (void)refreshSettings
459486
NSDictionary *previouslyCachedSettings = [self cachedSettings];
460487
if (previouslyCachedSettings && [previouslyCachedSettings count] > 0) {
461488
[self setCachedSettings:previouslyCachedSettings];
462-
[self configureEdgeFunctions:settings];
489+
[self configureEdgeFunctions:previouslyCachedSettings];
463490
} else if (self.configuration.defaultSettings != nil) {
464491
// If settings request fail, load a user-supplied version if present.
465492
// but make sure segment.io is in the integrations
466493
NSMutableDictionary *newSettings = [self.configuration.defaultSettings serializableMutableDeepCopy];
467-
newSettings[@"integrations"][@"Segment.io"][@"apiKey"] = self.configuration.writeKey;
494+
NSMutableDictionary *integrations = newSettings[@"integrations"];
495+
if (integrations != nil) {
496+
integrations[@"Segment.io"] = @{@"apiKey": self.configuration.writeKey, @"apiHost": [SEGUtils getAPIHost]};
497+
} else {
498+
newSettings[@"integrations"] = @{@"integrations": @{@"apiKey": self.configuration.writeKey, @"apiHost": [SEGUtils getAPIHost]}};
499+
}
500+
468501
[self setCachedSettings:newSettings];
469502
// don't configure edge functions here. it'll do the right thing on it's own.
470503
} else {
471504
// If settings request fail, fall back to using just Segment integration.
472505
// Doesn't address situations where this callback never gets called (though we don't expect that to ever happen).
473-
[self setCachedSettings:@{
474-
@"integrations" : @{
475-
@"Segment.io" : @{@"apiKey" : self.configuration.writeKey},
476-
},
477-
@"plan" : @{@"track" : @{}}
478-
}];
506+
[self setCachedSettings:[self defaultSettings]];
479507
// don't configure edge functions here. it'll do the right thing on it's own.
480508
}
481509
}

Segment/Internal/SEGState.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ NS_ASSUME_NONNULL_BEGIN
3939
+ (instancetype)sharedInstance;
4040
- (instancetype)init __unavailable;
4141

42-
- (void)setUserInfo:(SEGUserInfo *)userInfo;
4342
@end
4443

4544
NS_ASSUME_NONNULL_END

Segment/Internal/SEGUtils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ NS_ASSUME_NONNULL_BEGIN
1515
NS_SWIFT_NAME(Utilities)
1616
@interface SEGUtils : NSObject
1717

18+
+ (void)saveAPIHost:(nonnull NSString *)apiHost;
19+
+ (nonnull NSString *)getAPIHost;
20+
+ (nullable NSURL *)getAPIHostURL;
21+
1822
+ (NSData *_Nullable)dataFromPlist:(nonnull id)plist;
1923
+ (id _Nullable)plistFromData:(NSData *)data;
2024

Segment/Internal/SEGUtils.m

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#import "SEGAnalyticsConfiguration.h"
88
#import "SEGReachability.h"
99
#import "SEGAnalytics.h"
10+
#import "SEGHTTPClient.h"
1011

1112
#include <sys/sysctl.h>
1213

@@ -15,8 +16,37 @@
1516
static CTTelephonyNetworkInfo *_telephonyNetworkInfo;
1617
#endif
1718

19+
const NSString *segment_apiHost = @"segment_apihost";
20+
1821
@implementation SEGUtils
1922

23+
+ (void)saveAPIHost:(nonnull NSString *)apiHost
24+
{
25+
if (!apiHost) {
26+
return;
27+
}
28+
if (![apiHost containsString:@"https://"]) {
29+
apiHost = [NSString stringWithFormat:@"https://%@", apiHost];
30+
}
31+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
32+
[defaults setObject:apiHost forKey:[segment_apiHost copy]];
33+
}
34+
35+
+ (nonnull NSString *)getAPIHost
36+
{
37+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
38+
NSString *result = [defaults stringForKey:[segment_apiHost copy]];
39+
if (!result) {
40+
result = kSegmentAPIBaseHost;
41+
}
42+
return result;
43+
}
44+
45+
+ (nullable NSURL *)getAPIHostURL
46+
{
47+
return [NSURL URLWithString:[SEGUtils getAPIHost]];
48+
}
49+
2050
+ (NSData *_Nullable)dataFromPlist:(nonnull id)plist
2151
{
2252
NSError *error = nil;

0 commit comments

Comments
 (0)