diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..ca1ed085
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
+node_modules
+ios/RCTContacts.xcodeproj/xcuserdata
+ios/RCTContacts.xcodeproj/project.xcworkspace
+
+# Android/IJ
+.idea/workspace.xml
+.idea/libraries
+.gradle
+local.properties
+*.iml
+build
+
+.npm-debug.log
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 00000000..d838da98
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1 @@
+examples/
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..e5e9e348
--- /dev/null
+++ b/README.md
@@ -0,0 +1,117 @@
+# React Native Contacts
+Work in progress successor to react-native-addressbook
+
+## API
+`getContacts` (callback) - returns *all* contacts as an array of objects
+`addContact` (contact, callback) - adds a contact to the AddressBook.
+`updateContact` (contact, callback) - where contact is an object with a valid recordID
+`deleteContact` (contact, callback) - where contact is an object with a valid recordID
+
+####Permissions Methods (optional)
+`checkPermission` (callback) - checks permission to use AddressBook.
+`requestPermission` (callback) - request permission to use AddressBook.
+
+## Usage Example
+```js
+var AddressBook = require('react-native-addressbook')
+
+AddressBook.getContacts( (err, contacts) => {
+ if(err && err.type === 'permissionDenied'){
+ // x.x
+ }
+ else{
+ console.log(contacts)
+ }
+})
+```
+
+## Example Contact Record
+```js
+{
+ recordID: 1,
+ lastName: "Jung",
+ firstName: "Carl",
+ middleName: "",
+ emailAddresses: [{
+ label: "work",
+ email: "carl-jung@example.com",
+ }],
+ phoneNumbers: [{
+ label: "mobile",
+ number: "(555) 555-5555",
+ }],
+ thumbnailPath: "",
+}
+```
+
+## Adding Contacts
+Currently all fields from the contact record except for thumbnailPath are supported for writing
+```js
+var newPerson = {
+ lastName: "Nietzsche",
+ firstName: "Friedrich",
+ emailAddresses: [{
+ label: "work",
+ email: "mrniet@example.com",
+ }],
+}
+
+AddressBook.addContact(newPerson, (err) => { /*...*/ })
+```
+
+## Updating and Deleting Contacts
+```js
+//contrived example
+AddressBook.getContacts( (err, contacts) => {
+ //update the first record
+ let someRecord = contacts[0]
+ someRecord.emailAddresses.push({
+ label: "junk",
+ email: "mrniet+junkmail@test.com",
+ })
+ AddressBook.updateContact(someRecord, (err) => { /*...*/ })
+
+ //delete the second record
+ AddressBook.deleteContact(contacts[1], (err) => { /*...*/ })
+})
+```
+Update and delete reference contacts by their recordID (as returned by the OS in getContacts). Apple does not guarantee the recordID will not change, e.g. it may be reassigned during a phone migration. Consequently you should always grab a fresh contact list with `getContacts` before performing update and delete operations.
+
+You can also delete a record using only it's recordID like follows: `AddressBook.deleteContact({recordID: 1}, (err) => {})}`
+
+##Permissions
+Permissions will automatically be checked and if needed requested upon calling getContacts. If you need more granular control you can using the checkPermission and requestPermission methods as follows:
+```js
+AddressBook.checkPermission( (err, permission) => {
+ // AddressBook.PERMISSION_AUTHORIZED || AddressBook.PERMISSION_UNDEFINED || AddressBook.PERMISSION_DENIED
+ if(permission === 'undefined'){
+ AddressBook.requestPermission( (err, permission) => {
+ // ...
+ })
+ }
+ if(permission === 'authorized'){
+ // yay!
+ }
+ if(permission === 'denied'){
+ // x.x
+ }
+})
+```
+
+## Getting started
+1. `npm install react-native-addressbook`
+2. In XCode, in the project navigator, right click `Libraries` ➜ `Add Files to [your project's name]`
+3. add `./node_modules/react-native-addressbook/RCTAddressBook.xcodeproj`
+4. In the XCode project navigator, select your project, select the `Build Phases` tab and in the `Link Binary With Libraries` section add **libRCTAddressBook.a**
+
+## Todo
+- [x] `checkPermission` & `requestPermission`
+- [x] `getContacts` (get all contacts)
+- [x] `addContact`
+- [x] Update and Delete methods
+- [ ] `getContacts` options (a la camera roll's getPhotos)
+- [x] Automatic permission check & request in `getContacts`
+- [ ] Contact Groups support
+
+## Credits
+Thanks to @mattotodd whose [RCTAddressBook](https://github.com/mattotodd/react-native-addressbook-ios) this is largely derived from.
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 00000000..3724888f
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,34 @@
+buildscript {
+ repositories {
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.1.3'
+ }
+}
+
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.1"
+
+ defaultConfig {
+ minSdkVersion 16
+ targetSdkVersion 22
+ versionCode 1
+ versionName "1.0"
+ }
+ lintOptions {
+ abortOnError false
+ }
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ compile 'com.facebook.react:react-native:0.12.+'
+}
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..9a345fb8
--- /dev/null
+++ b/android/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/android/src/main/java/com/rt2zz/reactnativecontacts/ContactsManager.java b/android/src/main/java/com/rt2zz/reactnativecontacts/ContactsManager.java
new file mode 100644
index 00000000..77a98cee
--- /dev/null
+++ b/android/src/main/java/com/rt2zz/reactnativecontacts/ContactsManager.java
@@ -0,0 +1,36 @@
+package com.rt2zz.reactnativecontacts;
+
+import android.provider.ContactsContract;
+import android.content.ContentResolver;
+import android.content.Context;
+
+import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager.LoaderCallbacks;
+import android.widget.AdapterView;
+import android.database.Cursor;
+
+import com.facebook.react.bridge.NativeModule;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.Callback;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
+import com.facebook.react.bridge.ReactMethod;
+
+import java.util.Map;
+
+public class ContactsManager extends ReactContextBaseJavaModule {
+
+ public ContactsManager(ReactApplicationContext reactContext) {
+ super(reactContext);
+ }
+
+ @ReactMethod
+ public void getContacts(Callback callback) {
+ callback.invoke("@TODO");
+ }
+
+ @Override
+ public String getName() {
+ return "ReactNativeContacts";
+ }
+}
diff --git a/android/src/main/java/com/rt2zz/reactnativecontacts/ReactNativeContacts.java b/android/src/main/java/com/rt2zz/reactnativecontacts/ReactNativeContacts.java
new file mode 100644
index 00000000..ca9f4fb8
--- /dev/null
+++ b/android/src/main/java/com/rt2zz/reactnativecontacts/ReactNativeContacts.java
@@ -0,0 +1,98 @@
+package com.rt2zz.reactnativecontacts;
+
+import java.util.*;
+
+import com.facebook.react.ReactPackage;
+import com.facebook.react.bridge.NativeModule;
+import com.facebook.react.bridge.JavaScriptModule;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.uimanager.ViewManager;
+
+public class ReactNativeContacts implements ReactPackage {
+
+ @Override
+ public List createNativeModules(
+ ReactApplicationContext reactContext) {
+ List modules = new ArrayList<>();
+
+ modules.add(new ContactsManager(reactContext));
+ return modules;
+ }
+
+ @Override
+ public List> createJSModules() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List createViewManagers(ReactApplicationContext reactContext) {
+ return Arrays.asList();
+ }
+}
+
+
+// package com.rt2zz.reactnativecontacts;
+//
+// import com.facebook.react.bridge.NativeModule;
+// import com.facebook.react.bridge.ReactApplicationContext;
+// import com.facebook.react.bridge.Callback;
+// import com.facebook.react.bridge.ReactContext;
+// import com.facebook.react.bridge.ReactContextBaseJavaModule;
+// import com.facebook.react.bridge.ReactMethod;
+//
+// import java.util.Map;
+//
+// public class ReactNativeContacts extends ReactContextBaseJavaModule {
+//
+// public ReactNativeContacts(ReactApplicationContext reactContext) {
+// super(reactContext);
+// }
+//
+// @ReactMethod
+// public void getContacts(Callback callback, int duration) {
+// callback.invoke("hi");
+// }
+//
+// @Override
+// public String getName() {
+// return "ReactNativeContacts";
+// }
+// }
+
+// package com.rt2zz.reactnativecontacts;
+//
+// import com.facebook.react.ReactPackage;
+// import com.facebook.react.bridge.JavaScriptModule;
+// import com.facebook.react.bridge.NativeModule;
+// import com.facebook.react.bridge.Callback;
+// import com.facebook.react.bridge.ReactApplicationContext;
+// import com.facebook.react.uimanager.ViewManager;
+//
+// import java.util.ArrayList;
+// import java.util.Arrays;
+// import java.util.Collections;
+// import java.util.List;
+//
+// public class ReactNativeContacts implements ReactPackage {
+//
+// public ReactNativeContacts() {
+// }
+//
+// @Override
+// public List createNativeModules(
+// ReactApplicationContext reactContext) {
+// return new ArrayList<>();
+// }
+//
+// @Override
+// public List> createJSModules() {
+// return Collections.emptyList();
+// }
+//
+// @Override
+// public List createViewManagers(ReactApplicationContext reactContext) {
+// return Arrays.asList(
+// new IconManager(mAllIconFonts)
+// );
+// }
+// }
diff --git a/index.js b/index.js
new file mode 100644
index 00000000..7a88cb26
--- /dev/null
+++ b/index.js
@@ -0,0 +1,3 @@
+var ReactNative = require('react-native')
+console.log('rnn', ReactNative.NativeModules)
+module.exports = ReactNative.NativeModules.ReactNativeContacts
diff --git a/ios/RCTContacts.xcodeproj/project.pbxproj b/ios/RCTContacts.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..13a4c6fa
--- /dev/null
+++ b/ios/RCTContacts.xcodeproj/project.pbxproj
@@ -0,0 +1,274 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 144161911BD0A79300FA4F59 /* RCTContacts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 144161901BD0A79300FA4F59 /* RCTContacts.h */; };
+ 144161931BD0A79300FA4F59 /* RCTContacts.m in Sources */ = {isa = PBXBuildFile; fileRef = 144161921BD0A79300FA4F59 /* RCTContacts.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 1441618C1BD0A79300FA4F59 /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "include/$(PRODUCT_NAME)";
+ dstSubfolderSpec = 16;
+ files = (
+ 144161911BD0A79300FA4F59 /* RCTContacts.h in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1441618E1BD0A79300FA4F59 /* libRCTContacts.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTContacts.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 144161901BD0A79300FA4F59 /* RCTContacts.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTContacts.h; sourceTree = ""; };
+ 144161921BD0A79300FA4F59 /* RCTContacts.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTContacts.m; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 1441618B1BD0A79300FA4F59 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 1441618F1BD0A79300FA4F59 /* RCTContacts */ = {
+ isa = PBXGroup;
+ children = (
+ 144161901BD0A79300FA4F59 /* RCTContacts.h */,
+ 144161921BD0A79300FA4F59 /* RCTContacts.m */,
+ );
+ path = RCTContacts;
+ sourceTree = "";
+ };
+ 145CC5671AED80100006342E = {
+ isa = PBXGroup;
+ children = (
+ 1441618F1BD0A79300FA4F59 /* RCTContacts */,
+ 145CC5711AED80100006342E /* Products */,
+ );
+ sourceTree = "";
+ };
+ 145CC5711AED80100006342E /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 1441618E1BD0A79300FA4F59 /* libRCTContacts.a */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 1441618D1BD0A79300FA4F59 /* RCTContacts */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 144161941BD0A79300FA4F59 /* Build configuration list for PBXNativeTarget "RCTContacts" */;
+ buildPhases = (
+ 1441618A1BD0A79300FA4F59 /* Sources */,
+ 1441618B1BD0A79300FA4F59 /* Frameworks */,
+ 1441618C1BD0A79300FA4F59 /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = RCTContacts;
+ productName = RCTContacts;
+ productReference = 1441618E1BD0A79300FA4F59 /* libRCTContacts.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 145CC5681AED80100006342E /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0630;
+ ORGANIZATIONNAME = rt2zz;
+ TargetAttributes = {
+ 1441618D1BD0A79300FA4F59 = {
+ CreatedOnToolsVersion = 7.0.1;
+ };
+ };
+ };
+ buildConfigurationList = 145CC56B1AED80100006342E /* Build configuration list for PBXProject "RCTContacts" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 145CC5671AED80100006342E;
+ productRefGroup = 145CC5711AED80100006342E /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 1441618D1BD0A79300FA4F59 /* RCTContacts */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 1441618A1BD0A79300FA4F59 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 144161931BD0A79300FA4F59 /* RCTContacts.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 144161951BD0A79300FA4F59 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_TESTABILITY = YES;
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SRCROOT)/../../react-native/React/**",
+ );
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ OTHER_LDFLAGS = "-ObjC";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ };
+ name = Debug;
+ };
+ 144161961BD0A79300FA4F59 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SRCROOT)/../../react-native/React/**",
+ );
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ OTHER_LDFLAGS = "-ObjC";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ };
+ name = Release;
+ };
+ 145CC5821AED80100006342E /* 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;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ 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;
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SRCROOT)/node_modules/react-native/Libraries/**",
+ "$(SRCROOT)/node_modules/**",
+ );
+ IPHONEOS_DEPLOYMENT_TARGET = 8.3;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ };
+ name = Debug;
+ };
+ 145CC5831AED80100006342E /* 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 = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ 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;
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(SRCROOT)/node_modules/react-native/Libraries/**",
+ "$(SRCROOT)/node_modules/**",
+ );
+ IPHONEOS_DEPLOYMENT_TARGET = 8.3;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 144161941BD0A79300FA4F59 /* Build configuration list for PBXNativeTarget "RCTContacts" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 144161951BD0A79300FA4F59 /* Debug */,
+ 144161961BD0A79300FA4F59 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 145CC56B1AED80100006342E /* Build configuration list for PBXProject "RCTContacts" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 145CC5821AED80100006342E /* Debug */,
+ 145CC5831AED80100006342E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 145CC5681AED80100006342E /* Project object */;
+}
diff --git a/ios/RCTContacts/RCTContacts.h b/ios/RCTContacts/RCTContacts.h
new file mode 100644
index 00000000..d1a89599
--- /dev/null
+++ b/ios/RCTContacts/RCTContacts.h
@@ -0,0 +1,5 @@
+#import "RCTBridgeModule.h"
+
+@interface RCTContacts : NSObject
+
+@end
diff --git a/ios/RCTContacts/RCTContacts.m b/ios/RCTContacts/RCTContacts.m
new file mode 100644
index 00000000..2b4f11b1
--- /dev/null
+++ b/ios/RCTContacts/RCTContacts.m
@@ -0,0 +1,284 @@
+#import
+#import
+#import "RCTContacts.h"
+
+@implementation RCTContacts
+
+RCT_EXPORT_MODULE();
+
+- (NSDictionary *)constantsToExport
+{
+ return @{
+ @"PERMISSION_DENIED": @"denied",
+ @"PERMISSION_AUTHORIZED": @"authorized",
+ @"PERMISSION_UNDEFINED": @"undefined"
+ };
+}
+
+RCT_EXPORT_METHOD(checkPermission:(RCTResponseSenderBlock) callback)
+{
+ int authStatus = ABAddressBookGetAuthorizationStatus();
+ if ( authStatus == kABAuthorizationStatusDenied || authStatus == kABAuthorizationStatusRestricted){
+ callback(@[[NSNull null], @"denied"]);
+ } else if (authStatus == kABAuthorizationStatusAuthorized){
+ callback(@[[NSNull null], @"authorized"]);
+ } else { //ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined
+ callback(@[[NSNull null], @"undefined"]);
+ }
+}
+
+RCT_EXPORT_METHOD(requestPermission:(RCTResponseSenderBlock) callback)
+{
+ ABAddressBookRequestAccessWithCompletion(ABAddressBookCreateWithOptions(NULL, nil), ^(bool granted, CFErrorRef error) {
+ if (!granted){
+ [self checkPermission:callback];
+ return;
+ }
+ [self checkPermission:callback];
+ });
+}
+
+RCT_EXPORT_METHOD(getContacts:(RCTResponseSenderBlock) callback)
+{
+ ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, nil);
+ int authStatus = ABAddressBookGetAuthorizationStatus();
+ if(authStatus != kABAuthorizationStatusAuthorized){
+ ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
+ if(granted){
+ [self retrieveContactsFromAddressBook:addressBookRef withCallback:callback];
+ }else{
+ NSDictionary *error = @{
+ @"type": @"permissionDenied"
+ };
+ callback(@[error, [NSNull null]]);
+ }
+ });
+ }
+ else{
+ [self retrieveContactsFromAddressBook:addressBookRef withCallback:callback];
+ }
+}
+
+-(void) retrieveContactsFromAddressBook:(ABAddressBookRef)addressBookRef
+withCallback:(RCTResponseSenderBlock) callback
+{
+ NSArray *allContacts = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBookRef, NULL, kABPersonSortByLastName);
+ int totalContacts = (int)[allContacts count];
+ int currentIndex = 0;
+ int maxIndex = --totalContacts;
+
+ NSMutableArray *contacts = [[NSMutableArray alloc] init];
+
+ while (currentIndex <= maxIndex){
+ NSDictionary *contact = [self dictionaryRepresentationForABPerson: (ABRecordRef)[allContacts objectAtIndex:(long)currentIndex]];
+
+ if(contact){
+ [contacts addObject:contact];
+ }
+ currentIndex++;
+ }
+ callback(@[[NSNull null], contacts]);
+}
+
+-(NSDictionary*) dictionaryRepresentationForABPerson:(ABRecordRef) person
+{
+ NSMutableDictionary* contact = [NSMutableDictionary dictionary];
+
+ NSNumber *recordID = [NSNumber numberWithInteger:(ABRecordGetRecordID(person))];
+ NSString *firstName = (__bridge_transfer NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
+ NSString *lastName = (__bridge_transfer NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty));
+ NSString *middleName = (__bridge_transfer NSString *)(ABRecordCopyValue(person, kABPersonMiddleNameProperty));
+
+ [contact setObject: recordID forKey: @"recordID"];
+
+ BOOL hasName = false;
+ if (firstName) {
+ [contact setObject: firstName forKey:@"firstName"];
+ hasName = true;
+ }
+
+ if (lastName) {
+ [contact setObject: lastName forKey:@"lastName"];
+ hasName = true;
+ }
+
+ if(middleName){
+ [contact setObject: (middleName) ? middleName : @"" forKey:@"middleName"];
+ }
+
+ if(!hasName){
+ //nameless contact, do not include in results
+ return nil;
+ }
+
+ //handle phone numbers
+ NSMutableArray *phoneNumbers = [[NSMutableArray alloc] init];
+
+ ABMultiValueRef multiPhones = ABRecordCopyValue(person, kABPersonPhoneProperty);
+ for(CFIndex i=0;i
+
+@interface libRCTContacts : NSObject
+
+@end
diff --git a/ios/libRCTContacts/libRCTContacts.m b/ios/libRCTContacts/libRCTContacts.m
new file mode 100644
index 00000000..7d3a86c8
--- /dev/null
+++ b/ios/libRCTContacts/libRCTContacts.m
@@ -0,0 +1,13 @@
+//
+// libRCTContacts.m
+// libRCTContacts
+//
+// Created by Zack on 10/15/15.
+// Copyright © 2015 rt2zz. All rights reserved.
+//
+
+#import "libRCTContacts.h"
+
+@implementation libRCTContacts
+
+@end
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..4dac8174
--- /dev/null
+++ b/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "react-native-contacts",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/rt2zz/react-native-contacts.git"
+ },
+ "version": "0.0.1",
+ "description": "React Native Contacts (android & ios)",
+ "nativePackage": true,
+ "keywords": [
+ "react-native",
+ "react",
+ "react-component",
+ "addressbook",
+ "contacts"
+ ],
+ "bugs": {
+ "url": "https://github.com/rt2zz/react-native-contacts/issues"
+ },
+ "homepage": "https://github.com/rt2zz/react-native-contacts",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "rt2zz ",
+ "license": "MIT"
+}