From a1203dbb8a13ed74b33a2c81e853a032140234ac Mon Sep 17 00:00:00 2001 From: Naoufal Kadhom Date: Wed, 24 Jun 2015 21:43:28 -0400 Subject: [PATCH] intial version --- .codeclimate.yml | 5 + .gitignore | 8 +- README.md | 111 +++++++++ SafariViewManager.android.js | 17 ++ SafariViewManager.h | 7 + SafariViewManager.ios.js | 40 ++++ SafariViewManager.m | 43 ++++ SafariViewManager.xcodeproj/project.pbxproj | 251 ++++++++++++++++++++ package.json | 31 +++ 9 files changed, 507 insertions(+), 6 deletions(-) create mode 100644 .codeclimate.yml create mode 100644 README.md create mode 100644 SafariViewManager.android.js create mode 100644 SafariViewManager.h create mode 100644 SafariViewManager.ios.js create mode 100644 SafariViewManager.m create mode 100644 SafariViewManager.xcodeproj/project.pbxproj create mode 100644 package.json diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 0000000..881160f --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,5 @@ +exclude_paths: + - "TouchID.xcodeproj" + - "**/*.h" + - "**/*.m" + - "**/*.swift" diff --git a/.gitignore b/.gitignore index c964cd8..12246db 100644 --- a/.gitignore +++ b/.gitignore @@ -17,10 +17,6 @@ DerivedData *.ipa *.xcuserstate -# CocoaPods +# npm # -# We recommend against adding the Pods directory to your .gitignore. However -# you should judge for yourself, the pros and cons are mentioned at: -# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control -# -#Pods/ +node_modules/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..ef1f4f9 --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +# react-native-safari-view + +[![npm version](https://img.shields.io/npm/v/react-native-safari-view.svg?style=flat-square)](https://www.npmjs.com/package/react-native-safari-view) +[![npm downloads](https://img.shields.io/npm/dm/react-native-safari-view.svg?style=flat-square)](https://www.npmjs.com/package/react-native-safari-view) +[![Code Climate](https://img.shields.io/codeclimate/github/naoufal/react-native-safari-view.svg?style=flat-square)](https://codeclimate.com/github/naoufal/react-native-safari-view) + +__`react-native-safari-view`__ is a [Safari View Controller](https://developer.apple.com/videos/wwdc/2015/?id=504) wrapper for [React Native](https://facebook.github.io/react-native/). + +__Note: Safari View Controller is only available in [iOS 9](http://www.apple.com/ios/ios9-preview/), which is currently in beta.__ + +![react-native-safari-view](https://cloud.githubusercontent.com/assets/1627824/8345135/ed5f7fc4-1ab8-11e5-814a-a3e9df0ede06.gif) + +## Documentation +- [Install](https://github.com/naoufal/react-native-safari-view#install) +- [Usage](https://github.com/naoufal/react-native-safari-view#usage) +- [Example](https://github.com/naoufal/react-native-safari-view#example) +- [Methods](https://github.com/naoufal/react-native-safari-view#methods) +- [License](https://github.com/naoufal/react-native-safari-view#license) + +## Install +```shell +npm i --save react-native-safari-view +``` + +## Usage +### Linking the Library +In order to use Safari View, you must first link the library your project. There's excellent documentation on how to do this in the [React Native Docs](https://facebook.github.io/react-native/docs/linking-libraries.html#content). + +### Displaying the Safari View +Once you've linked the library, you'll want to make it available to your app by requiring it: + +```js +var SafariView = require('react-native-safari-view'); +``` + +Displaying the Safari View is as simple as calling: +```js +SafariView.show({ + url: 'https://github.com/naoufal' +}); +``` + +## Example +Using Safari View in your app will usually look like this: +```js +var SafariView = require('react-native-safari-view'); + +var YourComponent = React.createClass({ + _pressHandler() { + SafariView.isAvailable() + .then(SafariView.show({ + url: 'https://github.com/naoufal' + })) + .catch(error => { + // Fallback WebView code for iOS 8 and earlier + }); + }, + + render() { + return ( + + ... + + + ); + } +}); +``` + +## Methods + +### show(safariOptions) +Displays a Safari View with the provided url. + +__Arguments__ +- `safariOptions` - An `Object` containing a `url` key and optionally a `readerMode` key. + +__safariOptions__ +- `url` - A `String` containing the url you want to load in the Safari View +- `readerMode` - A `Boolean` indicating to use Safari's Reader Mode if available + +__Examples__ +```js +SafariView.show({ + url: 'http://facebook.github.io/react/blog/2015/03/26/introducing-react-native.html', + readerMode: true // optional +}); +``` + +### isAvailable() +Checks if Safari View is available on the device. + +__Example__ +```js +SafariView.isAvailable() + .then(available => { + console.log('SafariView is available.'); + }) + .catch(error => { + console.log(error); + }); +``` + +## License +Copyright (c) 2015, Naoufal Kadhom + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/SafariViewManager.android.js b/SafariViewManager.android.js new file mode 100644 index 0000000..c18bbe8 --- /dev/null +++ b/SafariViewManager.android.js @@ -0,0 +1,17 @@ +/** + * Stub of SafariViewManager for Android. + * + * @providesModule SafariViewManager + * @flow + */ +'use strict'; + +var warning = require('warning'); + +var SafariViewManager = { + test: function() { + warning("Not yet implemented for Android."); + } +}; + +module.exports = SafariViewManager; diff --git a/SafariViewManager.h b/SafariViewManager.h new file mode 100644 index 0000000..53daa5d --- /dev/null +++ b/SafariViewManager.h @@ -0,0 +1,7 @@ +#import "RCTBridgeModule.h" + +@import SafariServices; + +@interface SafariViewManager : NSObject + +@end diff --git a/SafariViewManager.ios.js b/SafariViewManager.ios.js new file mode 100644 index 0000000..a4389e3 --- /dev/null +++ b/SafariViewManager.ios.js @@ -0,0 +1,40 @@ +/** + * @providesModule SafariViewManager + * @flow + */ +'use strict'; + +var NativeSafariViewManager = require('NativeModules').SafariViewManager; +var invariant = require('invariant'); + +/** + * High-level docs for the SafariViewManager iOS API can be written here. + */ + +var SafariViewManager = { + show: function(options) { + return new Promise(function(resolve, reject) { + NativeSafariViewManager.show(options, function(error, success) { + if (error) { + return reject(error); + } + + resolve(true); + }); + }); + }, + + isAvailable: function(options) { + return new Promise(function(resolve, reject) { + NativeSafariViewManager.isAvailable(function(error, success) { + if (error) { + return reject(error); + } + + resolve(true); + }); + }); + }, +}; + +module.exports = SafariViewManager; diff --git a/SafariViewManager.m b/SafariViewManager.m new file mode 100644 index 0000000..930bff7 --- /dev/null +++ b/SafariViewManager.m @@ -0,0 +1,43 @@ +#import "SafariViewManager.h" +#import "RCTUtils.h" +#import "RCTLog.h" + +@implementation SafariViewManager + +RCT_EXPORT_MODULE() + +RCT_EXPORT_METHOD(show:(NSDictionary *)args callback:(RCTResponseSenderBlock)callback) +{ + // Error if no url is passed + if (!args[@"url"]) { + RCTLogError(@"[SafariView] You must specify a url."); + return; + } + + // Initialize the Safari View + SFSafariViewController *safariView = [[SFSafariViewController alloc] initWithURL:[NSURL URLWithString:args[@"url"]] entersReaderIfAvailable:args[@"readerMode"]]; + safariView.delegate = self; + + // Display the Safari View + UIViewController *ctrl = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; + [ctrl presentViewController:safariView animated:YES completion:nil]; +} + +RCT_EXPORT_METHOD(isAvailable:(RCTResponseSenderBlock)callback) +{ + if ([SFSafariViewController class]) { + // SafariView is available + return callback(@[[NSNull null], @true]); + } else { + return callback(@[RCTMakeError(@"SafariView is unavailable.", nil, nil)]); + } +} + + +-(void)safariViewControllerDidFinish:(nonnull SFSafariViewController *)controller +{ + [controller dismissViewControllerAnimated:true completion:nil]; + NSLog(@"SafariView dismissed."); +} + +@end diff --git a/SafariViewManager.xcodeproj/project.pbxproj b/SafariViewManager.xcodeproj/project.pbxproj new file mode 100644 index 0000000..981db08 --- /dev/null +++ b/SafariViewManager.xcodeproj/project.pbxproj @@ -0,0 +1,251 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 13BE3DEE1AC21097009241FE /* SafariViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* SafariViewManager.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 58B511D91A9E6C8500147676 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 134814201AA4EA6300B7C361 /* libSafariViewManager.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSafariViewManager.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 13BE3DEC1AC21097009241FE /* SafariViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SafariViewManager.h; sourceTree = ""; }; + 13BE3DED1AC21097009241FE /* SafariViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SafariViewManager.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 58B511D81A9E6C8500147676 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 134814211AA4EA7D00B7C361 /* Products */ = { + isa = PBXGroup; + children = ( + 134814201AA4EA6300B7C361 /* libSafariViewManager.a */, + ); + name = Products; + sourceTree = ""; + }; + 58B511D21A9E6C8500147676 = { + isa = PBXGroup; + children = ( + 13BE3DEC1AC21097009241FE /* SafariViewManager.h */, + 13BE3DED1AC21097009241FE /* SafariViewManager.m */, + 134814211AA4EA7D00B7C361 /* Products */, + ); + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 58B511DA1A9E6C8500147676 /* SafariViewManager */ = { + isa = PBXNativeTarget; + buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "SafariViewManager" */; + buildPhases = ( + 58B511D71A9E6C8500147676 /* Sources */, + 58B511D81A9E6C8500147676 /* Frameworks */, + 58B511D91A9E6C8500147676 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SafariViewManager; + productName = RCTDataManager; + productReference = 134814201AA4EA6300B7C361 /* libSafariViewManager.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 58B511D31A9E6C8500147676 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + 58B511DA1A9E6C8500147676 = { + CreatedOnToolsVersion = 6.1.1; + }; + }; + }; + buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "SafariViewManager" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 58B511D21A9E6C8500147676; + productRefGroup = 58B511D21A9E6C8500147676; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 58B511DA1A9E6C8500147676 /* SafariViewManager */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 58B511D71A9E6C8500147676 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13BE3DEE1AC21097009241FE /* SafariViewManager.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 58B511ED1A9E6C8500147676 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 58B511EE1A9E6C8500147676 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 58B511F01A9E6C8500147676 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../../React/**", + "$(SRCROOT)/../../node_modules/react-native/React/**", + ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = SafariViewManager; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 58B511F11A9E6C8500147676 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../../React/**", + ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = SafariViewManager; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "SafariViewManager" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58B511ED1A9E6C8500147676 /* Debug */, + 58B511EE1A9E6C8500147676 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "SafariViewManager" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58B511F01A9E6C8500147676 /* Debug */, + 58B511F11A9E6C8500147676 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 58B511D31A9E6C8500147676 /* Project object */; +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1661e6b --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "react-native-safari-view", + "version": "0.1.0", + "description": "A React Native wrapper for Safari View Controller", + "main": "SafariViewMananger.ios.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/naoufal/react-native-safari-view.git" + }, + "keywords": [ + "react-native", + "react", + "native", + "webview", + "web", + "view", + "safari", + "safariview", + "react-component", + "react-native-component" + ], + "author": "Naoufal Kadhom (https://github.com/naoufal)", + "license": "ISC", + "bugs": { + "url": "https://github.com/naoufal/react-native-safari-view/issues" + }, + "homepage": "https://github.com/naoufal/react-native-safari-view" +}