FDLURLComponents.m 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. /*
  2. * Copyright 2018 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <Foundation/Foundation.h>
  17. #import "FirebaseDynamicLinks/Sources/FDLURLComponents/FDLURLComponents+Private.h"
  18. #import "FirebaseDynamicLinks/Sources/FDLURLComponents/FIRDynamicLinkComponentsKeyProvider.h"
  19. #import "FirebaseDynamicLinks/Sources/Public/FDLURLComponents.h"
  20. #import "FirebaseDynamicLinks/Sources/Logging/FDLLogging.h"
  21. #import "FirebaseDynamicLinks/Sources/Utilities/FDLUtilities.h"
  22. // Label exceptions from FDL.
  23. NSString *const kFirebaseDurableDeepLinkErrorDomain = @"com.firebase.durabledeeplink";
  24. /// The exact behavior of dict[key] = value is unclear when value is nil. This function safely adds
  25. /// the key-value pair to the dictionary, even when value is nil.
  26. /// This function will treat empty string in the same way as nil.
  27. NS_INLINE void FDLSafelyAddKeyValuePairToDictionary(NSString *key,
  28. NSString *stringValue,
  29. NSMutableDictionary *dictionary) {
  30. if (stringValue != nil && stringValue.length > 0) {
  31. dictionary[key] = stringValue;
  32. } else {
  33. [dictionary removeObjectForKey:key];
  34. }
  35. }
  36. @implementation FIRDynamicLinkGoogleAnalyticsParameters {
  37. NSMutableDictionary<NSString *, NSString *> *_dictionary;
  38. }
  39. static NSString *const kFDLUTMSourceKey = @"utm_source";
  40. static NSString *const kFDLUTMMediumKey = @"utm_medium";
  41. static NSString *const kFDLUTMCampaignKey = @"utm_campaign";
  42. static NSString *const kFDLUTMTermKey = @"utm_term";
  43. static NSString *const kFDLUTMContentKey = @"utm_content";
  44. + (instancetype)parameters {
  45. return [[self alloc] init];
  46. }
  47. + (instancetype)parametersWithSource:(NSString *)source
  48. medium:(NSString *)medium
  49. campaign:(NSString *)campaign {
  50. return [[self alloc] initWithSource:source medium:medium campaign:campaign];
  51. }
  52. - (instancetype)init {
  53. self = [super init];
  54. if (self) {
  55. _dictionary = [NSMutableDictionary dictionary];
  56. }
  57. return self;
  58. }
  59. - (instancetype)initWithSource:(NSString *)source
  60. medium:(NSString *)medium
  61. campaign:(NSString *)campaign {
  62. self = [self init];
  63. if (self) {
  64. FDLSafelyAddKeyValuePairToDictionary(kFDLUTMSourceKey, [source copy], _dictionary);
  65. FDLSafelyAddKeyValuePairToDictionary(kFDLUTMMediumKey, [medium copy], _dictionary);
  66. FDLSafelyAddKeyValuePairToDictionary(kFDLUTMCampaignKey, [campaign copy], _dictionary);
  67. }
  68. return self;
  69. }
  70. - (void)setSource:(NSString *)source {
  71. FDLSafelyAddKeyValuePairToDictionary(kFDLUTMSourceKey, [source copy], _dictionary);
  72. }
  73. - (NSString *)source {
  74. return _dictionary[kFDLUTMSourceKey];
  75. }
  76. - (void)setMedium:(NSString *)medium {
  77. FDLSafelyAddKeyValuePairToDictionary(kFDLUTMMediumKey, [medium copy], _dictionary);
  78. }
  79. - (NSString *)medium {
  80. return _dictionary[kFDLUTMMediumKey];
  81. }
  82. - (void)setCampaign:(NSString *)campaign {
  83. FDLSafelyAddKeyValuePairToDictionary(kFDLUTMCampaignKey, [campaign copy], _dictionary);
  84. }
  85. - (NSString *)campaign {
  86. return _dictionary[kFDLUTMCampaignKey];
  87. }
  88. - (void)setTerm:(NSString *)term {
  89. FDLSafelyAddKeyValuePairToDictionary(kFDLUTMTermKey, [term copy], _dictionary);
  90. }
  91. - (NSString *)term {
  92. return _dictionary[kFDLUTMTermKey];
  93. }
  94. - (void)setContent:(NSString *)content {
  95. FDLSafelyAddKeyValuePairToDictionary(kFDLUTMContentKey, [content copy], _dictionary);
  96. }
  97. - (NSString *)content {
  98. return _dictionary[kFDLUTMContentKey];
  99. }
  100. - (NSDictionary<NSString *, NSString *> *)dictionaryRepresentation {
  101. return [_dictionary copy];
  102. }
  103. @end
  104. @implementation FIRDynamicLinkIOSParameters {
  105. NSMutableDictionary<NSString *, NSString *> *_dictionary;
  106. }
  107. static NSString *const kFDLIOSBundleIdentifierKey = @"ibi";
  108. static NSString *const kFDLIOSAppStoreIdentifierKey = @"isi";
  109. static NSString *const kFDLIOSFallbackURLKey = @"ifl";
  110. static NSString *const kFDLIOSCustomURLSchemeKey = @"ius";
  111. static NSString *const kFDLIOSMinimumVersionKey = @"imv";
  112. static NSString *const kFDLIOSIPadBundleIdentifierKey = @"ipbi";
  113. static NSString *const kFDLIOSIPadFallbackURLKey = @"ipfl";
  114. + (instancetype)parametersWithBundleID:(NSString *)bundleID {
  115. return [[self alloc] initWithBundleID:bundleID];
  116. }
  117. - (instancetype)initWithBundleID:(NSString *)bundleID {
  118. self = [super init];
  119. if (self) {
  120. _dictionary = [NSMutableDictionary dictionary];
  121. FDLSafelyAddKeyValuePairToDictionary(kFDLIOSBundleIdentifierKey, [bundleID copy], _dictionary);
  122. }
  123. return self;
  124. }
  125. - (NSString *)bundleID {
  126. return _dictionary[kFDLIOSBundleIdentifierKey];
  127. }
  128. - (void)setAppStoreID:(NSString *)appStoreID {
  129. FDLSafelyAddKeyValuePairToDictionary(kFDLIOSAppStoreIdentifierKey, [appStoreID copy],
  130. _dictionary);
  131. }
  132. - (NSString *)appStoreID {
  133. return _dictionary[kFDLIOSAppStoreIdentifierKey];
  134. }
  135. - (void)setFallbackURL:(NSURL *)fallbackURL {
  136. FDLSafelyAddKeyValuePairToDictionary(kFDLIOSFallbackURLKey, fallbackURL.absoluteString,
  137. _dictionary);
  138. }
  139. - (NSURL *)fallbackURL {
  140. NSString *fallbackURLString = _dictionary[kFDLIOSFallbackURLKey];
  141. return fallbackURLString != nil ? [NSURL URLWithString:fallbackURLString] : nil;
  142. }
  143. - (void)setCustomScheme:(NSString *)customScheme {
  144. FDLSafelyAddKeyValuePairToDictionary(kFDLIOSCustomURLSchemeKey, [customScheme copy], _dictionary);
  145. }
  146. - (NSString *)customScheme {
  147. return _dictionary[kFDLIOSCustomURLSchemeKey];
  148. }
  149. - (void)setMinimumAppVersion:(NSString *)minimumAppVersion {
  150. FDLSafelyAddKeyValuePairToDictionary(kFDLIOSMinimumVersionKey, [minimumAppVersion copy],
  151. _dictionary);
  152. }
  153. - (NSString *)minimumAppVersion {
  154. return _dictionary[kFDLIOSMinimumVersionKey];
  155. }
  156. - (void)setIPadBundleID:(NSString *)iPadBundleID {
  157. FDLSafelyAddKeyValuePairToDictionary(kFDLIOSIPadBundleIdentifierKey, [iPadBundleID copy],
  158. _dictionary);
  159. }
  160. - (NSString *)iPadBundleID {
  161. return _dictionary[kFDLIOSIPadBundleIdentifierKey];
  162. }
  163. - (void)setIPadFallbackURL:(NSURL *)iPadFallbackURL {
  164. FDLSafelyAddKeyValuePairToDictionary(kFDLIOSIPadFallbackURLKey, iPadFallbackURL.absoluteString,
  165. _dictionary);
  166. }
  167. - (NSURL *)iPadFallbackURL {
  168. NSString *fallbackURLString = _dictionary[kFDLIOSIPadFallbackURLKey];
  169. return fallbackURLString != nil ? [NSURL URLWithString:fallbackURLString] : nil;
  170. }
  171. - (NSDictionary<NSString *, NSString *> *)dictionaryRepresentation {
  172. return [_dictionary copy];
  173. }
  174. @end
  175. @implementation FIRDynamicLinkItunesConnectAnalyticsParameters {
  176. NSMutableDictionary<NSString *, NSString *> *_dictionary;
  177. }
  178. static NSString *const kFDLITunesConnectAffiliateTokeyKey = @"at";
  179. static NSString *const kFDLITunesConnectCampaignTokenKey = @"ct";
  180. static NSString *const kFDLITunesConnectProviderTokenKey = @"pt";
  181. + (instancetype)parameters {
  182. return [[self alloc] init];
  183. }
  184. - (instancetype)init {
  185. self = [super init];
  186. if (self) {
  187. _dictionary = [NSMutableDictionary dictionary];
  188. }
  189. return self;
  190. }
  191. - (void)setAffiliateToken:(NSString *)affiliateToken {
  192. FDLSafelyAddKeyValuePairToDictionary(kFDLITunesConnectAffiliateTokeyKey, [affiliateToken copy],
  193. _dictionary);
  194. }
  195. - (NSString *)affiliateToken {
  196. return _dictionary[kFDLITunesConnectAffiliateTokeyKey];
  197. }
  198. - (void)setCampaignToken:(NSString *)campaignToken {
  199. FDLSafelyAddKeyValuePairToDictionary(kFDLITunesConnectCampaignTokenKey, [campaignToken copy],
  200. _dictionary);
  201. }
  202. - (NSString *)campaignToken {
  203. return _dictionary[kFDLITunesConnectCampaignTokenKey];
  204. }
  205. - (void)setProviderToken:(NSString *)providerToken {
  206. FDLSafelyAddKeyValuePairToDictionary(kFDLITunesConnectProviderTokenKey, [providerToken copy],
  207. _dictionary);
  208. }
  209. - (NSString *)providerToken {
  210. return _dictionary[kFDLITunesConnectProviderTokenKey];
  211. }
  212. - (NSDictionary<NSString *, NSString *> *)dictionaryRepresentation {
  213. return [_dictionary copy];
  214. }
  215. @end
  216. @implementation FIRDynamicLinkAndroidParameters {
  217. NSMutableDictionary<NSString *, NSString *> *_dictionary;
  218. }
  219. static NSString *const kFDLAndroidMinimumVersionKey = @"amv";
  220. static NSString *const kFDLAndroidFallbackURLKey = @"afl";
  221. static NSString *const kFDLAndroidPackageNameKey = @"apn";
  222. + (instancetype)parametersWithPackageName:(NSString *)packageName {
  223. return [[self alloc] initWithPackageName:packageName];
  224. }
  225. - (instancetype)initWithPackageName:(NSString *)packageName {
  226. self = [super init];
  227. if (self) {
  228. _dictionary = [NSMutableDictionary dictionary];
  229. FDLSafelyAddKeyValuePairToDictionary(kFDLAndroidPackageNameKey, packageName, _dictionary);
  230. }
  231. return self;
  232. }
  233. - (NSString *)packageName {
  234. return _dictionary[kFDLAndroidPackageNameKey];
  235. }
  236. - (void)setMinimumVersion:(NSInteger)minimumVersion {
  237. _dictionary[kFDLAndroidMinimumVersionKey] = @(minimumVersion).stringValue;
  238. }
  239. - (NSInteger)minimumVersion {
  240. return _dictionary[kFDLAndroidMinimumVersionKey].integerValue;
  241. }
  242. - (void)setFallbackURL:(NSURL *)fallbackURL {
  243. FDLSafelyAddKeyValuePairToDictionary(kFDLAndroidFallbackURLKey, fallbackURL.absoluteString,
  244. _dictionary);
  245. }
  246. - (NSURL *)fallbackURL {
  247. NSString *fallbackURLString = _dictionary[kFDLAndroidFallbackURLKey];
  248. return fallbackURLString != nil ? [NSURL URLWithString:fallbackURLString] : nil;
  249. }
  250. - (NSDictionary<NSString *, NSString *> *)dictionaryRepresentation {
  251. return [_dictionary copy];
  252. }
  253. @end
  254. @implementation FIRDynamicLinkSocialMetaTagParameters {
  255. NSMutableDictionary<NSString *, NSString *> *_dictionary;
  256. }
  257. static NSString *const kFDLSocialTitleKey = @"st";
  258. static NSString *const kFDLSocialDescriptionKey = @"sd";
  259. static NSString *const kFDLSocialImageURLKey = @"si";
  260. + (instancetype)parameters {
  261. return [[self alloc] init];
  262. }
  263. - (instancetype)init {
  264. self = [super init];
  265. if (self) {
  266. _dictionary = [NSMutableDictionary dictionary];
  267. }
  268. return self;
  269. }
  270. - (void)setTitle:(NSString *)title {
  271. FDLSafelyAddKeyValuePairToDictionary(kFDLSocialTitleKey, [title copy], _dictionary);
  272. }
  273. - (NSString *)title {
  274. return _dictionary[kFDLSocialTitleKey];
  275. }
  276. - (void)setDescriptionText:(NSString *)descriptionText {
  277. FDLSafelyAddKeyValuePairToDictionary(kFDLSocialDescriptionKey, [descriptionText copy],
  278. _dictionary);
  279. }
  280. - (NSString *)descriptionText {
  281. return _dictionary[kFDLSocialDescriptionKey];
  282. }
  283. - (void)setImageURL:(NSURL *)imageURL {
  284. FDLSafelyAddKeyValuePairToDictionary(kFDLSocialImageURLKey, imageURL.absoluteString, _dictionary);
  285. }
  286. - (NSURL *)imageURL {
  287. NSString *imageURLString = _dictionary[kFDLSocialImageURLKey];
  288. return imageURLString != nil ? [NSURL URLWithString:imageURLString] : nil;
  289. }
  290. - (NSDictionary<NSString *, NSString *> *)dictionaryRepresentation {
  291. return [_dictionary copy];
  292. }
  293. @end
  294. @implementation FIRDynamicLinkNavigationInfoParameters {
  295. NSMutableDictionary<NSString *, NSString *> *_dictionary;
  296. }
  297. static NSString *const kFDLNavigationInfoForceRedirectKey = @"efr";
  298. + (instancetype)parameters {
  299. return [[self alloc] init];
  300. }
  301. - (instancetype)init {
  302. self = [super init];
  303. if (self) {
  304. _dictionary = [NSMutableDictionary dictionary];
  305. }
  306. return self;
  307. }
  308. - (BOOL)isForcedRedirectEnabled {
  309. return [_dictionary[kFDLNavigationInfoForceRedirectKey] boolValue];
  310. }
  311. - (void)setForcedRedirectEnabled:(BOOL)forcedRedirectEnabled {
  312. FDLSafelyAddKeyValuePairToDictionary(kFDLNavigationInfoForceRedirectKey,
  313. forcedRedirectEnabled ? @"1" : @"0", _dictionary);
  314. }
  315. - (NSDictionary<NSString *, NSString *> *)dictionaryRepresentation {
  316. return [_dictionary copy];
  317. }
  318. @end
  319. @implementation FIRDynamicLinkOtherPlatformParameters {
  320. NSMutableDictionary<NSString *, NSString *> *_dictionary;
  321. }
  322. static NSString *const kFDLOtherPlatformParametersFallbackURLKey = @"ofl";
  323. + (instancetype)parameters {
  324. return [[self alloc] init];
  325. }
  326. - (instancetype)init {
  327. self = [super init];
  328. if (self) {
  329. _dictionary = [NSMutableDictionary dictionary];
  330. }
  331. return self;
  332. }
  333. - (NSURL *)fallbackUrl {
  334. NSString *fallbackURLString = _dictionary[kFDLOtherPlatformParametersFallbackURLKey];
  335. return fallbackURLString != nil ? [NSURL URLWithString:fallbackURLString] : nil;
  336. }
  337. - (void)setFallbackUrl:(NSURL *)fallbackUrl {
  338. FDLSafelyAddKeyValuePairToDictionary(kFDLOtherPlatformParametersFallbackURLKey,
  339. fallbackUrl.absoluteString, _dictionary);
  340. }
  341. - (NSDictionary<NSString *, NSString *> *)dictionaryRepresentation {
  342. return [_dictionary copy];
  343. }
  344. @end
  345. @implementation FIRDynamicLinkComponentsOptions
  346. + (instancetype)options {
  347. return [[self alloc] init];
  348. }
  349. // This is implemented to silence the 'not implemented' warning.
  350. - (instancetype)init {
  351. return [super init];
  352. }
  353. @end
  354. @implementation FIRDynamicLinkComponents
  355. #pragma mark Deprecated Initializers.
  356. + (instancetype)componentsWithLink:(NSURL *)link domain:(NSString *)domain {
  357. return [[self alloc] initWithLink:link domain:domain];
  358. }
  359. - (instancetype)initWithLink:(NSURL *)link domain:(NSString *)domain {
  360. NSURL *domainURL = [NSURL URLWithString:domain];
  361. if (domainURL.scheme) {
  362. FDLLog(FDLLogLevelWarning, FDLLogIdentifierSetupWarnHTTPSScheme,
  363. @"You have supplied a domain with a scheme. Please enter a domain name without the "
  364. @"scheme.");
  365. }
  366. NSString *domainURIPrefix = [NSString stringWithFormat:@"https://%@", domain];
  367. self = [super init];
  368. if (self) {
  369. _link = link;
  370. _domain = domainURIPrefix;
  371. }
  372. return self;
  373. }
  374. #pragma mark Initializers.
  375. + (instancetype)componentsWithLink:(NSURL *)link domainURIPrefix:(NSString *)domainURIPrefix {
  376. return [[self alloc] initWithLink:link domainURIPrefix:domainURIPrefix];
  377. }
  378. - (instancetype)initWithLink:(NSURL *)link domainURIPrefix:(NSString *)domainURIPrefix {
  379. self = [super init];
  380. if (self) {
  381. _link = link;
  382. /// Must be a URL that conforms to RFC 2396.
  383. NSURL *domainURIPrefixURL = [NSURL URLWithString:domainURIPrefix];
  384. if (!domainURIPrefixURL) {
  385. FDLLog(FDLLogLevelError, FDLLogIdentifierSetupInvalidDomainURIPrefix,
  386. @"Invalid domainURIPrefix. Please input a valid URL.");
  387. return nil;
  388. }
  389. if (![[domainURIPrefixURL.scheme lowercaseString] isEqualToString:@"https"]) {
  390. FDLLog(FDLLogLevelError, FDLLogIdentifierSetupInvalidDomainURIPrefixScheme,
  391. @"Invalid domainURIPrefix scheme. Scheme needs to be https");
  392. return nil;
  393. }
  394. _domain = [domainURIPrefix copy];
  395. }
  396. return self;
  397. }
  398. + (void)shortenURL:(NSURL *)url
  399. options:(FIRDynamicLinkComponentsOptions *)options
  400. completion:(FIRDynamicLinkShortenerCompletion)completion {
  401. if (![FIRDynamicLinkComponentsKeyProvider APIKey]) {
  402. NSError *error = [NSError
  403. errorWithDomain:kFirebaseDurableDeepLinkErrorDomain
  404. code:0
  405. userInfo:@{
  406. NSLocalizedFailureReasonErrorKey : NSLocalizedString(
  407. @"API key is missing.", @"Error reason message when API key is missing"),
  408. }];
  409. completion(nil, nil, error);
  410. return;
  411. }
  412. NSURLRequest *request = [self shorteningRequestForLongURL:url options:options];
  413. if (!request) {
  414. NSError *error = [NSError errorWithDomain:kFirebaseDurableDeepLinkErrorDomain
  415. code:0
  416. userInfo:nil];
  417. completion(nil, nil, error);
  418. return;
  419. }
  420. [self sendHTTPRequest:request
  421. completion:^(NSData *_Nullable data, NSError *_Nullable error) {
  422. NSURL *shortURL;
  423. NSArray *warnings;
  424. if (data != nil && error == nil) {
  425. NSError *deserializationError;
  426. id JSONObject = [NSJSONSerialization JSONObjectWithData:data
  427. options:0
  428. error:&deserializationError];
  429. if ([JSONObject isKindOfClass:[NSDictionary class]]) {
  430. if ([JSONObject[@"shortLink"] isKindOfClass:[NSString class]]) {
  431. shortURL = [NSURL URLWithString:JSONObject[@"shortLink"]];
  432. } else {
  433. if ([JSONObject[@"error"] isKindOfClass:[NSDictionary class]]) {
  434. NSMutableDictionary *errorUserInfo = [[NSMutableDictionary alloc] init];
  435. NSDictionary *errorDictionary = JSONObject[@"error"];
  436. if ([errorDictionary[@"message"] isKindOfClass:[NSString class]]) {
  437. errorUserInfo[NSLocalizedFailureReasonErrorKey] =
  438. errorDictionary[@"message"];
  439. }
  440. if ([errorDictionary[@"status"] isKindOfClass:[NSString class]]) {
  441. errorUserInfo[@"remoteStatus"] = errorDictionary[@"status"];
  442. }
  443. if (errorDictionary[@"code"] &&
  444. [errorDictionary[@"code"] isKindOfClass:[NSNumber class]]) {
  445. errorUserInfo[@"remoteErrorCode"] = errorDictionary[@"code"];
  446. }
  447. error = [NSError errorWithDomain:kFirebaseDurableDeepLinkErrorDomain
  448. code:0
  449. userInfo:errorUserInfo];
  450. }
  451. }
  452. if ([JSONObject[@"warning"] isKindOfClass:[NSArray class]]) {
  453. NSArray *warningsServer = JSONObject[@"warning"];
  454. NSMutableArray *warningsTmp =
  455. [NSMutableArray arrayWithCapacity:[warningsServer count]];
  456. for (NSDictionary *warningServer in warningsServer) {
  457. if ([warningServer[@"warningMessage"] isKindOfClass:[NSString class]]) {
  458. [warningsTmp addObject:warningServer[@"warningMessage"]];
  459. }
  460. }
  461. if ([warningsTmp count] > 0) {
  462. warnings = [warningsTmp copy];
  463. }
  464. }
  465. } else if (deserializationError) {
  466. error = [NSError
  467. errorWithDomain:kFirebaseDurableDeepLinkErrorDomain
  468. code:0
  469. userInfo:@{
  470. NSLocalizedFailureReasonErrorKey : NSLocalizedString(
  471. @"Unrecognized server response",
  472. @"Error reason message when server response can't be parsed"),
  473. NSUnderlyingErrorKey : deserializationError,
  474. }];
  475. }
  476. }
  477. if (!shortURL && !error) {
  478. // provide generic error message if we have no additional details about failure
  479. error = [NSError errorWithDomain:kFirebaseDurableDeepLinkErrorDomain
  480. code:0
  481. userInfo:nil];
  482. }
  483. dispatch_async(dispatch_get_main_queue(), ^{
  484. completion(shortURL, warnings, error);
  485. });
  486. }];
  487. }
  488. - (void)shortenWithCompletion:(FIRDynamicLinkShortenerCompletion)completion {
  489. NSURL *url = [self url];
  490. if (!url) {
  491. NSError *error = [NSError errorWithDomain:kFirebaseDurableDeepLinkErrorDomain
  492. code:0
  493. userInfo:@{
  494. NSLocalizedFailureReasonErrorKey : NSLocalizedString(
  495. @"Unable to produce long URL",
  496. @"Error reason when long url can't be produced"),
  497. }];
  498. completion(nil, nil, error);
  499. return;
  500. }
  501. return [FIRDynamicLinkComponents shortenURL:url options:_options completion:completion];
  502. }
  503. - (NSURL *)url {
  504. static NSString *const kFDLURLComponentsLinkKey = @"link";
  505. NSMutableDictionary *queryDictionary =
  506. [NSMutableDictionary dictionaryWithObject:self.link.absoluteString
  507. forKey:kFDLURLComponentsLinkKey];
  508. void (^addEntriesFromDictionaryRepresentingConformerToDictionary)(id<FDLDictionaryRepresenting>) =
  509. ^(id<FDLDictionaryRepresenting> _Nullable dictionaryRepresentingConformer) {
  510. NSDictionary *dictionary = dictionaryRepresentingConformer.dictionaryRepresentation;
  511. if (dictionary.count > 0) {
  512. [queryDictionary addEntriesFromDictionary:dictionary];
  513. }
  514. };
  515. addEntriesFromDictionaryRepresentingConformerToDictionary(_analyticsParameters);
  516. addEntriesFromDictionaryRepresentingConformerToDictionary(_socialMetaTagParameters);
  517. addEntriesFromDictionaryRepresentingConformerToDictionary(_iOSParameters);
  518. addEntriesFromDictionaryRepresentingConformerToDictionary(_iTunesConnectParameters);
  519. addEntriesFromDictionaryRepresentingConformerToDictionary(_androidParameters);
  520. addEntriesFromDictionaryRepresentingConformerToDictionary(_navigationInfoParameters);
  521. addEntriesFromDictionaryRepresentingConformerToDictionary(_otherPlatformParameters);
  522. NSString *queryString = FIRDLURLQueryStringFromDictionary(queryDictionary);
  523. NSString *urlString = [NSString stringWithFormat:@"%@/%@", _domain, queryString];
  524. return [NSURL URLWithString:urlString];
  525. }
  526. #pragma mark Helper Methods
  527. + (void)sendHTTPRequest:(NSURLRequest *)request
  528. completion:(void (^)(NSData *_Nullable data, NSError *_Nullable error))completion {
  529. NSURLSession *session = [NSURLSession sharedSession];
  530. NSURLSessionDataTask *task =
  531. [session dataTaskWithRequest:request
  532. completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response,
  533. NSError *_Nullable error) {
  534. completion(data, error);
  535. }];
  536. [task resume];
  537. }
  538. + (NSURLRequest *)shorteningRequestForLongURL:(NSURL *)url
  539. options:(nullable FIRDynamicLinkComponentsOptions *)options {
  540. if (!url) {
  541. return nil;
  542. }
  543. static NSString *const kFDLURLShortenerAPIHost = @"https://firebasedynamiclinks.googleapis.com";
  544. static NSString *const kFDLURLShortenerAPIPath = @"/v1/shortLinks";
  545. static NSString *const kFDLURLShortenerAPIQuery = @"?key=";
  546. NSString *apiKey = [FIRDynamicLinkComponentsKeyProvider APIKey];
  547. NSString *postURLString =
  548. [NSString stringWithFormat:@"%@%@%@%@", kFDLURLShortenerAPIHost, kFDLURLShortenerAPIPath,
  549. kFDLURLShortenerAPIQuery, apiKey];
  550. NSURL *postURL = [NSURL URLWithString:postURLString];
  551. NSMutableDictionary *payloadDictionary =
  552. [NSMutableDictionary dictionaryWithObject:url.absoluteString forKey:@"longDynamicLink"];
  553. switch (options.pathLength) {
  554. case FIRShortDynamicLinkPathLengthShort:
  555. payloadDictionary[@"suffix"] = @{@"option" : @"SHORT"};
  556. break;
  557. case FIRShortDynamicLinkPathLengthUnguessable:
  558. payloadDictionary[@"suffix"] = @{@"option" : @"UNGUESSABLE"};
  559. break;
  560. default:
  561. break;
  562. }
  563. NSData *payload = [NSJSONSerialization dataWithJSONObject:payloadDictionary options:0 error:0];
  564. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:postURL];
  565. request.HTTPMethod = @"POST";
  566. request.HTTPBody = payload;
  567. [request setValue:[NSBundle mainBundle].bundleIdentifier
  568. forHTTPHeaderField:@"X-Ios-Bundle-Identifier"];
  569. NSString *contentType = @"application/json";
  570. [request setValue:contentType forHTTPHeaderField:@"Accept"];
  571. [request setValue:contentType forHTTPHeaderField:@"Content-Type"];
  572. return [request copy];
  573. }
  574. @end