FDLURLComponents.m 24 KB

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