Skip to content
This repository was archived by the owner on Jul 22, 2020. It is now read-only.

Commit bfaf75b

Browse files
authored
Feature/itbl 1883 deeplink track redirect (#31)
* Tracks and returns an iterable redirected URL * Adds format check for /a/ in the url before trying to call the url to track a click on deeplinked links. * Updated getAndTrackDeeplink to pass back original url if it isn't an app link. Added unit tests for getAndTrackDeeplink. * Updating deeplink link check with a more robust regex.
1 parent b6fc8d4 commit bfaf75b

File tree

4 files changed

+91
-35
lines changed

4 files changed

+91
-35
lines changed

Iterable-iOS-SDK/IterableAPI.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,16 @@ typedef NS_ENUM(NSInteger, PushServicePlatform) {
514514
*/
515515
-(void) showSystemNotification:(NSString *)title body:(NSString *)body buttonLeft:(NSString *)buttonLeft buttonRight:(NSString *)buttonRight callbackBlock:(ITEActionBlock)callbackBlock;
516516

517+
/*!
518+
@method
519+
520+
@abstract tracks a link click and passes the redirected URL to the callback
521+
522+
@param webpageURL the URL that was clicked
523+
@param callbackBlock the callback to send after the webpageURL is called
524+
525+
@discussion passes the string of the redirected URL to the callback
526+
*/+(void) getAndTrackDeeplink:(NSURL *)webpageURL callbackBlock:(ITEActionBlock)callbackBlock;
517527

518528
@end
519529

Iterable-iOS-SDK/IterableAPI.m

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,55 @@ - (instancetype)createSession:(NSDictionary *)launchOptions useCustomLaunchOptio
345345
/// @name Implementations of things documents in IterableAPI.h
346346
//////////////////////////////////////////////////////////////
347347

348+
// documented in IterableAPI.h
349+
+ (IterableAPI *)sharedInstance
350+
{
351+
if (sharedInstance == nil) {
352+
LogError(@"[sharedInstance called before sharedInstanceWithApiKey");
353+
}
354+
return sharedInstance;
355+
}
356+
357+
// documented in IterableAPI.h
358+
+ (IterableAPI *)sharedInstanceWithApiKey:(NSString *)apiKey andEmail:(NSString *)email launchOptions:(NSDictionary *)launchOptions
359+
{
360+
// threadsafe way to create a static singleton https://stackoverflow.com/questions/5720029/create-singleton-using-gcds-dispatch-once-in-objective-c
361+
static dispatch_once_t onceToken;
362+
dispatch_once(&onceToken, ^{
363+
sharedInstance = [[IterableAPI alloc] initWithApiKey:apiKey andEmail:email launchOptions:launchOptions];
364+
});
365+
return sharedInstance;
366+
}
367+
368+
// documented in IterableAPI.h
369+
+ (IterableAPI *)sharedInstanceWithApiKey:(NSString *)apiKey andUserId:(NSString *)userId launchOptions:(NSDictionary *)launchOptions
370+
{
371+
// threadsafe way to create a static singleton https://stackoverflow.com/questions/5720029/create-singleton-using-gcds-dispatch-once-in-objective-c
372+
static dispatch_once_t onceToken;
373+
dispatch_once(&onceToken, ^{
374+
sharedInstance = [[IterableAPI alloc] initWithApiKey:apiKey andUserId:userId launchOptions:launchOptions];
375+
});
376+
return sharedInstance;
377+
}
378+
379+
// documented in IterableAPI.h
380+
+(void) getAndTrackDeeplink:(NSURL *)webpageURL callbackBlock:(ITEActionBlock)callbackBlock
381+
{
382+
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:ITBL_DEEPLINK_IDENTIFIER options:0 error:NULL];
383+
NSString *urlString = webpageURL.absoluteString;
384+
NSTextCheckingResult *match = [regex firstMatchInString:urlString options:0 range:NSMakeRange(0, [urlString length])];
385+
386+
if (match == NULL) {
387+
callbackBlock(webpageURL.absoluteString);
388+
} else {
389+
NSURLSessionDataTask *trackAndRedirectTask = [[NSURLSession sharedSession]
390+
dataTaskWithURL:webpageURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
391+
callbackBlock(response.URL.absoluteString);
392+
}];
393+
[trackAndRedirectTask resume];
394+
}
395+
}
396+
348397
// documented in IterableAPI.h
349398
- (instancetype)initWithApiKey:(NSString *)apiKey andEmail:(NSString *)email
350399
{
@@ -437,41 +486,6 @@ - (void)trackInAppClick:(NSNumber*)campaignId templateId:(NSNumber*)templateId m
437486
[self sendRequest:request onSuccess:[IterableAPI defaultOnSuccess:@"trackInAppClick"] onFailure:[IterableAPI defaultOnFailure:@"trackInAppClick"]];
438487
}
439488

