├── .gitignore ├── DTSHashedContacts.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── DTSHashedContacts ├── DTSAppDelegate.h ├── DTSAppDelegate.m ├── DTSHashedContacts-Info.plist ├── DTSHashedContacts-Prefix.pch ├── DTSViewController.h ├── DTSViewController.m ├── en.lproj │ ├── DTSViewController.xib │ └── InfoPlist.strings └── main.m ├── LICENSE ├── README.markdown ├── Source ├── DTSHashedContactsProvider.h ├── DTSHashedContactsProvider.m ├── de.lproj │ └── Localizable.strings ├── en.lproj │ └── Localizable.strings └── nl.lproj │ └── Localizable.strings └── alert_example.png /.gitignore: -------------------------------------------------------------------------------- 1 | # xcode noise 2 | *.mode1v3 3 | *.pbxuser 4 | *.perspective 5 | *.perspectivev3 6 | *.pyc 7 | *~.nib/ 8 | */build/* 9 | 10 | # osx noise 11 | .DS_Store 12 | profile 13 | 14 | # Xcode4 User Data 15 | */xcuserdata 16 | */*/xcuserdata 17 | */*/*/xcuserdata 18 | -------------------------------------------------------------------------------- /DTSHashedContacts.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B5DBE11614EFB2570044E440 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B5DBE11814EFB2570044E440 /* Localizable.strings */; }; 11 | E2E8475214EC008600DADB76 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2E8475114EC008600DADB76 /* UIKit.framework */; }; 12 | E2E8475414EC008600DADB76 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2E8475314EC008600DADB76 /* Foundation.framework */; }; 13 | E2E8475614EC008600DADB76 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2E8475514EC008600DADB76 /* CoreGraphics.framework */; }; 14 | E2E8475C14EC008600DADB76 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E2E8475A14EC008600DADB76 /* InfoPlist.strings */; }; 15 | E2E8475E14EC008600DADB76 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E2E8475D14EC008600DADB76 /* main.m */; }; 16 | E2E8476214EC008600DADB76 /* DTSAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E2E8476114EC008600DADB76 /* DTSAppDelegate.m */; }; 17 | E2E8476514EC008600DADB76 /* DTSViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E2E8476414EC008600DADB76 /* DTSViewController.m */; }; 18 | E2E8476814EC008600DADB76 /* DTSViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E2E8476614EC008600DADB76 /* DTSViewController.xib */; }; 19 | E2E8477114EC011B00DADB76 /* DTSHashedContactsProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = E2E8477014EC011B00DADB76 /* DTSHashedContactsProvider.m */; }; 20 | E2E8477714EC07D000DADB76 /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E2E8477614EC07D000DADB76 /* AddressBook.framework */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 3E9FA65C14F01A800004D02C /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; 25 | B5DBE11714EFB2570044E440 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 26 | B5DBE11914EFB25D0044E440 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; 27 | E2E8474D14EC008600DADB76 /* DTSHashedContacts.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DTSHashedContacts.app; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | E2E8475114EC008600DADB76 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 29 | E2E8475314EC008600DADB76 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 30 | E2E8475514EC008600DADB76 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 31 | E2E8475914EC008600DADB76 /* DTSHashedContacts-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "DTSHashedContacts-Info.plist"; sourceTree = ""; }; 32 | E2E8475B14EC008600DADB76 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 33 | E2E8475D14EC008600DADB76 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 34 | E2E8475F14EC008600DADB76 /* DTSHashedContacts-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "DTSHashedContacts-Prefix.pch"; sourceTree = ""; }; 35 | E2E8476014EC008600DADB76 /* DTSAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DTSAppDelegate.h; sourceTree = ""; }; 36 | E2E8476114EC008600DADB76 /* DTSAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DTSAppDelegate.m; sourceTree = ""; }; 37 | E2E8476314EC008600DADB76 /* DTSViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DTSViewController.h; sourceTree = ""; }; 38 | E2E8476414EC008600DADB76 /* DTSViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DTSViewController.m; sourceTree = ""; }; 39 | E2E8476714EC008600DADB76 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/DTSViewController.xib; sourceTree = ""; }; 40 | E2E8476F14EC011B00DADB76 /* DTSHashedContactsProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DTSHashedContactsProvider.h; path = Source/DTSHashedContactsProvider.h; sourceTree = SOURCE_ROOT; }; 41 | E2E8477014EC011B00DADB76 /* DTSHashedContactsProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DTSHashedContactsProvider.m; path = Source/DTSHashedContactsProvider.m; sourceTree = SOURCE_ROOT; }; 42 | E2E8477614EC07D000DADB76 /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; 43 | /* End PBXFileReference section */ 44 | 45 | /* Begin PBXFrameworksBuildPhase section */ 46 | E2E8474A14EC008600DADB76 /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | E2E8477714EC07D000DADB76 /* AddressBook.framework in Frameworks */, 51 | E2E8475214EC008600DADB76 /* UIKit.framework in Frameworks */, 52 | E2E8475414EC008600DADB76 /* Foundation.framework in Frameworks */, 53 | E2E8475614EC008600DADB76 /* CoreGraphics.framework in Frameworks */, 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | /* End PBXFrameworksBuildPhase section */ 58 | 59 | /* Begin PBXGroup section */ 60 | E2E8474214EC008600DADB76 = { 61 | isa = PBXGroup; 62 | children = ( 63 | E2E8477614EC07D000DADB76 /* AddressBook.framework */, 64 | E2E8475714EC008600DADB76 /* DTSHashedContacts */, 65 | E2E8475014EC008600DADB76 /* Frameworks */, 66 | E2E8474E14EC008600DADB76 /* Products */, 67 | ); 68 | sourceTree = ""; 69 | }; 70 | E2E8474E14EC008600DADB76 /* Products */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | E2E8474D14EC008600DADB76 /* DTSHashedContacts.app */, 74 | ); 75 | name = Products; 76 | sourceTree = ""; 77 | }; 78 | E2E8475014EC008600DADB76 /* Frameworks */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | E2E8475114EC008600DADB76 /* UIKit.framework */, 82 | E2E8475314EC008600DADB76 /* Foundation.framework */, 83 | E2E8475514EC008600DADB76 /* CoreGraphics.framework */, 84 | ); 85 | name = Frameworks; 86 | sourceTree = ""; 87 | }; 88 | E2E8475714EC008600DADB76 /* DTSHashedContacts */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | E2E8476E14EC00FD00DADB76 /* Library */, 92 | E2E8476014EC008600DADB76 /* DTSAppDelegate.h */, 93 | E2E8476114EC008600DADB76 /* DTSAppDelegate.m */, 94 | E2E8476314EC008600DADB76 /* DTSViewController.h */, 95 | E2E8476414EC008600DADB76 /* DTSViewController.m */, 96 | E2E8476614EC008600DADB76 /* DTSViewController.xib */, 97 | E2E8475814EC008600DADB76 /* Supporting Files */, 98 | ); 99 | path = DTSHashedContacts; 100 | sourceTree = ""; 101 | }; 102 | E2E8475814EC008600DADB76 /* Supporting Files */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | E2E8475914EC008600DADB76 /* DTSHashedContacts-Info.plist */, 106 | E2E8475A14EC008600DADB76 /* InfoPlist.strings */, 107 | E2E8475D14EC008600DADB76 /* main.m */, 108 | E2E8475F14EC008600DADB76 /* DTSHashedContacts-Prefix.pch */, 109 | ); 110 | name = "Supporting Files"; 111 | sourceTree = ""; 112 | }; 113 | E2E8476E14EC00FD00DADB76 /* Library */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | E2E8476F14EC011B00DADB76 /* DTSHashedContactsProvider.h */, 117 | E2E8477014EC011B00DADB76 /* DTSHashedContactsProvider.m */, 118 | B5DBE11814EFB2570044E440 /* Localizable.strings */, 119 | ); 120 | name = Library; 121 | sourceTree = ""; 122 | }; 123 | /* End PBXGroup section */ 124 | 125 | /* Begin PBXNativeTarget section */ 126 | E2E8474C14EC008600DADB76 /* DTSHashedContacts */ = { 127 | isa = PBXNativeTarget; 128 | buildConfigurationList = E2E8476B14EC008600DADB76 /* Build configuration list for PBXNativeTarget "DTSHashedContacts" */; 129 | buildPhases = ( 130 | E2E8474914EC008600DADB76 /* Sources */, 131 | E2E8474A14EC008600DADB76 /* Frameworks */, 132 | E2E8474B14EC008600DADB76 /* Resources */, 133 | ); 134 | buildRules = ( 135 | ); 136 | dependencies = ( 137 | ); 138 | name = DTSHashedContacts; 139 | productName = DTSHashedContacts; 140 | productReference = E2E8474D14EC008600DADB76 /* DTSHashedContacts.app */; 141 | productType = "com.apple.product-type.application"; 142 | }; 143 | /* End PBXNativeTarget section */ 144 | 145 | /* Begin PBXProject section */ 146 | E2E8474414EC008600DADB76 /* Project object */ = { 147 | isa = PBXProject; 148 | attributes = { 149 | LastUpgradeCheck = 0420; 150 | ORGANIZATIONNAME = "Cross Forward Consulting, LLC"; 151 | }; 152 | buildConfigurationList = E2E8474714EC008600DADB76 /* Build configuration list for PBXProject "DTSHashedContacts" */; 153 | compatibilityVersion = "Xcode 3.2"; 154 | developmentRegion = English; 155 | hasScannedForEncodings = 0; 156 | knownRegions = ( 157 | en, 158 | de, 159 | nl, 160 | ); 161 | mainGroup = E2E8474214EC008600DADB76; 162 | productRefGroup = E2E8474E14EC008600DADB76 /* Products */; 163 | projectDirPath = ""; 164 | projectRoot = ""; 165 | targets = ( 166 | E2E8474C14EC008600DADB76 /* DTSHashedContacts */, 167 | ); 168 | }; 169 | /* End PBXProject section */ 170 | 171 | /* Begin PBXResourcesBuildPhase section */ 172 | E2E8474B14EC008600DADB76 /* Resources */ = { 173 | isa = PBXResourcesBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | E2E8475C14EC008600DADB76 /* InfoPlist.strings in Resources */, 177 | E2E8476814EC008600DADB76 /* DTSViewController.xib in Resources */, 178 | B5DBE11614EFB2570044E440 /* Localizable.strings in Resources */, 179 | ); 180 | runOnlyForDeploymentPostprocessing = 0; 181 | }; 182 | /* End PBXResourcesBuildPhase section */ 183 | 184 | /* Begin PBXSourcesBuildPhase section */ 185 | E2E8474914EC008600DADB76 /* Sources */ = { 186 | isa = PBXSourcesBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | E2E8475E14EC008600DADB76 /* main.m in Sources */, 190 | E2E8476214EC008600DADB76 /* DTSAppDelegate.m in Sources */, 191 | E2E8476514EC008600DADB76 /* DTSViewController.m in Sources */, 192 | E2E8477114EC011B00DADB76 /* DTSHashedContactsProvider.m in Sources */, 193 | ); 194 | runOnlyForDeploymentPostprocessing = 0; 195 | }; 196 | /* End PBXSourcesBuildPhase section */ 197 | 198 | /* Begin PBXVariantGroup section */ 199 | B5DBE11814EFB2570044E440 /* Localizable.strings */ = { 200 | isa = PBXVariantGroup; 201 | children = ( 202 | B5DBE11714EFB2570044E440 /* en */, 203 | B5DBE11914EFB25D0044E440 /* de */, 204 | 3E9FA65C14F01A800004D02C /* nl */, 205 | ); 206 | name = Localizable.strings; 207 | path = Source; 208 | sourceTree = SOURCE_ROOT; 209 | }; 210 | E2E8475A14EC008600DADB76 /* InfoPlist.strings */ = { 211 | isa = PBXVariantGroup; 212 | children = ( 213 | E2E8475B14EC008600DADB76 /* en */, 214 | ); 215 | name = InfoPlist.strings; 216 | sourceTree = ""; 217 | }; 218 | E2E8476614EC008600DADB76 /* DTSViewController.xib */ = { 219 | isa = PBXVariantGroup; 220 | children = ( 221 | E2E8476714EC008600DADB76 /* en */, 222 | ); 223 | name = DTSViewController.xib; 224 | sourceTree = ""; 225 | }; 226 | /* End PBXVariantGroup section */ 227 | 228 | /* Begin XCBuildConfiguration section */ 229 | E2E8476914EC008600DADB76 /* Debug */ = { 230 | isa = XCBuildConfiguration; 231 | buildSettings = { 232 | ALWAYS_SEARCH_USER_PATHS = NO; 233 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 234 | CLANG_ENABLE_OBJC_ARC = YES; 235 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 236 | COPY_PHASE_STRIP = NO; 237 | GCC_C_LANGUAGE_STANDARD = gnu99; 238 | GCC_DYNAMIC_NO_PIC = NO; 239 | GCC_OPTIMIZATION_LEVEL = 0; 240 | GCC_PREPROCESSOR_DEFINITIONS = ( 241 | "DEBUG=1", 242 | "$(inherited)", 243 | ); 244 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 245 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 246 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 247 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 248 | GCC_WARN_UNUSED_VARIABLE = YES; 249 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 250 | SDKROOT = iphoneos; 251 | }; 252 | name = Debug; 253 | }; 254 | E2E8476A14EC008600DADB76 /* Release */ = { 255 | isa = XCBuildConfiguration; 256 | buildSettings = { 257 | ALWAYS_SEARCH_USER_PATHS = NO; 258 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 259 | CLANG_ENABLE_OBJC_ARC = YES; 260 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 261 | COPY_PHASE_STRIP = YES; 262 | GCC_C_LANGUAGE_STANDARD = gnu99; 263 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 264 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 265 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 266 | GCC_WARN_UNUSED_VARIABLE = YES; 267 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 268 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 269 | SDKROOT = iphoneos; 270 | VALIDATE_PRODUCT = YES; 271 | }; 272 | name = Release; 273 | }; 274 | E2E8476C14EC008600DADB76 /* Debug */ = { 275 | isa = XCBuildConfiguration; 276 | buildSettings = { 277 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 278 | GCC_PREFIX_HEADER = "DTSHashedContacts/DTSHashedContacts-Prefix.pch"; 279 | INFOPLIST_FILE = "DTSHashedContacts/DTSHashedContacts-Info.plist"; 280 | IPHONEOS_DEPLOYMENT_TARGET = 4.0; 281 | PRODUCT_NAME = "$(TARGET_NAME)"; 282 | WRAPPER_EXTENSION = app; 283 | }; 284 | name = Debug; 285 | }; 286 | E2E8476D14EC008600DADB76 /* Release */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 290 | GCC_PREFIX_HEADER = "DTSHashedContacts/DTSHashedContacts-Prefix.pch"; 291 | INFOPLIST_FILE = "DTSHashedContacts/DTSHashedContacts-Info.plist"; 292 | IPHONEOS_DEPLOYMENT_TARGET = 4.0; 293 | PRODUCT_NAME = "$(TARGET_NAME)"; 294 | WRAPPER_EXTENSION = app; 295 | }; 296 | name = Release; 297 | }; 298 | /* End XCBuildConfiguration section */ 299 | 300 | /* Begin XCConfigurationList section */ 301 | E2E8474714EC008600DADB76 /* Build configuration list for PBXProject "DTSHashedContacts" */ = { 302 | isa = XCConfigurationList; 303 | buildConfigurations = ( 304 | E2E8476914EC008600DADB76 /* Debug */, 305 | E2E8476A14EC008600DADB76 /* Release */, 306 | ); 307 | defaultConfigurationIsVisible = 0; 308 | defaultConfigurationName = Release; 309 | }; 310 | E2E8476B14EC008600DADB76 /* Build configuration list for PBXNativeTarget "DTSHashedContacts" */ = { 311 | isa = XCConfigurationList; 312 | buildConfigurations = ( 313 | E2E8476C14EC008600DADB76 /* Debug */, 314 | E2E8476D14EC008600DADB76 /* Release */, 315 | ); 316 | defaultConfigurationIsVisible = 0; 317 | defaultConfigurationName = Release; 318 | }; 319 | /* End XCConfigurationList section */ 320 | }; 321 | rootObject = E2E8474414EC008600DADB76 /* Project object */; 322 | } 323 | -------------------------------------------------------------------------------- /DTSHashedContacts.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DTSHashedContacts/DTSAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // DTSAppDelegate.h 3 | // DTSHashedContacts 4 | // 5 | // Created by David Smith on 2/15/12. 6 | // Copyright (c) 2012 Cross Forward Consulting, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class DTSViewController; 12 | 13 | @interface DTSAppDelegate : UIResponder 14 | 15 | @property (strong, nonatomic) UIWindow *window; 16 | 17 | @property (strong, nonatomic) DTSViewController *viewController; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /DTSHashedContacts/DTSAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // DTSAppDelegate.m 3 | // DTSHashedContacts 4 | // 5 | // Created by David Smith on 2/15/12. 6 | // Copyright (c) 2012 Cross Forward Consulting, LLC. All rights reserved. 7 | // 8 | 9 | #import "DTSAppDelegate.h" 10 | 11 | #import "DTSViewController.h" 12 | 13 | @implementation DTSAppDelegate 14 | 15 | @synthesize window = _window; 16 | @synthesize viewController = _viewController; 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 19 | { 20 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 21 | // Override point for customization after application launch. 22 | self.viewController = [[DTSViewController alloc] initWithNibName:@"DTSViewController" bundle:nil]; 23 | self.window.rootViewController = self.viewController; 24 | [self.window makeKeyAndVisible]; 25 | return YES; 26 | } 27 | 28 | - (void)applicationWillResignActive:(UIApplication *)application 29 | { 30 | /* 31 | Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 32 | Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 33 | */ 34 | } 35 | 36 | - (void)applicationDidEnterBackground:(UIApplication *)application 37 | { 38 | /* 39 | Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 40 | If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 41 | */ 42 | } 43 | 44 | - (void)applicationWillEnterForeground:(UIApplication *)application 45 | { 46 | /* 47 | Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 48 | */ 49 | } 50 | 51 | - (void)applicationDidBecomeActive:(UIApplication *)application 52 | { 53 | /* 54 | Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 55 | */ 56 | } 57 | 58 | - (void)applicationWillTerminate:(UIApplication *)application 59 | { 60 | /* 61 | Called when the application is about to terminate. 62 | Save data if appropriate. 63 | See also applicationDidEnterBackground:. 64 | */ 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /DTSHashedContacts/DTSHashedContacts-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFiles 12 | 13 | CFBundleIdentifier 14 | org.david-smith.${PRODUCT_NAME:rfc1034identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | 1.0 27 | LSRequiresIPhoneOS 28 | 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /DTSHashedContacts/DTSHashedContacts-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'DTSHashedContacts' target in the 'DTSHashedContacts' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_4_0 8 | #warning "This project uses features only available in iOS SDK 4.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /DTSHashedContacts/DTSViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DTSViewController.h 3 | // DTSHashedContacts 4 | // 5 | // Created by David Smith on 2/15/12. 6 | // Copyright (c) 2012 Cross Forward Consulting, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "DTSHashedContactsProvider.h" 11 | 12 | @interface DTSViewController : UIViewController 13 | 14 | @property (strong) IBOutlet UITextView* allTokensView; 15 | 16 | @property (strong) DTSHashedContactsProvider* hashedContactsProvider; 17 | 18 | -(IBAction)requestEmailTokens:(id)sender; 19 | -(IBAction)requestPhoneNumberTokens:(id)sender; 20 | -(IBAction)resetConfirmation:(id)sender; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /DTSHashedContacts/DTSViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DTSViewController.m 3 | // DTSHashedContacts 4 | // 5 | // Created by David Smith on 2/15/12. 6 | // Copyright (c) 2012 Cross Forward Consulting, LLC. All rights reserved. 7 | // 8 | 9 | #import "DTSViewController.h" 10 | 11 | @implementation DTSViewController 12 | 13 | @synthesize allTokensView, hashedContactsProvider; 14 | 15 | -(void)viewDidLoad { 16 | self.hashedContactsProvider = [[DTSHashedContactsProvider alloc] init]; 17 | self.hashedContactsProvider.hashingMethod = DTSHashWithSHA512; 18 | } 19 | 20 | -(IBAction)requestEmailTokens:(id)sender { 21 | [self.hashedContactsProvider 22 | emailTokensWithConfirmation:^(NSArray* tokens) { 23 | 24 | if([tokens count] > 0) { 25 | allTokensView.text = [tokens componentsJoinedByString:@"\n\n"]; 26 | } else { 27 | allTokensView.text = @"No Contacts"; 28 | } 29 | 30 | } whenDeclined:^{ 31 | allTokensView.text = @"Declined"; 32 | }]; 33 | } 34 | -(IBAction)requestPhoneNumberTokens:(id)sender { 35 | [self.hashedContactsProvider 36 | phoneNumberTokensWithConfirmation:^(NSArray* tokens) { 37 | 38 | if([tokens count] > 0) { 39 | allTokensView.text = [tokens componentsJoinedByString:@"\n\n"]; 40 | } else { 41 | allTokensView.text = @"No Contacts"; 42 | } 43 | 44 | } whenDeclined:^{ 45 | allTokensView.text = @"Declined"; 46 | }]; 47 | } 48 | 49 | 50 | -(IBAction)resetConfirmation:(id)sender { 51 | [self.hashedContactsProvider resetConfirmationAlerts]; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /DTSHashedContacts/en.lproj/DTSViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1280 5 | 11C74 6 | 1938 7 | 1138.23 8 | 567.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 933 12 | 13 | 14 | IBUITextView 15 | IBUIButton 16 | IBUIView 17 | IBProxyObject 18 | 19 | 20 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 21 | 22 | 23 | PluginDependencyRecalculationVersion 24 | 25 | 26 | 27 | 28 | IBFilesOwner 29 | IBCocoaTouchFramework 30 | 31 | 32 | IBFirstResponder 33 | IBCocoaTouchFramework 34 | 35 | 36 | 37 | 274 38 | 39 | 40 | 41 | 274 42 | {{0, 110}, {320, 263}} 43 | 44 | 45 | 46 | _NS:639 47 | 48 | 1 49 | MSAxIDEAA 50 | 51 | YES 52 | YES 53 | IBCocoaTouchFramework 54 | NO 55 | 56 | 57 | 2 58 | IBCocoaTouchFramework 59 | 60 | 61 | Courier 62 | Courier 63 | 0 64 | 14 65 | 66 | 67 | Courier 68 | 14 69 | 16 70 | 71 | 72 | 73 | 74 | 292 75 | {{73, 20}, {174, 37}} 76 | 77 | 78 | 79 | _NS:225 80 | NO 81 | IBCocoaTouchFramework 82 | 0 83 | 0 84 | 1 85 | Get All Email Tokens 86 | 87 | 3 88 | MQA 89 | 90 | 91 | 1 92 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 93 | 94 | 95 | 3 96 | MC41AA 97 | 98 | 99 | 2 100 | 15 101 | 102 | 103 | Helvetica-Bold 104 | 15 105 | 16 106 | 107 | 108 | 109 | 110 | 292 111 | {{79, 403}, {163, 37}} 112 | 113 | 114 | 115 | _NS:225 116 | NO 117 | IBCocoaTouchFramework 118 | 0 119 | 0 120 | 1 121 | Reset Confirmation 122 | 123 | 124 | 1 125 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 292 134 | {{0, 381}, {320, 14}} 135 | 136 | 137 | 138 | _NS:196 139 | 140 | 1 141 | MCAwIDAAA 142 | 143 | IBCocoaTouchFramework 144 | 145 | 146 | 147 | 292 148 | {{40, 65}, {240, 37}} 149 | 150 | 151 | _NS:225 152 | NO 153 | IBCocoaTouchFramework 154 | 0 155 | 0 156 | 1 157 | Get All Phone Number Tokens 158 | 159 | 160 | 1 161 | MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA 162 | 163 | 164 | 165 | 166 | 167 | 168 | {{0, 20}, {320, 460}} 169 | 170 | 171 | 172 | 173 | 3 174 | MC43NQA 175 | 176 | 2 177 | 178 | 179 | NO 180 | 181 | IBCocoaTouchFramework 182 | 183 | 184 | 185 | 186 | 187 | 188 | view 189 | 190 | 191 | 192 | 7 193 | 194 | 195 | 196 | allTokensView 197 | 198 | 199 | 200 | 19 201 | 202 | 203 | 204 | requestEmailTokens: 205 | 206 | 207 | 7 208 | 209 | 26 210 | 211 | 212 | 213 | resetConfirmation: 214 | 215 | 216 | 7 217 | 218 | 17 219 | 220 | 221 | 222 | requestPhoneNumberTokens: 223 | 224 | 225 | 7 226 | 227 | 25 228 | 229 | 230 | 231 | 232 | 233 | 0 234 | 235 | 236 | 237 | 238 | 239 | -1 240 | 241 | 242 | File's Owner 243 | 244 | 245 | -2 246 | 247 | 248 | 249 | 250 | 6 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 8 263 | 264 | 265 | 266 | 267 | 9 268 | 269 | 270 | 271 | 272 | 11 273 | 274 | 275 | 276 | 277 | 22 278 | 279 | 280 | 281 | 282 | 23 283 | 284 | 285 | 286 | 287 | 288 | 289 | DTSViewController 290 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 291 | UIResponder 292 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 293 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 294 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 295 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 296 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 297 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 298 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 299 | 300 | 301 | 302 | 303 | 304 | 26 305 | 306 | 307 | 308 | 309 | DTSViewController 310 | UIViewController 311 | 312 | id 313 | id 314 | id 315 | 316 | 317 | 318 | requestEmailTokens: 319 | id 320 | 321 | 322 | requestPhoneNumberTokens: 323 | id 324 | 325 | 326 | resetConfirmation: 327 | id 328 | 329 | 330 | 331 | allTokensView 332 | UITextView 333 | 334 | 335 | allTokensView 336 | 337 | allTokensView 338 | UITextView 339 | 340 | 341 | 342 | IBProjectSource 343 | ./Classes/DTSViewController.h 344 | 345 | 346 | 347 | 348 | 0 349 | IBCocoaTouchFramework 350 | YES 351 | 3 352 | 933 353 | 354 | 355 | -------------------------------------------------------------------------------- /DTSHashedContacts/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /DTSHashedContacts/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // DTSHashedContacts 4 | // 5 | // Created by David Smith on 2/15/12. 6 | // Copyright (c) 2012 Cross Forward Consulting, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "DTSAppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([DTSAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Licensed under the MIT – No Open Source License 2 | 3 | Copyright (c) 2012 David Smith 4 | 5 | Permission is hereby granted, free of charge, to any person (except for ) obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | Under no circumstances shall be granted use of this software, source code, documentation or other related material. Persons dealing in the Software agree not to knowingly distribute these materials or any derivative works to . 10 | 11 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | 13 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | ## Hashed Contacts 2 | 3 | Hashed Contacts is a drop-in iOS library that provides a wrapper around the Address Book frameworks designed to ensure the privacy of user data. It does this in two ways: 4 | 5 | 1. Before access is ever made to the Address Book the user will first be prompted for permission 6 | 1. Once access is granted the library returns hashed tokens representing the user's private data rather than the data itself. 7 | 8 | These two steps do much to establish user trust with how you treat their data. 9 | 10 | ## Installation 11 | 12 | Copy the contents of the `Source` directory into your project. This will include the two main files `DTSHashedContactsProvider.m` and `DTSHashedContactsProvider.h` in addition to the localization files. 13 | 14 | Add the `AddressBook.framework` to your project. 15 | 16 | __Supports iOS 4.0 and above__ 17 | 18 | Replace the value of `DTSHashSALT` in `DTSHashedContactsProvider.m` and remove the warning. 19 | 20 | ## Usage 21 | 22 | See a full working example of how this library can be used by opening the included in this repository. 23 | 24 | To use the library simply initialize an instance of the `DTSHashedContactsProvider` class. _Note:_ You will need to retain a reference to this instance until you are done loading the user's data. 25 | 26 | You may configure this in three ways: 27 | 28 | 1. Choose the hashing method used via the `hashingMethod` property. Options are SHA1 and SHA512 29 | 1. Specify the alert title via the `alertTitle` property 30 | 1. Specify the alert message via the `alertMessage` property 31 | 32 | A default value for the alert is provided based on the application display name. 33 | 34 | ![](https://github.com/crossforward/HashedContacts/raw/master/alert_example.png) 35 | 36 | Once that is done simply call the desired retrieval method to get your tokens. 37 | 38 | [hashedContactsProvider emailTokensWithConfirmation:^(NSArray* tokens) { 39 | //When permission given 40 | } whenDeclined:^{ 41 | //When permission denied 42 | }]; 43 | 44 | The result provided in `tokens` will look like this: 45 | 46 | `[@"eca7cf4a5981108abb6e2b9754c96b62e6c241db",@"a927a7e701521fd46b89db0162a05c1159cbd2aa"]` 47 | -------------------------------------------------------------------------------- /Source/DTSHashedContactsProvider.h: -------------------------------------------------------------------------------- 1 | // 2 | // DTSHashedContacts.h 3 | // DTSHashedContacts 4 | // 5 | // Created by David Smith on 2/15/12. 6 | // Copyright (c) 2012 Cross Forward Consulting, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void (^DTSConfirmationBlock)(NSArray* tokens); 12 | typedef void (^DTSDeclinedBlock)(void); 13 | 14 | typedef enum _DTSHashMethod { 15 | DTSHashWithSHA1 = 0, 16 | DTSHashWithSHA512 = 1, 17 | } DTSHashMethod; 18 | 19 | 20 | @interface DTSHashedContactsProvider : NSObject 21 | 22 | //Request tokens for contact fields. A Confirmation will show asking the user for permission 23 | -(void)emailTokensWithConfirmation:(DTSConfirmationBlock)confirmed whenDeclined:(DTSDeclinedBlock)declined; 24 | -(void)phoneNumberTokensWithConfirmation:(DTSConfirmationBlock)confirmed whenDeclined:(DTSDeclinedBlock)declined; 25 | 26 | //Tokenize the given string using the current hashing setup 27 | -(NSString*)tokenForString:(NSString*)string; 28 | 29 | //The user will be prompted to give permission before access to addressbook is granted 30 | -(BOOL)userHasGivenPermission; 31 | -(void)resetConfirmationAlerts; 32 | 33 | //Choose an option from DTSHashMethod to use for hashing 34 | @property DTSHashMethod hashingMethod; 35 | @property (strong) NSString* alertTitle; 36 | @property (strong) NSString* alertMessage; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Source/DTSHashedContactsProvider.m: -------------------------------------------------------------------------------- 1 | // 2 | // DTSHashedContacts.m 3 | // DTSHashedContacts 4 | // 5 | // Created by David Smith on 2/15/12. 6 | // Copyright (c) 2012 Cross Forward Consulting, LLC. All rights reserved. 7 | // 8 | 9 | #import "DTSHashedContactsProvider.h" 10 | #import 11 | #import 12 | 13 | #warning Update the value of DTSHashSALT to something specific to your application 14 | #define DTSHashSALT @"CHANGE ME PLEASE" 15 | 16 | #define DTSHashedContactsHasGivenPermission @"DTSHashedContactsHasGivenPermission" 17 | 18 | typedef enum _DTSFieldToReturn { 19 | DTSEmailField = 0, 20 | DTSPhoneNumberField = 1, 21 | } DTSFieldToReturn; 22 | 23 | @interface DTSHashedContactsProvider () 24 | 25 | @property (copy) DTSConfirmationBlock confirmationBlock; 26 | @property (copy) DTSDeclinedBlock declinedBlock; 27 | @property DTSFieldToReturn contactFieldToReturn; 28 | 29 | @end 30 | 31 | @implementation DTSHashedContactsProvider 32 | 33 | @synthesize confirmationBlock, declinedBlock, hashingMethod, contactFieldToReturn, alertTitle, alertMessage; 34 | 35 | - (id)init { 36 | self = [super init]; 37 | if (self) { 38 | //Default to SHA1 39 | self.hashingMethod = DTSHashWithSHA1; 40 | 41 | //Default Messaging 42 | self.alertTitle = NSLocalizedString(@"Allow Access to Contacts?", @"Alert title"); 43 | NSString *appname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"]; 44 | self.alertMessage = [NSString stringWithFormat:NSLocalizedString(@"%@ would like access to your contact information.", @"Alert message"), appname]; 45 | 46 | } 47 | return self; 48 | } 49 | 50 | -(NSString *)createSHA512:(NSString *)string 51 | { 52 | const char *cstr = [string cStringUsingEncoding:NSUTF8StringEncoding]; 53 | NSData *data = [NSData dataWithBytes:cstr length:string.length]; 54 | uint8_t digest[CC_SHA512_DIGEST_LENGTH]; 55 | CC_SHA512(data.bytes, data.length, digest); 56 | NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA512_DIGEST_LENGTH * 2]; 57 | for(int i = 0; i < CC_SHA512_DIGEST_LENGTH; i++) 58 | [output appendFormat:@"%02x", digest[i]]; 59 | return output; 60 | } 61 | 62 | -(NSString *)createSHA1:(NSString *)string 63 | { 64 | const char *cstr = [string cStringUsingEncoding:NSUTF8StringEncoding]; 65 | NSData *data = [NSData dataWithBytes:cstr length:string.length]; 66 | uint8_t digest[CC_SHA1_DIGEST_LENGTH]; 67 | CC_SHA1(data.bytes, data.length, digest); 68 | NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2]; 69 | for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) 70 | [output appendFormat:@"%02x", digest[i]]; 71 | return output; 72 | } 73 | 74 | -(NSString*)tokenForString:(NSString*)string { 75 | //The concatenation of the application hash given in DTSHashSALT and the given string is used for hashing. 76 | NSString* stringToHash = [NSString stringWithFormat:@"%@%@", DTSHashSALT, string]; 77 | if(self.hashingMethod == DTSHashWithSHA1) { 78 | return [self createSHA1:stringToHash]; 79 | } else { 80 | return [self createSHA512:stringToHash]; 81 | } 82 | } 83 | 84 | -(NSString*)cleanNumber:(NSString*)number 85 | { 86 | NSCharacterSet *numbers = [[NSCharacterSet characterSetWithCharactersInString:@"+0123456789"] invertedSet]; 87 | return [[number componentsSeparatedByCharactersInSet:numbers] componentsJoinedByString:@""]; 88 | } 89 | 90 | -(NSString*)cleanEmail:(NSString*)email 91 | { 92 | // This cleans email+additional@provider.com to be email@provider.com 93 | // which is another problematic scenario.. 94 | 95 | NSRange plusRange = [email rangeOfString:@"+"]; 96 | if(plusRange.length > 0) 97 | { 98 | // found 99 | NSRange atRange = [email rangeOfString:@"@"]; 100 | 101 | NSString * start = [email substringToIndex:plusRange.location]; 102 | NSString * rest = [email substringFromIndex:atRange.location]; 103 | email = [start stringByAppendingString:rest]; 104 | } 105 | 106 | return email; 107 | } 108 | 109 | -(void)getTokens { 110 | //Retrieve entries on background thread so that we don't block the main one 111 | dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 112 | NSMutableArray* tokens = [NSMutableArray array]; 113 | 114 | ABAddressBookRef addressBook = ABAddressBookCreate(); 115 | CFArrayRef contacts = ABAddressBookCopyArrayOfAllPeople(addressBook); 116 | CFIndex nPeople = ABAddressBookGetPersonCount(addressBook); 117 | 118 | for( int i = 0; i < nPeople; ++i ) { 119 | 120 | ABRecordRef ref = CFArrayGetValueAtIndex(contacts,i); 121 | ABMultiValueRef contactRef; 122 | 123 | if(self.contactFieldToReturn == DTSEmailField) { 124 | contactRef = ABRecordCopyValue(ref, kABPersonEmailProperty); 125 | } else if(self.contactFieldToReturn == DTSPhoneNumberField) { 126 | contactRef = ABRecordCopyValue(ref, kABPersonPhoneProperty); 127 | } else { 128 | break; 129 | } 130 | 131 | CFIndex numberOfEntries = ABMultiValueGetCount(contactRef); 132 | CFArrayRef entries = ABMultiValueCopyArrayOfAllValues(contactRef); 133 | for( int j = 0; j < numberOfEntries; ++j ) { 134 | NSString* email = (__bridge NSString *)CFArrayGetValueAtIndex(entries, j); 135 | 136 | if(self.contactFieldToReturn == DTSEmailField) { 137 | email = [self cleanEmail:email]; 138 | } else if(self.contactFieldToReturn == DTSPhoneNumberField) { 139 | email = [self cleanNumber:email]; 140 | } 141 | 142 | NSString* hashed = [self tokenForString:email]; 143 | [tokens addObject:hashed]; 144 | }; 145 | 146 | // clean up 147 | if(entries) { 148 | CFRelease(entries); 149 | } 150 | if(contactRef) { 151 | CFRelease(contactRef); 152 | } 153 | } 154 | 155 | if(contacts) { 156 | CFRelease(contacts); 157 | } 158 | 159 | //Always return data on the main thread 160 | dispatch_async(dispatch_get_main_queue(), ^{ 161 | self.confirmationBlock(tokens); 162 | self.confirmationBlock = nil; 163 | self.declinedBlock = nil; 164 | }); 165 | 166 | }); 167 | 168 | } 169 | #pragma Mark Permission Confirmation 170 | 171 | -(void)resetConfirmationAlerts { 172 | NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 173 | [defaults setBool:NO forKey:DTSHashedContactsHasGivenPermission]; 174 | [defaults synchronize]; 175 | } 176 | 177 | -(BOOL)userHasGivenPermission { 178 | NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 179 | return [defaults boolForKey:DTSHashedContactsHasGivenPermission]; 180 | } 181 | 182 | -(void)askForPermission { 183 | UIAlertView* alert = [[UIAlertView alloc] 184 | initWithTitle:self.alertTitle 185 | message:self.alertMessage 186 | delegate:self 187 | cancelButtonTitle:NSLocalizedString(@"Don’t Allow", @"Cancel button title") 188 | otherButtonTitles:NSLocalizedString(@"Allow", @"Allow button title"), nil]; 189 | [alert show]; 190 | } 191 | 192 | #pragma Mark UIAlertViewDelegate 193 | - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { 194 | 195 | NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 196 | if(buttonIndex == 1) { 197 | [defaults setBool:YES forKey:DTSHashedContactsHasGivenPermission]; 198 | } else { 199 | [defaults setBool:NO forKey:DTSHashedContactsHasGivenPermission]; 200 | } 201 | [defaults synchronize]; 202 | 203 | 204 | if ([self userHasGivenPermission]) { 205 | [self getTokens]; 206 | } else { 207 | self.declinedBlock(); 208 | self.confirmationBlock = nil; 209 | self.declinedBlock = nil; 210 | } 211 | } 212 | 213 | -(void)processRequest { 214 | if ([self userHasGivenPermission]) { 215 | [self getTokens]; 216 | } else { 217 | [self askForPermission]; 218 | } 219 | } 220 | #pragma Mark Token Requests 221 | -(void)phoneNumberTokensWithConfirmation:(DTSConfirmationBlock)confirmation whenDeclined:(DTSDeclinedBlock)declined { 222 | self.confirmationBlock = confirmation; 223 | self.declinedBlock = declined; 224 | self.contactFieldToReturn = DTSPhoneNumberField; 225 | [self processRequest]; 226 | } 227 | 228 | 229 | -(void)emailTokensWithConfirmation:(DTSConfirmationBlock)confirmation whenDeclined:(DTSDeclinedBlock)declined { 230 | self.confirmationBlock = confirmation; 231 | self.declinedBlock = declined; 232 | self.contactFieldToReturn = DTSEmailField; 233 | [self processRequest]; 234 | } 235 | 236 | 237 | @end 238 | -------------------------------------------------------------------------------- /Source/de.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crossforward/HashedContacts/4d0cbe5bff12f30030cb8a5d912905fcc36674f8/Source/de.lproj/Localizable.strings -------------------------------------------------------------------------------- /Source/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crossforward/HashedContacts/4d0cbe5bff12f30030cb8a5d912905fcc36674f8/Source/en.lproj/Localizable.strings -------------------------------------------------------------------------------- /Source/nl.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crossforward/HashedContacts/4d0cbe5bff12f30030cb8a5d912905fcc36674f8/Source/nl.lproj/Localizable.strings -------------------------------------------------------------------------------- /alert_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crossforward/HashedContacts/4d0cbe5bff12f30030cb8a5d912905fcc36674f8/alert_example.png --------------------------------------------------------------------------------