Bgerstle has uploaded a new change for review. https://gerrit.wikimedia.org/r/226644
Change subject: fix crash when saving empty/nil title ...................................................................... fix crash when saving empty/nil title Bug: T106028 Change-Id: I8f52107717fd301c89bdd0e3022e53d1862fbcd2 --- M MediaWikiKit/MediaWikiKit/MWKSavedPageEntry.m M MediaWikiKit/MediaWikiKit/MWKSavedPageList.h M MediaWikiKit/MediaWikiKit/MWKSavedPageList.m M MediaWikiKit/MediaWikiKit/MediaWikiKit.h A MediaWikiKit/MediaWikiKit/NSError+MWKErrors.h A MediaWikiKit/MediaWikiKit/NSError+MWKErrors.m A MediaWikiKit/MediaWikiKitTests/MWKSavedPageListTests.m M Wikipedia.xcodeproj/project.pbxproj M Wikipedia/EventLogging/SavedPagesFunnel.h M Wikipedia/EventLogging/SavedPagesFunnel.m M Wikipedia/Global.h M Wikipedia/Session/SessionSingleton.h M Wikipedia/Session/SessionSingleton.m M Wikipedia/View Controllers/WebView/WebViewController.m A Wikipedia/mw-utils/WMFOutParamUtils.h A WikipediaUnitTests/WMFSafeAssignTests.m 16 files changed, 284 insertions(+), 24 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/apps/ios/wikipedia refs/changes/44/226644/1 diff --git a/MediaWikiKit/MediaWikiKit/MWKSavedPageEntry.m b/MediaWikiKit/MediaWikiKit/MWKSavedPageEntry.m index 9bfa6e4..72b26b8 100644 --- a/MediaWikiKit/MediaWikiKit/MWKSavedPageEntry.m +++ b/MediaWikiKit/MediaWikiKit/MWKSavedPageEntry.m @@ -47,4 +47,10 @@ return [NSDictionary dictionaryWithDictionary:dict]; } +WMF_SYNTHESIZE_IS_EQUAL(MWKSavedPageEntry, isEqualToSavedEntry:) + +- (BOOL)isEqualToSavedEntry:(MWKSavedPageEntry*)rhs { + return WMF_RHS_PROP_EQUAL(title, isEqualToTitle:); +} + @end diff --git a/MediaWikiKit/MediaWikiKit/MWKSavedPageList.h b/MediaWikiKit/MediaWikiKit/MWKSavedPageList.h index fda336a..fafd391 100644 --- a/MediaWikiKit/MediaWikiKit/MWKSavedPageList.h +++ b/MediaWikiKit/MediaWikiKit/MWKSavedPageList.h @@ -22,6 +22,16 @@ - (MWKSavedPageEntry*)entryForTitle:(MWKTitle*)title; - (BOOL)isSaved:(MWKTitle*)title; +/** + * Toggle the save state for `title`. + * + * @param title Title to toggle state for, either saving or un-saving it. + * @param error Out-param of any error that occurred while toggling save state and saving. + * + * @return The new saved state for `title` (i.e. `YES` if saved, `NO` if not). + */ +- (BOOL)toggleSaveStateForTitle:(MWKTitle*)title error:(NSError**)error; + /// Add a new entry to the saved page list! - (void)addEntry:(MWKSavedPageEntry*)entry; /// Remove one. diff --git a/MediaWikiKit/MediaWikiKit/MWKSavedPageList.m b/MediaWikiKit/MediaWikiKit/MWKSavedPageList.m index 5aeafef..bdce33c 100644 --- a/MediaWikiKit/MediaWikiKit/MWKSavedPageList.m +++ b/MediaWikiKit/MediaWikiKit/MWKSavedPageList.m @@ -7,6 +7,7 @@ // #import "MediaWikiKit.h" +#import "NSError+MWKErrors.h" @interface MWKSavedPageList () @@ -18,6 +19,21 @@ @implementation MWKSavedPageList +- (BOOL)toggleSaveStateForTitle:(MWKTitle*)title error:(NSError* __autoreleasing*)error { + if (!title.text.length) { + WMFSafeAssign(error, [NSError wmf_emptyTitleError]); + return NO; + } + MWKSavedPageEntry* entry = [self entryForTitle:title]; + if (!entry) { + [self addEntry:[[MWKSavedPageEntry alloc] initWithTitle:title]]; + return YES; + } else { + [self removeEntry:entry]; + return NO; + } +} + - (NSUInteger)length { return [self.entries count]; } diff --git a/MediaWikiKit/MediaWikiKit/MediaWikiKit.h b/MediaWikiKit/MediaWikiKit/MediaWikiKit.h index 2001f21..9072924 100644 --- a/MediaWikiKit/MediaWikiKit/MediaWikiKit.h +++ b/MediaWikiKit/MediaWikiKit/MediaWikiKit.h @@ -10,6 +10,7 @@ #import <Foundation/Foundation.h> +#import "NSError+MWKErrors.h" #import "MWKDataObject.h" #import "MWKSiteDataObject.h" diff --git a/MediaWikiKit/MediaWikiKit/NSError+MWKErrors.h b/MediaWikiKit/MediaWikiKit/NSError+MWKErrors.h new file mode 100644 index 0000000..9a2eae4 --- /dev/null +++ b/MediaWikiKit/MediaWikiKit/NSError+MWKErrors.h @@ -0,0 +1,23 @@ +// +// NSError+MWKErrors.h +// Wikipedia +// +// Created by Brian Gerstle on 7/23/15. +// Copyright (c) 2015 Wikimedia Foundation. All rights reserved. +// + +#import <Foundation/Foundation.h> + +extern NSString* const MWKErrorDomain; + +typedef NS_ENUM (NSInteger, MWKErrorCode) { + MWKEmptyTitleError = 1 +}; + +@interface NSError (MWKErrors) + ++ (instancetype)wmf_emptyTitleError; + ++ (instancetype)wmf_mwkError:(MWKErrorCode)code userInfo:(NSDictionary*)userInfo; + +@end diff --git a/MediaWikiKit/MediaWikiKit/NSError+MWKErrors.m b/MediaWikiKit/MediaWikiKit/NSError+MWKErrors.m new file mode 100644 index 0000000..17c5cfc --- /dev/null +++ b/MediaWikiKit/MediaWikiKit/NSError+MWKErrors.m @@ -0,0 +1,25 @@ +// +// NSError+MWKErrors.m +// Wikipedia +// +// Created by Brian Gerstle on 7/23/15. +// Copyright (c) 2015 Wikimedia Foundation. All rights reserved. +// + +#import "NSError+MWKErrors.h" + +NSString* const MWKErrorDomain = @"MWKErrorDomain"; + +@implementation NSError (MWKErrors) + ++ (instancetype)wmf_emptyTitleError { + return [self wmf_mwkError:MWKEmptyTitleError userInfo:@{ + NSLocalizedDescriptionKey: MWLocalizedString(@"mwk-empty-title-error", nil) + }]; +} + ++ (instancetype)wmf_mwkError:(MWKErrorCode)code userInfo:(NSDictionary*)userInfo { + return [NSError errorWithDomain:MWKErrorDomain code:code userInfo:userInfo]; +} + +@end diff --git a/MediaWikiKit/MediaWikiKitTests/MWKSavedPageListTests.m b/MediaWikiKit/MediaWikiKitTests/MWKSavedPageListTests.m new file mode 100644 index 0000000..b59f8e6 --- /dev/null +++ b/MediaWikiKit/MediaWikiKitTests/MWKSavedPageListTests.m @@ -0,0 +1,67 @@ +// +// MWKSavedPageListTests.m +// Wikipedia +// +// Created by Brian Gerstle on 7/23/15. +// Copyright (c) 2015 Wikimedia Foundation. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import <XCTest/XCTest.h> +#import "MWKSavedPageList.h" + +#define MOCKITO_SHORTHAND 1 +#import <OCMockito/OCMockito.h> + +@interface MWKSavedPageListTests : XCTestCase +@property (nonatomic, strong) MWKSavedPageList* list; +@end + +@implementation MWKSavedPageListTests + +- (void)setUp { + self.list = [[MWKSavedPageList alloc] init]; +} + +- (MWKSavedPageEntry*)entryWithTitleText:(NSString*)titleText { + MWKTitle* title = + [[MWKTitle alloc] initWithSite:[MWKSite siteWithCurrentLocale] normalizedTitle:titleText fragment:nil]; + return [[MWKSavedPageEntry alloc] initWithTitle:title]; +} + +- (void)testTogglingSavedPageReturnsNoAndRemovesFromList { + MWKSavedPageEntry* savedEntry = [self entryWithTitleText:@"foo"]; + [self.list addEntry:savedEntry]; + NSError* saveError; + BOOL postToggleSaveState = [self.list toggleSaveStateForTitle:savedEntry.title error:&saveError]; + XCTAssertNil(saveError); + XCTAssertFalse(postToggleSaveState); + XCTAssertNil([self.list entryForTitle:savedEntry.title]); +} + +- (void)testToggleUnsavedPageReturnsYesAndAddsToList { + MWKSavedPageEntry* unsavedEntry = [self entryWithTitleText:@"foo"]; + NSError* toggleError; + BOOL postToggleSaveState = [self.list toggleSaveStateForTitle:unsavedEntry.title error:&toggleError]; + XCTAssertNil(toggleError); + XCTAssertTrue(postToggleSaveState); + XCTAssertEqualObjects([self.list entryForTitle:unsavedEntry.title], unsavedEntry); +} + +- (void)testTogglePageWithNilTitleReturnsFalseWithError { + NSError* toggleError; + BOOL postToggleSaveState = [self.list toggleSaveStateForTitle:nil error:&toggleError]; + XCTAssertFalse(postToggleSaveState); + XCTAssertEqual(toggleError.code, MWKEmptyTitleError); +} + +- (void)testTogglePageWithEmptyTitleReturnsFalseWithError { + MWKTitle* emptyTitle = mock([MWKTitle class]); + [given([emptyTitle text]) willReturn:@""]; + NSError* toggleError; + BOOL postToggleSaveState = [self.list toggleSaveStateForTitle:emptyTitle error:&toggleError]; + XCTAssertFalse(postToggleSaveState); + XCTAssertEqual(toggleError.code, MWKEmptyTitleError); +} + +@end diff --git a/Wikipedia.xcodeproj/project.pbxproj b/Wikipedia.xcodeproj/project.pbxproj index ae8fc76..539f453 100644 --- a/Wikipedia.xcodeproj/project.pbxproj +++ b/Wikipedia.xcodeproj/project.pbxproj @@ -244,6 +244,9 @@ BC5FE5751B1DFF5400273BC0 /* ArticleFetcherTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5FE5741B1DFF5400273BC0 /* ArticleFetcherTests.m */; }; BC69C3141AB0C1FF0090B039 /* WMFImageInfoController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC69C3131AB0C1FF0090B039 /* WMFImageInfoController.m */; }; BC6BF4001B19213600362968 /* XCTestCase+WMFLocaleTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = BC6BF3FE1B19209900362968 /* XCTestCase+WMFLocaleTesting.m */; }; + BC725EC71B6111E600E0A64C /* NSError+MWKErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = BC725EC61B6111E600E0A64C /* NSError+MWKErrors.m */; }; + BC725ECA1B617E1A00E0A64C /* WMFSafeAssignTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC725EC91B617E1A00E0A64C /* WMFSafeAssignTests.m */; }; + BC725ECC1B6182C800E0A64C /* MWKSavedPageListTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC725ECB1B6182C800E0A64C /* MWKSavedPageListTests.m */; }; BC7DFCD61AA4E5FE000035C3 /* WMFImageURLParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7DFCD51AA4E5FE000035C3 /* WMFImageURLParsing.m */; }; BC7E4A521B34B53E00EECD8B /* MWKLanguageLinkControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7E4A511B34B53E00EECD8B /* MWKLanguageLinkControllerTests.m */; }; BC86B9361A92966B00B4C039 /* AFHTTPRequestOperationManager+UniqueRequests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC86B9351A92966B00B4C039 /* AFHTTPRequestOperationManager+UniqueRequests.m */; }; @@ -796,6 +799,11 @@ BC6BF3FD1B19209900362968 /* XCTestCase+WMFLocaleTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "XCTestCase+WMFLocaleTesting.h"; sourceTree = "<group>"; }; BC6BF3FE1B19209900362968 /* XCTestCase+WMFLocaleTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "XCTestCase+WMFLocaleTesting.m"; sourceTree = "<group>"; }; BC6FEAE01A9B7EFD00A1D890 /* WMFCodingStyle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFCodingStyle.m; sourceTree = "<group>"; }; + BC725EC51B6111E600E0A64C /* NSError+MWKErrors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+MWKErrors.h"; sourceTree = "<group>"; }; + BC725EC61B6111E600E0A64C /* NSError+MWKErrors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+MWKErrors.m"; sourceTree = "<group>"; }; + BC725EC81B61255400E0A64C /* WMFOutParamUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMFOutParamUtils.h; sourceTree = "<group>"; }; + BC725EC91B617E1A00E0A64C /* WMFSafeAssignTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFSafeAssignTests.m; sourceTree = "<group>"; }; + BC725ECB1B6182C800E0A64C /* MWKSavedPageListTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MWKSavedPageListTests.m; sourceTree = "<group>"; }; BC7A4A231B17B3510003E73E /* NSObjectUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSObjectUtilities.h; sourceTree = "<group>"; }; BC7ACB621AB34C9C00791497 /* WMFAsyncTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WMFAsyncTestCase.h; path = ../WMFAsyncTestCase.h; sourceTree = "<group>"; }; BC7ACB631AB34C9C00791497 /* WMFAsyncTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WMFAsyncTestCase.m; path = ../WMFAsyncTestCase.m; sourceTree = "<group>"; }; @@ -2149,6 +2157,7 @@ BC092BA61B19189100093C59 /* MWKSiteInfoFetcherTests.m */, BC7E4A511B34B53E00EECD8B /* MWKLanguageLinkControllerTests.m */, BC905BCF1B60391F0010227E /* MWKLanguageLinkFetcherTests.m */, + BC725EC91B617E1A00E0A64C /* WMFSafeAssignTests.m */, ); name = Tests; path = WikipediaUnitTests; @@ -2291,6 +2300,8 @@ BCB6697C1A83F6C300C7B1FE /* Metadata classes */, BCB669951A83F6C300C7B1FE /* Data classes */, BCB669A21A83F6C300C7B1FE /* Data i/o */, + BC725EC51B6111E600E0A64C /* NSError+MWKErrors.h */, + BC725EC61B6111E600E0A64C /* NSError+MWKErrors.m */, ); name = MediaWikiKit; path = MediaWikiKit/MediaWikiKit; @@ -2316,6 +2327,7 @@ BCFE02691B41ABB5003752B7 /* MWKHistoryListCorruptDataTests.m */, BCFE026E1B41B482003752B7 /* MWKSavedPageListCorruptDataTests.m */, BCFE02771B41FA12003752B7 /* MWKHistoryListPerformanceTests.m */, + BC725ECB1B6182C800E0A64C /* MWKSavedPageListTests.m */, ); name = MediaWikiKitTests; path = ../MediaWikiKit/MediaWikiKitTests; @@ -2444,6 +2456,7 @@ BC7A4A231B17B3510003E73E /* NSObjectUtilities.h */, BC092B971B18E8AF00093C59 /* NSString+WMFPageUtilities.h */, BC092B951B18E89200093C59 /* NSString+WMFPageUtilities.m */, + BC725EC81B61255400E0A64C /* WMFOutParamUtils.h */, ); path = "mw-utils"; sourceTree = "<group>"; @@ -2985,6 +2998,7 @@ BC0FED731AAA026C002488D7 /* NSMutableDictionary+MaybeSetTests.m in Sources */, 048830D31AB775E3005BF3A1 /* UIScrollView+WMFScrollsToTop.m in Sources */, BC0FED6E1AAA0268002488D7 /* MWKImageListTests.m in Sources */, + BC725ECC1B6182C800E0A64C /* MWKSavedPageListTests.m in Sources */, BC23759E1AB8928600B0BAA8 /* WMFDateFormatterTests.m in Sources */, BC0FED771AAA026C002488D7 /* WMFImageURLParsingTests.m in Sources */, BCEC778F1AC9AEC800D9DDA5 /* MWKImage+AssociationTestUtils.m in Sources */, @@ -3023,6 +3037,7 @@ 04F122671ACB818F002FC3B5 /* NSString+FormattedAttributedString.m in Sources */, BC0FED711AAA026C002488D7 /* WMFJoinedPropertyParametersTests.m in Sources */, BC0FED6A1AAA0268002488D7 /* MWKDataStorePathTests.m in Sources */, + BC725ECA1B617E1A00E0A64C /* WMFSafeAssignTests.m in Sources */, BC0FED761AAA026C002488D7 /* NSString+WMFHTMLParsingTests.m in Sources */, BCA676571AC05FE200A16160 /* XCTestCase+WMFBundleConvenience.m in Sources */, BC0FED671AAA0268002488D7 /* MWKTitleTests.m in Sources */, @@ -3093,6 +3108,7 @@ 043F18E518D9691D00D8489A /* UINavigationController+TopActionSheet.m in Sources */, 044396231A3D33030081557D /* UICollectionViewCell+DynamicCellHeight.m in Sources */, 04414DDF1A1420EB00A41B4E /* WikiDataShortDescriptionFetcher.m in Sources */, + BC725EC71B6111E600E0A64C /* NSError+MWKErrors.m in Sources */, 0EFB0F1F1B31EE2D00D05C08 /* DiscoveryContext.m in Sources */, 04D686F61AB2949C0009B44A /* MenuLabel.m in Sources */, 04D308281998A8AA0034F106 /* NearbyThumbnailView.m in Sources */, diff --git a/Wikipedia/EventLogging/SavedPagesFunnel.h b/Wikipedia/EventLogging/SavedPagesFunnel.h index b370f62..cf5d970 100644 --- a/Wikipedia/EventLogging/SavedPagesFunnel.h +++ b/Wikipedia/EventLogging/SavedPagesFunnel.h @@ -12,7 +12,8 @@ @property NSString* appInstallId; -- (id)init; ++ (void)logStateChange:(BOOL)didSave; + /** * Log the saving of a new page to the saved set. diff --git a/Wikipedia/EventLogging/SavedPagesFunnel.m b/Wikipedia/EventLogging/SavedPagesFunnel.m index 21a1a74..612e899 100644 --- a/Wikipedia/EventLogging/SavedPagesFunnel.m +++ b/Wikipedia/EventLogging/SavedPagesFunnel.m @@ -13,7 +13,7 @@ @implementation SavedPagesFunnel -- (id)init { +- (instancetype)init { // http://meta.wikimedia.org/wiki/Schema:MobileWikiAppSavedPages self = [super initWithSchema:@"MobileWikiAppSavedPages" version:10375480]; if (self) { @@ -22,6 +22,15 @@ return self; } ++ (void)logStateChange:(BOOL)didSave { + SavedPagesFunnel* funnel = [self new]; + if (didSave) { + [funnel logSaveNew]; + } else { + [funnel logDelete]; + } +} + - (void)logSaveNew { [self log:@{@"action": @"savenew"}]; } diff --git a/Wikipedia/Global.h b/Wikipedia/Global.h index b6bca8a..e1df3f1 100644 --- a/Wikipedia/Global.h +++ b/Wikipedia/Global.h @@ -10,6 +10,7 @@ #import "WMFGCDHelpers.h" #import "NSObjectUtilities.h" +#import "WMFOutParamUtils.h" #import "RootViewController.h" #import "CenterNavController.h" diff --git a/Wikipedia/Session/SessionSingleton.h b/Wikipedia/Session/SessionSingleton.h index aa86bb7..2afbaef 100644 --- a/Wikipedia/Session/SessionSingleton.h +++ b/Wikipedia/Session/SessionSingleton.h @@ -85,4 +85,12 @@ // file is known to be associated with an article title. @property (strong, nonatomic) NSMutableDictionary* titleToTempDirThumbURLMap; +/** + * Toggle save state of the current article, if there is one. + * @param error Out-param of any error that occurred while toggling save state and saving. + * @return The new saved state of the currentArticle (i.e. `YES` if saved, `NO` if not). + * @see -[MWKSavedPageList toggleSaveStateForTitle:error:] + */ +- (BOOL)toggleSaveStateForCurrentArticle:(NSError* __autoreleasing*)error; + @end diff --git a/Wikipedia/Session/SessionSingleton.m b/Wikipedia/Session/SessionSingleton.m index e5cf3a7..ca63426 100644 --- a/Wikipedia/Session/SessionSingleton.m +++ b/Wikipedia/Session/SessionSingleton.m @@ -180,4 +180,23 @@ [[QueuesSingleton sharedInstance] reset]; } +#pragma mark - Save + +- (BOOL)toggleSaveStateForCurrentArticle:(NSError* __autoreleasing*)error { + NSError* toggleSaveError; + BOOL didSave = [self.userDataStore.savedPageList toggleSaveStateForTitle:self.currentArticle.title + error:&toggleSaveError]; + if (toggleSaveError) { + WMFSafeAssign(error, toggleSaveError); + return NO; + } + [self.userDataStore save:&toggleSaveError]; + if (toggleSaveError) { + WMFSafeAssign(error, toggleSaveError); + return NO; + } + [SavedPagesFunnel logStateChange:didSave]; + return didSave; +} + @end diff --git a/Wikipedia/View Controllers/WebView/WebViewController.m b/Wikipedia/View Controllers/WebView/WebViewController.m index da8259f..efbe623 100644 --- a/Wikipedia/View Controllers/WebView/WebViewController.m +++ b/Wikipedia/View Controllers/WebView/WebViewController.m @@ -966,34 +966,19 @@ #pragma Saved Pages - (void)saveCurrentPage { - MWKTitle* title = self.session.currentArticle.title; - MWKUserDataStore* store = self.session.userDataStore; - MWKSavedPageList* list = store.savedPageList; - MWKSavedPageEntry* entry = [list entryForTitle:title]; - - SavedPagesFunnel* funnel = [[SavedPagesFunnel alloc] init]; - - if (entry == nil) { - // Show alert. - [self showPageSavedAlertMessageForTitle:title.text]; - - // Actually perform the save. - entry = [[MWKSavedPageEntry alloc] initWithTitle:title]; - [list addEntry:entry]; - - [store save]; - [funnel logSaveNew]; + NSError* error; + BOOL didSave = [self.session toggleSaveStateForCurrentArticle:&error]; + if (error) { + [self showAlert:error.localizedDescription type:ALERT_TYPE_TOP duration:2.f]; + } else if (didSave) { + [self showPageSavedAlertMessageForTitle:self.session.currentArticle.title.text]; } else { - // Unsave! - [list removeEntry:entry]; - [store save]; - [self fadeAlert]; - [funnel logDelete]; } } - (void)showPageSavedAlertMessageForTitle:(NSString*)title { + NSParameterAssert(title.length); // First show saved message. NSString* savedMessage = MWLocalizedString(@"share-menu-page-saved", nil); diff --git a/Wikipedia/mw-utils/WMFOutParamUtils.h b/Wikipedia/mw-utils/WMFOutParamUtils.h new file mode 100644 index 0000000..73d42c3 --- /dev/null +++ b/Wikipedia/mw-utils/WMFOutParamUtils.h @@ -0,0 +1,36 @@ +// +// WMFOutParamUtils.h +// Wikipedia +// +// Created by Brian Gerstle on 7/23/15. +// Copyright (c) 2015 Wikimedia Foundation. All rights reserved. +// + +#ifndef Wikipedia_WMFOutParamUtils_h +#define Wikipedia_WMFOutParamUtils_h + +/** + * Safely assign a value to an outParam (or any other double pointer). + * + * For example: + @code + - (BOOL)myMethod:(NSError**)outErr { + NSError* error = [self somethingDangerous]; + if (error) { + WMFSafeAssign(outErr, error); + return NO; + } + return YES; + } + @endcode + * + * @note This needs to be a macro due to problems casting between ObjC & C indirect pointers. + */ +#define WMFSafeAssign(outParam, value) \ + do { \ + if (outParam != NULL) { \ + *(outParam) = (value); \ + } \ + } while (0) + +#endif diff --git a/WikipediaUnitTests/WMFSafeAssignTests.m b/WikipediaUnitTests/WMFSafeAssignTests.m new file mode 100644 index 0000000..173339e --- /dev/null +++ b/WikipediaUnitTests/WMFSafeAssignTests.m @@ -0,0 +1,37 @@ +// +// WMFSafeAssignTests.m +// Wikipedia +// +// Created by Brian Gerstle on 7/23/15. +// Copyright (c) 2015 Wikimedia Foundation. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import <XCTest/XCTest.h> +#import "WMFOutParamUtils.h" + +@interface WMFSafeAssignTests : XCTestCase + +@end + +@implementation WMFSafeAssignTests + +- (void)assignValue:(id)value toOutParam:(NSObject**)outObj { + WMFSafeAssign(outObj, value); +} + +- (void)testAssigningNilToNil { + XCTAssertNoThrow([self assignValue:nil toOutParam:nil]); +} + +- (void)testAssigningValueToRef { + id outValue; + XCTAssertNoThrow([self assignValue:@1 toOutParam:&outValue]); + XCTAssertEqualObjects(@1, outValue); +} + +- (void)testAssigningValueToNil { + XCTAssertNoThrow([self assignValue:@1 toOutParam:nil]); +} + +@end -- To view, visit https://gerrit.wikimedia.org/r/226644 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I8f52107717fd301c89bdd0e3022e53d1862fbcd2 Gerrit-PatchSet: 1 Gerrit-Project: apps/ios/wikipedia Gerrit-Branch: master Gerrit-Owner: Bgerstle <bgers...@wikimedia.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits