Skip to content

Commit 8ef8f53

Browse files
author
Conrad Kramer
committed
Added XML App Link Resolver
1 parent 35f21ff commit 8ef8f53

8 files changed

+387
-142
lines changed

Bolts.podspec

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@ Pod::Spec.new do |s|
2020
s.ios.deployment_target = "5.0"
2121
s.ios.source_files = "Bolts/**/*.[hm]"
2222
s.ios.public_header_files = "Bolts/**/*.h"
23-
23+
s.ios.libraries = 'xml2'
24+
s.ios.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' }
25+
2426
s.osx.deployment_target = "10.7"
2527
s.osx.source_files = "Bolts/Common/*.[hm]"
2628
s.osx.public_header_files = "Bolts/Common/*.h"
29+
s.osx.libraries = 'xml2'
30+
s.osx.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' }
2731
end

Bolts.xcodeproj/project.pbxproj

+26
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@
6363
8EA6BF691805CF5600337041 /* BoltsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8EA6BF681805CF5600337041 /* BoltsTests.m */; };
6464
8EDDA63017E17DDC00655F8A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E9C3CEC17DE9DE000427E62 /* Foundation.framework */; };
6565
B242FABB19A567660097ECAE /* BFMeasurementEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = B242FAB919A567660097ECAE /* BFMeasurementEvent.m */; };
66+
D0A9104F1A86BF8500BF399F /* BFXMLAppLinkResolver.h in Headers */ = {isa = PBXBuildFile; fileRef = D0A9104D1A86BF8500BF399F /* BFXMLAppLinkResolver.h */; };
67+
D0A910501A86BF8500BF399F /* BFXMLAppLinkResolver.m in Sources */ = {isa = PBXBuildFile; fileRef = D0A9104E1A86BF8500BF399F /* BFXMLAppLinkResolver.m */; };
68+
D0A910521A86C4AF00BF399F /* BFAppLinkResolving.m in Sources */ = {isa = PBXBuildFile; fileRef = D0A910511A86C4AF00BF399F /* BFAppLinkResolving.m */; };
69+
D0A910541A86C83E00BF399F /* BFAppLinkResolvingPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = D0A910531A86C83E00BF399F /* BFAppLinkResolvingPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
6670
/* End PBXBuildFile section */
6771

6872
/* Begin PBXContainerItemProxy section */
@@ -148,6 +152,10 @@
148152
B242FAB919A567660097ECAE /* BFMeasurementEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BFMeasurementEvent.m; sourceTree = "<group>"; };
149153
B242FABA19A567660097ECAE /* BFURL_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BFURL_Internal.h; sourceTree = "<group>"; };
150154
B242FAC019A599CD0097ECAE /* BFMeasurementEvent_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BFMeasurementEvent_Internal.h; sourceTree = "<group>"; };
155+
D0A9104D1A86BF8500BF399F /* BFXMLAppLinkResolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BFXMLAppLinkResolver.h; sourceTree = "<group>"; };
156+
D0A9104E1A86BF8500BF399F /* BFXMLAppLinkResolver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BFXMLAppLinkResolver.m; sourceTree = "<group>"; };
157+
D0A910511A86C4AF00BF399F /* BFAppLinkResolving.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BFAppLinkResolving.m; sourceTree = "<group>"; };
158+
D0A910531A86C83E00BF399F /* BFAppLinkResolvingPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BFAppLinkResolvingPrivate.h; sourceTree = "<group>"; };
151159
/* End PBXFileReference section */
152160

