Fjalapeno has uploaded a new change for review. https://gerrit.wikimedia.org/r/223194
Change subject: Creating Fetcher to encapsulate async article fetching logic ...................................................................... Creating Fetcher to encapsulate async article fetching logic T104590 Created WMFArticleFetcher New fetcher wraps ArticleFetcher and adds promise API Refactored WebVC to use new WMFArticleFetcher Refactored saved pages fetcher to handle Article Fetcher API changes Added new NSError for redirects Removed some reliance of the Article Fetcher on the session singleton (but not all) Removed Article Fetcher code that assumes "singleton" status of the article fetcher Removed "titleToTempDirThumbURLMap" from session singleton (images are expected to be saved in the datastore) Removed nearby code that did the above and filed a ticket to re-add Changed article fetcher API to separate initialization from fetching Changed article fetcher to accept titles (instead of articles) Change-Id: I7b5200f791bd4485cedab88b98344204531bb16a --- M NSError+WMFExtensions.h M NSError+WMFExtensions.m M Pods/Pods.xcodeproj/xcshareddata/xcschemes/Pods-WikipediaUnitTests-SDWebImage.xcscheme M Wikipedia.xcodeproj/project.pbxproj M Wikipedia/Networking/Fetchers/ArticleFetcher.h M Wikipedia/Networking/Fetchers/ArticleFetcher.m M Wikipedia/Networking/Fetchers/NearbyFetcher.m M Wikipedia/Networking/Fetchers/SavedArticlesFetcher.m M Wikipedia/Networking/Fetchers/SearchResultFetcher.h M Wikipedia/Networking/Fetchers/SearchResultFetcher.m M Wikipedia/Session/SessionSingleton.h M Wikipedia/Session/SessionSingleton.m A Wikipedia/UI-V5/WMFArticleFetcher.h A Wikipedia/UI-V5/WMFArticleFetcher.m M Wikipedia/UI-V5/WMFArticleViewController.m M Wikipedia/View Controllers/WebView/WebViewController.m M Wikipedia/View Controllers/WebView/WebViewController_Private.h 17 files changed, 307 insertions(+), 266 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/apps/ios/wikipedia refs/changes/94/223194/1 diff --git a/NSError+WMFExtensions.h b/NSError+WMFExtensions.h index ca8f88c..974f2b3 100644 --- a/NSError+WMFExtensions.h +++ b/NSError+WMFExtensions.h @@ -1,16 +1,30 @@ #import <Foundation/Foundation.h> +@class MWKTitle; + extern NSString* const WMFErrorDomain; +extern NSString* const WMFRedirectTitleKey; typedef NS_ENUM(NSInteger, WMFErrorType) { WMFErrorTypeStringLength, WMFErrorTypeStringMissingParameter, + WMFErrorTypeRedirected, + }; @interface NSError (WMFExtensions) + (NSError*)wmf_errorWithType:(WMFErrorType)type userInfo:(NSDictionary*)userInfo; ++ (NSError*)wmf_redirectedErrorWithTitle:(MWKTitle*)redirectedtitle; + +@end + + +@interface NSDictionary (WMFErrorExtensions) + +- (MWKTitle*)wmf_redirectTitle; + @end diff --git a/NSError+WMFExtensions.m b/NSError+WMFExtensions.m index 14e11ad..bc8b3e2 100644 --- a/NSError+WMFExtensions.m +++ b/NSError+WMFExtensions.m @@ -2,6 +2,7 @@ #import "NSError+WMFExtensions.h" NSString* const WMFErrorDomain = @"WMFErrorDomain"; +NSString* const WMFRedirectTitleKey = @"WMFRedirectTitleKey"; @implementation NSError (WMFExtensions) @@ -10,4 +11,20 @@ return [NSError errorWithDomain:WMFErrorDomain code:type userInfo:userInfo]; } ++ (NSError*)wmf_redirectedErrorWithTitle:(MWKTitle*)redirectedTitle{ + + return [self wmf_errorWithType:WMFErrorTypeRedirected userInfo:redirectedTitle ? @{WMFRedirectTitleKey : redirectedTitle}: nil]; + +} + + +@end + + +@implementation NSDictionary (WMFErrorExtensions) + +- (MWKTitle*)wmf_redirectTitle{ + return self[WMFRedirectTitleKey]; +} + @end diff --git a/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Pods-WikipediaUnitTests-SDWebImage.xcscheme b/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Pods-WikipediaUnitTests-SDWebImage.xcscheme index be30f81..7c517da 100644 --- a/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Pods-WikipediaUnitTests-SDWebImage.xcscheme +++ b/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Pods-WikipediaUnitTests-SDWebImage.xcscheme @@ -14,7 +14,7 @@ buildForAnalyzing = "YES"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "92F8EB23140D42C02EBABE71" + BlueprintIdentifier = "DAA05C73B23753A7EC46363D" BuildableName = "libPods-WikipediaUnitTests-SDWebImage.a" BlueprintName = "Pods-WikipediaUnitTests-SDWebImage" ReferencedContainer = "container:Pods.xcodeproj"> @@ -39,15 +39,6 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" allowLocationSimulation = "YES"> - <MacroExpansion> - <BuildableReference - BuildableIdentifier = "primary" - BlueprintIdentifier = "5907BCD658CC44B83B452185" - BuildableName = "libPods-WikipediaUnitTests-SDWebImage.a" - BlueprintName = "Pods-WikipediaUnitTests-SDWebImage" - ReferencedContainer = "container:Pods.xcodeproj"> - </BuildableReference> - </MacroExpansion> <AdditionalOptions> </AdditionalOptions> </LaunchAction> diff --git a/Wikipedia.xcodeproj/project.pbxproj b/Wikipedia.xcodeproj/project.pbxproj index 28a062c..2d01adc 100644 --- a/Wikipedia.xcodeproj/project.pbxproj +++ b/Wikipedia.xcodeproj/project.pbxproj @@ -187,6 +187,7 @@ 08D631F71A69B1AB00D87AD0 /* WMFImageGalleryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 08D631F61A69B1AB00D87AD0 /* WMFImageGalleryViewController.m */; }; 0E2B06F61B2CE45800EA2F53 /* WMFSavedPagesDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E2B06F51B2CE45800EA2F53 /* WMFSavedPagesDataSource.m */; }; 0E2B07021B2D1DE200EA2F53 /* WMFBottomStackLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E2B07011B2D1DE200EA2F53 /* WMFBottomStackLayout.m */; }; + 0E34AC571B45DC9500475A1A /* WMFArticleFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E34AC561B45DC9500475A1A /* WMFArticleFetcher.m */; }; 0E366B361B2F176700ABFB86 /* WMFOffScreenFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E366B351B2F176700ABFB86 /* WMFOffScreenFlowLayout.m */; }; 0E366B3A1B2F33BC00ABFB86 /* WMFSearchResults.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E366B391B2F33BC00ABFB86 /* WMFSearchResults.m */; }; 0E366B3F1B2F5C4500ABFB86 /* WMFSearchFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E366B3E1B2F5C4500ABFB86 /* WMFSearchFetcher.m */; }; @@ -740,6 +741,8 @@ 0E2B06F51B2CE45800EA2F53 /* WMFSavedPagesDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFSavedPagesDataSource.m; sourceTree = "<group>"; }; 0E2B07001B2D1DE200EA2F53 /* WMFBottomStackLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFBottomStackLayout.h; sourceTree = "<group>"; }; 0E2B07011B2D1DE200EA2F53 /* WMFBottomStackLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFBottomStackLayout.m; sourceTree = "<group>"; }; + 0E34AC551B45DC9500475A1A /* WMFArticleFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFArticleFetcher.h; sourceTree = "<group>"; }; + 0E34AC561B45DC9500475A1A /* WMFArticleFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFArticleFetcher.m; sourceTree = "<group>"; }; 0E366B341B2F176700ABFB86 /* WMFOffScreenFlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFOffScreenFlowLayout.h; sourceTree = "<group>"; }; 0E366B351B2F176700ABFB86 /* WMFOffScreenFlowLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFOffScreenFlowLayout.m; sourceTree = "<group>"; }; 0E366B381B2F33BC00ABFB86 /* WMFSearchResults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFSearchResults.h; sourceTree = "<group>"; }; @@ -2190,6 +2193,8 @@ children = ( 0E94AFF41B209882000BC5EA /* WMFArticleViewController.h */, 0E94AFF51B209882000BC5EA /* WMFArticleViewController.m */, + 0E34AC551B45DC9500475A1A /* WMFArticleFetcher.h */, + 0E34AC561B45DC9500475A1A /* WMFArticleFetcher.m */, ); name = Article; sourceTree = "<group>"; @@ -3228,6 +3233,7 @@ BCA96E771AAA35EE009A61FA /* UIView+WMFDefaultNib.m in Sources */, 04414DDB1A140FAF00A41B4E /* SearchDidYouMeanButton.m in Sources */, 0E94AFFB1B20A22C000BC5EA /* WMFStyleManager.m in Sources */, + 0E34AC571B45DC9500475A1A /* WMFArticleFetcher.m in Sources */, 040D83591AB0ECFD000896D5 /* WMFCenteredPathView.m in Sources */, BCB669B41A83F6C400C7B1FE /* MWKArticle.m in Sources */, 0E2B06F61B2CE45800EA2F53 /* WMFSavedPagesDataSource.m in Sources */, diff --git a/Wikipedia/Networking/Fetchers/ArticleFetcher.h b/Wikipedia/Networking/Fetchers/ArticleFetcher.h index 941a4ef..1cf372d 100644 --- a/Wikipedia/Networking/Fetchers/ArticleFetcher.h +++ b/Wikipedia/Networking/Fetchers/ArticleFetcher.h @@ -4,7 +4,8 @@ #import <Foundation/Foundation.h> #import "FetcherBase.h" -@class Article, AFHTTPRequestOperationManager, ArticleFetcher, MWKArticle; +@class AFHTTPRequestOperationManager, ArticleFetcher, AFHTTPRequestOperation, MWKTitle; + @protocol ArticleFetcherDelegate <FetchFinishedDelegate> @@ -17,12 +18,13 @@ @interface ArticleFetcher : FetcherBase -@property (strong, nonatomic, readonly) MWKArticle* article; +@property (nonatomic, strong, readonly) MWKDataStore* dataStore; +@property (nonatomic, strong, readonly) MWKTitle* title; -// Kick-off method. Results are reported to "delegate" via the FetchFinishedDelegate protocol method. -- (instancetype)initAndFetchSectionsForArticle:(MWKArticle*)articleStore - withManager:(AFHTTPRequestOperationManager*)manager - thenNotifyDelegate:(id<ArticleFetcherDelegate>)delegate; +- (AFHTTPRequestOperation*)fetchSectionsForTitle:(MWKTitle*)title + inDataStore:(MWKDataStore*)store + withManager:(AFHTTPRequestOperationManager*)manager + thenNotifyDelegate:(id<ArticleFetcherDelegate>)delegate; @property (nonatomic, weak) id<ArticleFetcherDelegate> fetchFinishedDelegate; diff --git a/Wikipedia/Networking/Fetchers/ArticleFetcher.m b/Wikipedia/Networking/Fetchers/ArticleFetcher.m index 29eed6a..bc03874 100644 --- a/Wikipedia/Networking/Fetchers/ArticleFetcher.m +++ b/Wikipedia/Networking/Fetchers/ArticleFetcher.m @@ -4,10 +4,9 @@ #import "ArticleFetcher.h" #import "WMFNetworkUtilities.h" #import "Defines.h" -#import "QueuesSingleton.h" +#import "SessionSingleton.h" #import "NSString+Extras.h" #import "AFHTTPRequestOperationManager.h" -#import "SessionSingleton.h" #import "ReadingActionFunnel.h" #import "NSString+Extras.h" #import "NSObject+Extras.h" @@ -16,14 +15,19 @@ #import <CoreTelephony/CTTelephonyNetworkInfo.h> #import <SystemConfiguration/SystemConfiguration.h> #import "WMFArticleParsing.h" +#import "ZeroConfigState.h" // Reminder: For caching reasons, don't do "(scale * 320)" here. #define LEAD_IMAGE_WIDTH (([UIScreen mainScreen].scale > 1) ? 640 : 320) @interface ArticleFetcher () -// The Article object to be updated with the downloaded data. -@property (nonatomic, strong) MWKArticle* article; +@property (nonatomic, strong, readwrite) MWKDataStore* dataStore; +@property (nonatomic, strong, readwrite) ZeroConfigState* zeroConfig; +@property (nonatomic, assign, readwrite) BOOL sendUsageReports; + + +@property (nonatomic, strong, readwrite) MWKTitle* title; @end @@ -31,47 +35,33 @@ @dynamic fetchFinishedDelegate; -- (instancetype)initAndFetchSectionsForArticle:(MWKArticle*)article - withManager:(AFHTTPRequestOperationManager*)manager - thenNotifyDelegate:(id<ArticleFetcherDelegate> )delegate { - self = [super init]; - assert(article != nil); +- (AFHTTPRequestOperation*)fetchSectionsForTitle:(MWKTitle*)title + inDataStore:(MWKDataStore*)store + withManager:(AFHTTPRequestOperationManager*)manager + thenNotifyDelegate:(id<ArticleFetcherDelegate> )delegate { + assert(title != nil); + assert(store != nil); assert(manager != nil); assert(delegate != nil); - if (self) { - self.article = article; - self.fetchFinishedDelegate = delegate; - [self fetchWithManager:manager]; - } - return self; + self.title = title; + self.dataStore = store; + self.fetchFinishedDelegate = delegate; + return [self fetchWithManager:manager]; } -- (void)fetchWithManager:(AFHTTPRequestOperationManager*)manager { - NSString* title = self.article.title.text; - NSString* subdomain = self.article.title.site.language; - - if (!self.article) { - NSLog(@"NO ARTICLE OBJECT"); - return; +- (AFHTTPRequestOperation*)fetchWithManager:(AFHTTPRequestOperationManager*)manager { + if (!self.title.text) { + return nil; } - if (!self.fetchFinishedDelegate) { - NSLog(@"NO DOWNLOAD DELEGATE"); - return; - } - if (!subdomain) { - NSLog(@"NO DOMAIN"); - return; - } - if (!title) { - NSLog(@"NO TITLE"); - return; + if (!self.title.site.language) { + return nil; } - NSURL* url = [[SessionSingleton sharedInstance] urlForLanguage:subdomain]; + NSURL* url = [[SessionSingleton sharedInstance] urlForLanguage:self.title.site.language]; // First retrieve lead section data, then get the remaining sections data. - NSDictionary* params = [self getParamsForTitle:title]; + NSDictionary* params = [self getParamsForTitle:self.title]; [[MWNetworkActivityIndicatorManager sharedManager] push]; @@ -85,15 +75,13 @@ //NSLog(@"JSON: %@", responseObject); [[MWNetworkActivityIndicatorManager sharedManager] pop]; + MWKArticle* article = [self.dataStore articleWithTitle:self.title]; // Convert the raw NSData response to a dictionary. NSDictionary* responseDictionary = [self dictionaryFromDataResponse:localResponseObject]; - // Clear any MCCMNC header - needed because manager is a singleton. - [self removeMCCMNCHeaderFromRequestSerializer:manager.requestSerializer]; - @try { - [self.article importMobileViewJSON:responseDictionary[@"mobileview"]]; - [self.article save]; + [article importMobileViewJSON:responseDictionary[@"mobileview"]]; + [article save]; }@catch (NSException* e) { NSLog(@"%@", e); NSError* err = [NSError errorWithDomain:@"ArticleFetcher" code:666 userInfo:@{ @"exception": e }]; @@ -101,28 +89,23 @@ return; } - for (int section = 0; section < [self.article.sections count]; section++) { - (void)self.article.sections[section].images; // hack - WMFInjectArticleWithImagesFromSection(self.article, self.article.sections[section].text, section); + for (int section = 0; section < [article.sections count]; section++) { + (void)article.sections[section].images; // hack + WMFInjectArticleWithImagesFromSection(article, article.sections[section].text, section); } - - [self associateThumbFromTempDirWithArticle]; // Update article and section image data. // Reminder: don't recall article save here as it expensively re-writes all section html. - [self.article saveWithoutSavingSectionText]; + [article saveWithoutSavingSectionText]; dispatch_async(dispatch_get_main_queue(), ^{ [self finishWithError:nil - fetchedData:self.article]; + fetchedData:article]; }); }); } failure:^(AFHTTPRequestOperation* operation, NSError* error) { NSLog(@"Error: %@", error); [[MWNetworkActivityIndicatorManager sharedManager] pop]; - - // Clear any MCCMNC header - needed because manager is a singleton. - [self removeMCCMNCHeaderFromRequestSerializer:manager.requestSerializer]; [self finishWithError:error fetchedData:nil]; @@ -141,9 +124,11 @@ [self.fetchFinishedDelegate articleFetcher:self didUpdateProgress:progress]; } }]; + + return operation; } -- (NSDictionary*)getParamsForTitle:(NSString*)title { +- (NSDictionary*)getParamsForTitle:(MWKTitle*)title { NSMutableDictionary* params = @{ @"format": @"json", @"action": @"mobileview", @@ -151,7 +136,7 @@ @"fromtitle", @"index"]), @"noheadings": @"true", @"sections": @"all", - @"page": title, + @"page": title.text, @"thumbwidth": @(LEAD_IMAGE_WIDTH), @"prop": WMFJoinedPropertyParameters(@[@"sections", @"text", @"lastmodified", @"lastmodifiedby", @"languagecount", @"id", @"protection", @"editable", @"displaytitle", @@ -177,6 +162,8 @@ || ([url.relativePath rangeOfString:@"/w/api.php"].location == NSNotFound) ) { + [requestSerializer setValue:nil forHTTPHeaderField:@"X-MCCMNC"]; + return; } else { CTCarrier* mno = [[[CTTelephonyNetworkInfo alloc] init] subscriberCellularProvider]; @@ -207,63 +194,6 @@ [SessionSingleton sharedInstance].zeroConfigState.sentMCCMNC = true; [requestSerializer setValue:mccMnc forHTTPHeaderField:@"X-MCCMNC"]; - - // NSLog(@"%@", mccMnc); - } - } - } -} - -- (void)removeMCCMNCHeaderFromRequestSerializer:(AFHTTPRequestSerializer*)requestSerializer { - [requestSerializer setValue:nil forHTTPHeaderField:@"X-MCCMNC"]; -} - -- (void)associateThumbFromTempDirWithArticle { - BOOL foundThumbInTempDir = NO; - - // Map which search and nearby populates with title/thumb url mappings. - NSDictionary* map = [SessionSingleton sharedInstance].titleToTempDirThumbURLMap; - NSString* title = self.article.title.text; - if (title) { - NSString* thumbURL = map[title]; - if (thumbURL) { - // Associate Search/Nearby thumb url with article.thumbnailURL. - if (thumbURL) { - self.article.thumbnailURL = thumbURL; - } - - if ([[self.article existingImageWithURL:thumbURL] isDownloaded]) { - return; - } - - NSString* cacheFilePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) - firstObject] - stringByAppendingPathComponent:thumbURL.lastPathComponent]; - BOOL isDirectory = NO; - BOOL cachedFileExists = [[NSFileManager defaultManager] fileExistsAtPath:cacheFilePath - isDirectory:&isDirectory]; - if (cachedFileExists) { - NSError* error = nil; - NSData* data = [NSData dataWithContentsOfFile:cacheFilePath options:0 error:&error]; - if (!error) { - // Copy Search/Nearby thumb binary to core data store so it doesn't have to be re-downloaded. - MWKImage* image = [self.article importImageURL:thumbURL sectionId:kMWKArticleSectionNone]; - [self.article importImageData:data image:image]; - foundThumbInTempDir = YES; - } - } - } - } - if (!foundThumbInTempDir) { - MWKImageList* images = self.article.images; - // If no image found in temp dir, use first article image. - if (images.count > 0) { - MWKImage* image = images[0]; - self.article.thumbnailURL = image.sourceURL; - } else { - // If still no image, use article image if there is one. - if (self.article.imageURL) { - self.article.thumbnailURL = self.article.imageURL; } } } diff --git a/Wikipedia/Networking/Fetchers/NearbyFetcher.m b/Wikipedia/Networking/Fetchers/NearbyFetcher.m index 668c5eb..a4c0e27 100644 --- a/Wikipedia/Networking/Fetchers/NearbyFetcher.m +++ b/Wikipedia/Networking/Fetchers/NearbyFetcher.m @@ -67,18 +67,6 @@ NSMutableArray* output = @[].mutableCopy; if (!error) { output = [self getSanitizedResponse:responseObject]; - - // Populate the map so the article fetcher can grab thumb - // from temp dir. - NSMutableDictionary* map = [SessionSingleton sharedInstance].titleToTempDirThumbURLMap; - [map removeAllObjects]; - for (NSDictionary* result in output) { - NSString* title = result[@"title"]; - NSString* thumbUrl = result[@"thumbnail"][@"source"]; - if (title && thumbUrl) { - map[title] = thumbUrl; - } - } } if (output.count == 0) { diff --git a/Wikipedia/Networking/Fetchers/SavedArticlesFetcher.m b/Wikipedia/Networking/Fetchers/SavedArticlesFetcher.m index def23de..69110ee 100644 --- a/Wikipedia/Networking/Fetchers/SavedArticlesFetcher.m +++ b/Wikipedia/Networking/Fetchers/SavedArticlesFetcher.m @@ -62,10 +62,10 @@ self.fetchedArticles = [NSMutableArray array]; for (MWKSavedPageEntry* entry in self.savedPageList) { - MWKArticle* article = [self.dataStore articleWithTitle:entry.title]; - if (entry.title) { - self.fetchersByArticleTitle[entry.title] = [[ArticleFetcher alloc] initAndFetchSectionsForArticle:article withManager:manager thenNotifyDelegate:self]; + ArticleFetcher* fetcher = [[ArticleFetcher alloc] init]; + self.fetchersByArticleTitle[entry.title] = fetcher; + [fetcher fetchSectionsForTitle:entry.title inDataStore:self.dataStore withManager:manager thenNotifyDelegate:self]; } } }); diff --git a/Wikipedia/Networking/Fetchers/SearchResultFetcher.h b/Wikipedia/Networking/Fetchers/SearchResultFetcher.h index 70450e1..53f978d 100644 --- a/Wikipedia/Networking/Fetchers/SearchResultFetcher.h +++ b/Wikipedia/Networking/Fetchers/SearchResultFetcher.h @@ -4,7 +4,9 @@ #import <Foundation/Foundation.h> #import "FetcherBase.h" #import "Defines.h" -#import <AFNetworking/AFHTTPRequestOperation.h> + +@class AFHTTPRequestOperationManager; +@class AFHTTPRequestOperation; typedef NS_ENUM (NSInteger, SearchResultFetcherErrorType) { SEARCH_RESULT_ERROR_UNKNOWN = 0, @@ -26,8 +28,6 @@ SEARCH_REASON_SUPPLEMENT_PREFIX_WITH_FULL_TEXT }; -@class AFHTTPRequestOperationManager; - @interface SearchResultFetcher : FetcherBase @property (nonatomic, strong, readonly) NSString* searchTerm; @@ -37,7 +37,6 @@ @property (nonatomic, strong, readonly) NSArray* searchResults; @property (nonatomic, strong, readonly) NSString* searchSuggestion; -@property (nonatomic, strong, readonly) NSDictionary* articleTitleToImageMap; // Kick-off method. Results are reported to "delegate" via the FetchFinishedDelegate protocol method. - (instancetype)initAndSearchForTerm:(NSString*)searchTerm diff --git a/Wikipedia/Networking/Fetchers/SearchResultFetcher.m b/Wikipedia/Networking/Fetchers/SearchResultFetcher.m index 833ec0e..676bd99 100644 --- a/Wikipedia/Networking/Fetchers/SearchResultFetcher.m +++ b/Wikipedia/Networking/Fetchers/SearchResultFetcher.m @@ -2,7 +2,8 @@ // Copyright (c) 2014 Wikimedia Foundation. Provided under MIT-style license; please copy and modify! #import "SearchResultFetcher.h" -#import "AFHTTPRequestOperationManager.h" +#import <AFNetworking/AFHTTPRequestOperation.h> +#import <AFNetworking/AFHTTPRequestOperationManager.h> #import "MWNetworkActivityIndicatorManager.h" #import "SessionSingleton.h" #import "NSObject+Extras.h" diff --git a/Wikipedia/Session/SessionSingleton.h b/Wikipedia/Session/SessionSingleton.h index aa86bb7..5674f2b 100644 --- a/Wikipedia/Session/SessionSingleton.h +++ b/Wikipedia/Session/SessionSingleton.h @@ -78,11 +78,4 @@ - (NSURL*)urlForLanguage:(NSString*)language __deprecated_msg("Use -[MWKSite apiEndpoint] instead."); -// Search and Nearby fetch thumbnails which are tossed in the tmp dir so we -// don't have to worry about pruning. However, when we then load an article -// we need to yank out the thumb for that article so it can be saved in the -// data store. This dictionary gives us an easy place to see what temp thumb -// file is known to be associated with an article title. -@property (strong, nonatomic) NSMutableDictionary* titleToTempDirThumbURLMap; - @end diff --git a/Wikipedia/Session/SessionSingleton.m b/Wikipedia/Session/SessionSingleton.m index db06fa8..386d589 100644 --- a/Wikipedia/Session/SessionSingleton.m +++ b/Wikipedia/Session/SessionSingleton.m @@ -59,8 +59,6 @@ self.userDataStore = [dataStore userDataStore]; _currentArticleSite = [self lastKnownSite]; - - self.titleToTempDirThumbURLMap = @{}.mutableCopy; } return self; } diff --git a/Wikipedia/UI-V5/WMFArticleFetcher.h b/Wikipedia/UI-V5/WMFArticleFetcher.h new file mode 100644 index 0000000..869489d --- /dev/null +++ b/Wikipedia/UI-V5/WMFArticleFetcher.h @@ -0,0 +1,23 @@ + +#import <Foundation/Foundation.h> + +@class MWKSite; +@class MWKTitle; + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^ WMFArticleFetcherProgress)(CGFloat progress); + +@interface WMFArticleFetcher : NSObject + +@property (nonatomic, strong, readonly) MWKDataStore* dataStore; + +- (instancetype)initWithDataStore:(MWKDataStore*)dataStore; + +- (AnyPromise*)fetchArticleForPageTitle:(MWKTitle*)pageTitle progress:(WMFArticleFetcherProgress)progress; + +- (void)cancelCurrentFetch; + +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/Wikipedia/UI-V5/WMFArticleFetcher.m b/Wikipedia/UI-V5/WMFArticleFetcher.m new file mode 100644 index 0000000..113b40d --- /dev/null +++ b/Wikipedia/UI-V5/WMFArticleFetcher.m @@ -0,0 +1,105 @@ + +#import "WMFArticleFetcher.h" +#import "AFHTTPRequestOperationManager+WMFConfig.h" +#import "ArticleFetcher.h" +#import "Wikipedia-Swift.h" +#import "PromiseKit.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface WMFArticleFetcher ()<ArticleFetcherDelegate> + +@property (nonatomic, strong, readwrite) MWKDataStore* dataStore; + +@property (nonatomic, strong) AFHTTPRequestOperationManager* operationManager; + +@property (nonatomic, strong) ArticleFetcher* fetcher; + +@property (nonatomic, strong, nullable) AFHTTPRequestOperation* operation; +@property (nonatomic, copy, nullable) WMFArticleFetcherProgress progressBlock; +@property (nonatomic, copy, nullable) PMKResolver resolver; + +@end + +@implementation WMFArticleFetcher + +- (instancetype)initWithDataStore:(MWKDataStore*)dataStore { + self = [super init]; + if (self) { + self.dataStore = dataStore; + AFHTTPRequestOperationManager* manager = [AFHTTPRequestOperationManager wmf_createDefaultManager]; + manager.responseSerializer = [AFHTTPResponseSerializer serializer]; + self.operationManager = manager; + } + return self; +} + +- (AnyPromise*)fetchArticleForPageTitle:(MWKTitle*)pageTitle progress:(WMFArticleFetcherProgress)progress { + [self cancelCurrentFetch]; + + self.progressBlock = progress; + + return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { + self.resolver = resolve; + + self.fetcher = [[ArticleFetcher alloc] init]; + self.operation = [self.fetcher fetchSectionsForTitle:pageTitle inDataStore:self.dataStore withManager:self.operationManager thenNotifyDelegate:self]; + + if (self.operation == nil) { + resolve([NSError wmf_errorWithType:WMFErrorTypeStringMissingParameter userInfo:nil]); + [self reset]; + } + }]; +} + +- (void)articleFetcher:(ArticleFetcher*)savedArticlesFetcher + didUpdateProgress:(CGFloat)progress { + if (!self.progressBlock) { + return; + } + + dispatchOnMainQueue(^{ + self.progressBlock(progress); + }); +} + +- (void)fetchFinished:(id)sender + fetchedData:(id)fetchedData + status:(FetchFinalStatus)status + error:(NSError*)error { + if (!self.resolver) { + [self reset]; + return; + } + + MWKArticle* article = fetchedData; + + if (!error) { + MWKTitle* redirectedTitle = article.redirected; + if (redirectedTitle) { + error = [NSError wmf_redirectedErrorWithTitle:redirectedTitle]; + } + } + + if (!error) { + self.resolver(article); + } else { + self.resolver(error); + } + [self reset]; +} + +- (void)cancelCurrentFetch { + [self.operation cancel]; + [self reset]; +} + +- (void)reset { + self.operation = nil; + self.progressBlock = NULL; + self.resolver = nil; +} + +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/Wikipedia/UI-V5/WMFArticleViewController.m b/Wikipedia/UI-V5/WMFArticleViewController.m index 6863bb0..944f325 100644 --- a/Wikipedia/UI-V5/WMFArticleViewController.m +++ b/Wikipedia/UI-V5/WMFArticleViewController.m @@ -1,4 +1,3 @@ - #import "WMFArticleViewController.h" #import <Masonry/Masonry.h> #import "Wikipedia-Swift.h" diff --git a/Wikipedia/View Controllers/WebView/WebViewController.m b/Wikipedia/View Controllers/WebView/WebViewController.m index ba2c546..047aef4 100644 --- a/Wikipedia/View Controllers/WebView/WebViewController.m +++ b/Wikipedia/View Controllers/WebView/WebViewController.m @@ -8,6 +8,7 @@ #import "UIBarButtonItem+WMFButtonConvenience.h" #import "RandomArticleFetcher.h" #import "MWKSiteInfoFetcher.h" +#import "WMFArticleFetcher.h" #import "MWKSiteInfo.h" #import "UIViewController+WMFStoryboardUtilities.h" #import "MWKLanguageLink.h" @@ -43,7 +44,7 @@ @property (nonatomic) BOOL isAnimatingTopAndBottomMenuHidden; @property (readonly, strong, nonatomic) MWKSiteInfoFetcher* siteInfoFetcher; - +@property (strong, nonatomic) WMFArticleFetcher* articleFetcher; @property (strong, nonatomic) UIPopoverController* popover; @property (strong, nonatomic) WMFShareFunnel* shareFunnel; @property (strong, nonatomic) WMFShareOptionsViewController* shareOptionsViewController; @@ -1180,10 +1181,6 @@ #pragma mark Memory -- (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; -} - - (void)updateHistoryDateVisitedForArticleBeingNavigatedFrom { // This is a quick hack to help with the natural back/forward behavior of the case // where you go back and forth from some master article to others. @@ -1199,24 +1196,12 @@ #pragma mark - Article loading -- (void)navigateToPage:(MWKTitle*)title - discoveryMethod:(MWKHistoryDiscoveryMethod)discoveryMethod { - NSString* cleanTitle = title.text; - NSParameterAssert(cleanTitle.length); - - [self hideKeyboard]; - - // Show loading message - //[self showAlert:MWLocalizedString(@"search-loading-section-zero", nil) type:ALERT_TYPE_TOP duration:-1]; - - self.jumpToFragment = title.fragment; - - if (discoveryMethod != MWKHistoryDiscoveryMethodBackForward && discoveryMethod != MWKHistoryDiscoveryMethodReloadFromNetwork && discoveryMethod != MWKHistoryDiscoveryMethodReloadFromCache) { - [self updateHistoryDateVisitedForArticleBeingNavigatedFrom]; +- (WMFArticleFetcher*)articleFetcher { + if (!_articleFetcher) { + _articleFetcher = [[WMFArticleFetcher alloc] initWithDataStore:self.session.dataStore]; } - [self retrieveArticleForPageTitle:title - discoveryMethod:discoveryMethod]; + return _articleFetcher; } - (void)reloadCurrentArticleFromNetwork { @@ -1236,23 +1221,24 @@ } } -- (void)cancelArticleLoading { - [[QueuesSingleton sharedInstance].articleFetchManager.operationQueue cancelAllOperations]; -} +- (void)navigateToPage:(MWKTitle*)title + discoveryMethod:(MWKHistoryDiscoveryMethod)discoveryMethod { + NSString* cleanTitle = title.text; + NSParameterAssert(cleanTitle.length); -- (void)cancelSearchLoading { - [[QueuesSingleton sharedInstance].searchResultsFetchManager.operationQueue cancelAllOperations]; -} + [self hideKeyboard]; -- (void)retrieveArticleForPageTitle:(MWKTitle*)pageTitle - discoveryMethod:(MWKHistoryDiscoveryMethod)discoveryMethod { - // Cancel certain in-progress fetches. [self cancelSearchLoading]; [self cancelArticleLoading]; - self.currentTitle = pageTitle; + if (discoveryMethod != MWKHistoryDiscoveryMethodBackForward && discoveryMethod != MWKHistoryDiscoveryMethodReloadFromNetwork && discoveryMethod != MWKHistoryDiscoveryMethodReloadFromCache) { + [self updateHistoryDateVisitedForArticleBeingNavigatedFrom]; + } MWKArticle* article = [self.session.dataStore articleWithTitle:self.currentTitle]; + + self.jumpToFragment = title.fragment; + self.currentTitle = title; self.session.currentArticle = article; self.session.currentArticleDiscoveryMethod = discoveryMethod; @@ -1275,100 +1261,74 @@ } } - // If article is cached if ([article isCached] && !needsRefresh) { [self displayArticle:self.session.currentArticle.title]; - //[self showAlert:MWLocalizedString(@"search-loading-article-loaded", nil) type:ALERT_TYPE_TOP duration:-1]; [self fadeAlert]; - } else { - [self showProgressViewAnimated:YES]; - self.isFetchingArticle = YES; + return; + } - // "fetchFinished:" above will be notified when articleFetcher has actually retrieved some data. - // Note: cast to void to avoid compiler warning: http://stackoverflow.com/a/7915839 - (void)[[ArticleFetcher alloc] initAndFetchSectionsForArticle:self.session.currentArticle - withManager:[QueuesSingleton sharedInstance].articleFetchManager - thenNotifyDelegate:self]; + [self loadArticleWithTitleFromNetwork:title]; +} + +- (void)cancelArticleLoading { + [self.articleFetcher cancelCurrentFetch]; +} + +- (void)cancelSearchLoading { + [[QueuesSingleton sharedInstance].searchResultsFetchManager.operationQueue cancelAllOperations]; +} + +- (void)loadArticleWithTitleFromNetwork:(MWKTitle*)title { + [self showProgressViewAnimated:YES]; + self.isFetchingArticle = YES; + + [self.articleFetcher fetchArticleForPageTitle:title progress:^(CGFloat progress){ + [self updateProgress:[self totalProgressWithArticleFetcherProgress:progress] animated:YES completion:NULL]; + }].then(^(MWKArticle* article){ + [self handleFetchedArticle:article]; + }).catch(^(NSError* error){ + [self handleFetchArticleError:error]; + }); +} + +- (void)handleFetchedArticle:(MWKArticle*)article { + self.isFetchingArticle = NO; + + [self displayArticle:article.title]; + + [self hideAlert]; +} + +- (void)handleFetchArticleError:(NSError*)error { + MWKTitle* redirect = [[error userInfo] wmf_redirectTitle]; + + if (redirect) { + [self handleRedirectForTitle:redirect]; + } else { + self.isFetchingArticle = NO; + + [self displayArticle:self.session.currentArticle.title]; + + NSString* errorMsg = error.localizedDescription; + [self showAlert:errorMsg type:ALERT_TYPE_TOP duration:-1]; } } -- (void)presentPopupForArticle:(MWKArticle*)article { - WMFArticleViewController* vc = [WMFArticleViewController articleViewControllerFromDefaultStoryBoard]; - vc.savedPages = self.session.userDataStore.savedPageList; - vc.article = article; - vc.contentTopInset = 64.0; +- (void)handleRedirectForTitle:(MWKTitle*)title { + MWKHistoryEntry* history = [self.session.userDataStore.historyList entryForTitle:title]; + MWKHistoryDiscoveryMethod discoveryMethod = + (history) ? history.discoveryMethod : MWKHistoryDiscoveryMethodSearch; - self.popupTransition = [[WMFArticlePopupTransition alloc] initWithPresentingViewController:self presentedViewController:vc contentScrollView:nil]; - self.popupTransition.nonInteractiveDuration = 0.5; - vc.transitioningDelegate = self.popupTransition; - vc.modalPresentationStyle = UIModalPresentationCustom; - - [self presentViewController:vc animated:YES completion:NULL]; + [self navigateToPage:title discoveryMethod:discoveryMethod]; } -#pragma mark - ArticleFetcherDelegate - -- (void)articleFetcher:(ArticleFetcher*)savedArticlesFetcher - didUpdateProgress:(CGFloat)progress { - [self updateProgress:[self totalProgressWithArticleFetcherProgress:progress] animated:YES completion:NULL]; -} +#pragma mark - FetchFinishedDelegate - (void)fetchFinished:(id)sender fetchedData:(id)fetchedData status:(FetchFinalStatus)status error:(NSError*)error { - if ([sender isKindOfClass:[ArticleFetcher class]]) { - MWKArticle* article = self.session.currentArticle; - - switch (status) { - case FETCH_FINAL_STATUS_SUCCEEDED: - { - // Redirect if necessary. - MWKTitle* redirectedTitle = article.redirected; - if (redirectedTitle) { - // Get discovery method for call to "retrieveArticleForPageTitle:". - // There should only be a single history item (at most). - MWKHistoryEntry* history = [self.session.userDataStore.historyList entryForTitle:article.title]; - // Get the article's discovery method. - MWKHistoryDiscoveryMethod discoveryMethod = - (history) ? history.discoveryMethod : MWKHistoryDiscoveryMethodSearch; - - // Redirect! - [self retrieveArticleForPageTitle:redirectedTitle - discoveryMethod:discoveryMethod]; - return; - } - - self.isFetchingArticle = NO; - - // Update the toc and web view. - [self displayArticle:article.title]; - - [self hideAlert]; - } - break; - - case FETCH_FINAL_STATUS_FAILED: - { - self.isFetchingArticle = NO; - - [self displayArticle:article.title]; - - NSString* errorMsg = error.localizedDescription; - [self showAlert:errorMsg type:ALERT_TYPE_TOP duration:-1]; - } - break; - - case FETCH_FINAL_STATUS_CANCELLED: - { - self.isFetchingArticle = NO; - } - break; - - default: - break; - } - } else if ([sender isKindOfClass:[WikipediaZeroMessageFetcher class]]) { + if ([sender isKindOfClass:[WikipediaZeroMessageFetcher class]]) { switch (status) { case FETCH_FINAL_STATUS_SUCCEEDED: { @@ -1421,6 +1381,22 @@ } } +#pragma mark - Article Popup + +- (void)presentPopupForArticle:(MWKArticle*)article { + WMFArticleViewController* vc = [WMFArticleViewController articleViewControllerFromDefaultStoryBoard]; + vc.savedPages = self.session.userDataStore.savedPageList; + vc.article = article; + vc.contentTopInset = 64.0; + + self.popupTransition = [[WMFArticlePopupTransition alloc] initWithPresentingViewController:self presentedViewController:vc contentScrollView:nil]; + self.popupTransition.nonInteractiveDuration = 0.5; + vc.transitioningDelegate = self.popupTransition; + vc.modalPresentationStyle = UIModalPresentationCustom; + + [self presentViewController:vc animated:YES completion:NULL]; +} + #pragma mark - Lead image - (NSString*)leadImageGetHtml { diff --git a/Wikipedia/View Controllers/WebView/WebViewController_Private.h b/Wikipedia/View Controllers/WebView/WebViewController_Private.h index 431cb65..f87d5b5 100644 --- a/Wikipedia/View Controllers/WebView/WebViewController_Private.h +++ b/Wikipedia/View Controllers/WebView/WebViewController_Private.h @@ -42,7 +42,6 @@ #import "WikiGlyphLabel.h" #import "NSString+FormattedAttributedString.h" #import "SavedPagesFunnel.h" -#import "ArticleFetcher.h" #import "AssetsFileFetcher.h" #import "DataMigrationProgressViewController.h" -- To view, visit https://gerrit.wikimedia.org/r/223194 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I7b5200f791bd4485cedab88b98344204531bb16a Gerrit-PatchSet: 1 Gerrit-Project: apps/ios/wikipedia Gerrit-Branch: 5.0 Gerrit-Owner: Fjalapeno <cfl...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits