From 574b4502665bf63d94d2d42498a9b54bdb1b017c Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Mon, 17 Aug 2015 19:28:49 -0700 Subject: [PATCH 1/5] Build only master for pushes on Travis-CI. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2fc962fba..174fd4c6e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,6 @@ +branches: + only: + - master language: objective-c osx_image: xcode6.4 cache: From e26b7e1dfeb9066ef9397ab974fff8914b5f307d Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Mon, 17 Aug 2015 19:24:15 -0700 Subject: [PATCH 2/5] Updated README to use new method signature. --- Readme.md => README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) rename Readme.md => README.md (99%) diff --git a/Readme.md b/README.md similarity index 99% rename from Readme.md rename to README.md index b9d7690ad..1970ae770 100644 --- a/Readme.md +++ b/README.md @@ -333,7 +333,7 @@ With these tools, it's easy to make your own asynchronous functions that return func fetchAsync(object: PFObject) -> BFTask { var task = BFTaskCompletionSource() object.fetchInBackgroundWithBlock { - (object: PFObject!, error: NSError!) -> Void in + (object: PFObject?, error: NSError?) -> Void in if error == nil { task.setResult(object) } else { @@ -498,7 +498,7 @@ MYCancellationToken *cancellationToken = [[MYCancellationToken alloc] init]; [cancellationToken cancel]; ``` -**Note:** The cancellation token implementation should be thread-safe. +**Note:** The cancellation token implementation should be thread-safe. We are likely to add some concept like this to Bolts at some point in the future. # App Links @@ -517,25 +517,25 @@ For example, you can use the `BFURL` utility class to parse an incoming URL in y sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { BFURL *parsedUrl = [BFURL URLWithInboundURL:url sourceApplication:sourceApplication]; - + // Use the target URL from the App Link to locate content. if ([parsedUrl.targetURL.pathComponents[1] isEqualToString:@"profiles"]) { // Open a profile viewer. } - + // You can also check the query string easily. NSString *query = parsedUrl.targetQueryParameters[@"query"]; - + // Apps that have existing deep-linking support and map their App Links to existing // deep-linking functionality may instead want to perform these operations on the input URL. // Use the target URL from the App Link to locate content. if ([parsedUrl.inputURL.pathComponents[1] isEqualToString:@"profiles"]) { // Open a profile viewer. } - + // You can also check the query string easily. NSString *query = parsedUrl.inputQueryParameters[@"query"]; - + // Apps can easily check the Extras and App Link data from the App Link as well. NSString *fbAccessToken = parsedUrl.appLinkExtras[@"fb_access_token"]; NSDictionary *refererData = parsedUrl.appLinkExtras[@"referer"]; @@ -612,7 +612,7 @@ When an application is opened via an App Link, a banner allowing the user to "To // self.returnToRefererView is a BFAppLinkReturnToRefererView. // You may initialize the view either by loading it from a NIB or programmatically. self.returnToRefererController.view = self.returnToRefererView; - + // If you have a UINavigationController in the view, then the bar must be shown above it. [self.returnToRefererController] } From 12555cf8b90ade3f090906da4998bace4577bd6b Mon Sep 17 00:00:00 2001 From: Ming Li Date: Fri, 14 Aug 2015 10:41:58 -0700 Subject: [PATCH 3/5] Remove the need to check canOpenURL and just use openURL instead. Fixed unit tests, and tested using the app links sample apps (AppLinkPasteboardSample). Signed-off-by: Ming Li --- Bolts/iOS/BFAppLinkNavigation.m | 34 ++++++++++++--------------------- BoltsTests/AppLinkTests.m | 10 ++++++++-- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/Bolts/iOS/BFAppLinkNavigation.m b/Bolts/iOS/BFAppLinkNavigation.m index bc4d58ca3..dd10d1483 100644 --- a/Bolts/iOS/BFAppLinkNavigation.m +++ b/Bolts/iOS/BFAppLinkNavigation.m @@ -94,31 +94,25 @@ - (NSURL *)appLinkURLWithTargetURL:(NSURL *)targetUrl error:(NSError **)error { } - (BFAppLinkNavigationType)navigate:(NSError **)error { - // Find the first eligible/launchable target in the BFAppLink. - BFAppLinkTarget *eligibleTarget = nil; - for (BFAppLinkTarget *target in self.appLink.targets) { - if ([[UIApplication sharedApplication] canOpenURL:target.URL]) { - eligibleTarget = target; - break; - } - } - - NSURL *appLinkURLToOpen = nil; + NSURL *openedURL = nil; NSError *encodingError = nil; BFAppLinkNavigationType retType = BFAppLinkNavigationTypeFailure; - if (eligibleTarget) { - NSURL *appLinkAppURL = [self appLinkURLWithTargetURL:eligibleTarget.URL error:&encodingError]; + + // Find the first eligible/launchable target in the BFAppLink. + for (BFAppLinkTarget *target in self.appLink.targets) { + NSURL *appLinkAppURL = [self appLinkURLWithTargetURL:target.URL error:&encodingError]; if (encodingError || !appLinkAppURL) { if (error) { *error = encodingError; } - } else if ([[UIApplication sharedApplication] canOpenURL:appLinkAppURL]) { + } else if ([[UIApplication sharedApplication] openURL:appLinkAppURL]) { retType = BFAppLinkNavigationTypeApp; - appLinkURLToOpen = appLinkAppURL; + openedURL = appLinkAppURL; + break; } } - if (!appLinkURLToOpen && self.appLink.webURL) { + if (!openedURL && self.appLink.webURL) { // Fall back to opening the url in the browser if available. NSURL *appLinkBrowserURL = [self appLinkURLWithTargetURL:self.appLink.webURL error:&encodingError]; if (encodingError || !appLinkBrowserURL) { @@ -126,20 +120,16 @@ - (BFAppLinkNavigationType)navigate:(NSError **)error { if (error) { *error = encodingError; } - } else if ([[UIApplication sharedApplication] canOpenURL:appLinkBrowserURL]) { + } else if ([[UIApplication sharedApplication] openURL:appLinkBrowserURL]) { // This was a browser navigation. retType = BFAppLinkNavigationTypeBrowser; - appLinkURLToOpen = appLinkBrowserURL; + openedURL = appLinkBrowserURL; } } - [self postAppLinkNavigateEventNotificationWithTargetURL:appLinkURLToOpen + [self postAppLinkNavigateEventNotificationWithTargetURL:openedURL error:error ? *error : nil type:retType]; - if (appLinkURLToOpen) { - [[UIApplication sharedApplication] openURL:appLinkURLToOpen]; - } - // Otherwise, navigation fails. return retType; } diff --git a/BoltsTests/AppLinkTests.m b/BoltsTests/AppLinkTests.m index 4decea595..af0c39d7e 100644 --- a/BoltsTests/AppLinkTests.m +++ b/BoltsTests/AppLinkTests.m @@ -40,8 +40,14 @@ - (NSURL *)dataUrlForHtml:(NSString *)html { Swizzled-in replacement for UIApplication openUrl so that we can capture results. */ - (BOOL)openURLReplacement:(NSURL *)url { - [openedUrls addObject:url]; - return YES; + if ([url.absoluteString hasPrefix:@"bolts://"] + || [url.absoluteString hasPrefix:@"bolts2://"] + || [url.absoluteString hasPrefix:@"http://"] + || [url.absoluteString hasPrefix:@"file://"]) { + [openedUrls addObject:url]; + return YES; + } + return NO; } /*! From 4ea4f51607ce2aa1231b9bcdcc3ad15a46325409 Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Mon, 24 Aug 2015 17:40:34 -0700 Subject: [PATCH 4/5] Fixed deprecations in iOS 9 that cause warnings. --- Bolts.xcodeproj/project.pbxproj | 10 ++--- Bolts/iOS/BFAppLinkNavigation.m | 4 ++ Bolts/iOS/BFWebViewAppLinkResolver.m | 63 +++++++++++++++------------- 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/Bolts.xcodeproj/project.pbxproj b/Bolts.xcodeproj/project.pbxproj index edbad2d5d..9e8038155 100644 --- a/Bolts.xcodeproj/project.pbxproj +++ b/Bolts.xcodeproj/project.pbxproj @@ -912,6 +912,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; + MACOSX_DEPLOYMENT_TARGET = 10.7; ONLY_ACTIVE_ARCH = YES; }; name = Debug; @@ -944,6 +946,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; + MACOSX_DEPLOYMENT_TARGET = 10.7; VALIDATE_PRODUCT = YES; }; name = Release; @@ -960,8 +964,6 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Bolts/Bolts-Prefix.pch"; INFOPLIST_FILE = "Bolts/Resources/iOS-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 5.0; - "IPHONEOS_DEPLOYMENT_TARGET[arch=arm64]" = 6.0; MACH_O_TYPE = staticlib; MODULEMAP_FILE = Bolts/Resources/iOS.modulemap; OTHER_LDFLAGS = "-ObjC"; @@ -983,8 +985,6 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Bolts/Bolts-Prefix.pch"; INFOPLIST_FILE = "Bolts/Resources/iOS-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 5.0; - "IPHONEOS_DEPLOYMENT_TARGET[arch=arm64]" = 6.0; MACH_O_TYPE = staticlib; MODULEMAP_FILE = Bolts/Resources/iOS.modulemap; OTHER_LDFLAGS = "-ObjC"; @@ -1006,7 +1006,6 @@ GCC_PREFIX_HEADER = "Bolts/Bolts-Prefix.pch"; INFOPLIST_FILE = "Bolts/Resources/Mac-Info.plist"; MACH_O_TYPE = mh_dylib; - MACOSX_DEPLOYMENT_TARGET = 10.7; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = Bolts; SDKROOT = macosx; @@ -1026,7 +1025,6 @@ GCC_PREFIX_HEADER = "Bolts/Bolts-Prefix.pch"; INFOPLIST_FILE = "Bolts/Resources/Mac-Info.plist"; MACH_O_TYPE = mh_dylib; - MACOSX_DEPLOYMENT_TARGET = 10.7; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = Bolts; SDKROOT = macosx; diff --git a/Bolts/iOS/BFAppLinkNavigation.m b/Bolts/iOS/BFAppLinkNavigation.m index dd10d1483..5d287be40 100644 --- a/Bolts/iOS/BFAppLinkNavigation.m +++ b/Bolts/iOS/BFAppLinkNavigation.m @@ -49,11 +49,15 @@ + (instancetype)navigationWithAppLink:(BFAppLink *)appLink } - (NSString *)stringByEscapingQueryString:(NSString *)string { +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0 || __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_9 + return [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; +#else return (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)string, NULL, (CFStringRef) @":/?#[]@!$&'()*+,;=", kCFStringEncodingUTF8)); +#endif } - (NSURL *)appLinkURLWithTargetURL:(NSURL *)targetUrl error:(NSError **)error { diff --git a/Bolts/iOS/BFWebViewAppLinkResolver.m b/Bolts/iOS/BFWebViewAppLinkResolver.m index 0fce1a044..720838b3a 100644 --- a/Bolts/iOS/BFWebViewAppLinkResolver.m +++ b/Bolts/iOS/BFWebViewAppLinkResolver.m @@ -101,36 +101,39 @@ - (BFTask *)followRedirects:(NSURL *)url { // or a dictionary with the response data to be returned. BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - [request setValue:BFWebViewAppLinkResolverMetaTagPrefix - forHTTPHeaderField:BFWebViewAppLinkResolverPreferHeader]; - [NSURLConnection sendAsynchronousRequest:request - queue:[NSOperationQueue mainQueue] - completionHandler:^(NSURLResponse *response, - NSData *data, - NSError *connectionError) { - if (connectionError) { - [tcs setError:connectionError]; - return; - } - - if ([response isKindOfClass:[NSHTTPURLResponse class]]) { - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - - // NSURLConnection usually follows redirects automatically, but the - // documentation is unclear what the default is. This helps it along. - if (httpResponse.statusCode >= 300 && httpResponse.statusCode < 400) { - NSString *redirectString = httpResponse.allHeaderFields[@"Location"]; - NSURL *redirectURL = [NSURL URLWithString:redirectString]; - [tcs setResult:redirectURL]; - return; - } - } - - [tcs setResult:@{ - @"response" : response, - @"data" : data - }]; - }]; + [request setValue:BFWebViewAppLinkResolverMetaTagPrefix forHTTPHeaderField:BFWebViewAppLinkResolverPreferHeader]; + + void (^completion)(NSURLResponse *response, NSData *data, NSError *error) = ^(NSURLResponse *response, NSData *data, NSError *error) { + if (error) { + [tcs setError:error]; + return; + } + + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + + // NSURLConnection usually follows redirects automatically, but the + // documentation is unclear what the default is. This helps it along. + if (httpResponse.statusCode >= 300 && httpResponse.statusCode < 400) { + NSString *redirectString = httpResponse.allHeaderFields[@"Location"]; + NSURL *redirectURL = [NSURL URLWithString:redirectString]; + [tcs setResult:redirectURL]; + return; + } + } + + [tcs setResult:@{ @"response" : response, @"data" : data }]; + }; + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0 || __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_9 + NSURLSession *session = [NSURLSession sharedSession]; + [[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + completion(response, data, error); + }] resume]; +#else + [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:completion]; +#endif + return [tcs.task continueWithSuccessBlock:^id(BFTask *task) { // If we redirected, just keep recursing. if ([task.result isKindOfClass:[NSURL class]]) { From dfc2f46c7e4bb347f82f458cf8aa390383e79089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20W=C3=BCnsch?= Date: Tue, 25 Aug 2015 22:46:44 +0200 Subject: [PATCH 5/5] Fixed handling of BFIncludeStatusBarInSizeAlways. --- Bolts/iOS/BFAppLinkReturnToRefererView.m | 2 +- BoltsTests/AppLinkReturnToRefererViewTests.m | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Bolts/iOS/BFAppLinkReturnToRefererView.m b/Bolts/iOS/BFAppLinkReturnToRefererView.m index 7ca6d25e0..d9cb2a77b 100644 --- a/Bolts/iOS/BFAppLinkReturnToRefererView.m +++ b/Bolts/iOS/BFAppLinkReturnToRefererView.m @@ -146,7 +146,7 @@ - (CGFloat)statusBarHeight { BOOL include; switch (_includeStatusBarInSize) { case BFIncludeStatusBarInSizeAlways: - include = NO; + include = YES; break; case BFIncludeStatusBarInSizeIOS7AndLater: { float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue]; diff --git a/BoltsTests/AppLinkReturnToRefererViewTests.m b/BoltsTests/AppLinkReturnToRefererViewTests.m index 9cf19ce77..6742de0e4 100644 --- a/BoltsTests/AppLinkReturnToRefererViewTests.m +++ b/BoltsTests/AppLinkReturnToRefererViewTests.m @@ -74,17 +74,30 @@ - (void)testValidRefererDataResultsInNonZeroSizeThatFits { XCTAssert(sizeThatFits.width > 0.0); } -- (void)testNotIncludingStatusBarResultsInSmallerHeight { +- (void)testIncludesStatusBarResultsInLargerHeight { NSURL *url = [NSURL URLWithString:BFURLWithRefererData]; BFAppLink *appLink = [[BFURL URLWithURL:url] appLinkReferer]; BFAppLinkReturnToRefererView *view = [[BFAppLinkReturnToRefererView alloc] init]; view.refererAppLink = appLink; + view.includeStatusBarInSize = BFIncludeStatusBarInSizeNever; + CGSize sizeThatFitsNotIncludingStatusBar = [view sizeThatFits:CGSizeMake(100.0, 100.0)]; + view.includeStatusBarInSize = BFIncludeStatusBarInSizeAlways; CGSize sizeThatFitsIncludingStatusBar = [view sizeThatFits:CGSizeMake(100.0, 100.0)]; - view.includeStatusBarInSize = BFIncludeStatusBarInSizeNever; + XCTAssert(sizeThatFitsIncludingStatusBar.height > sizeThatFitsNotIncludingStatusBar.height); +} +- (void)testNotIncludingStatusBarResultsInSmallerHeight { + NSURL *url = [NSURL URLWithString:BFURLWithRefererData]; + BFAppLink *appLink = [[BFURL URLWithURL:url] appLinkReferer]; + + BFAppLinkReturnToRefererView *view = [[BFAppLinkReturnToRefererView alloc] init]; + view.refererAppLink = appLink; + CGSize sizeThatFitsIncludingStatusBar = [view sizeThatFits:CGSizeMake(100.0, 100.0)]; + + view.includeStatusBarInSize = BFIncludeStatusBarInSizeNever; CGSize sizeThatFitsNotIncludingStatusBar = [view sizeThatFits:CGSizeMake(100.0, 100.0)]; XCTAssert(sizeThatFitsIncludingStatusBar.height > sizeThatFitsNotIncludingStatusBar.height);