153161
/* Begin PBXFrameworksBuildPhase section */
@@ -247,8 +255,12 @@
247255
8103FA5A19900A84000BAE3F /* BFAppLinkNavigation.h */,
248256
8103FA5B19900A84000BAE3F /* BFAppLinkNavigation.m */,
249257
8103FA5C19900A84000BAE3F /* BFAppLinkResolving.h */,
258+
D0A910531A86C83E00BF399F /* BFAppLinkResolvingPrivate.h */,
259+
D0A910511A86C4AF00BF399F /* BFAppLinkResolving.m */,
250260
8103FA6619900A84000BAE3F /* BFWebViewAppLinkResolver.h */,
251261
8103FA6719900A84000BAE3F /* BFWebViewAppLinkResolver.m */,
262+
D0A9104D1A86BF8500BF399F /* BFXMLAppLinkResolver.h */,
263+
D0A9104E1A86BF8500BF399F /* BFXMLAppLinkResolver.m */,
252264
8103FA5D19900A84000BAE3F /* BFAppLinkReturnToRefererController.h */,
253265
8103FA5E19900A84000BAE3F /* BFAppLinkReturnToRefererController.m */,
254266
8103FA5F19900A84000BAE3F /* BFAppLinkReturnToRefererView.h */,
@@ -358,11 +370,13 @@
358370
81D0EE8F19AFAA5F0000AE75 /* BFAppLinkReturnToRefererController.h in Headers */,
359371
81D0EE8E19AFAA5F0000AE75 /* BFAppLinkResolving.h in Headers */,
360372
81D0EE9019AFAA5F0000AE75 /* BFAppLinkReturnToRefererView.h in Headers */,
373+
D0A9104F1A86BF8500BF399F /* BFXMLAppLinkResolver.h in Headers */,
361374
81D0EE8719AFAA220000AE75 /* BFExecutor.h in Headers */,
362375
81D0EE8919AFAA2B0000AE75 /* BFTaskCompletionSource.h in Headers */,
363376
81D0EE9119AFAA6F0000AE75 /* BFAppLinkTarget.h in Headers */,
364377
81D0EE8019AFA9E20000AE75 /* BoltsVersion.h in Headers */,
365378
81D0EE8C19AFAA5F0000AE75 /* BFAppLink.h in Headers */,
379+
D0A910541A86C83E00BF399F /* BFAppLinkResolvingPrivate.h in Headers */,
366380
81D0EE9219AFAA6F0000AE75 /* BFMeasurementEvent.h in Headers */,
367381
81D0EE9319AFAA6F0000AE75 /* BFURL.h in Headers */,
368382
81D0EE8419AFAA100000AE75 /* Bolts.h in Headers */,
@@ -571,8 +585,10 @@
571585
isa = PBXSourcesBuildPhase;
572586
buildActionMask = 2147483647;
573587
files = (
588+
D0A910501A86BF8500BF399F /* BFXMLAppLinkResolver.m in Sources */,
574589
8103FA7819900A84000BAE3F /* BFAppLinkTarget.m in Sources */,
575590
8103FA6C19900A84000BAE3F /* BFTaskCompletionSource.m in Sources */,
591+
D0A910521A86C4AF00BF399F /* BFAppLinkResolving.m in Sources */,
576592
8103FA7619900A84000BAE3F /* BFAppLinkReturnToRefererView.m in Sources */,
577593
8103FA7419900A84000BAE3F /* BFAppLinkReturnToRefererController.m in Sources */,
578594
8103FA7C19900A84000BAE3F /* BFWebViewAppLinkResolver.m in Sources */,
@@ -849,7 +865,12 @@
849865
GCC_WARN_ABOUT_RETURN_TYPE = YES;
850866
GCC_WARN_UNINITIALIZED_AUTOS = YES;
851867
GCC_WARN_UNUSED_VARIABLE = YES;
868+
HEADER_SEARCH_PATHS = (
869+
"$(inherited)",
870+
"\"$(SDKROOT)/usr/include/libxml2\"",
871+
);
852872
ONLY_ACTIVE_ARCH = YES;
873+
OTHER_LDFLAGS = "-lxml2";
853874
};
854875
name = Debug;
855876
};
@@ -870,6 +891,11 @@
870891
GCC_WARN_ABOUT_RETURN_TYPE = YES;
871892
GCC_WARN_UNINITIALIZED_AUTOS = YES;
872893
GCC_WARN_UNUSED_VARIABLE = YES;
894+
HEADER_SEARCH_PATHS = (
895+
"$(inherited)",
896+
"\"$(SDKROOT)/usr/include/libxml2\"",
897+
);
898+
OTHER_LDFLAGS = "-lxml2";
873899
VALIDATE_PRODUCT = YES;
874900
};
875901
name = Release;