440-
//////////////////////////////////////////////////////////////
441-
/// @name Implementations of things documents in IterableAPI.h
442-
//////////////////////////////////////////////////////////////
443-
444-
// documented in IterableAPI.h
445-
+ (IterableAPI *)sharedInstance
446-
{
447-
if (sharedInstance == nil) {
448-
LogError(@"[sharedInstance called before sharedInstanceWithApiKey");
449-
}
450-
return sharedInstance;
451-
}
452-
453-
// documented in IterableAPI.h
454-
+ (IterableAPI *)sharedInstanceWithApiKey:(NSString *)apiKey andEmail:(NSString *)email launchOptions:(NSDictionary *)launchOptions
455-
{
456-
// threadsafe way to create a static singleton https://stackoverflow.com/questions/5720029/create-singleton-using-gcds-dispatch-once-in-objective-c
457-
static dispatch_once_t onceToken;
458-
dispatch_once(&onceToken, ^{
459-
sharedInstance = [[IterableAPI alloc] initWithApiKey:apiKey andEmail:email launchOptions:launchOptions];
460-
});
461-
return sharedInstance;
462-
}
463-
464-
// documented in IterableAPI.h
465-
+ (IterableAPI *)sharedInstanceWithApiKey:(NSString *)apiKey andUserId:(NSString *)userId launchOptions:(NSDictionary *)launchOptions
466-
{
467-
// threadsafe way to create a static singleton https://stackoverflow.com/questions/5720029/create-singleton-using-gcds-dispatch-once-in-objective-c
468-
static dispatch_once_t onceToken;
469-
dispatch_once(&onceToken, ^{
470-
sharedInstance = [[IterableAPI alloc] initWithApiKey:apiKey andUserId:userId launchOptions:launchOptions];
471-
});
472-
return sharedInstance;
473-
}
474-
475489
// documented in IterableAPI.h
476490
- (void)registerToken:(NSData *)token appName:(NSString *)appName pushServicePlatform:(PushServicePlatform)pushServicePlatform
477491
{
@@ -807,11 +821,13 @@ - (void)getInAppMessages:(NSNumber *)count onSuccess:(OnSuccessHandler)onSuccess
807821
[self sendRequest:request onSuccess:onSuccess onFailure:onFailure];
808822
}
809823

824+
// documented in IterableAPI.h
810825
-(void) showSystemNotification:(NSString *)title body:(NSString *)body button:(NSString *)button callbackBlock:(ITEActionBlock)callbackBlock
811826
{
812827
[IterableInAppManager showSystemNotification:title body:body buttonLeft:button buttonRight:nil callbackBlock:callbackBlock];
813828
}
814829

830+
// documented in IterableAPI.h
815831
-(void) showSystemNotification:(NSString *)title body:(NSString *)body buttonLeft:(NSString *)buttonLeft buttonRight:(NSString *)buttonRight callbackBlock:(ITEActionBlock)callbackBlock
816832
{
817833
[IterableInAppManager showSystemNotification:title body:body buttonLeft:buttonLeft buttonRight:buttonRight callbackBlock:callbackBlock];

Iterable-iOS-SDK/IterableConstants.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ extern NSString *const ITBL_DEVICE_USER_INTERFACE;
6767
#define ITBL_KEY_UNSPECIFIED @"Unspecified"
6868

6969

70+
#define ITBL_DEEPLINK_IDENTIFIER @"/a/[a-zA-Z0-9]+"
71+
72+
7073
//In-App Constants
7174
#define ITERABLE_IN_APP_TITLE @"title"
7275
#define ITERABLE_IN_APP_BODY @"body"

Iterable-iOS-SDKTests/IterableAPITests.m

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,31 @@ - (void)testUserInterfaceIdionEnumToString {
8282
XCTAssertEqualObjects(@"Unspecified", [IterableAPI userInterfaceIdiomEnumToString:192387]);
8383
}
8484

85+
- (void)testUniversalDeepLinkRewriting {
86+
NSURL *iterableLink = [NSURL URLWithString:@"http://links.iterable.com/a/60402396fbd5433eb35397b47ab2fb83?_e=joneng%40iterable.com&_m=93125f33ba814b13a882358f8e0852e0"];
87+
88+
XCTestExpectation *expectation =
89+
[self expectationWithDescription:@"High Expectations"];
90+
ITEActionBlock aBlock = ^(NSString* redirectUrl) {
91+
[expectation fulfill];
92+
XCTAssertEqualObjects(@"https://links.iterable.com/api/docs#!/email", redirectUrl);
93+
94+
};
95+
[IterableAPI getAndTrackDeeplink:iterableLink callbackBlock:aBlock];
96+
97+
NSURL *normalLink = [NSURL URLWithString:@"http://links.iterable.com/u/60402396fbd5433eb35397b47ab2fb83?_e=joneng%40iterable.com&_m=93125f33ba814b13a882358f8e0852e0"];
98+
99+
ITEActionBlock uBlock = ^(NSString* redirectUrl) {
100+
XCTAssertEqualObjects(@"http://links.iterable.com/u/60402396fbd5433eb35397b47ab2fb83?_e=joneng%40iterable.com&_m=93125f33ba814b13a882358f8e0852e0", redirectUrl);
101+
102+
};
103+
[IterableAPI getAndTrackDeeplink:normalLink callbackBlock:uBlock];
104+
105+
[self waitForExpectationsWithTimeout:1.0 handler:^(NSError *error) {
106+
if (error) {
107+
NSLog(@"Timeout Error: %@", error);
108+
}
109+
}];
110+
}
111+
85112
@end

0 commit comments

Comments
 (0)