From 3affe26fea393e8c1926fd5a3707d912726f66a1 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Wed, 14 Dec 2022 10:29:52 +1300 Subject: [PATCH 1/8] Re-implement `RemoteReaderPost.readingTimeForWordCount` --- WordPressKit.xcodeproj/project.pbxproj | 12 ++++++++---- WordPressKit/ReaderPostsEnvelope.swift | 17 +++++++++++++++++ WordPressKit/RemoteReaderPost.m | 14 +------------- WordPressKit/RemoteReaderPost.swift | 23 ++++++++++++----------- WordPressKitTests/RemoteReaderPostTests.m | 13 +++++-------- 5 files changed, 43 insertions(+), 36 deletions(-) create mode 100644 WordPressKit/ReaderPostsEnvelope.swift diff --git a/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit.xcodeproj/project.pbxproj index 93fec4ce..a32f2993 100644 --- a/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit.xcodeproj/project.pbxproj @@ -132,6 +132,7 @@ 4A68E3D329406AA0004AC3DC /* RemoteMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3D029406AA0004AC3DC /* RemoteMenu.swift */; }; 4A68E3D429406AA0004AC3DC /* RemoteMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3D129406AA0004AC3DC /* RemoteMenuItem.swift */; }; 4A68E3D529406AA0004AC3DC /* RemoteMenuLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3D229406AA0004AC3DC /* RemoteMenuLocation.swift */; }; + 4A68E40B294922A8004AC3DC /* RemoteReaderPost.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E40A294922A8004AC3DC /* RemoteReaderPost.swift */; }; 57BCD3D426209D9500292CB3 /* AppTransportSecuritySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57BCD3D326209D9500292CB3 /* AppTransportSecuritySettings.swift */; }; 730E869F21E44EFD00753E1A /* WordPressComServiceRemote+SiteVerticals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 730E869E21E44EFD00753E1A /* WordPressComServiceRemote+SiteVerticals.swift */; }; 731BA83621DECD61000FDFCD /* SiteCreationRequestEncodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 731BA83521DECD61000FDFCD /* SiteCreationRequestEncodingTests.swift */; }; @@ -348,7 +349,7 @@ 82FFBF521F45F04100F4573F /* RemoteBlogJetpackMonitorSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82FFBF511F45F04100F4573F /* RemoteBlogJetpackMonitorSettings.swift */; }; 82FFBF561F460DD400F4573F /* BlogJetpackSettingsServiceRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82FFBF551F460DD400F4573F /* BlogJetpackSettingsServiceRemote.swift */; }; 8B074A4E27AC2FFD003A2EB8 /* dashboard-400-invalid-card.json in Resources */ = {isa = PBXBuildFile; fileRef = 8B074A4D27AC2FFD003A2EB8 /* dashboard-400-invalid-card.json */; }; - 8B16CE8E25250039007BE5A9 /* RemoteReaderPost.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B16CE8D25250039007BE5A9 /* RemoteReaderPost.swift */; }; + 8B16CE8E25250039007BE5A9 /* ReaderPostsEnvelope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B16CE8D25250039007BE5A9 /* ReaderPostsEnvelope.swift */; }; 8B16CE92252502C4007BE5A9 /* RemoteReaderPostTests+V2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B16CE91252502C4007BE5A9 /* RemoteReaderPostTests+V2.swift */; }; 8B16CE962525045F007BE5A9 /* reader-posts-success.json in Resources */ = {isa = PBXBuildFile; fileRef = 8B16CE952525045F007BE5A9 /* reader-posts-success.json */; }; 8B2F4BE524ABB3C70056C08A /* RemoteReaderPostTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B2F4BE424ABB3C70056C08A /* RemoteReaderPostTests.m */; }; @@ -786,6 +787,7 @@ 4A68E3D029406AA0004AC3DC /* RemoteMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteMenu.swift; sourceTree = ""; }; 4A68E3D129406AA0004AC3DC /* RemoteMenuItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteMenuItem.swift; sourceTree = ""; }; 4A68E3D229406AA0004AC3DC /* RemoteMenuLocation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteMenuLocation.swift; sourceTree = ""; }; + 4A68E40A294922A8004AC3DC /* RemoteReaderPost.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteReaderPost.swift; sourceTree = ""; }; 57BCD3D326209D9500292CB3 /* AppTransportSecuritySettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTransportSecuritySettings.swift; sourceTree = ""; }; 6C2A33D76FD1052D6F30466D /* Pods-WordPressKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WordPressKit/Pods-WordPressKit.debug.xcconfig"; sourceTree = ""; }; 6F2E0CC4FA01B5475A378DA2 /* Pods-WordPressKitTests.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressKitTests.release-alpha.xcconfig"; path = "Pods/Target Support Files/Pods-WordPressKitTests/Pods-WordPressKitTests.release-alpha.xcconfig"; sourceTree = ""; }; @@ -1004,7 +1006,7 @@ 82FFBF511F45F04100F4573F /* RemoteBlogJetpackMonitorSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteBlogJetpackMonitorSettings.swift; sourceTree = ""; }; 82FFBF551F460DD400F4573F /* BlogJetpackSettingsServiceRemote.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlogJetpackSettingsServiceRemote.swift; sourceTree = ""; }; 8B074A4D27AC2FFD003A2EB8 /* dashboard-400-invalid-card.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "dashboard-400-invalid-card.json"; sourceTree = ""; }; - 8B16CE8D25250039007BE5A9 /* RemoteReaderPost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteReaderPost.swift; sourceTree = ""; }; + 8B16CE8D25250039007BE5A9 /* ReaderPostsEnvelope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderPostsEnvelope.swift; sourceTree = ""; }; 8B16CE91252502C4007BE5A9 /* RemoteReaderPostTests+V2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RemoteReaderPostTests+V2.swift"; sourceTree = ""; }; 8B16CE952525045F007BE5A9 /* reader-posts-success.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "reader-posts-success.json"; sourceTree = ""; }; 8B2F4BE424ABB3C70056C08A /* RemoteReaderPostTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RemoteReaderPostTests.m; sourceTree = ""; }; @@ -1948,11 +1950,12 @@ 74E2294F1F1E741B0085F7F2 /* RemotePublicizeConnection.swift */, 74E2294D1F1E73FE0085F7F2 /* RemotePublicizeService.swift */, 7430C9D61F1933200051B8E6 /* RemoteReaderCrossPostMeta.swift */, - 8B16CE8D25250039007BE5A9 /* RemoteReaderPost.swift */, + 8B16CE8D25250039007BE5A9 /* ReaderPostsEnvelope.swift */, 8B2F4BEE24ACCC120056C08A /* RemoteReaderCard.swift */, 8B2F4BF024ACE3C30056C08A /* RemoteReaderInterest.swift */, 7430C9A91F1927C50051B8E6 /* RemoteReaderPost.h */, 7430C9AA1F1927C50051B8E6 /* RemoteReaderPost.m */, + 4A68E40A294922A8004AC3DC /* RemoteReaderPost.swift */, FACBDD1D25ECA7F90026705B /* RemoteReaderSimplePost.swift */, 4A68E3DC294070A7004AC3DC /* RemoteReaderSite.swift */, 4A68E3E0294076C1004AC3DC /* RemoteReaderSiteInfo.swift */, @@ -3036,7 +3039,7 @@ F9E56DF624EB11EF00916770 /* FeatureFlag.swift in Sources */, 8B2F4BE724ABC8A90056C08A /* ReaderPostServiceRemote+Cards.swift in Sources */, 4A68E3D529406AA0004AC3DC /* RemoteMenuLocation.swift in Sources */, - 8B16CE8E25250039007BE5A9 /* RemoteReaderPost.swift in Sources */, + 8B16CE8E25250039007BE5A9 /* ReaderPostsEnvelope.swift in Sources */, 74E2294E1F1E73FE0085F7F2 /* RemotePublicizeService.swift in Sources */, 8B749DED25AF3E4600023F03 /* JetpackCapabilitiesServiceRemote.swift in Sources */, 9F3E0BA22087345F009CB5BA /* ServiceRequest.swift in Sources */, @@ -3056,6 +3059,7 @@ 9F3E0B9E208733C3009CB5BA /* ReaderServiceDeliveryFrequency.swift in Sources */, 74E2295E1F1E777B0085F7F2 /* RemoteSharingButton.swift in Sources */, 93BD27701EE737A8002BB00B /* ServiceRemoteWordPressComREST.m in Sources */, + 4A68E40B294922A8004AC3DC /* RemoteReaderPost.swift in Sources */, E61A51A621B172A900A5F902 /* RemoteWpcomPlan.swift in Sources */, 93BD277F1EE73944002BB00B /* WordPressComOAuthClient.swift in Sources */, 740B23B91F17EC7300067A2A /* PostServiceRemoteREST.m in Sources */, diff --git a/WordPressKit/ReaderPostsEnvelope.swift b/WordPressKit/ReaderPostsEnvelope.swift new file mode 100644 index 00000000..f3c99989 --- /dev/null +++ b/WordPressKit/ReaderPostsEnvelope.swift @@ -0,0 +1,17 @@ +import Foundation + +struct ReaderPostsEnvelope: Decodable { + var posts: [RemoteReaderPost] + var nextPageHandle: String? + + private enum CodingKeys: String, CodingKey { + case posts + case nextPageHandle = "next_page_handle" + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let postDictionary = try container.decode([String: Any].self, forKey: .posts) + posts = [RemoteReaderPost(dictionary: postDictionary)] + } +} diff --git a/WordPressKit/RemoteReaderPost.m b/WordPressKit/RemoteReaderPost.m index ee0032ed..8f5a6ec8 100644 --- a/WordPressKit/RemoteReaderPost.m +++ b/WordPressKit/RemoteReaderPost.m @@ -75,8 +75,6 @@ NSString * const CrossPostMetaXPostOrigin = @"xpost_origin"; NSString * const CrossPostMetaCommentPrefix = @"comment-"; -static const NSInteger AvgWordsPerMinuteRead = 250; -static const NSInteger MinutesToReadThreshold = 2; static const NSUInteger ReaderPostTitleLength = 30; @implementation RemoteReaderPost @@ -158,7 +156,7 @@ - (instancetype)initWithDictionary:(NSDictionary *)dict; self.isExternal = [[dict numberForKey:PostRESTKeyIsExternal] boolValue]; self.isJetpack = [[dict numberForKey:PostRESTKeyIsJetpack] boolValue]; self.wordCount = [dict numberForKey:PostRESTKeyWordCount]; - self.readingTime = [self readingTimeForWordCount:self.wordCount]; + self.readingTime = [RemoteReaderPost readingTimeForWordCount:self.wordCount]; NSDictionary *railcar = [dict dictionaryForKey:PostRESTKeyRailcar]; if (railcar) { @@ -282,16 +280,6 @@ - (NSDictionary *)primaryAndSecondaryTagsFromPostDictionary:(NSDictionary *)dict }; } -- (NSNumber *)readingTimeForWordCount:(NSNumber *)wordCount -{ - NSInteger count = [wordCount integerValue]; - NSInteger minutesToRead = count / AvgWordsPerMinuteRead; - if (minutesToRead < MinutesToReadThreshold) { - return @(0); - } - return @(minutesToRead); -} - /** Composes discover attribution if needed. diff --git a/WordPressKit/RemoteReaderPost.swift b/WordPressKit/RemoteReaderPost.swift index f3c99989..f0873b4f 100644 --- a/WordPressKit/RemoteReaderPost.swift +++ b/WordPressKit/RemoteReaderPost.swift @@ -1,17 +1,18 @@ import Foundation -struct ReaderPostsEnvelope: Decodable { - var posts: [RemoteReaderPost] - var nextPageHandle: String? +public extension RemoteReaderPost { - private enum CodingKeys: String, CodingKey { - case posts - case nextPageHandle = "next_page_handle" + @objc(readingTimeForWordCount:) + class func readingTime(forWordCount wordCount: NSNumber) -> NSNumber { + let count = wordCount.intValue + let minutesToRead = count / avgWordsPerMinuteRead; + if minutesToRead < minutesToReadThreshold { + return 0 + } + return minutesToRead as NSNumber } - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let postDictionary = try container.decode([String: Any].self, forKey: .posts) - posts = [RemoteReaderPost(dictionary: postDictionary)] - } } + +private let avgWordsPerMinuteRead = 250 +private let minutesToReadThreshold = 2 diff --git a/WordPressKitTests/RemoteReaderPostTests.m b/WordPressKitTests/RemoteReaderPostTests.m index 50a3c751..8c4a85e3 100644 --- a/WordPressKitTests/RemoteReaderPostTests.m +++ b/WordPressKitTests/RemoteReaderPostTests.m @@ -19,7 +19,6 @@ - (BOOL)isWPComFromPostDictionary:(NSDictionary *)dict; - (NSString *)authorEmailFromAuthorDictionary:(NSDictionary *)dict; - (NSString *)sanitizeFeaturedImageString:(NSString *)img; - (NSDictionary *)primaryAndSecondaryTagsFromPostDictionary:(NSDictionary *)dict; -- (NSNumber *)readingTimeForWordCount:(NSNumber *)wordCount; - (NSString *)removeInlineStyles:(NSString *)string; - (NSString *)removeForbiddenTags:(NSString *)string; - (NSString *)postTitleFromPostDictionary:(NSDictionary *)dict; @@ -337,22 +336,20 @@ - (void)testEditorialTagsFromDictionary - (void)testReadingTimeFromDictionary { - RemoteReaderPost *remoteReaderPost = [RemoteReaderPost alloc]; - NSNumber *readingTime; - readingTime = [remoteReaderPost readingTimeForWordCount:@0]; + readingTime = [RemoteReaderPost readingTimeForWordCount:@0]; XCTAssertTrue([readingTime integerValue] == 0, @"Zero wordcount should return zero reading time."); - readingTime = [remoteReaderPost readingTimeForWordCount:@250]; + readingTime = [RemoteReaderPost readingTimeForWordCount:@250]; XCTAssertTrue([readingTime integerValue] == 0, @"Brief word count should return zero reading time."); - readingTime = [remoteReaderPost readingTimeForWordCount:@500]; + readingTime = [RemoteReaderPost readingTimeForWordCount:@500]; XCTAssertTrue([readingTime integerValue] == 2, @"500 words should take about 2 minutes to read"); - readingTime = [remoteReaderPost readingTimeForWordCount:@700]; + readingTime = [RemoteReaderPost readingTimeForWordCount:@700]; XCTAssertTrue([readingTime integerValue] == 2, @"700 words should take about 2 minutes to read."); - readingTime = [remoteReaderPost readingTimeForWordCount:@1000]; + readingTime = [RemoteReaderPost readingTimeForWordCount:@1000]; XCTAssertTrue([readingTime integerValue] == 4, @"1000 words should take about 4 minutes to read"); } From b9794b244a235f01603a6cb6bdf58985a5d11884 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Wed, 14 Dec 2022 10:42:30 +1300 Subject: [PATCH 2/8] Re-implement `RemoteReaderPost.authorEmailFromAuthorDictionary` --- WordPressKit/RemoteReaderPost.m | 20 +------------------- WordPressKit/RemoteReaderPost.swift | 19 +++++++++++++++++++ WordPressKitTests/RemoteReaderPostTests.m | 7 ++----- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/WordPressKit/RemoteReaderPost.m b/WordPressKit/RemoteReaderPost.m index 8f5a6ec8..eb32a348 100644 --- a/WordPressKit/RemoteReaderPost.m +++ b/WordPressKit/RemoteReaderPost.m @@ -17,7 +17,6 @@ NSString * const PostRESTKeyDiscoverMetadata = @"discover_metadata"; NSString * const PostRESTKeyDiscussion = @"discussion"; NSString * const PostRESTKeyEditorial = @"editorial"; -NSString * const PostRESTKeyEmail = @"email"; NSString * const PostRESTKeyExcerpt = @"excerpt"; NSString * const PostRESTKeyFeaturedMedia = @"featured_media"; NSString * const PostRESTKeyFeaturedImage = @"featured_image"; @@ -94,7 +93,7 @@ - (instancetype)initWithDictionary:(NSDictionary *)dict; self.author = [self stringOrEmptyString:[authorDict stringForKey:PostRESTKeyNiceName]]; // typically the author's screen name self.authorAvatarURL = [self stringOrEmptyString:[authorDict stringForKey:PostRESTKeyAvatarURL]]; self.authorDisplayName = [[self stringOrEmptyString:[authorDict stringForKey:PostRESTKeyName]] stringByDecodingXMLCharacters]; // Typically the author's given name - self.authorEmail = [self authorEmailFromAuthorDictionary:authorDict]; + self.authorEmail = [RemoteReaderPost authorEmailFromAuthorDictionary:authorDict]; self.authorURL = [self stringOrEmptyString:[authorDict stringForKey:PostRESTKeyURL]]; self.siteIconURL = [self stringOrEmptyString:[dict stringForKeyPath:@"meta.data.site.icon.img"]]; self.blogName = [self siteNameFromPostDictionary:dict]; @@ -383,23 +382,6 @@ - (NSString *)sanitizeFeaturedImageString:(NSString *)img #pragma mark - Data sanitization methods -/** - The v1 API result is inconsistent in that it will return a 0 when there is no author email. - - @param dict The author dictionary. - @return The author's email address or an empty string. - */ -- (NSString *)authorEmailFromAuthorDictionary:(NSDictionary *)dict -{ - NSString *authorEmail = [dict stringForKey:PostRESTKeyEmail]; - - // if 0 or less than minimum email length. a@a.aa - if ([authorEmail isEqualToString:@"0"] || [authorEmail length] < 6) { - authorEmail = @""; - } - - return authorEmail; -} /** Parse whether the post belongs to a wpcom blog. diff --git a/WordPressKit/RemoteReaderPost.swift b/WordPressKit/RemoteReaderPost.swift index f0873b4f..7e766baf 100644 --- a/WordPressKit/RemoteReaderPost.swift +++ b/WordPressKit/RemoteReaderPost.swift @@ -12,7 +12,26 @@ public extension RemoteReaderPost { return minutesToRead as NSNumber } + /// The v1 API result is inconsistent in that it will return a 0 when there is no author email. + /// + /// - Parameter dict The author dictionary. + /// - Returns The author's email address or an empty string. + @objc(authorEmailFromAuthorDictionary:) + class func authorEmail(fromAuthorDictionary dict: NSDictionary) -> String { + if let authorEmail = dict.string(forKey: postRESTKeyEmail), + authorEmail.count >= minimalEmailLength { + return authorEmail + } + + return "" + } + } +private let postRESTKeyEmail = "email" + +// The minimum email length: a@a.aa +private let minimalEmailLength = 6 + private let avgWordsPerMinuteRead = 250 private let minutesToReadThreshold = 2 diff --git a/WordPressKitTests/RemoteReaderPostTests.m b/WordPressKitTests/RemoteReaderPostTests.m index 8c4a85e3..5f35d18a 100644 --- a/WordPressKitTests/RemoteReaderPostTests.m +++ b/WordPressKitTests/RemoteReaderPostTests.m @@ -16,7 +16,6 @@ - (NSString *)siteNameFromPostDictionary:(NSDictionary *)dict; - (NSString *)featuredImageFromPostDictionary:(NSDictionary *)dict; - (NSDate *)sortDateFromPostDictionary:(NSDictionary *)dict; - (BOOL)isWPComFromPostDictionary:(NSDictionary *)dict; -- (NSString *)authorEmailFromAuthorDictionary:(NSDictionary *)dict; - (NSString *)sanitizeFeaturedImageString:(NSString *)img; - (NSDictionary *)primaryAndSecondaryTagsFromPostDictionary:(NSDictionary *)dict; - (NSString *)removeInlineStyles:(NSString *)string; @@ -250,16 +249,14 @@ - (void)testIsWPComFromDictionary { } - (void)testAuthorEmailFromDictionary { - RemoteReaderPost *remoteReaderPost = [RemoteReaderPost alloc]; - NSString *emailStr = @"a@a.aa"; NSDictionary *dict = @{@"email": emailStr}; - NSString *str = [remoteReaderPost authorEmailFromAuthorDictionary:dict]; + NSString *str = [RemoteReaderPost authorEmailFromAuthorDictionary:dict]; XCTAssertEqual(emailStr, str, @"The email returned did not match."); emailStr = @"0"; dict = @{@"email": emailStr}; - str = [remoteReaderPost authorEmailFromAuthorDictionary:dict]; + str = [RemoteReaderPost authorEmailFromAuthorDictionary:dict]; XCTAssertTrue([str length] == 0, @"If the value of email is 0, an empty string should be returned."); } From c1767fe41281944982cea7e8cd6d1954aeb13f92 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Wed, 14 Dec 2022 10:48:12 +1300 Subject: [PATCH 3/8] Re-implement `RemoteReaderPost.isWPComFromPostDictionary` --- WordPressKit/RemoteReaderPost.m | 17 +---------------- WordPressKit/RemoteReaderPost.swift | 13 +++++++++++++ WordPressKitTests/RemoteReaderPostTests.m | 7 ++----- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/WordPressKit/RemoteReaderPost.m b/WordPressKit/RemoteReaderPost.m index eb32a348..aa9881f3 100644 --- a/WordPressKit/RemoteReaderPost.m +++ b/WordPressKit/RemoteReaderPost.m @@ -112,7 +112,7 @@ - (instancetype)initWithDictionary:(NSDictionary *)dict; self.isFollowing = [[dict numberForKey:PostRESTKeyIsFollowing] boolValue]; self.isLiked = [[dict numberForKey:PostRESTKeyILike] boolValue]; self.isReblogged = [[dict numberForKey:PostRESTKeyIsReblogged] boolValue]; - self.isWPCom = [self isWPComFromPostDictionary:dict]; + self.isWPCom = [RemoteReaderPost isWPComFromPostDictionary:dict]; self.likeCount = [dict numberForKey:PostRESTKeyLikeCount]; self.permalink = [self stringOrEmptyString:[dict stringForKey:PostRESTKeyURL]]; self.postID = [dict numberForKey:PostRESTKeyID]; @@ -382,21 +382,6 @@ - (NSString *)sanitizeFeaturedImageString:(NSString *)img #pragma mark - Data sanitization methods - -/** - Parse whether the post belongs to a wpcom blog. - - @param dict A dictionary representing a post object from the REST API - @return YES if the post belongs to a wpcom blog, else NO - */ -- (BOOL)isWPComFromPostDictionary:(NSDictionary *)dict -{ - BOOL isExternal = [[dict numberForKey:PostRESTKeyIsExternal] boolValue]; - BOOL isJetpack = [[dict numberForKey:PostRESTKeyIsJetpack] boolValue]; - - return !isJetpack && !isExternal; -} - /** Get the tags assigned to a post and return them as a comma separated string. diff --git a/WordPressKit/RemoteReaderPost.swift b/WordPressKit/RemoteReaderPost.swift index 7e766baf..913a5977 100644 --- a/WordPressKit/RemoteReaderPost.swift +++ b/WordPressKit/RemoteReaderPost.swift @@ -26,9 +26,22 @@ public extension RemoteReaderPost { return "" } + /// Parse whether the post belongs to a wpcom blog. + /// + /// - Parameter dict A dictionary representing a post object from the REST API + /// - Returns YES if the post belongs to a wpcom blog, else NO + @objc(isWPComFromPostDictionary:) + class func isWPCom(fromPostDictionary dict: NSDictionary) -> Bool { + let isExternal = dict.number(forKey: postRESTKeyIsExternal)?.boolValue ?? false + let isJetpack = dict.number(forKey: postRESTKeyIsJetpack)?.boolValue ?? false + return !isJetpack && !isExternal + } + } private let postRESTKeyEmail = "email" +private let postRESTKeyIsExternal = "is_external" +private let postRESTKeyIsJetpack = "is_jetpack" // The minimum email length: a@a.aa private let minimalEmailLength = 6 diff --git a/WordPressKitTests/RemoteReaderPostTests.m b/WordPressKitTests/RemoteReaderPostTests.m index 5f35d18a..2f1da212 100644 --- a/WordPressKitTests/RemoteReaderPostTests.m +++ b/WordPressKitTests/RemoteReaderPostTests.m @@ -15,7 +15,6 @@ - (NSString *)siteURLFromPostDictionary:(NSDictionary *)dict; - (NSString *)siteNameFromPostDictionary:(NSDictionary *)dict; - (NSString *)featuredImageFromPostDictionary:(NSDictionary *)dict; - (NSDate *)sortDateFromPostDictionary:(NSDictionary *)dict; -- (BOOL)isWPComFromPostDictionary:(NSDictionary *)dict; - (NSString *)sanitizeFeaturedImageString:(NSString *)img; - (NSDictionary *)primaryAndSecondaryTagsFromPostDictionary:(NSDictionary *)dict; - (NSString *)removeInlineStyles:(NSString *)string; @@ -234,16 +233,14 @@ - (void)testSortDateFromDictionary { } - (void)testIsWPComFromDictionary { - RemoteReaderPost *remoteReaderPost = [RemoteReaderPost alloc]; - NSString *jsonStrFalse = @"{\"is_external\": false}"; NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:[jsonStrFalse dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil]; - BOOL isWPCom = [remoteReaderPost isWPComFromPostDictionary:dict]; + BOOL isWPCom = [RemoteReaderPost isWPComFromPostDictionary:dict]; XCTAssertTrue(isWPCom, @"A blog that is not external should be wpcom"); NSString *jsonStrTrue = @"{\"is_external\": true}"; dict = [NSJSONSerialization JSONObjectWithData:[jsonStrTrue dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil]; - isWPCom = [remoteReaderPost isWPComFromPostDictionary:dict]; + isWPCom = [RemoteReaderPost isWPComFromPostDictionary:dict]; XCTAssertFalse(isWPCom, @"A blog that is external should not be wpcom"); } From a556894c58c8441b70e48059fe7f048e3faa84e7 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Wed, 14 Dec 2022 11:27:30 +1300 Subject: [PATCH 4/8] Re-implement `RemoteReaderPost.tagsFromPostDictionary` --- WordPressKit.xcodeproj/project.pbxproj | 4 ++ WordPressKit/RemoteReaderPost.m | 19 +------ WordPressKit/RemoteReaderPost.swift | 14 +++++ WordPressKitTests/RemoteReaderPostTests.swift | 57 +++++++++++++++++++ 4 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 WordPressKitTests/RemoteReaderPostTests.swift diff --git a/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit.xcodeproj/project.pbxproj index a32f2993..42110477 100644 --- a/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit.xcodeproj/project.pbxproj @@ -133,6 +133,7 @@ 4A68E3D429406AA0004AC3DC /* RemoteMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3D129406AA0004AC3DC /* RemoteMenuItem.swift */; }; 4A68E3D529406AA0004AC3DC /* RemoteMenuLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E3D229406AA0004AC3DC /* RemoteMenuLocation.swift */; }; 4A68E40B294922A8004AC3DC /* RemoteReaderPost.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E40A294922A8004AC3DC /* RemoteReaderPost.swift */; }; + 4A68E40D294930CC004AC3DC /* RemoteReaderPostTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A68E40C294930CC004AC3DC /* RemoteReaderPostTests.swift */; }; 57BCD3D426209D9500292CB3 /* AppTransportSecuritySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57BCD3D326209D9500292CB3 /* AppTransportSecuritySettings.swift */; }; 730E869F21E44EFD00753E1A /* WordPressComServiceRemote+SiteVerticals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 730E869E21E44EFD00753E1A /* WordPressComServiceRemote+SiteVerticals.swift */; }; 731BA83621DECD61000FDFCD /* SiteCreationRequestEncodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 731BA83521DECD61000FDFCD /* SiteCreationRequestEncodingTests.swift */; }; @@ -788,6 +789,7 @@ 4A68E3D129406AA0004AC3DC /* RemoteMenuItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteMenuItem.swift; sourceTree = ""; }; 4A68E3D229406AA0004AC3DC /* RemoteMenuLocation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteMenuLocation.swift; sourceTree = ""; }; 4A68E40A294922A8004AC3DC /* RemoteReaderPost.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteReaderPost.swift; sourceTree = ""; }; + 4A68E40C294930CC004AC3DC /* RemoteReaderPostTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteReaderPostTests.swift; sourceTree = ""; }; 57BCD3D326209D9500292CB3 /* AppTransportSecuritySettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTransportSecuritySettings.swift; sourceTree = ""; }; 6C2A33D76FD1052D6F30466D /* Pods-WordPressKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WordPressKit/Pods-WordPressKit.debug.xcconfig"; sourceTree = ""; }; 6F2E0CC4FA01B5475A378DA2 /* Pods-WordPressKitTests.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressKitTests.release-alpha.xcconfig"; path = "Pods/Target Support Files/Pods-WordPressKitTests/Pods-WordPressKitTests.release-alpha.xcconfig"; sourceTree = ""; }; @@ -1507,6 +1509,7 @@ 7430C9BB1F192C0F0051B8E6 /* ReaderTopicServiceRemoteTests.m */, 17CE77F320C701C8001DEA5A /* ReaderSiteSearchServiceRemoteTests.swift */, 8B2F4BE424ABB3C70056C08A /* RemoteReaderPostTests.m */, + 4A68E40C294930CC004AC3DC /* RemoteReaderPostTests.swift */, 8B2F4BE824ABC9DC0056C08A /* ReaderPostServiceRemote+CardsTests.swift */, FACBDD3725ECB4480026705B /* ReaderPostServiceRemote+RelatedPostsTests.swift */, FA87FE0624EB39C4003FBEE3 /* ReaderPostServiceRemote+SubscriptionTests.swift */, @@ -3271,6 +3274,7 @@ 803DE81128FFA9C4007D4E9C /* RemoteConfigRemoteTests.swift in Sources */, 74B5F0DE1EF82A9600B411E7 /* BlogServiceRemoteRESTTests.m in Sources */, ABD95B7F25DD6C4B00735BEE /* CommentServiceRemoteRESTLikesTests.swift in Sources */, + 4A68E40D294930CC004AC3DC /* RemoteReaderPostTests.swift in Sources */, 8B749E8225AF7DDA00023F03 /* JetpackCapabilitiesServiceRemoteTests.swift in Sources */, 74E2294B1F1E73340085F7F2 /* SharingServiceRemoteTests.m in Sources */, FEFFD99B26C1598F00F34231 /* ShareAppContentServiceRemoteTests.swift in Sources */, diff --git a/WordPressKit/RemoteReaderPost.m b/WordPressKit/RemoteReaderPost.m index aa9881f3..11cb78cc 100644 --- a/WordPressKit/RemoteReaderPost.m +++ b/WordPressKit/RemoteReaderPost.m @@ -123,7 +123,7 @@ - (instancetype)initWithDictionary:(NSDictionary *)dict; self.sortRank = @(self.sortDate.timeIntervalSinceReferenceDate); self.status = [self stringOrEmptyString:[dict stringForKey:PostRESTKeyStatus]]; self.summary = [self postSummaryFromPostDictionary:dict orPostContent:self.content]; - self.tags = [self tagsFromPostDictionary:dict]; + self.tags = [RemoteReaderPost tagsFromPostDictionary:dict]; self.isSharingEnabled = [[dict numberForKey:PostRESTKeySharingEnabled] boolValue]; self.isLikesEnabled = [[dict numberForKey:PostRESTKeyLikesEnabled] boolValue]; self.organizationID = [dict numberForKeyPath:PostRESTKeyOrganizationID] ?: @0; @@ -382,23 +382,6 @@ - (NSString *)sanitizeFeaturedImageString:(NSString *)img #pragma mark - Data sanitization methods -/** - Get the tags assigned to a post and return them as a comma separated string. - - @param dict A dictionary representing a post object from the REST API. - @return A comma separated list of tags, or an empty string if no tags are found. - */ -- (NSString *)tagsFromPostDictionary:(NSDictionary *)dict -{ - NSDictionary *tagsDict = [dict dictionaryForKey:PostRESTKeyTags]; - NSArray *tagsList = [NSArray arrayWithArray:[tagsDict allKeys]]; - NSString *tags = [tagsList componentsJoinedByString:@", "]; - if (tags == nil) { - tags = @""; - } - return tags; -} - /** Get the date the post should be sorted by. diff --git a/WordPressKit/RemoteReaderPost.swift b/WordPressKit/RemoteReaderPost.swift index 913a5977..0dd24501 100644 --- a/WordPressKit/RemoteReaderPost.swift +++ b/WordPressKit/RemoteReaderPost.swift @@ -37,11 +37,25 @@ public extension RemoteReaderPost { return !isJetpack && !isExternal } + /// Get the tags assigned to a post and return them as a comma separated string. + /// + /// - Parameter dict A dictionary representing a post object from the REST API. + /// - Returns A comma separated list of tags, or an empty string if no tags are found. + @objc(tagsFromPostDictionary:) + class func tags(fromPostDictionary dict: NSDictionary) -> String { + if let tagsDict = dict[postRESTKeyTags] as? [String: Any] { + return tagsDict.keys.joined(separator: ", ") + } + + return "" + } + } private let postRESTKeyEmail = "email" private let postRESTKeyIsExternal = "is_external" private let postRESTKeyIsJetpack = "is_jetpack" +private let postRESTKeyTags = "tags"; // The minimum email length: a@a.aa private let minimalEmailLength = 6 diff --git a/WordPressKitTests/RemoteReaderPostTests.swift b/WordPressKitTests/RemoteReaderPostTests.swift new file mode 100644 index 00000000..24b28551 --- /dev/null +++ b/WordPressKitTests/RemoteReaderPostTests.swift @@ -0,0 +1,57 @@ +import XCTest + +@testable import WordPressKit + +class NewRemoteReaderPostTests: XCTestCase { + + func testParsingEmptyTags() throws { + // REST API returns an empty _array_ if there is no tags associated with the post. + let jsonString = """ + { + "tags": [] + } + """ + let json = try XCTUnwrap(JSONSerialization.jsonObject(with: jsonString.data(using: .utf8)!) as? NSDictionary) + XCTAssertEqual(RemoteReaderPost.tags(fromPostDictionary: json), "") + } + + func testParsingTags() throws { + let jsonString = """ + { + "tags": { + "another-random-art-tag": { + "ID": 1, + "name": "another-random-art-tag", + "slug": "another-random-art-tag", + "description": "", + "post_count": 1, + "meta": { + "links": { + } + }, + "display_name": "another-random-art-tag" + }, + "random-art-tag": { + "ID": 2, + "name": "random-art-tag", + "slug": "random-art-tag", + "description": "", + "post_count": 1, + "meta": { + "links": { + } + }, + "display_name": "random-art-tag" + } + } + } + """ + let json = try XCTUnwrap(JSONSerialization.jsonObject(with: jsonString.data(using: .utf8)!) as? NSDictionary) + let tags = RemoteReaderPost.tags(fromPostDictionary: json) + XCTAssertTrue( + tags == "random-art-tag, another-random-art-tag" + || tags == "another-random-art-tag, random-art-tag" + ) + } + +} From 6e6b7e5b095f3675209284ad9611352b0aa57bdd Mon Sep 17 00:00:00 2001 From: Tony Li Date: Wed, 14 Dec 2022 11:39:29 +1300 Subject: [PATCH 5/8] Re-implement `RemoteReaderPost.siteNameFromPostDictionary` --- WordPressKit/RemoteReaderPost.m | 29 +---------------------- WordPressKit/RemoteReaderPost.swift | 23 ++++++++++++++++++ WordPressKitTests/RemoteReaderPostTests.m | 11 ++++----- 3 files changed, 28 insertions(+), 35 deletions(-) diff --git a/WordPressKit/RemoteReaderPost.m b/WordPressKit/RemoteReaderPost.m index 11cb78cc..34f8d7d0 100644 --- a/WordPressKit/RemoteReaderPost.m +++ b/WordPressKit/RemoteReaderPost.m @@ -43,7 +43,6 @@ NSString * const PostRESTKeySiteID = @"site_ID"; NSString * const PostRESTKeySiteIsAtomic = @"site_is_atomic"; NSString * const PostRESTKeySiteIsPrivate = @"site_is_private"; -NSString * const PostRESTKeySiteName = @"site_name"; NSString * const PostRESTKeySiteURL = @"site_URL"; NSString * const PostRESTKeySlug = @"slug"; NSString * const PostRESTKeyStatus = @"status"; @@ -96,7 +95,7 @@ - (instancetype)initWithDictionary:(NSDictionary *)dict; self.authorEmail = [RemoteReaderPost authorEmailFromAuthorDictionary:authorDict]; self.authorURL = [self stringOrEmptyString:[authorDict stringForKey:PostRESTKeyURL]]; self.siteIconURL = [self stringOrEmptyString:[dict stringForKeyPath:@"meta.data.site.icon.img"]]; - self.blogName = [self siteNameFromPostDictionary:dict]; + self.blogName = [RemoteReaderPost siteNameFromPostDictionary:dict]; self.blogDescription = [self siteDescriptionFromPostDictionary:dict]; self.blogURL = [self siteURLFromPostDictionary:dict]; self.commentCount = [discussionDict numberForKey:PostRESTKeyCommentCount]; @@ -467,32 +466,6 @@ - (NSString *)suitableImageFromPostContent:(NSDictionary *)dict { return [self stringOrEmptyString:imageToDisplay]; } -/** - Get the name of the post's site. - - @param dict A dictionary representing a post object from the REST API. - @return The name of the post's site or an empty string. - */ -- (NSString *)siteNameFromPostDictionary:(NSDictionary *)dict -{ - // Blog Name - NSString *siteName = [self stringOrEmptyString:[dict stringForKey:PostRESTKeySiteName]]; - - // For some endpoints blogname is defined in meta - NSString *metaBlogName = [dict stringForKeyPath:@"meta.data.site.name"]; - if (metaBlogName != nil) { - siteName = metaBlogName; - } - - // Values set in editorial trumps the rest - NSString *editorialSiteName = [dict stringForKeyPath:@"editorial.blog_name"]; - if (editorialSiteName != nil) { - siteName = editorialSiteName; - } - - return [self makePlainText:siteName]; -} - /** Get the description of the post's site. diff --git a/WordPressKit/RemoteReaderPost.swift b/WordPressKit/RemoteReaderPost.swift index 0dd24501..5c4c257e 100644 --- a/WordPressKit/RemoteReaderPost.swift +++ b/WordPressKit/RemoteReaderPost.swift @@ -50,12 +50,35 @@ public extension RemoteReaderPost { return "" } + /// Get the name of the post's site. + /// + /// - Parameter dict A dictionary representing a post object from the REST API. + /// - Returns The name of the post's site or an empty string. + @objc(siteNameFromPostDictionary:) + class func siteName(fromPostDictionary dict: NSDictionary) -> String { + // Blog Name + var siteName = dict.string(forKey: postRESTKeySiteName) ?? "" + + // For some endpoints blogname is defined in meta + if let metaBlogName = dict.string(forKeyPath: "meta.data.site.name") { + siteName = metaBlogName; + } + + // Values set in editorial trumps the rest + if let editorialSiteName = dict.string(forKeyPath: "editorial.blog_name") { + siteName = editorialSiteName + } + + return (siteName as NSString).summarized() + } + } private let postRESTKeyEmail = "email" private let postRESTKeyIsExternal = "is_external" private let postRESTKeyIsJetpack = "is_jetpack" private let postRESTKeyTags = "tags"; +private let postRESTKeySiteName = "site_name"; // The minimum email length: a@a.aa private let minimalEmailLength = 6 diff --git a/WordPressKitTests/RemoteReaderPostTests.m b/WordPressKitTests/RemoteReaderPostTests.m index 2f1da212..496b5dc7 100644 --- a/WordPressKitTests/RemoteReaderPostTests.m +++ b/WordPressKitTests/RemoteReaderPostTests.m @@ -12,7 +12,6 @@ - (RemoteReaderPost *)formatPostDictionary:(NSDictionary *)dict; - (BOOL)siteIsAtomicFromPostDictionary:(NSDictionary *)dict; - (BOOL)siteIsPrivateFromPostDictionary:(NSDictionary *)dict; - (NSString *)siteURLFromPostDictionary:(NSDictionary *)dict; -- (NSString *)siteNameFromPostDictionary:(NSDictionary *)dict; - (NSString *)featuredImageFromPostDictionary:(NSDictionary *)dict; - (NSDate *)sortDateFromPostDictionary:(NSDictionary *)dict; - (NSString *)sanitizeFeaturedImageString:(NSString *)img; @@ -153,25 +152,23 @@ - (void)testSiteURLFromDictionary { } - (void)testSiteNameFromDictionary { - RemoteReaderPost *remoteReaderPost = [RemoteReaderPost alloc]; - NSString *name = @"foo"; NSDictionary *dict = @{@"site_name": name}; - NSString *siteName = [remoteReaderPost siteNameFromPostDictionary:dict]; + NSString *siteName = [RemoteReaderPost siteNameFromPostDictionary:dict]; XCTAssertEqualObjects(siteName, name, @"The returned site name did not match what was expected."); dict = [self metaDictionaryWithKey:@"name" value:name]; - siteName = [remoteReaderPost siteNameFromPostDictionary:dict]; + siteName = [RemoteReaderPost siteNameFromPostDictionary:dict]; XCTAssertEqualObjects(siteName, name, @"The returned site name did not match what was expected."); dict = [self editorialDictionaryWithKey:@"blog_name" value:name]; - siteName = [remoteReaderPost siteNameFromPostDictionary:dict]; + siteName = [RemoteReaderPost siteNameFromPostDictionary:dict]; XCTAssertEqualObjects(siteName, name, @"The returned site name did not match what was expected."); // Make sure editorial trumps other content. NSMutableDictionary *mDict = [dict mutableCopy]; [mDict setObject:@"bar" forKey:@"site_name"]; - siteName = [remoteReaderPost siteNameFromPostDictionary:dict]; + siteName = [RemoteReaderPost siteNameFromPostDictionary:dict]; XCTAssertEqualObjects(siteName, name, @"The returned site name did not match what was expected."); } From 56b6801f7804bf1bce401ebe9668f6bcb856f44e Mon Sep 17 00:00:00 2001 From: Tony Li Date: Wed, 14 Dec 2022 11:45:45 +1300 Subject: [PATCH 6/8] Re-implement `RemoteReaderPost.siteURLFromPostDictionary` --- WordPressKit/RemoteReaderPost.m | 21 +-------------------- WordPressKit/RemoteReaderPost.swift | 12 ++++++++++++ WordPressKitTests/RemoteReaderPostTests.m | 7 ++----- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/WordPressKit/RemoteReaderPost.m b/WordPressKit/RemoteReaderPost.m index 34f8d7d0..65fedf46 100644 --- a/WordPressKit/RemoteReaderPost.m +++ b/WordPressKit/RemoteReaderPost.m @@ -43,7 +43,6 @@ NSString * const PostRESTKeySiteID = @"site_ID"; NSString * const PostRESTKeySiteIsAtomic = @"site_is_atomic"; NSString * const PostRESTKeySiteIsPrivate = @"site_is_private"; -NSString * const PostRESTKeySiteURL = @"site_URL"; NSString * const PostRESTKeySlug = @"slug"; NSString * const PostRESTKeyStatus = @"status"; NSString * const PostRESTKeyTitle = @"title"; @@ -97,7 +96,7 @@ - (instancetype)initWithDictionary:(NSDictionary *)dict; self.siteIconURL = [self stringOrEmptyString:[dict stringForKeyPath:@"meta.data.site.icon.img"]]; self.blogName = [RemoteReaderPost siteNameFromPostDictionary:dict]; self.blogDescription = [self siteDescriptionFromPostDictionary:dict]; - self.blogURL = [self siteURLFromPostDictionary:dict]; + self.blogURL = [RemoteReaderPost siteURLFromPostDictionary:dict]; self.commentCount = [discussionDict numberForKey:PostRESTKeyCommentCount]; self.commentsOpen = [[discussionDict numberForKey:PostRESTKeyCommentsOpen] boolValue]; self.content = [self postContentFromPostDictionary:dict]; @@ -478,24 +477,6 @@ - (NSString *)siteDescriptionFromPostDictionary:(NSDictionary *)dict return [self makePlainText:description]; } -/** - Retrives the post site's URL - - @param dict A dictionary representing a post object from the REST API. - @return The URL path of the post's site. - */ -- (NSString *)siteURLFromPostDictionary:(NSDictionary *)dict -{ - NSString *siteURL = [self stringOrEmptyString:[dict stringForKey:PostRESTKeySiteURL]]; - - NSString *metaSiteURL = [dict stringForKeyPath:@"meta.data.site.URL"]; - if (metaSiteURL != nil) { - siteURL = metaSiteURL; - } - - return siteURL; -} - /** Retrives the post content from results dictionary diff --git a/WordPressKit/RemoteReaderPost.swift b/WordPressKit/RemoteReaderPost.swift index 5c4c257e..22f214f8 100644 --- a/WordPressKit/RemoteReaderPost.swift +++ b/WordPressKit/RemoteReaderPost.swift @@ -72,6 +72,17 @@ public extension RemoteReaderPost { return (siteName as NSString).summarized() } + /// Retrives the post site's URL + /// + /// - Parameter dict A dictionary representing a post object from the REST API. + /// - Returns The URL path of the post's site. + @objc(siteURLFromPostDictionary:) + class func siteURL(romPostDictionary dict: NSDictionary) -> String { + dict.string(forKeyPath: "meta.data.site.URL") + ?? dict.string(forKey: postRESTKeySiteURL) + ?? "" + } + } private let postRESTKeyEmail = "email" @@ -79,6 +90,7 @@ private let postRESTKeyIsExternal = "is_external" private let postRESTKeyIsJetpack = "is_jetpack" private let postRESTKeyTags = "tags"; private let postRESTKeySiteName = "site_name"; +private let postRESTKeySiteURL = "site_URL"; // The minimum email length: a@a.aa private let minimalEmailLength = 6 diff --git a/WordPressKitTests/RemoteReaderPostTests.m b/WordPressKitTests/RemoteReaderPostTests.m index 496b5dc7..24806fcf 100644 --- a/WordPressKitTests/RemoteReaderPostTests.m +++ b/WordPressKitTests/RemoteReaderPostTests.m @@ -11,7 +11,6 @@ @interface RemoteReaderPost () - (RemoteReaderPost *)formatPostDictionary:(NSDictionary *)dict; - (BOOL)siteIsAtomicFromPostDictionary:(NSDictionary *)dict; - (BOOL)siteIsPrivateFromPostDictionary:(NSDictionary *)dict; -- (NSString *)siteURLFromPostDictionary:(NSDictionary *)dict; - (NSString *)featuredImageFromPostDictionary:(NSDictionary *)dict; - (NSDate *)sortDateFromPostDictionary:(NSDictionary *)dict; - (NSString *)sanitizeFeaturedImageString:(NSString *)img; @@ -139,15 +138,13 @@ - (void)testSiteIsPrivate { } - (void)testSiteURLFromDictionary { - RemoteReaderPost *remoteReaderPost = [RemoteReaderPost alloc]; - NSString *site = @"http://site.com"; NSDictionary *dict = @{@"site_URL": site}; - NSString *siteURL = [remoteReaderPost siteURLFromPostDictionary:dict]; + NSString *siteURL = [RemoteReaderPost siteURLFromPostDictionary:dict]; XCTAssertEqual(siteURL, site, @"The returned site did not match what was expected."); dict = [self metaDictionaryWithKey:@"URL" value:site]; - siteURL = [remoteReaderPost siteURLFromPostDictionary:dict]; + siteURL = [RemoteReaderPost siteURLFromPostDictionary:dict]; XCTAssertEqual(siteURL, site, @"The returned site did not match what was expected."); } From 2ed47f46fc72cd91641c86ddaf6afd8dc5cd9ac0 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Wed, 14 Dec 2022 13:15:58 +1300 Subject: [PATCH 7/8] Re-implement RemoteReaderPost's isAtomic and isPrivate --- WordPressKit/RemoteReaderPost.m | 31 ++--------------------- WordPressKit/RemoteReaderPost.swift | 19 ++++++++++++++ WordPressKitTests/RemoteReaderPostTests.m | 20 +++++---------- 3 files changed, 28 insertions(+), 42 deletions(-) diff --git a/WordPressKit/RemoteReaderPost.m b/WordPressKit/RemoteReaderPost.m index 65fedf46..1db4b96f 100644 --- a/WordPressKit/RemoteReaderPost.m +++ b/WordPressKit/RemoteReaderPost.m @@ -41,8 +41,6 @@ NSString * const PostRESTKeyScore = @"score"; NSString * const PostRESTKeySharingEnabled = @"sharing_enabled"; NSString * const PostRESTKeySiteID = @"site_ID"; -NSString * const PostRESTKeySiteIsAtomic = @"site_is_atomic"; -NSString * const PostRESTKeySiteIsPrivate = @"site_is_private"; NSString * const PostRESTKeySlug = @"slug"; NSString * const PostRESTKeyStatus = @"status"; NSString * const PostRESTKeyTitle = @"title"; @@ -105,8 +103,8 @@ - (instancetype)initWithDictionary:(NSDictionary *)dict; self.feedID = [dict numberForKey:PostRESTKeyFeedID]; self.feedItemID = [dict numberForKey:PostRESTKeyFeedItemID]; self.globalID = [self stringOrEmptyString:[dict stringForKey:PostRESTKeyGlobalID]]; - self.isBlogAtomic = [self siteIsAtomicFromPostDictionary:dict]; - self.isBlogPrivate = [self siteIsPrivateFromPostDictionary:dict]; + self.isBlogAtomic = [RemoteReaderPost siteIsAtomicFromPostDictionary:dict]; + self.isBlogPrivate = [RemoteReaderPost siteIsPrivateFromPostDictionary:dict]; self.isFollowing = [[dict numberForKey:PostRESTKeyIsFollowing] boolValue]; self.isLiked = [[dict numberForKey:PostRESTKeyILike] boolValue]; self.isReblogged = [[dict numberForKey:PostRESTKeyIsReblogged] boolValue]; @@ -516,31 +514,6 @@ - (NSString *)postSummaryFromPostDictionary:(NSDictionary *)dict orPostContent:( return summary; } -- (BOOL)siteIsAtomicFromPostDictionary:(NSDictionary *)dict -{ - NSNumber *isAtomic = [dict numberForKey:PostRESTKeySiteIsAtomic]; - - return [isAtomic boolValue]; -} - -/** - Retrives the privacy preference for the post's site. - - @param dict A dictionary representing a post object from the REST API. - @return YES if the site is private. - */ -- (BOOL)siteIsPrivateFromPostDictionary:(NSDictionary *)dict -{ - NSNumber *isPrivate = [dict numberForKey:PostRESTKeySiteIsPrivate]; - - NSNumber *metaIsPrivate = [dict numberForKeyPath:@"meta.data.site.is_private"]; - if (metaIsPrivate != nil) { - isPrivate = metaIsPrivate; - } - - return [isPrivate boolValue]; -} - - (NSArray *)slugsFromDiscoverPostTaxonomies:(NSArray *)discoverPostTaxonomies { return [discoverPostTaxonomies wp_map:^id(NSDictionary *dict) { diff --git a/WordPressKit/RemoteReaderPost.swift b/WordPressKit/RemoteReaderPost.swift index 22f214f8..ec7b4176 100644 --- a/WordPressKit/RemoteReaderPost.swift +++ b/WordPressKit/RemoteReaderPost.swift @@ -83,6 +83,23 @@ public extension RemoteReaderPost { ?? "" } + @objc(siteIsAtomicFromPostDictionary:) + class func siteIsAtomic(fromPostDictionary dict: NSDictionary) -> Bool { + dict.number(forKey: postRESTKeySiteIsAtomic)?.boolValue ?? false + } + + + /// Retrives the privacy preference for the post's site. + /// + /// - Parameter dict A dictionary representing a post object from the REST API. + /// - Returns YES if the site is private. + @objc(siteIsPrivateFromPostDictionary:) + class func siteIsPrivate(fromPostDictionary dict: NSDictionary) -> Bool { + dict.number(forKeyPath: "meta.data.site.is_private")?.boolValue + ?? dict.number(forKey: postRESTKeySiteIsPrivate)?.boolValue + ?? false + } + } private let postRESTKeyEmail = "email" @@ -91,6 +108,8 @@ private let postRESTKeyIsJetpack = "is_jetpack" private let postRESTKeyTags = "tags"; private let postRESTKeySiteName = "site_name"; private let postRESTKeySiteURL = "site_URL"; +private let postRESTKeySiteIsAtomic = "site_is_atomic"; +private let postRESTKeySiteIsPrivate = "site_is_private"; // The minimum email length: a@a.aa private let minimalEmailLength = 6 diff --git a/WordPressKitTests/RemoteReaderPostTests.m b/WordPressKitTests/RemoteReaderPostTests.m index 24806fcf..7f8411fe 100644 --- a/WordPressKitTests/RemoteReaderPostTests.m +++ b/WordPressKitTests/RemoteReaderPostTests.m @@ -9,8 +9,6 @@ @interface RemoteReaderPost () - (RemoteReaderPost *)formatPostDictionary:(NSDictionary *)dict; -- (BOOL)siteIsAtomicFromPostDictionary:(NSDictionary *)dict; -- (BOOL)siteIsPrivateFromPostDictionary:(NSDictionary *)dict; - (NSString *)featuredImageFromPostDictionary:(NSDictionary *)dict; - (NSDate *)sortDateFromPostDictionary:(NSDictionary *)dict; - (NSString *)sanitizeFeaturedImageString:(NSString *)img; @@ -102,38 +100,34 @@ - (void)testSummaryIsPlainText { - (void)testSiteIsAtomic { NSString *key = @"site_is_atomic"; - RemoteReaderPost *remoteReaderPost = [RemoteReaderPost alloc]; - NSDictionary *dict = @{key: @"1"}; - BOOL isAtomic = [remoteReaderPost siteIsAtomicFromPostDictionary:dict]; + BOOL isAtomic = [RemoteReaderPost siteIsAtomicFromPostDictionary:dict]; XCTAssertTrue(isAtomic, @"Site should be atomic."); dict = @{key: @"0"}; - isAtomic = [remoteReaderPost siteIsAtomicFromPostDictionary:dict]; + isAtomic = [RemoteReaderPost siteIsAtomicFromPostDictionary:dict]; XCTAssertFalse(isAtomic, @"Site should not be atomic."); dict = @{}; - isAtomic = [remoteReaderPost siteIsAtomicFromPostDictionary:dict]; + isAtomic = [RemoteReaderPost siteIsAtomicFromPostDictionary:dict]; XCTAssertFalse(isAtomic, @"Site should not be atomic."); } - (void)testSiteIsPrivate { - RemoteReaderPost *remoteReaderPost = [RemoteReaderPost alloc]; - NSDictionary *dict = @{@"site_is_private": @"1"}; - BOOL isPrivate = [remoteReaderPost siteIsPrivateFromPostDictionary:dict]; + BOOL isPrivate = [RemoteReaderPost siteIsPrivateFromPostDictionary:dict]; XCTAssertTrue(isPrivate, @"Site should be private."); dict = @{@"site_is_private": @"0"}; - isPrivate = [remoteReaderPost siteIsPrivateFromPostDictionary:dict]; + isPrivate = [RemoteReaderPost siteIsPrivateFromPostDictionary:dict]; XCTAssertFalse(isPrivate, @"Site should not be private."); dict = [self metaDictionaryWithKey:@"is_private" value:@"1"]; - isPrivate = [remoteReaderPost siteIsPrivateFromPostDictionary:dict]; + isPrivate = [RemoteReaderPost siteIsPrivateFromPostDictionary:dict]; XCTAssertTrue(isPrivate, @"Meta site should be private."); dict = [self metaDictionaryWithKey:@"is_private" value:@"0"]; - isPrivate = [remoteReaderPost siteIsPrivateFromPostDictionary:dict]; + isPrivate = [RemoteReaderPost siteIsPrivateFromPostDictionary:dict]; XCTAssertFalse(isPrivate, @"Meta site should not be private."); } From 0d5e0bd34967fbf43304914684fd295031506b30 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Thu, 15 Dec 2022 09:56:37 +1300 Subject: [PATCH 8/8] Remove semicolons --- WordPressKit/RemoteReaderPost.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/WordPressKit/RemoteReaderPost.swift b/WordPressKit/RemoteReaderPost.swift index ec7b4176..78fc789a 100644 --- a/WordPressKit/RemoteReaderPost.swift +++ b/WordPressKit/RemoteReaderPost.swift @@ -5,7 +5,7 @@ public extension RemoteReaderPost { @objc(readingTimeForWordCount:) class func readingTime(forWordCount wordCount: NSNumber) -> NSNumber { let count = wordCount.intValue - let minutesToRead = count / avgWordsPerMinuteRead; + let minutesToRead = count / avgWordsPerMinuteRead if minutesToRead < minutesToReadThreshold { return 0 } @@ -61,7 +61,7 @@ public extension RemoteReaderPost { // For some endpoints blogname is defined in meta if let metaBlogName = dict.string(forKeyPath: "meta.data.site.name") { - siteName = metaBlogName; + siteName = metaBlogName } // Values set in editorial trumps the rest @@ -105,11 +105,11 @@ public extension RemoteReaderPost { private let postRESTKeyEmail = "email" private let postRESTKeyIsExternal = "is_external" private let postRESTKeyIsJetpack = "is_jetpack" -private let postRESTKeyTags = "tags"; -private let postRESTKeySiteName = "site_name"; -private let postRESTKeySiteURL = "site_URL"; -private let postRESTKeySiteIsAtomic = "site_is_atomic"; -private let postRESTKeySiteIsPrivate = "site_is_private"; +private let postRESTKeyTags = "tags" +private let postRESTKeySiteName = "site_name" +private let postRESTKeySiteURL = "site_URL" +private let postRESTKeySiteIsAtomic = "site_is_atomic" +private let postRESTKeySiteIsPrivate = "site_is_private" // The minimum email length: a@a.aa private let minimalEmailLength = 6