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

Reply via email to