Bolts/iOS/BFAppLinkResolving.m

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright (c) 2014, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
*/
10+
11+
#import <UIKit/UIKit.h>
12+
13+
#import "BFAppLinkResolving.h"
14+
#import "BFAppLinkResolvingPrivate.h"
15+
#import "BFAppLink.h"
16+
#import "BFAppLinkTarget.h"
17+
18+
NSString *const BFAppLinkResolverPreferHeader = @"Prefer-Html-Meta-Tags";
19+
NSString *const BFAppLinkResolverMetaTagPrefix = @"al";
20+
21+
static NSString *const BFAppLinkResolverIOSURLKey = @"url";
22+
static NSString *const BFAppLinkResolverIOSAppStoreIdKey = @"app_store_id";
23+
static NSString *const BFAppLinkResolverIOSAppNameKey = @"app_name";
24+
static NSString *const BFAppLinkResolverDictionaryValueKey = @"_value";
25+
static NSString *const BFAppLinkResolverWebKey = @"web";
26+
static NSString *const BFAppLinkResolverIOSKey = @"ios";
27+
static NSString *const BFAppLinkResolverIPhoneKey = @"iphone";
28+
static NSString *const BFAppLinkResolverIPadKey = @"ipad";
29+
static NSString *const BFAppLinkResolverWebURLKey = @"url";
30+
static NSString *const BFAppLinkResolverShouldFallbackKey = @"should_fallback";
31+
32+
NSDictionary *BFAppLinkResolverParseALData(NSArray *dataArray) {
33+
NSMutableDictionary *al = [NSMutableDictionary dictionary];
34+
for (NSDictionary *tag in dataArray) {
35+
NSString *name = tag[@"property"];
36+
if (![name isKindOfClass:[NSString class]]) {
37+
continue;
38+
}
39+
NSArray *nameComponents = [name componentsSeparatedByString:@":"];
40+
if (![nameComponents[0] isEqualToString:BFAppLinkResolverMetaTagPrefix]) {
41+
continue;
42+
}
43+
NSMutableDictionary *root = al;
44+
for (int i = 1; i < nameComponents.count; i++) {
45+
NSMutableArray *children = root[nameComponents[i]];
46+
if (!children) {
47+
children = [NSMutableArray array];
48+
root[nameComponents[i]] = children;
49+
}
50+
NSMutableDictionary *child = children.lastObject;
51+
if (!child || i == nameComponents.count - 1) {
52+
child = [NSMutableDictionary dictionary];
53+
[children addObject:child];
54+
}
55+
root = child;
56+
}
57+
if (tag[@"content"]) {
58+
root[BFAppLinkResolverDictionaryValueKey] = tag[@"content"];
59+
}
60+
}
61+
return al;
62+
}
63+
64+
BFAppLink *BFAppLinkResolverAppLinkFromALData(NSDictionary *appLinkDict, NSURL *destination) {
65+
NSMutableArray *linkTargets = [NSMutableArray array];
66+
67+
NSArray *platformData = nil;
68+
switch (UI_USER_INTERFACE_IDIOM()) {
69+
case UIUserInterfaceIdiomPad:
70+
platformData = @[appLinkDict[BFAppLinkResolverIPadKey] ?: @{},
71+
appLinkDict[BFAppLinkResolverIOSKey] ?: @{}];
72+
break;
73+
case UIUserInterfaceIdiomPhone:
74+
platformData = @[appLinkDict[BFAppLinkResolverIPhoneKey] ?: @{},
75+
appLinkDict[BFAppLinkResolverIOSKey] ?: @{}];
76+
break;
77+
default:
78+
// Future-proofing. Other User Interface idioms should only hit ios.
79+
platformData = @[appLinkDict[BFAppLinkResolverIOSKey] ?: @{}];
80+
break;
81+
}
82+
83+
for (NSArray *platformObjects in platformData) {
84+
for (NSDictionary *platformDict in platformObjects) {
85+
// The schema requires a single url/app store id/app name,
86+
// but we could find multiple of them. We'll make a best effort
87+
// to interpret this data.
88+
NSArray *urls = platformDict[BFAppLinkResolverIOSURLKey];
89+
NSArray *appStoreIds = platformDict[BFAppLinkResolverIOSAppStoreIdKey];
90+
NSArray *appNames = platformDict[BFAppLinkResolverIOSAppNameKey];
91+
92+
NSUInteger maxCount = MAX(urls.count, MAX(appStoreIds.count, appNames.count));
93+
94+
for (NSUInteger i = 0; i < maxCount; i++) {
95+
NSString *urlString = urls[i][BFAppLinkResolverDictionaryValueKey];
96+
NSURL *url = urlString ? [NSURL URLWithString:urlString] : nil;
97+
NSString *appStoreId = appStoreIds[i][BFAppLinkResolverDictionaryValueKey];
98+
NSString *appName = appNames[i][BFAppLinkResolverDictionaryValueKey];
99+
BFAppLinkTarget *target = [BFAppLinkTarget appLinkTargetWithURL:url
100+
appStoreId:appStoreId
101+
appName:appName];
102+
[linkTargets addObject:target];
103+
}
104+
}
105+
}
106+
107+
NSDictionary *webDict = appLinkDict[BFAppLinkResolverWebKey][0];
108+
NSString *webUrlString = webDict[BFAppLinkResolverWebURLKey][0][BFAppLinkResolverDictionaryValueKey];
109+
NSString *shouldFallbackString = webDict[BFAppLinkResolverShouldFallbackKey][0][BFAppLinkResolverDictionaryValueKey];
110+
111+
NSURL *webUrl = destination;
112+
113+
if (shouldFallbackString &&
114+
[@[@"no", @"false", @"0"] containsObject:[shouldFallbackString lowercaseString]]) {
115+
webUrl = nil;
116+
}
117+
if (webUrl && webUrlString) {
118+
webUrl = [NSURL URLWithString:webUrlString];
119+
}
120+
121+
return [BFAppLink appLinkWithSourceURL:destination
122+
targets:linkTargets
123+
webURL:webUrl];
124+
}

Bolts/iOS/BFAppLinkResolvingPrivate.h

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) 2014, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
*/
10+
11+
@class BFAppLink;
12+
13+
/*
14+
Builds up a data structure filled with the app link data from the meta tags on a page.
15+
The structure of this object is a dictionary where each key holds an array of app link
16+
data dictionaries. Values are stored in a key called "_value".
17+
*/
18+
extern NSDictionary *BFAppLinkResolverParseALData(NSArray *dataArray);
19+
20+
/*
21+
Converts app link data into a BFAppLink containing the targets relevant for this platform.
22+
*/
23+
extern BFAppLink *BFAppLinkResolverAppLinkFromALData(NSDictionary *appLinkDict, NSURL *destination);
24+
25+
extern NSString *const BFAppLinkResolverPreferHeader;
26+
extern NSString *const BFAppLinkResolverMetaTagPrefix;

0 commit comments

Comments
 (0)