├── .gitignore ├── LICENSE ├── README.md ├── RMErrorRecoveryAttempterSampleProject ├── RMErrorRecoveryAttempterSampleProject.xcodeproj │ └── project.pbxproj └── RMErrorRecoveryAttempterSampleProject │ ├── Default-568h@2x.png │ ├── Default.png │ ├── Default@2x.png │ ├── RMAppDelegate.h │ ├── RMAppDelegate.m │ ├── RMErrorRecoveryAttempterSampleProject-Constants.h │ ├── RMErrorRecoveryAttempterSampleProject-Constants.m │ ├── RMErrorRecoveryAttempterSampleProject-Info.plist │ ├── RMErrorRecoveryAttempterSampleProject-Prefix.pch │ ├── RMLockableItem.h │ ├── RMLockableItem.m │ ├── RMMasterViewController.h │ ├── RMMasterViewController.m │ ├── en-GB.lproj │ ├── InfoPlist.strings │ └── MainStoryboard.storyboard │ ├── main.m │ ├── padlock.png │ └── padlock@2x.png └── Source ├── RMErrorRecoveryAttempter.h ├── RMErrorRecoveryAttempter.m ├── UIResponder+RMErrorRecovery.h └── UIResponder+RMErrorRecovery.m /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .AppleDouble 3 | .LSOverride 4 | Icon 5 | 6 | # Thumbnails 7 | ._* 8 | 9 | # Files that might appear on external disk 10 | .Spotlight-V100 11 | .Trashes 12 | 13 | # Xcode 14 | .DS_Store 15 | build/ 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | *.xcworkspace 25 | !default.xcworkspace 26 | xcuserdata 27 | profile 28 | *.moved-aside 29 | DerivedData 30 | .idea/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2012 Realmac Software Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining 5 | // a copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject 10 | // to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 19 | // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | // -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RMErrorRecoveryAttempter 2 | 3 | RMErrorRecoveryAttempter is a class that conforms to the `NSErrorRecoveryAttempting` informal protocol and harnesses the power of blocks allowing you to provide recovery options for an error. 4 | 5 | Read the [Cocoa Error Handling and Recovery](http://www.realmacsoftware.com/blog/cocoa-error-handling-and-recovery) blog post on the Realmac Blog for more error handling tips. 6 | 7 | ## Sample Project 8 | 9 | The sample project, RMErrorRecoveryAttempterSampleProject, is an iOS app where locked and unlocked items can be created. If you swipe-to-delete an item that is locked an error is created. The user info dictionary of this error contains an `RMErrorRecoveryAttempter` object which has two recovery options, each with a title and a block object. The titles of these recovery options are used to populate the buttons of an alert and upon tapping a button the corresponding block object is executed. The recovery options return `YES` or `NO` to inform the caller whether to resend the original message that failed. 10 | 11 | RMErrorRecoveryAttempter *errorRecoveryAttempter = [[RMErrorRecoveryAttempter alloc] init]; 12 | [errorRecoveryAttempter addRecoveryOptionWithLocalizedTitle:NSLocalizedString(@"Don\u2019t Unlock", @"RMMasterViewController delete locked item error don't unlock recovery option") recoveryBlock:^ BOOL (void) { 13 | // Do not attempt to recover from the error. Return NO to inform the caller that they should not resend the message that failed. 14 | return NO; 15 | }]; 16 | [errorRecoveryAttempter addRecoveryOptionWithLocalizedTitle:NSLocalizedString(@"Unlock & Delete", @"RMMasterViewController delete locked item error unlock & delete recovery option") recoveryBlock:^ BOOL (void) { 17 | // Do the required work to recover from the error; unlock the item. 18 | [item setLocked:NO]; 19 | // Return YES to inform the caller that they should resend the message that failed, not that the original functionality of that message has been performed. 20 | return YES; 21 | }]; 22 | /* 23 | The `userInfo` dictionary populated with our localized title and messages strings and `RMErrorRecoveryAttempter`. If you have an underlying 24 | error, for example an error from a failed `-[NSManagedObjectContext save:]`, you can include it under the `NSUnderlyingErrorKey`. 25 | */ 26 | NSDictionary *userInfo = @{ 27 | NSLocalizedDescriptionKey : NSLocalizedString(@"Cannot delete a locked item", @"RMMasterViewController delete locked item error description"), 28 | NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"This item cannot be deleted because it is currently locked. Would you like to Unlock & Delete this item?", @"RMMasterViewController delete locked item error recovery suggestion"), 29 | NSRecoveryAttempterErrorKey : errorRecoveryAttempter, 30 | NSLocalizedRecoveryOptionsErrorKey : [errorRecoveryAttempter recoveryOptions], 31 | }; 32 | *errorRef = [NSError errorWithDomain:RMErrorRecoveryAttempterSampleProjectErrorDomain code:RMErrorRecoveryAttempterSampleProjectErrorCodeLockedItem userInfo:userInfo]; 33 | 34 | The alert is presented using the `UIResponder+RMErrorRecovery` category. If the `recovered` parameter of the completion handler is `YES` then the user chose a recovery path and so the message to delete the item is resent. 35 | 36 | - (void)rm_presentError:(NSError *)error completionHandler:(void (^)(BOOL recovered))completionHandler; 37 | 38 | On OS X you can use either the following two AppKit methods to present the error. 39 | 40 | - (BOOL)presentError:(NSError *)error; 41 | - (void)presentError:(NSError *)error modalForWindow:(NSWindow *)window delegate:(id)delegate didPresentSelector:(SEL)didPresentSelector contextInfo:(void *)contextInfo; 42 | 43 | ## Requirements 44 | 45 | - Either iOS 5.0 and above, or OS X 10.7 and above 46 | - LLVM Compiler 4.0 and above 47 | - ARC 48 | 49 | If your project is not using ARC, you’ll need to set the `-fobjc-arc` compiler flag on the `RMErrorRecoveryAttempter` and `UIResponder+RMErrorRecovery` source files. To set these in Xcode, go to your active target and select the Build Phases tab. Expand the Compile Sources section, select the mentioned source files, press Enter, and insert `-fobjc-arc`. 50 | 51 | ## Contact 52 | 53 | Please contact James Beith regarding this project, [james@realmacsoftware.com](mailto:james@realmacsoftware.com?subject=RMErrorRecoveryAttempter) 54 | 55 | ## Credits 56 | 57 | Keith Duncan, [@keith_duncan](https://twitter.com/account/redirect_by_id?id=15379821) 58 | Damien DeVille, [@DamienDeVille](https://twitter.com/account/redirect_by_id?id=40584312) 59 | James Beith, [@jamesbeith](https://twitter.com/account/redirect_by_id?id=35832158) 60 | 61 | ## License 62 | 63 | See the LICENSE file for more info. -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1A0A669F1626D14A00791428 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A0A669E1626D14A00791428 /* UIKit.framework */; }; 11 | 1A0A66A11626D14A00791428 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A0A66A01626D14A00791428 /* Foundation.framework */; }; 12 | 1A0A66A31626D14A00791428 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A0A66A21626D14A00791428 /* CoreGraphics.framework */; }; 13 | 1A0A66A91626D14A00791428 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1A0A66A71626D14A00791428 /* InfoPlist.strings */; }; 14 | 1A0A66AB1626D14A00791428 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A0A66AA1626D14A00791428 /* main.m */; }; 15 | 1A0A66AF1626D14A00791428 /* RMAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A0A66AE1626D14A00791428 /* RMAppDelegate.m */; }; 16 | 1A0A66B11626D14A00791428 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 1A0A66B01626D14A00791428 /* Default.png */; }; 17 | 1A0A66B31626D14A00791428 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1A0A66B21626D14A00791428 /* Default@2x.png */; }; 18 | 1A0A66B51626D14A00791428 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1A0A66B41626D14A00791428 /* Default-568h@2x.png */; }; 19 | 1A0A66B81626D14A00791428 /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1A0A66B61626D14A00791428 /* MainStoryboard.storyboard */; }; 20 | 1A0A66BB1626D14A00791428 /* RMMasterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A0A66BA1626D14A00791428 /* RMMasterViewController.m */; }; 21 | 1A0A66CF1626D42B00791428 /* RMErrorRecoveryAttempter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A0A66CA1626D42B00791428 /* RMErrorRecoveryAttempter.m */; }; 22 | 1A597CA5162DBA7700480B9D /* UIResponder+RMErrorRecovery.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A597CA4162DBA7700480B9D /* UIResponder+RMErrorRecovery.m */; }; 23 | 1AF668311626E05500A35DC4 /* padlock.png in Resources */ = {isa = PBXBuildFile; fileRef = 1AF6682F1626E05500A35DC4 /* padlock.png */; }; 24 | 1AF668321626E05500A35DC4 /* padlock@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1AF668301626E05500A35DC4 /* padlock@2x.png */; }; 25 | 1AF668351626E2BC00A35DC4 /* RMLockableItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AF668341626E2BC00A35DC4 /* RMLockableItem.m */; }; 26 | 1AF66A951627044100A35DC4 /* RMErrorRecoveryAttempterSampleProject-Constants.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AF66A941627044100A35DC4 /* RMErrorRecoveryAttempterSampleProject-Constants.m */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | 1A0A669A1626D14A00791428 /* RMErrorRecoveryAttempterSampleProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RMErrorRecoveryAttempterSampleProject.app; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | 1A0A669E1626D14A00791428 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 32 | 1A0A66A01626D14A00791428 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 33 | 1A0A66A21626D14A00791428 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 34 | 1A0A66A61626D14A00791428 /* RMErrorRecoveryAttempterSampleProject-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "RMErrorRecoveryAttempterSampleProject-Info.plist"; sourceTree = ""; }; 35 | 1A0A66A81626D14A00791428 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/InfoPlist.strings"; sourceTree = ""; }; 36 | 1A0A66AA1626D14A00791428 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 37 | 1A0A66AC1626D14A00791428 /* RMErrorRecoveryAttempterSampleProject-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RMErrorRecoveryAttempterSampleProject-Prefix.pch"; sourceTree = ""; }; 38 | 1A0A66AD1626D14A00791428 /* RMAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RMAppDelegate.h; sourceTree = ""; }; 39 | 1A0A66AE1626D14A00791428 /* RMAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RMAppDelegate.m; sourceTree = ""; }; 40 | 1A0A66B01626D14A00791428 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 41 | 1A0A66B21626D14A00791428 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 42 | 1A0A66B41626D14A00791428 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 43 | 1A0A66B71626D14A00791428 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = "en-GB"; path = "en-GB.lproj/MainStoryboard.storyboard"; sourceTree = ""; }; 44 | 1A0A66B91626D14A00791428 /* RMMasterViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RMMasterViewController.h; sourceTree = ""; }; 45 | 1A0A66BA1626D14A00791428 /* RMMasterViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RMMasterViewController.m; sourceTree = ""; }; 46 | 1A0A66C91626D42B00791428 /* RMErrorRecoveryAttempter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMErrorRecoveryAttempter.h; sourceTree = ""; }; 47 | 1A0A66CA1626D42B00791428 /* RMErrorRecoveryAttempter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMErrorRecoveryAttempter.m; sourceTree = ""; }; 48 | 1A597CA3162DBA7700480B9D /* UIResponder+RMErrorRecovery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIResponder+RMErrorRecovery.h"; sourceTree = ""; }; 49 | 1A597CA4162DBA7700480B9D /* UIResponder+RMErrorRecovery.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIResponder+RMErrorRecovery.m"; sourceTree = ""; }; 50 | 1AF6682F1626E05500A35DC4 /* padlock.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = padlock.png; sourceTree = ""; }; 51 | 1AF668301626E05500A35DC4 /* padlock@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "padlock@2x.png"; sourceTree = ""; }; 52 | 1AF668331626E2BC00A35DC4 /* RMLockableItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMLockableItem.h; sourceTree = ""; }; 53 | 1AF668341626E2BC00A35DC4 /* RMLockableItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMLockableItem.m; sourceTree = ""; }; 54 | 1AF66A931627044100A35DC4 /* RMErrorRecoveryAttempterSampleProject-Constants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RMErrorRecoveryAttempterSampleProject-Constants.h"; sourceTree = ""; }; 55 | 1AF66A941627044100A35DC4 /* RMErrorRecoveryAttempterSampleProject-Constants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RMErrorRecoveryAttempterSampleProject-Constants.m"; sourceTree = ""; }; 56 | /* End PBXFileReference section */ 57 | 58 | /* Begin PBXFrameworksBuildPhase section */ 59 | 1A0A66971626D14A00791428 /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | 1A0A669F1626D14A00791428 /* UIKit.framework in Frameworks */, 64 | 1A0A66A11626D14A00791428 /* Foundation.framework in Frameworks */, 65 | 1A0A66A31626D14A00791428 /* CoreGraphics.framework in Frameworks */, 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | /* End PBXFrameworksBuildPhase section */ 70 | 71 | /* Begin PBXGroup section */ 72 | 1A0A668F1626D14A00791428 = { 73 | isa = PBXGroup; 74 | children = ( 75 | 1A0A66C81626D42B00791428 /* Source */, 76 | 1A0A66A41626D14A00791428 /* RMErrorRecoveryAttempterSampleProject */, 77 | 1A0A669D1626D14A00791428 /* Frameworks */, 78 | 1A0A669B1626D14A00791428 /* Products */, 79 | ); 80 | sourceTree = ""; 81 | }; 82 | 1A0A669B1626D14A00791428 /* Products */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 1A0A669A1626D14A00791428 /* RMErrorRecoveryAttempterSampleProject.app */, 86 | ); 87 | name = Products; 88 | sourceTree = ""; 89 | }; 90 | 1A0A669D1626D14A00791428 /* Frameworks */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 1A0A669E1626D14A00791428 /* UIKit.framework */, 94 | 1A0A66A01626D14A00791428 /* Foundation.framework */, 95 | 1A0A66A21626D14A00791428 /* CoreGraphics.framework */, 96 | ); 97 | name = Frameworks; 98 | sourceTree = ""; 99 | }; 100 | 1A0A66A41626D14A00791428 /* RMErrorRecoveryAttempterSampleProject */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 1A0A66AD1626D14A00791428 /* RMAppDelegate.h */, 104 | 1A0A66AE1626D14A00791428 /* RMAppDelegate.m */, 105 | 1A0A66B61626D14A00791428 /* MainStoryboard.storyboard */, 106 | 1A0A66B91626D14A00791428 /* RMMasterViewController.h */, 107 | 1A0A66BA1626D14A00791428 /* RMMasterViewController.m */, 108 | 1AF66A9716270F2000A35DC4 /* Model */, 109 | 1AF66A961627055900A35DC4 /* Other */, 110 | 1A0A66A51626D14A00791428 /* Supporting Files */, 111 | ); 112 | path = RMErrorRecoveryAttempterSampleProject; 113 | sourceTree = ""; 114 | }; 115 | 1A0A66A51626D14A00791428 /* Supporting Files */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 1A0A66A61626D14A00791428 /* RMErrorRecoveryAttempterSampleProject-Info.plist */, 119 | 1A0A66A71626D14A00791428 /* InfoPlist.strings */, 120 | 1A0A66AA1626D14A00791428 /* main.m */, 121 | 1A0A66AC1626D14A00791428 /* RMErrorRecoveryAttempterSampleProject-Prefix.pch */, 122 | 1A0A66B01626D14A00791428 /* Default.png */, 123 | 1A0A66B21626D14A00791428 /* Default@2x.png */, 124 | 1A0A66B41626D14A00791428 /* Default-568h@2x.png */, 125 | 1AF6682F1626E05500A35DC4 /* padlock.png */, 126 | 1AF668301626E05500A35DC4 /* padlock@2x.png */, 127 | ); 128 | name = "Supporting Files"; 129 | sourceTree = ""; 130 | }; 131 | 1A0A66C81626D42B00791428 /* Source */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 1A0A66C91626D42B00791428 /* RMErrorRecoveryAttempter.h */, 135 | 1A0A66CA1626D42B00791428 /* RMErrorRecoveryAttempter.m */, 136 | 1A597CA3162DBA7700480B9D /* UIResponder+RMErrorRecovery.h */, 137 | 1A597CA4162DBA7700480B9D /* UIResponder+RMErrorRecovery.m */, 138 | ); 139 | name = Source; 140 | path = ../Source; 141 | sourceTree = ""; 142 | }; 143 | 1AF66A961627055900A35DC4 /* Other */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 1AF66A931627044100A35DC4 /* RMErrorRecoveryAttempterSampleProject-Constants.h */, 147 | 1AF66A941627044100A35DC4 /* RMErrorRecoveryAttempterSampleProject-Constants.m */, 148 | ); 149 | name = Other; 150 | sourceTree = ""; 151 | }; 152 | 1AF66A9716270F2000A35DC4 /* Model */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 1AF668331626E2BC00A35DC4 /* RMLockableItem.h */, 156 | 1AF668341626E2BC00A35DC4 /* RMLockableItem.m */, 157 | ); 158 | name = Model; 159 | sourceTree = ""; 160 | }; 161 | /* End PBXGroup section */ 162 | 163 | /* Begin PBXNativeTarget section */ 164 | 1A0A66991626D14A00791428 /* RMErrorRecoveryAttempterSampleProject */ = { 165 | isa = PBXNativeTarget; 166 | buildConfigurationList = 1A0A66C11626D14A00791428 /* Build configuration list for PBXNativeTarget "RMErrorRecoveryAttempterSampleProject" */; 167 | buildPhases = ( 168 | 1A0A66961626D14A00791428 /* Sources */, 169 | 1A0A66971626D14A00791428 /* Frameworks */, 170 | 1A0A66981626D14A00791428 /* Resources */, 171 | ); 172 | buildRules = ( 173 | ); 174 | dependencies = ( 175 | ); 176 | name = RMErrorRecoveryAttempterSampleProject; 177 | productName = RMErrorRecoveryAttempterSampleProject; 178 | productReference = 1A0A669A1626D14A00791428 /* RMErrorRecoveryAttempterSampleProject.app */; 179 | productType = "com.apple.product-type.application"; 180 | }; 181 | /* End PBXNativeTarget section */ 182 | 183 | /* Begin PBXProject section */ 184 | 1A0A66911626D14A00791428 /* Project object */ = { 185 | isa = PBXProject; 186 | attributes = { 187 | CLASSPREFIX = RM; 188 | LastUpgradeCheck = 0450; 189 | ORGANIZATIONNAME = "Realmac Software"; 190 | }; 191 | buildConfigurationList = 1A0A66941626D14A00791428 /* Build configuration list for PBXProject "RMErrorRecoveryAttempterSampleProject" */; 192 | compatibilityVersion = "Xcode 3.2"; 193 | developmentRegion = "en-GB"; 194 | hasScannedForEncodings = 0; 195 | knownRegions = ( 196 | en, 197 | "en-GB", 198 | ); 199 | mainGroup = 1A0A668F1626D14A00791428; 200 | productRefGroup = 1A0A669B1626D14A00791428 /* Products */; 201 | projectDirPath = ""; 202 | projectRoot = ""; 203 | targets = ( 204 | 1A0A66991626D14A00791428 /* RMErrorRecoveryAttempterSampleProject */, 205 | ); 206 | }; 207 | /* End PBXProject section */ 208 | 209 | /* Begin PBXResourcesBuildPhase section */ 210 | 1A0A66981626D14A00791428 /* Resources */ = { 211 | isa = PBXResourcesBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | 1A0A66A91626D14A00791428 /* InfoPlist.strings in Resources */, 215 | 1A0A66B11626D14A00791428 /* Default.png in Resources */, 216 | 1A0A66B31626D14A00791428 /* Default@2x.png in Resources */, 217 | 1A0A66B51626D14A00791428 /* Default-568h@2x.png in Resources */, 218 | 1A0A66B81626D14A00791428 /* MainStoryboard.storyboard in Resources */, 219 | 1AF668311626E05500A35DC4 /* padlock.png in Resources */, 220 | 1AF668321626E05500A35DC4 /* padlock@2x.png in Resources */, 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | }; 224 | /* End PBXResourcesBuildPhase section */ 225 | 226 | /* Begin PBXSourcesBuildPhase section */ 227 | 1A0A66961626D14A00791428 /* Sources */ = { 228 | isa = PBXSourcesBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | 1A0A66AB1626D14A00791428 /* main.m in Sources */, 232 | 1A0A66AF1626D14A00791428 /* RMAppDelegate.m in Sources */, 233 | 1A0A66BB1626D14A00791428 /* RMMasterViewController.m in Sources */, 234 | 1AF668351626E2BC00A35DC4 /* RMLockableItem.m in Sources */, 235 | 1A0A66CF1626D42B00791428 /* RMErrorRecoveryAttempter.m in Sources */, 236 | 1AF66A951627044100A35DC4 /* RMErrorRecoveryAttempterSampleProject-Constants.m in Sources */, 237 | 1A597CA5162DBA7700480B9D /* UIResponder+RMErrorRecovery.m in Sources */, 238 | ); 239 | runOnlyForDeploymentPostprocessing = 0; 240 | }; 241 | /* End PBXSourcesBuildPhase section */ 242 | 243 | /* Begin PBXVariantGroup section */ 244 | 1A0A66A71626D14A00791428 /* InfoPlist.strings */ = { 245 | isa = PBXVariantGroup; 246 | children = ( 247 | 1A0A66A81626D14A00791428 /* en-GB */, 248 | ); 249 | name = InfoPlist.strings; 250 | sourceTree = ""; 251 | }; 252 | 1A0A66B61626D14A00791428 /* MainStoryboard.storyboard */ = { 253 | isa = PBXVariantGroup; 254 | children = ( 255 | 1A0A66B71626D14A00791428 /* en-GB */, 256 | ); 257 | name = MainStoryboard.storyboard; 258 | sourceTree = ""; 259 | }; 260 | /* End PBXVariantGroup section */ 261 | 262 | /* Begin XCBuildConfiguration section */ 263 | 1A0A66BF1626D14A00791428 /* Debug */ = { 264 | isa = XCBuildConfiguration; 265 | buildSettings = { 266 | ALWAYS_SEARCH_USER_PATHS = NO; 267 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 268 | CLANG_CXX_LIBRARY = "libc++"; 269 | CLANG_ENABLE_OBJC_ARC = YES; 270 | CLANG_WARN_EMPTY_BODY = YES; 271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 272 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 273 | COPY_PHASE_STRIP = NO; 274 | GCC_C_LANGUAGE_STANDARD = gnu99; 275 | GCC_DYNAMIC_NO_PIC = NO; 276 | GCC_OPTIMIZATION_LEVEL = 0; 277 | GCC_PREPROCESSOR_DEFINITIONS = ( 278 | "DEBUG=1", 279 | "$(inherited)", 280 | ); 281 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 282 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 283 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 284 | GCC_WARN_UNUSED_VARIABLE = YES; 285 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 286 | ONLY_ACTIVE_ARCH = YES; 287 | SDKROOT = iphoneos; 288 | }; 289 | name = Debug; 290 | }; 291 | 1A0A66C01626D14A00791428 /* Release */ = { 292 | isa = XCBuildConfiguration; 293 | buildSettings = { 294 | ALWAYS_SEARCH_USER_PATHS = NO; 295 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 296 | CLANG_CXX_LIBRARY = "libc++"; 297 | CLANG_ENABLE_OBJC_ARC = YES; 298 | CLANG_WARN_EMPTY_BODY = YES; 299 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 300 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 301 | COPY_PHASE_STRIP = YES; 302 | GCC_C_LANGUAGE_STANDARD = gnu99; 303 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 304 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 305 | GCC_WARN_UNUSED_VARIABLE = YES; 306 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 307 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 308 | SDKROOT = iphoneos; 309 | VALIDATE_PRODUCT = YES; 310 | }; 311 | name = Release; 312 | }; 313 | 1A0A66C21626D14A00791428 /* Debug */ = { 314 | isa = XCBuildConfiguration; 315 | buildSettings = { 316 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 317 | GCC_PREFIX_HEADER = "RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject-Prefix.pch"; 318 | INFOPLIST_FILE = "RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject-Info.plist"; 319 | PRODUCT_NAME = "$(TARGET_NAME)"; 320 | WRAPPER_EXTENSION = app; 321 | }; 322 | name = Debug; 323 | }; 324 | 1A0A66C31626D14A00791428 /* Release */ = { 325 | isa = XCBuildConfiguration; 326 | buildSettings = { 327 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 328 | GCC_PREFIX_HEADER = "RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject-Prefix.pch"; 329 | INFOPLIST_FILE = "RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject-Info.plist"; 330 | PRODUCT_NAME = "$(TARGET_NAME)"; 331 | WRAPPER_EXTENSION = app; 332 | }; 333 | name = Release; 334 | }; 335 | /* End XCBuildConfiguration section */ 336 | 337 | /* Begin XCConfigurationList section */ 338 | 1A0A66941626D14A00791428 /* Build configuration list for PBXProject "RMErrorRecoveryAttempterSampleProject" */ = { 339 | isa = XCConfigurationList; 340 | buildConfigurations = ( 341 | 1A0A66BF1626D14A00791428 /* Debug */, 342 | 1A0A66C01626D14A00791428 /* Release */, 343 | ); 344 | defaultConfigurationIsVisible = 0; 345 | defaultConfigurationName = Release; 346 | }; 347 | 1A0A66C11626D14A00791428 /* Build configuration list for PBXNativeTarget "RMErrorRecoveryAttempterSampleProject" */ = { 348 | isa = XCConfigurationList; 349 | buildConfigurations = ( 350 | 1A0A66C21626D14A00791428 /* Debug */, 351 | 1A0A66C31626D14A00791428 /* Release */, 352 | ); 353 | defaultConfigurationIsVisible = 0; 354 | defaultConfigurationName = Release; 355 | }; 356 | /* End XCConfigurationList section */ 357 | }; 358 | rootObject = 1A0A66911626D14A00791428 /* Project object */; 359 | } 360 | -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realmacsoftware/RMErrorRecoveryAttempter/3bf40cc9704cc0188d8797a70e3852f7b0bcd1be/RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/Default-568h@2x.png -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realmacsoftware/RMErrorRecoveryAttempter/3bf40cc9704cc0188d8797a70e3852f7b0bcd1be/RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/Default.png -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realmacsoftware/RMErrorRecoveryAttempter/3bf40cc9704cc0188d8797a70e3852f7b0bcd1be/RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/Default@2x.png -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/RMAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2012 Realmac Software Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining 5 | // a copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject 10 | // to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 19 | // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | @interface RMAppDelegate : UIResponder 27 | 28 | @property (strong, nonatomic) UIWindow *window; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/RMAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2012 Realmac Software Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining 5 | // a copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject 10 | // to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 19 | // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | // 23 | 24 | #import "RMAppDelegate.h" 25 | 26 | @implementation RMAppDelegate 27 | 28 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 29 | { 30 | return YES; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject-Constants.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2012 Realmac Software Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining 5 | // a copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject 10 | // to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 19 | // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | // 23 | 24 | extern NSString * const RMErrorRecoveryAttempterSampleProjectErrorDomain; 25 | 26 | typedef NS_ENUM(NSInteger, RMErrorRecoveryAttempterSampleProjectErrorCode) { 27 | RMErrorRecoveryAttempterSampleProjectErrorCodeUnknown = 0, 28 | 29 | RMErrorRecoveryAttempterSampleProjectErrorCodeLockedItem = -100, 30 | }; 31 | -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject-Constants.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2012 Realmac Software Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining 5 | // a copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject 10 | // to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 19 | // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | // 23 | 24 | NSString *const RMErrorRecoveryAttempterSampleProjectErrorDomain = @"com.realmacsoftware.dts.rmerrorrecoveryattemptersampleproject"; 25 | -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en-GB 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.realmacsoftware.dts.rmerrorrecoveryattemptersampleproject 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | MainStoryboard 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarTintParameters 34 | 35 | UINavigationBar 36 | 37 | Style 38 | UIBarStyleDefault 39 | Translucent 40 | 41 | 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2012 Realmac Software Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining 5 | // a copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject 10 | // to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 19 | // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | #ifndef __IPHONE_5_0 27 | #warning "This project uses features only available in iOS SDK 5.0 and later." 28 | #endif 29 | 30 | #ifdef __OBJC__ 31 | #import 32 | #import 33 | #endif 34 | -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/RMLockableItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2012 Realmac Software Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining 5 | // a copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject 10 | // to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 19 | // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | @interface RMLockableItem : NSObject 27 | 28 | - (id)initWithTitle:(NSString *)title locked:(BOOL)locked; 29 | 30 | @property (copy, nonatomic) NSString *title; 31 | @property (getter = isLocked, nonatomic) BOOL locked; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/RMLockableItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2012 Realmac Software Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining 5 | // a copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject 10 | // to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 19 | // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | // 23 | 24 | #import "RMLockableItem.h" 25 | 26 | @implementation RMLockableItem 27 | 28 | - (id)initWithTitle:(NSString *)title locked:(BOOL)locked 29 | { 30 | self = [self init]; 31 | if (self == nil) { 32 | return nil; 33 | } 34 | 35 | _title = [title copy]; 36 | _locked = locked; 37 | 38 | return self; 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/RMMasterViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2012 Realmac Software Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining 5 | // a copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject 10 | // to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 19 | // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | @interface RMMasterViewController : UITableViewController 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/RMMasterViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2012 Realmac Software Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining 5 | // a copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject 10 | // to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 19 | // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | // 23 | 24 | #import "RMMasterViewController.h" 25 | 26 | #import "RMLockableItem.h" 27 | #import "RMErrorRecoveryAttempter.h" 28 | #import "UIResponder+RMErrorRecovery.h" 29 | #import "RMErrorRecoveryAttempterSampleProject-Constants.h" 30 | 31 | @interface RMMasterViewController () 32 | 33 | @property (strong, nonatomic) NSMutableArray *items; 34 | 35 | - (IBAction)addUnlockedItem:(id)sender; 36 | - (IBAction)addLockedItem:(id)sender; 37 | 38 | @end 39 | 40 | @implementation RMMasterViewController 41 | 42 | - (void)awakeFromNib 43 | { 44 | [super awakeFromNib]; 45 | 46 | [self _addItemLocked:NO]; 47 | [self _addItemLocked:YES]; 48 | } 49 | 50 | #pragma mark - IBActions 51 | 52 | - (IBAction)addUnlockedItem:(id)sender 53 | { 54 | [self _addItemLocked:NO]; 55 | } 56 | 57 | - (IBAction)addLockedItem:(id)sender 58 | { 59 | [self _addItemLocked:YES]; 60 | } 61 | 62 | #pragma mark - Table View 63 | 64 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 65 | { 66 | return 1; 67 | } 68 | 69 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 70 | { 71 | return [[self items] count]; 72 | } 73 | 74 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 75 | { 76 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; 77 | RMLockableItem *lockableItem = [self items][[indexPath row]]; 78 | [[cell textLabel] setText:[lockableItem title]]; 79 | if ([lockableItem isLocked]) { 80 | UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"padlock"]]; 81 | [cell setAccessoryView:imageView]; 82 | } 83 | else { 84 | [cell setAccessoryView:nil]; 85 | } 86 | return cell; 87 | } 88 | 89 | - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath 90 | { 91 | return YES; 92 | } 93 | 94 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 95 | { 96 | if (editingStyle == UITableViewCellEditingStyleDelete) { 97 | RMLockableItem *item = [self items][[indexPath row]]; 98 | [self _tryDeleteItem:item]; 99 | return; 100 | } 101 | } 102 | 103 | #pragma mark - Add/Delete Items 104 | 105 | - (void)_addItemLocked:(BOOL)locked 106 | { 107 | if ([self items] == nil) { 108 | [self setItems:[NSMutableArray array]]; 109 | } 110 | 111 | RMLockableItem *lockableItem = [[RMLockableItem alloc] initWithTitle:[[NSDate date] description] locked:locked]; 112 | 113 | [[self items] insertObject:lockableItem atIndex:0]; 114 | [[self tableView] insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic]; 115 | } 116 | 117 | - (void)_tryDeleteItem:(RMLockableItem *)item 118 | { 119 | NSError *deleteError = nil; 120 | BOOL delete = [self _deleteItem:item error:&deleteError]; 121 | if (!delete) { 122 | __weak RMMasterViewController *weakSelf = self; 123 | [self rm_presentError:deleteError completionHandler:^ (BOOL recovered) { 124 | if (!recovered) { 125 | // The user didn't choose a recovery path, abort the operation. 126 | return; 127 | } 128 | 129 | __strong RMMasterViewController *strongSelf = weakSelf; 130 | if (strongSelf != nil) { 131 | // The user chose a recovery path, resend the original message. 132 | [strongSelf _tryDeleteItem:item]; 133 | } 134 | }]; 135 | return; 136 | } 137 | 138 | // Successfully deleted the item. 139 | return; 140 | } 141 | 142 | - (BOOL)_deleteItem:(RMLockableItem *)item error:(NSError **)errorRef 143 | { 144 | if ([item isLocked]) { 145 | if (errorRef != NULL) { 146 | RMErrorRecoveryAttempter *errorRecoveryAttempter = [[RMErrorRecoveryAttempter alloc] init]; 147 | [errorRecoveryAttempter addRecoveryOptionWithLocalizedTitle:NSLocalizedString(@"Don\u2019t Unlock", @"RMMasterViewController delete locked item error don't unlock recovery option") recoveryBlock:^ BOOL (void) { 148 | // Do not attempt to recover from the error. Return NO to inform the caller that they should not resend the message that failed. 149 | return NO; 150 | }]; 151 | [errorRecoveryAttempter addRecoveryOptionWithLocalizedTitle:NSLocalizedString(@"Unlock & Delete", @"RMMasterViewController delete locked item error unlock & delete recovery option") recoveryBlock:^ BOOL (void) { 152 | // Do the required work to recover from the error; unlock the item. 153 | [item setLocked:NO]; 154 | // Return YES to inform the caller that they should resend the message that failed, not that the original functionality of that message has been performed. 155 | return YES; 156 | }]; 157 | /* 158 | The `userInfo` dictionary populated with our localized title and messages strings and `RMErrorRecoveryAttempter`. If you have an underlying 159 | error, for example an error from a failed `-[NSManagedObjectContext save:]`, you can include it under the `NSUnderlyingErrorKey`. 160 | */ 161 | NSDictionary *userInfo = @{ 162 | NSLocalizedDescriptionKey : NSLocalizedString(@"Cannot delete a locked item", @"RMMasterViewController delete locked item error description"), 163 | NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"This item cannot be deleted because it is currently locked. Would you like to Unlock & Delete this item?", @"RMMasterViewController delete locked item error recovery suggestion"), 164 | NSRecoveryAttempterErrorKey : errorRecoveryAttempter, 165 | NSLocalizedRecoveryOptionsErrorKey : [errorRecoveryAttempter recoveryOptions], 166 | }; 167 | *errorRef = [NSError errorWithDomain:RMErrorRecoveryAttempterSampleProjectErrorDomain code:RMErrorRecoveryAttempterSampleProjectErrorCodeLockedItem userInfo:userInfo]; 168 | } 169 | return NO; 170 | } 171 | 172 | NSUInteger index = [[self items] indexOfObject:item]; 173 | [[self items] removeObjectAtIndex:index]; 174 | [[self tableView] deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:0]] withRowAnimation:UITableViewRowAnimationFade]; 175 | 176 | return YES; 177 | } 178 | 179 | @end 180 | -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/en-GB.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/en-GB.lproj/MainStoryboard.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2012 Realmac Software Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining 5 | // a copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject 10 | // to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 19 | // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | #import "RMAppDelegate.h" 27 | 28 | int main(int argc, char *argv[]) 29 | { 30 | @autoreleasepool { 31 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([RMAppDelegate class])); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/padlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realmacsoftware/RMErrorRecoveryAttempter/3bf40cc9704cc0188d8797a70e3852f7b0bcd1be/RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/padlock.png -------------------------------------------------------------------------------- /RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/padlock@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realmacsoftware/RMErrorRecoveryAttempter/3bf40cc9704cc0188d8797a70e3852f7b0bcd1be/RMErrorRecoveryAttempterSampleProject/RMErrorRecoveryAttempterSampleProject/padlock@2x.png -------------------------------------------------------------------------------- /Source/RMErrorRecoveryAttempter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2012 Realmac Software Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining 5 | // a copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject 10 | // to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 19 | // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | /*! 27 | \brief 28 | For use as an NSRecoveryAttempterErrorKey. 29 | */ 30 | @interface RMErrorRecoveryAttempter : NSObject 31 | 32 | #if NS_BLOCKS_AVAILABLE 33 | 34 | /*! 35 | \brief 36 | Build up the recovery options. 37 | */ 38 | - (void)addRecoveryOptionWithLocalizedTitle:(NSString *)localizedTitle recoveryBlock:(BOOL (^)(void))recoveryBlock; 39 | 40 | /*! 41 | \brief 42 | Adds a cancel recovery option that returns `NO` from the recovery block. 43 | */ 44 | - (void)addCancelRecoveryOption; 45 | 46 | /*! 47 | \brief 48 | Extract the recovery options for use as `NSLocalizedRecoveryOptionsErrorKey`. 49 | */ 50 | - (NSArray *)recoveryOptions; 51 | 52 | #endif 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /Source/RMErrorRecoveryAttempter.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2012 Realmac Software Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining 5 | // a copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject 10 | // to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 19 | // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | // 23 | 24 | #if !defined(__has_feature) 25 | #define __has_feature(x) 0 26 | #endif 27 | 28 | #if !__has_feature(objc_arc) 29 | #error This source file must be built with ARC 30 | #endif 31 | 32 | #import "RMErrorRecoveryAttempter.h" 33 | 34 | #import 35 | 36 | @interface RMErrorRecoveryAttempter () 37 | 38 | @property (strong, nonatomic) NSMutableArray *titles; 39 | @property (strong, nonatomic) NSMutableArray *blocks; 40 | 41 | @end 42 | 43 | @interface RMErrorRecoveryAttempter (RMFoundationDelegate) 44 | 45 | @end 46 | 47 | @implementation RMErrorRecoveryAttempter 48 | 49 | - (id)init 50 | { 51 | self = [super init]; 52 | if (self == nil) { 53 | return nil; 54 | } 55 | 56 | _titles = [[NSMutableArray alloc] init]; 57 | _blocks = [[NSMutableArray alloc] init]; 58 | 59 | return self; 60 | } 61 | 62 | - (void)addRecoveryOptionWithLocalizedTitle:(NSString *)localizedTitle recoveryBlock:(BOOL (^)(void))recoveryBlock 63 | { 64 | NSParameterAssert(localizedTitle != nil); 65 | NSParameterAssert(recoveryBlock != nil); 66 | 67 | [[self titles] addObject:[localizedTitle copy]]; 68 | [[self blocks] addObject:[recoveryBlock copy]]; 69 | } 70 | 71 | - (void)addCancelRecoveryOption 72 | { 73 | [self addRecoveryOptionWithLocalizedTitle:NSLocalizedString(@"Cancel", @"RMErrorRecoveryAttempter cancel recovery option") recoveryBlock:^ BOOL (void) { 74 | return NO; 75 | }]; 76 | } 77 | 78 | - (NSArray *)recoveryOptions 79 | { 80 | return [_titles copy]; 81 | } 82 | 83 | - (BOOL (^)(void))_recoveryHandlerAtIndex:(NSUInteger)idx 84 | { 85 | return (BOOL (^)(void))[[self blocks] objectAtIndex:idx]; 86 | } 87 | 88 | @end 89 | 90 | @implementation RMErrorRecoveryAttempter (RMFoundationDelegate) 91 | 92 | - (BOOL)attemptRecoveryFromError:(NSError *)error optionIndex:(NSUInteger)recoveryOptionIndex 93 | { 94 | return [self _recoveryHandlerAtIndex:recoveryOptionIndex](); 95 | } 96 | 97 | - (void)attemptRecoveryFromError:(NSError *)error optionIndex:(NSUInteger)recoveryOptionIndex delegate:(id)delegate didRecoverSelector:(SEL)didRecoverSelector contextInfo:(void *)contextInfo 98 | { 99 | void (^originalDidRecover)(BOOL) = ^ (BOOL didRecover) { 100 | ((void (*)(id, SEL, BOOL, void *))objc_msgSend)(delegate, didRecoverSelector, didRecover, contextInfo); 101 | }; 102 | 103 | BOOL didRecover = [self _recoveryHandlerAtIndex:recoveryOptionIndex](); 104 | originalDidRecover(didRecover); 105 | } 106 | 107 | @end 108 | -------------------------------------------------------------------------------- /Source/UIResponder+RMErrorRecovery.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2012 Realmac Software Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining 5 | // a copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject 10 | // to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 19 | // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | /*! 27 | \brief 28 | Presenting an alert from an error. 29 | */ 30 | @interface UIResponder (RMErrorRecovery) 31 | 32 | #if NS_BLOCKS_AVAILABLE 33 | 34 | /*! 35 | \brief 36 | Present an alert from an error. An error that includes an `NSRecoveryAttempterErrorKey` object will include buttons with the `NSLocalizedRecoveryOptionsErrorKey` strings as titles. 37 | */ 38 | - (void)rm_presentError:(NSError *)error completionHandler:(void (^)(BOOL recovered))completionHandler; 39 | 40 | #endif 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Source/UIResponder+RMErrorRecovery.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2012 Realmac Software Ltd 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining 5 | // a copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject 10 | // to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be 13 | // included in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 19 | // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 | // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | // 23 | 24 | #if !defined(__has_feature) 25 | #define __has_feature(x) 0 26 | #endif 27 | 28 | #if !__has_feature(objc_arc) 29 | #error This source file must be built with ARC 30 | #endif 31 | 32 | #import "UIResponder+RMErrorRecovery.h" 33 | 34 | #import 35 | 36 | static NSString *_RMAlertViewDelegateContext = @"_RMAlertViewDelegateContext"; 37 | 38 | @interface _RMAlertViewDelegate : NSObject 39 | 40 | @property (strong, nonatomic) NSError *error; 41 | @property (copy, nonatomic) void (^completionHandler)(BOOL recovered); 42 | 43 | @end 44 | 45 | @implementation _RMAlertViewDelegate 46 | 47 | - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex 48 | { 49 | NSError *error = [self error]; 50 | BOOL recovered = [[error recoveryAttempter] attemptRecoveryFromError:error optionIndex:buttonIndex]; 51 | if ([self completionHandler] != nil) { 52 | [self completionHandler](recovered); 53 | } 54 | } 55 | 56 | @end 57 | 58 | #pragma mark - 59 | 60 | @implementation UIResponder (RMErrorRecovery) 61 | 62 | - (void)rm_presentError:(NSError *)error completionHandler:(void (^)(BOOL recovered))completionHandler 63 | { 64 | _RMAlertViewDelegate *alertViewDelegate = [[_RMAlertViewDelegate alloc] init]; 65 | [alertViewDelegate setError:error]; 66 | [alertViewDelegate setCompletionHandler:completionHandler]; 67 | 68 | UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[error localizedDescription] message:[error localizedRecoverySuggestion] delegate:alertViewDelegate cancelButtonTitle:nil otherButtonTitles:nil]; 69 | 70 | NSArray *recoveryOptions = [error localizedRecoveryOptions]; 71 | if (recoveryOptions == nil || [recoveryOptions count] == 0) { 72 | [alertView addButtonWithTitle:NSLocalizedString(@"OK", @"UIResponder+RMErrorRecovery OK button title")]; 73 | } 74 | else { 75 | [recoveryOptions enumerateObjectsUsingBlock:^ (NSString *title, NSUInteger idx, BOOL *stop) { 76 | [alertView addButtonWithTitle:title]; 77 | }]; 78 | } 79 | 80 | objc_setAssociatedObject(alertView, &_RMAlertViewDelegateContext, alertViewDelegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 81 | 82 | [alertView show]; 83 | } 84 | 85 | @end 86 | --------------------------------------------------------------------------------