Skip to content

SPM Prep – Rewrite RemoteBlogSettings encoding and decoding in Swift #780

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: trunk
Choose a base branch
from
Open
2 changes: 1 addition & 1 deletion Sources/WordPressKit/Models/RemoteBlogOptionsHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ import Foundation
}

public class func remoteBlogSettings(fromXMLRPCDictionaryOptions options: NSDictionary) -> RemoteBlogSettings {
let remoteSettings = RemoteBlogSettings()
let remoteSettings = RemoteBlogSettings(jsonDictionary: [:])
remoteSettings.name = options.string(forKeyPath: "blog_title.value")?.stringByDecodingXMLCharacters()
remoteSettings.tagline = options.string(forKeyPath: "blog_tagline.value")?.stringByDecodingXMLCharacters()
if options["blog_public"] != nil {
Expand Down
193 changes: 193 additions & 0 deletions Sources/WordPressKit/Models/RemoteBlogSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,182 @@ public class RemoteBlogSettings: NSObject {
///
@objc public var sharingDisabledReblogs: NSNumber?

// Defined as CodingKey-conforming already to simplify Codable support in the future.
enum CodingKeys: String, CodingKey {
case name = "name"
case tagline = "description"
case privacy = "blog_public"
case languageID = "lang_id"
case iconMediaID = "site_icon"
case gmtOffset = "gmt_offset"
case timezoneString = "timezone_string"
case settings = "settings"
case defaultCategory = "default_category"
case defaultPostFormat = "default_post_format"
case dateFormat = "date_format"
case timeFormat = "time_format"
case startOfWeek = "start_of_week"
case postsPerPage = "posts_per_page"
case commentsAllowed = "default_comment_status"
case commentsBlocklistKeys = "blacklist_keys"
case commentsCloseAutomatically = "close_comments_for_old_posts"
case commentsCloseAutomaticallyAfterDays = "close_comments_days_old"
case commentsKnownUsersAllowlist = "comment_whitelist"
case commentsMaxLinks = "comment_max_links"
case commentsModerationKeys = "moderation_keys"
case commentsPagingEnabled = "page_comments"
case commentsPageSize = "comments_per_page"
case commentsRequireModeration = "comment_moderation"
case commentsRequireNameAndEmail = "require_name_email"
case commentsRequireRegistration = "comment_registration"
case commentsSortOrder = "comment_order"
case commentsThreadingEnabled = "thread_comments"
case commentsThreadingDepth = "thread_comments_depth"
case pingbackOutbound = "default_pingback_flag"
case pingbackInbound = "default_ping_status"
case relatedPostsAllowed = "jetpack_relatedposts_allowed"
case relatedPostsEnabled = "jetpack_relatedposts_enabled"
case relatedPostsShowHeadline = "jetpack_relatedposts_show_headline"
case relatedPostsShowThumbnails = "jetpack_relatedposts_show_thumbnails"
case ampSupported = "amp_is_supported"
case ampEnabled = "amp_is_enabled"

case sharingButtonStyle = "sharing_button_style"
case sharingLabel = "sharing_label"
case sharingTwitterName = "twitter_via"
case sharingCommentLikesEnabled = "jetpack_comment_likes_enabled"
case sharingDisabledLikes = "disabled_likes"
case sharingDisabledReblogs = "disabled_reblogs"
}

/// Parses details from a JSON dictionary, as returned by the WordPress.com REST API.
@objc
public init(jsonDictionary json: [String: Any]) {
let rawSettings = json[CodingKeys.settings.rawValue] as? [String: Any] ?? [:]

name = json[CodingKeys.name.rawValue] as? String
tagline = json[CodingKeys.tagline.rawValue] as? String
privacy = rawSettings[CodingKeys.privacy.rawValue] as? NSNumber
// The value here can be a String, so we need a custom conversion
languageID = rawSettings.number(forKey: CodingKeys.languageID.rawValue)
iconMediaID = rawSettings[CodingKeys.iconMediaID.rawValue] as? NSNumber
// The value here can be a String, so we need a custom conversion
gmtOffset = rawSettings.number(forKey: CodingKeys.gmtOffset.rawValue)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I worry too much, but do you think we should use number(forKey:) to parse all properties instead of just these keys, because that's what the original Objective-C uses?.

We happen to know these weird cases, simply because we have one test json file for it. There is no indication in the original Objective-C code that these keys need special treatment. And, may be there are other keys needs a special "string" to "number" parsing, which is not detected by that one single test json file?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the string(forKey:) parsing in the original Objective-C code can parse a number to a string, which we don't do here. So, maybe we should use string(forKey:) and number(forKey:) instead of type casting?

timezoneString = rawSettings[CodingKeys.timezoneString.rawValue] as? String

defaultCategoryID = rawSettings[CodingKeys.defaultCategory.rawValue] as? NSNumber ?? 1

// Note: the backend might send '0' as a number, OR a string value.
// See https://github.com/wordpress-mobile/WordPress-iOS/issues/4187
let defaultPostFormatValue = rawSettings[CodingKeys.defaultPostFormat.rawValue]
if let defaultPostFormatNumber = defaultPostFormatValue as? NSNumber, defaultPostFormatNumber == 0 ||
defaultPostFormatValue as? String == "0" {
defaultPostFormat = "standard"
} else {
defaultPostFormat = defaultPostFormatValue as? String
}

dateFormat = rawSettings[CodingKeys.dateFormat.rawValue] as? String
timeFormat = rawSettings[CodingKeys.timeFormat.rawValue] as? String
startOfWeek = rawSettings[CodingKeys.startOfWeek.rawValue] as? String
postsPerPage = rawSettings[CodingKeys.postsPerPage.rawValue] as? NSNumber

commentsAllowed = rawSettings[CodingKeys.commentsAllowed.rawValue] as? NSNumber
commentsBlocklistKeys = rawSettings[CodingKeys.commentsBlocklistKeys.rawValue] as? String
commentsCloseAutomatically = rawSettings[CodingKeys.commentsCloseAutomatically.rawValue] as? NSNumber
commentsCloseAutomaticallyAfterDays = rawSettings[CodingKeys.commentsCloseAutomaticallyAfterDays.rawValue] as? NSNumber
commentsFromKnownUsersAllowlisted = rawSettings[CodingKeys.commentsKnownUsersAllowlist.rawValue] as? NSNumber
commentsMaximumLinks = rawSettings[CodingKeys.commentsMaxLinks.rawValue] as? NSNumber
commentsModerationKeys = rawSettings[CodingKeys.commentsModerationKeys.rawValue] as? String
commentsPagingEnabled = rawSettings[CodingKeys.commentsPagingEnabled.rawValue] as? NSNumber
commentsPageSize = rawSettings[CodingKeys.commentsPageSize.rawValue] as? NSNumber
commentsRequireManualModeration = rawSettings[CodingKeys.commentsRequireModeration.rawValue] as? NSNumber
commentsRequireNameAndEmail = rawSettings[CodingKeys.commentsRequireNameAndEmail.rawValue] as? NSNumber
commentsRequireRegistration = rawSettings[CodingKeys.commentsRequireRegistration.rawValue] as? NSNumber
commentsSortOrder = rawSettings[CodingKeys.commentsSortOrder.rawValue] as? String
commentsThreadingEnabled = rawSettings[CodingKeys.commentsThreadingEnabled.rawValue] as? NSNumber
commentsThreadingDepth = rawSettings[CodingKeys.commentsThreadingDepth.rawValue] as? NSNumber
pingbackOutboundEnabled = rawSettings[CodingKeys.pingbackOutbound.rawValue] as? NSNumber
pingbackInboundEnabled = rawSettings[CodingKeys.pingbackInbound.rawValue] as? NSNumber

relatedPostsAllowed = rawSettings[CodingKeys.relatedPostsAllowed.rawValue] as? NSNumber
relatedPostsEnabled = rawSettings[CodingKeys.relatedPostsEnabled.rawValue] as? NSNumber
relatedPostsShowHeadline = rawSettings[CodingKeys.relatedPostsShowHeadline.rawValue] as? NSNumber
relatedPostsShowThumbnails = rawSettings[CodingKeys.relatedPostsShowThumbnails.rawValue] as? NSNumber

ampSupported = rawSettings[CodingKeys.ampSupported.rawValue] as? NSNumber
ampEnabled = rawSettings[CodingKeys.ampEnabled.rawValue] as? NSNumber

sharingButtonStyle = rawSettings[CodingKeys.sharingButtonStyle.rawValue] as? String
sharingLabel = rawSettings[CodingKeys.sharingLabel.rawValue] as? String
sharingTwitterName = rawSettings[CodingKeys.sharingTwitterName.rawValue] as? String
sharingCommentLikesEnabled = rawSettings[CodingKeys.sharingCommentLikesEnabled.rawValue] as? NSNumber
sharingDisabledLikes = rawSettings[CodingKeys.sharingDisabledLikes.rawValue] as? NSNumber
sharingDisabledReblogs = rawSettings[CodingKeys.sharingDisabledReblogs.rawValue] as? NSNumber
}

@objc
public var dictionaryRepresentation: [String: Any] {
var parameters: [String: Any] = [:]

// name and tagline/description use different keys...
parameters["blogname"] = name
parameters["blogdescription"] = tagline

parameters[CodingKeys.privacy.rawValue] = privacy
parameters[CodingKeys.languageID.rawValue] = languageID
parameters[CodingKeys.iconMediaID.rawValue] = iconMediaID
parameters[CodingKeys.gmtOffset.rawValue] = gmtOffset
parameters[CodingKeys.timezoneString.rawValue] = timezoneString

parameters[CodingKeys.defaultCategory.rawValue] = defaultCategoryID
parameters[CodingKeys.defaultPostFormat.rawValue] = defaultPostFormat
parameters[CodingKeys.dateFormat.rawValue] = dateFormat
parameters[CodingKeys.timeFormat.rawValue] = timeFormat
parameters[CodingKeys.startOfWeek.rawValue] = startOfWeek
parameters[CodingKeys.postsPerPage.rawValue] = postsPerPage

parameters[CodingKeys.commentsAllowed.rawValue] = commentsAllowed
parameters[CodingKeys.commentsBlocklistKeys.rawValue] = commentsBlocklistKeys
parameters[CodingKeys.commentsCloseAutomatically.rawValue] = commentsCloseAutomatically
parameters[CodingKeys.commentsCloseAutomaticallyAfterDays.rawValue] = commentsCloseAutomaticallyAfterDays
parameters[CodingKeys.commentsKnownUsersAllowlist.rawValue] = commentsFromKnownUsersAllowlisted
parameters[CodingKeys.commentsMaxLinks.rawValue] = commentsMaximumLinks
parameters[CodingKeys.commentsModerationKeys.rawValue] = commentsModerationKeys
parameters[CodingKeys.commentsPagingEnabled.rawValue] = commentsPagingEnabled
parameters[CodingKeys.commentsPageSize.rawValue] = commentsPageSize
parameters[CodingKeys.commentsRequireModeration.rawValue] = commentsRequireManualModeration
parameters[CodingKeys.commentsRequireNameAndEmail.rawValue] = commentsRequireNameAndEmail
parameters[CodingKeys.commentsRequireRegistration.rawValue] = commentsRequireRegistration
parameters[CodingKeys.commentsSortOrder.rawValue] = commentsSortOrder
parameters[CodingKeys.commentsThreadingEnabled.rawValue] = commentsThreadingEnabled
parameters[CodingKeys.commentsThreadingDepth.rawValue] = commentsThreadingDepth

parameters[CodingKeys.pingbackOutbound.rawValue] = pingbackOutboundEnabled
parameters[CodingKeys.pingbackInbound.rawValue] = pingbackInboundEnabled

// Note: releatedPostsAllowed was not set in the Objective-C implementation.
// There was no comment about it, so I assumed it was simply something that was never noticed.
parameters[CodingKeys.relatedPostsAllowed.rawValue] = relatedPostsAllowed
parameters[CodingKeys.relatedPostsEnabled.rawValue] = relatedPostsEnabled
parameters[CodingKeys.relatedPostsShowHeadline.rawValue] = relatedPostsShowHeadline
parameters[CodingKeys.relatedPostsShowThumbnails.rawValue] = relatedPostsShowThumbnails

// Note: ampSupported was not set in the Objective-C implementation.
// There was no comment about it, so I assumed it was simply something that was never noticed.
parameters[CodingKeys.ampSupported.rawValue] = ampSupported
parameters[CodingKeys.ampEnabled.rawValue] = ampEnabled

parameters[CodingKeys.sharingButtonStyle.rawValue] = sharingButtonStyle
parameters[CodingKeys.sharingLabel.rawValue] = sharingLabel
parameters[CodingKeys.sharingTwitterName.rawValue] = sharingTwitterName
parameters[CodingKeys.sharingCommentLikesEnabled.rawValue] = sharingCommentLikesEnabled
parameters[CodingKeys.sharingDisabledLikes.rawValue] = sharingDisabledLikes
parameters[CodingKeys.sharingDisabledReblogs.rawValue] = sharingDisabledReblogs

return parameters
}

// MARK: - Helpers

/// Computed property, meant to help conversion from Remote / String-Based values, into their Integer counterparts
Expand All @@ -204,3 +380,20 @@ public class RemoteBlogSettings: NSObject {
private static let AscendingStringValue = "asc"
private static let DescendingStringValue = "desc"
}

extension Dictionary where Key == String {

func number(forKey key: String) -> NSNumber? {
guard let value = self[key] else { return .none }

if let value = value as? NSNumber {
return value
} else if let value = value as? String {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .none
return numberFormatter.number(from: value)
} else {
return .none
}
}
}
Loading