Skip to content

Commit d917bac

Browse files
authored
Allow setting the domainURIPrefix for custom domain names/paths when creating dynamic links (#2071)
* Add support for creating DL with custom domain names/paths. The new domainURIPrefix parameter requires either a. a valid FDL domain name created in the Firebase console or b. a custom domain name or c. a custom domain name with path registered for DL. All domainURIPrefixes need to start with a valid (https) scheme. * Fix style. * style * Fix typo in DEPRECATED_MSG_ATTRIBUTE, other nits. * Fix styling. * Check incoming domainURIPrefix for validity using NSURL and check for an https scheme.
1 parent 5038e26 commit d917bac

File tree

6 files changed

+153
-31
lines changed

6 files changed

+153
-31
lines changed

Example/DynamicLinks/FDLBuilderTestAppObjC/ViewController.m

+5-4
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,9 @@ - (void)_initDefaultValues {
155155
},
156156
// The default value of domain appcode belongs to project: app-invites-qa
157157
@{
158-
@"id" : @"domain",
159-
@"label" : @"App domain (required)",
160-
@"defaultValue" : @"testfdl.page.link",
158+
@"id" : @"domainURIPrefix",
159+
@"label" : @"App domainURIPrefix (required)",
160+
@"defaultValue" : @"https://testfdl.page.link",
161161
},
162162
// analytics params
163163
@{
@@ -289,7 +289,8 @@ - (void)_initDefaultValues {
289289
- (void)_buildFDLLink {
290290
NSURL *link = [NSURLURLWithString:_paramValues[@"linkString"]];
291291
FIRDynamicLinkComponents *components =
292-
[FIRDynamicLinkComponents componentsWithLink:linkdomain:_paramValues[@"domain"]];
292+
[FIRDynamicLinkComponents componentsWithLink:link
293+
domainURIPrefix:_paramValues[@"https://domain"]];
293294

294295
FIRDynamicLinkGoogleAnalyticsParameters *analyticsParams =
295296
[FIRDynamicLinkGoogleAnalyticsParameters

Example/DynamicLinks/Tests/FDLURLComponentsTests.m

+66-21
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121

2222
#import<OCMock/OCMock.h>
2323

24-
staticNSString *constkFDLURLDomain = @"xyz.page.link";
24+
staticNSString *constkFDLURLDomain = @"https://xyz.page.link";
25+
staticNSString *constkFDLURLCustomDomain = @"https://foo.com/path";
2526

2627
@interfaceFDLURLComponentsTests : XCTestCase
2728
@end
@@ -461,14 +462,14 @@ - (void)testLinkOptionsParamsPropertiesSetProperly {
461462

462463
- (void)testFDLComponentsFactoryReturnsInstanceOfCorrectClass {
463464
NSURL *link = [NSURLURLWithString:@"https://google.com"];
464-
id returnValue = [FIRDynamicLinkComponents componentsWithLink:linkdomain:kFDLURLDomain];
465+
id returnValue = [FIRDynamicLinkComponents componentsWithLink:linkdomainURIPrefix:kFDLURLDomain];
465466
XCTAssertTrue([returnValue isKindOfClass:[FIRDynamicLinkComponents class]]);
466467
}
467468

468469
- (void)testFDLComponentsFactoryReturnsInstanceWithAllNilProperties {
469470
NSURL *link = [NSURLURLWithString:@"https://google.com"];
470471
FIRDynamicLinkComponents *components =
471-
[FIRDynamicLinkComponents componentsWithLink:linkdomain:kFDLURLDomain];
472+
[FIRDynamicLinkComponents componentsWithLink:linkdomainURIPrefix:kFDLURLDomain];
472473

473474
XCTAssertNil(components.analyticsParameters);
474475
XCTAssertNil(components.socialMetaTagParameters);
@@ -484,11 +485,27 @@ - (void)testFDLComponentsCreatesSimplestLinkCorrectly {
484485
NSURL *link = [NSURLURLWithString:linkString];
485486

486487
NSString *expectedURLString =
487-
[NSStringstringWithFormat:@"https://%@/?link=%@", kFDLURLDomain, endcodedLinkString];
488+
[NSStringstringWithFormat:@"%@/?link=%@", kFDLURLDomain, endcodedLinkString];
488489
NSURL *expectedURL = [NSURLURLWithString:expectedURLString];
489490

490491
FIRDynamicLinkComponents *components =
491-
[FIRDynamicLinkComponents componentsWithLink:linkdomain:kFDLURLDomain];
492+
[FIRDynamicLinkComponents componentsWithLink:linkdomainURIPrefix:kFDLURLDomain];
493+
NSURL *actualURL = components.url;
494+
495+
XCTAssertEqualObjects(actualURL, expectedURL);
496+
}
497+
498+
- (void)testFDLComponentsCustomDomainWithPath {
499+
NSString *linkString = @"https://google.com";
500+
NSString *endcodedLinkString = @"https%3A%2F%2Fgoogle%2Ecom";
501+
NSURL *link = [NSURLURLWithString:linkString];
502+
503+
NSString *expectedURLString =
504+
[NSStringstringWithFormat:@"%@/?link=%@", kFDLURLCustomDomain, endcodedLinkString];
505+
NSURL *expectedURL = [NSURLURLWithString:expectedURLString];
506+
507+
FIRDynamicLinkComponents *components =
508+
[FIRDynamicLinkComponents componentsWithLink:linkdomainURIPrefix:kFDLURLCustomDomain];
492509
NSURL *actualURL = components.url;
493510

494511
XCTAssertEqualObjects(actualURL, expectedURL);
@@ -499,7 +516,8 @@ - (void)testFDLComponentsFailsOnMalformedDomain {
499516
NSURL *link = [NSURLURLWithString:linkString];
500517

501518
FIRDynamicLinkComponents *components =
502-
[FIRDynamicLinkComponents componentsWithLink:linkdomain:@"this is invalid domain"];
519+
[FIRDynamicLinkComponents componentsWithLink:link
520+
domainURIPrefix:@"this is invalid domain URI Prefix"];
503521

504522
XCTAssertNil(components.url);
505523
}
@@ -553,7 +571,7 @@ - (void)testFDLComponentsCreatesFullLinkCorrectly {
553571

554572
NSURL *link = [NSURLURLWithString:@"https://google.com"];
555573
FIRDynamicLinkComponents *fdlComponents =
556-
[FIRDynamicLinkComponents componentsWithLink:linkdomain:kFDLURLDomain];
574+
[FIRDynamicLinkComponents componentsWithLink:linkdomainURIPrefix:kFDLURLDomain];
557575
fdlComponents.analyticsParameters = analyticsParams;
558576
fdlComponents.iOSParameters = iosParams;
559577
fdlComponents.iTunesConnectParameters = itcParams;
@@ -642,7 +660,43 @@ - (void)testShortenURL {
642660
XCTestExpectation *expectation = [selfexpectationWithDescription:@"completion called"];
643661
NSURL *link = [NSURLURLWithString:@"https://google.com/abc"];
644662
FIRDynamicLinkComponents *components =
645-
[FIRDynamicLinkComponents componentsWithLink:linkdomain:kFDLURLDomain];
663+
[FIRDynamicLinkComponents componentsWithLink:linkdomainURIPrefix:kFDLURLDomain];
664+
[components
665+
shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray<NSString *> *_Nullable warnings,
666+
NSError *_Nullable error) {
667+
XCTAssertEqualObjects(shortURL.absoluteString, shortURLString);
668+
[expectation fulfill];
669+
}];
670+
[selfwaitForExpectationsWithTimeout:0.1handler:nil];
671+
672+
[keyProviderClassMock verify];
673+
[keyProviderClassMock stopMocking];
674+
[componentsClassMock verify];
675+
[componentsClassMock stopMocking];
676+
}
677+
678+
- (void)testDeprecatedMethodComponentsWithLinkForDomain {
679+
NSString *shortURLString = @"https://xyz.page.link/abcd";
680+
681+
// Mock key provider
682+
id keyProviderClassMock = OCMClassMock([FIRDynamicLinkComponentsKeyProvider class]);
683+
[[[keyProviderClassMock expect] andReturn:@"fake-api-key"] APIKey];
684+
685+
id componentsClassMock = OCMClassMock([FIRDynamicLinkComponents class]);
686+
[[componentsClassMock expect]
687+
sendHTTPRequest:OCMOCK_ANY
688+
completion:[OCMArg checkWithBlock:^BOOL(id obj) {
689+
void (^completion)(NSData *_Nullable, NSError *_Nullable) = obj;
690+
NSDictionary *JSON = @{@"shortLink" : shortURLString};
691+
NSData *JSONData = [NSJSONSerializationdataWithJSONObject:JSON options:0error:0];
692+
completion(JSONData, nil);
693+
returnYES;
694+
}]];
695+
696+
XCTestExpectation *expectation = [selfexpectationWithDescription:@"completion called"];
697+
NSURL *link = [NSURLURLWithString:@"https://google.com/abc"];
698+
FIRDynamicLinkComponents *components =
699+
[FIRDynamicLinkComponents componentsWithLink:linkdomain:@"xyz.page.link"];
646700
[components
647701
shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray<NSString *> *_Nullable warnings,
648702
NSError *_Nullable error) {
@@ -679,7 +733,7 @@ - (void)testShortenURLReturnsErrorWhenAPIKeyMissing {
679733
[selfexpectationWithDescription:@"completion called with error"];
680734
NSURL *link = [NSURLURLWithString:@"https://google.com/abc"];
681735
FIRDynamicLinkComponents *components =
682-
[FIRDynamicLinkComponents componentsWithLink:linkdomain:kFDLURLDomain];
736+
[FIRDynamicLinkComponents componentsWithLink:linkdomainURIPrefix:kFDLURLDomain];
683737
[components
684738
shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray<NSString *> *_Nullable warnings,
685739
NSError *_Nullable error) {
@@ -714,20 +768,11 @@ - (void)testShortenURLReturnsErrorWhenDomainIsMalformed {
714768
returnYES;
715769
}]];
716770

717-
XCTestExpectation *expectation =
718-
[selfexpectationWithDescription:@"completion called with error"];
719771
NSURL *link = [NSURLURLWithString:@"https://google.com/abc"];
720772
FIRDynamicLinkComponents *components =
721-
[FIRDynamicLinkComponents componentsWithLink:linkdomain:@"this is invalid domain"];
722-
[components
723-
shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray<NSString *> *_Nullable warnings,
724-
NSError *_Nullable error) {
725-
XCTAssertNil(shortURL);
726-
if (error) {
727-
[expectation fulfill];
728-
}
729-
}];
730-
[selfwaitForExpectationsWithTimeout:0.1handler:nil];
773+
[FIRDynamicLinkComponents componentsWithLink:link
774+
domainURIPrefix:@"this is invalid domain URI Prefix"];
775+
XCTAssertNil(components);
731776

732777
[keyProviderClassMock verify];
733778
[keyProviderClassMock stopMocking];

Example/DynamicLinks/Tests/FIRDynamicLinkNetworkingTests.m

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
#import<XCTest/XCTest.h>
1818

19-
#import"OCMock.h"
19+
#import<OCMock/OCMock.h>
2020

2121
#import<GoogleUtilities/GULSwizzler.h>
2222
#import"DynamicLinks/FIRDynamicLinkNetworking+Private.h"

Firebase/DynamicLinks/FDLURLComponents/FDLURLComponents.m

+43-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#import"DynamicLinks/FDLURLComponents/FIRDynamicLinkComponentsKeyProvider.h"
2121
#import"DynamicLinks/Public/FDLURLComponents.h"
2222

23+
#import"DynamicLinks/Logging/FDLLogging.h"
2324
#import"DynamicLinks/Utilities/FDLUtilities.h"
2425

2526
/// The exact behavior of dict[key] = value is unclear when value is nil. This function safely adds
@@ -448,15 +449,54 @@ - (instancetype)init {
448449

449450
@implementationFIRDynamicLinkComponents
450451

452+
#pragma mark Deprecated Initializers.
451453
+ (instancetype)componentsWithLink:(NSURL *)linkdomain:(NSString *)domain {
452-
return [[selfalloc] initWithLink:linkdomain:domain];
454+
NSURL *domainURL = [NSURLURLWithString:domain];
455+
NSString *domainURIPrefix =
456+
domainURL.scheme ? domain : [NSStringstringWithFormat:@"https://%@", domain];
457+
return [FIRDynamicLinkComponents componentsWithLink:linkdomainURIPrefix:domainURIPrefix];
453458
}
454459

455460
- (instancetype)initWithLink:(NSURL *)linkdomain:(NSString *)domain {
461+
NSURL *domainURL = [NSURLURLWithString:domain];
462+
NSString *domainURIPrefix =
463+
domainURL.scheme ? domain : [NSStringstringWithFormat:@"https://%@", domain];
464+
return [selfinitWithLink:linkdomainURIPrefix:domainURIPrefix];
465+
}
466+
467+
#pragma mark Initializers.
468+
+ (instancetype)componentsWithLink:(NSURL *)linkdomainURIPrefix:(NSString *)domainURIPrefix {
469+
NSURL *domainURIPrefixURL = [NSURLURLWithString:domainURIPrefix];
470+
if (!domainURIPrefixURL) {
471+
FDLLog(FDLLogLevelError, FDLLogIdentifierSetupInvalidDomainURIPrefix,
472+
@"Invalid domainURIPrefix. Please input a valid URL.");
473+
returnnil;
474+
}
475+
if (![[domainURIPrefixURL.scheme lowercaseString] hasPrefix:@"https"]) {
476+
FDLLog(FDLLogLevelError, FDLLogIdentifierSetupInvalidDomainURIPrefixScheme,
477+
@"Invalid domainURIPrefix scheme. Scheme needs to be https");
478+
returnnil;
479+
}
480+
return [[selfalloc] initWithLink:linkdomainURIPrefix:domainURIPrefix];
481+
}
482+
483+
- (instancetype)initWithLink:(NSURL *)linkdomainURIPrefix:(NSString *)domainURIPrefix {
456484
self = [superinit];
457485
if (self) {
458486
_link = link;
459-
_domain = [domain copy];
487+
/// Must be a URL that conforms to RFC 2396.
488+
NSURL *domainURIPrefixURL = [NSURLURLWithString:domainURIPrefix];
489+
if (!domainURIPrefixURL) {
490+
FDLLog(FDLLogLevelError, FDLLogIdentifierSetupInvalidDomainURIPrefix,
491+
@"Invalid domainURIPrefix. Please input a valid URL.");
492+
returnnil;
493+
}
494+
if (![[domainURIPrefixURL.scheme lowercaseString] hasPrefix:@"https"]) {
495+
FDLLog(FDLLogLevelError, FDLLogIdentifierSetupInvalidDomainURIPrefixScheme,
496+
@"Invalid domainURIPrefix scheme. Scheme needs to be https");
497+
returnnil;
498+
}
499+
_domain = [domainURIPrefix copy];
460500
}
461501
return self;
462502
}
@@ -593,7 +633,7 @@ - (NSURL *)url {
593633
addEntriesFromDictionaryRepresentingConformerToDictionary(_otherPlatformParameters);
594634

595635
NSString *queryString = FIRDLURLQueryStringFromDictionary(queryDictionary);
596-
NSString *urlString = [NSStringstringWithFormat:@"https://%@/%@", _domain, queryString];
636+
NSString *urlString = [NSStringstringWithFormat:@"%@/%@", _domain, queryString];
597637
return [NSURLURLWithString:urlString];
598638
}
599639

Firebase/DynamicLinks/Logging/FDLLogging.h

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ typedef NS_ENUM(NSInteger, FDLLogIdentifier) {
3333
FDLLogIdentifierSetupNilAPIKey = 0,
3434
FDLLogIdentifierSetupNilClientID = 1,
3535
FDLLogIdentifierSetupNonDefaultApp = 2,
36+
FDLLogIdentifierSetupInvalidDomainURIPrefixScheme = 3,
37+
FDLLogIdentifierSetupInvalidDomainURIPrefix = 4,
3638
};
3739

3840
/** The appropriate formatter for using NSInteger in FIRLogger. */

Firebase/DynamicLinks/Public/FDLURLComponents.h

+36-2
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,9 @@ FIR_SWIFT_NAME(DynamicLinkComponents)
516516
*/
517517
+ (instancetype)componentsWithLink:(NSURL *)link
518518
domain:(NSString *)domain
519-
NS_SWIFT_UNAVAILABLE("Use init(link:domain:)");
519+
NS_SWIFT_UNAVAILABLE("Use init(link:domain:)")DEPRECATED_MSG_ATTRIBUTE(
520+
"This method is deprecated. Please use the new method with support for "
521+
"domainURIPrefix- componentsWithLink:domainURIPrefix.");
520522

521523
/**
522524
* @method initWithLink:domain:
@@ -527,7 +529,39 @@ FIR_SWIFT_NAME(DynamicLinkComponents)
527529
* @param domain Domain of your App. This value must be equal to your assigned domain from Firebase
528530
* Console.
529531
*/
530-
- (instancetype)initWithLink:(NSURL *)link domain:(NSString *)domain;
532+
- (instancetype)initWithLink:(NSURL *)link
533+
domain:(NSString *)domain
534+
DEPRECATED_MSG_ATTRIBUTE(
535+
"This method is deprecated. Please use the new method with support for "
536+
"domainURIPrefix- initWithLink:domainURIPrefix.");
537+
538+
/**
539+
* @method componentsWithLink:domain:
540+
* @abstract Generates a Dynamic Link URL components object with the minimum necessary parameters
541+
* set to generate a fully-functional Dynamic Link.
542+
* @param link Deep link to be stored in created Dynamic link. This link also called "payload" of
543+
* the Dynamic link.
544+
* @param domainURIPrefix Domain URI Prefix of your App. This value must be either a. your assigned
545+
* domain from the Firebase console or b. your custom domain or c. your custom domain with a valid
546+
* path that is registered for Dynamic Links. The domain URI prefix must start with a valid scheme
547+
* (https://)
548+
*/
549+
+ (instancetype)componentsWithLink:(NSURL *)link
550+
domainURIPrefix:(NSString *)domainURIPrefix
551+
NS_SWIFT_UNAVAILABLE("Use init(link:domainURIPrefix:)");
552+
553+
/**
554+
* @method initWithLink:domain:
555+
* @abstract Generates a Dynamic Link URL components object with the minimum necessary parameters
556+
* set to generate a fully-functional Dynamic Link.
557+
* @param link Deep link to be stored in created Dynamic link. This link also called "payload" of
558+
* the Dynamic link.
559+
* @param domainURIPrefix Domain URI Prefix of your App. This value must be either a. your assigned
560+
* domain from the Firebase console or b. your custom domain or c. your custom domain with a valid
561+
* path that is registered for Dynamic Links. The domain URI prefix must start with a valid scheme
562+
* (https://).
563+
*/
564+
- (instancetype)initWithLink:(NSURL *)link domainURIPrefix:(NSString *)domainURIPrefix;
531565

532566
/**
533567
* @method shortenURL:options:completion:

0 commit comments

Comments
 (0)
close