diff --git a/.swift-version b/.swift-version index a3ec5a4bd..5186d0706 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -3.2 +4.0 diff --git a/Aztec.xcodeproj/project.pbxproj b/Aztec.xcodeproj/project.pbxproj index d7493e783..33e04db18 100644 --- a/Aztec.xcodeproj/project.pbxproj +++ b/Aztec.xcodeproj/project.pbxproj @@ -41,6 +41,9 @@ B542D6421E9EB122009D12D3 /* PreFormaterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B542D6411E9EB122009D12D3 /* PreFormaterTests.swift */; }; B551A4A01E770B3800EE3A7F /* UIFont+Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = B551A49F1E770B3800EE3A7F /* UIFont+Emoji.swift */; }; B572AC281E817CFE008948C2 /* CommentAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = B572AC271E817CFE008948C2 /* CommentAttachment.swift */; }; + B574F4A41FB0CF3B0048F355 /* NSAttributedStringKey+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = B574F4A31FB0CF3A0048F355 /* NSAttributedStringKey+Conversion.swift */; }; + B574F4AD1FB103430048F355 /* NSAttributedStringKey+Aztec.swift in Sources */ = {isa = PBXBuildFile; fileRef = B574F4AC1FB103430048F355 /* NSAttributedStringKey+Aztec.swift */; }; + B574F4AF1FB110850048F355 /* NSAttributedStringKeyHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B574F4AE1FB110850048F355 /* NSAttributedStringKeyHelperTests.swift */; }; B57534501F267D0B009D4904 /* Array+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B575344F1F267D0B009D4904 /* Array+Helpers.swift */; }; B57534521F267D63009D4904 /* ArrayHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57534511F267D63009D4904 /* ArrayHelperTests.swift */; }; B57D1C3D1E92C38000EA4B16 /* HTMLAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57D1C3C1E92C38000EA4B16 /* HTMLAttachment.swift */; }; @@ -219,6 +222,9 @@ B542D6411E9EB122009D12D3 /* PreFormaterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreFormaterTests.swift; sourceTree = ""; }; B551A49F1E770B3800EE3A7F /* UIFont+Emoji.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIFont+Emoji.swift"; sourceTree = ""; }; B572AC271E817CFE008948C2 /* CommentAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommentAttachment.swift; sourceTree = ""; }; + B574F4A31FB0CF3A0048F355 /* NSAttributedStringKey+Conversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedStringKey+Conversion.swift"; sourceTree = ""; }; + B574F4AC1FB103430048F355 /* NSAttributedStringKey+Aztec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAttributedStringKey+Aztec.swift"; sourceTree = ""; }; + B574F4AE1FB110850048F355 /* NSAttributedStringKeyHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NSAttributedStringKeyHelperTests.swift; path = Extensions/NSAttributedStringKeyHelperTests.swift; sourceTree = ""; }; B575344F1F267D0B009D4904 /* Array+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Helpers.swift"; sourceTree = ""; }; B57534511F267D63009D4904 /* ArrayHelperTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ArrayHelperTests.swift; path = Extensions/ArrayHelperTests.swift; sourceTree = ""; }; B57D1C3C1E92C38000EA4B16 /* HTMLAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLAttachment.swift; sourceTree = ""; }; @@ -484,6 +490,7 @@ children = ( 599F25201D8BC9A1002871D6 /* HTMLConstants.swift */, 599F25211D8BC9A1002871D6 /* Metrics.swift */, + B574F4AC1FB103430048F355 /* NSAttributedStringKey+Aztec.swift */, ); path = Constants; sourceTree = ""; @@ -502,6 +509,7 @@ B5BC4FED1DA2C17800614582 /* NSAttributedString+Lists.swift */, F10BE6191EA7AE9D002E4625 /* NSAttributedString+ReplaceOcurrences.swift */, B50CE7311F1FA6260018CAA1 /* NSAttributedString+Strip.swift */, + B574F4A31FB0CF3A0048F355 /* NSAttributedStringKey+Conversion.swift */, FFD0FEB61DAE59A700430586 /* NSLayoutManager+Attachments.swift */, F10BE6171EA7ADA6002E4625 /* NSMutableAttributedString+ReplaceOcurrences.swift */, F18733C41DA096EE005AEB80 /* NSRange+Helpers.swift */, @@ -774,6 +782,7 @@ B5BC4FF11DA2D17000614582 /* NSAttributedStringListsTests.swift */, F10BE61B1EA7B1DB002E4625 /* NSAttributedStringReplaceOcurrencesTests.swift */, F17BC8741F4E48FF00398E2B /* NSAttributedStringHTMLInitializerTests.swift */, + B574F4AE1FB110850048F355 /* NSAttributedStringKeyHelperTests.swift */, F14665441EA7C230008DE2B8 /* NSMutableAttributedStringReplaceOcurrencesTests.swift */, F18733C71DA09737005AEB80 /* NSRangeComparisonTests.swift */, F1000CE81EAA5C720000B15B /* StringEndOfLineTests.swift */, @@ -910,12 +919,12 @@ 5951CB8D1D8BC93600E1866F = { CreatedOnToolsVersion = 8.0; DevelopmentTeam = PZYM8XX95Q; - LastSwiftMigration = 0810; + LastSwiftMigration = 0910; ProvisioningStyle = Manual; }; 5951CB961D8BC93600E1866F = { CreatedOnToolsVersion = 8.0; - LastSwiftMigration = 0820; + LastSwiftMigration = 0910; ProvisioningStyle = Automatic; }; E8CE3EFE1F213AAA003254AB = { @@ -995,6 +1004,7 @@ B5C99D3F1E72E2E700335355 /* UIStackView+Helpers.swift in Sources */, F1C05B991E37F99D007510EA /* Character+Name.swift in Sources */, FF20D6421EDC389A00294B78 /* Processor.swift in Sources */, + B574F4AD1FB103430048F355 /* NSAttributedStringKey+Aztec.swift in Sources */, 599F253B1D8BC9A1002871D6 /* InNodesConverter.swift in Sources */, F18B81EB1EA5601000885F43 /* StringUnicodeScalarView+RangeConversion.swift in Sources */, F127F7141F0591AD008A00D7 /* CSSAttribute.swift in Sources */, @@ -1006,6 +1016,7 @@ 599F254F1D8BC9A1002871D6 /* FormatBarItem.swift in Sources */, F17BC8931F4E4BA500398E2B /* HTMLRepresentation.swift in Sources */, F18B81ED1EA560B700885F43 /* StringUTF16+RangeConversion.swift in Sources */, + B574F4A41FB0CF3B0048F355 /* NSAttributedStringKey+Conversion.swift in Sources */, F12F58631EF20394008AE298 /* AttributeFormatter.swift in Sources */, F19544051F588F1A00671B73 /* CSSParser.swift in Sources */, 599F254E1D8BC9A1002871D6 /* FormatBarDelegate.swift in Sources */, @@ -1103,6 +1114,7 @@ F1953E251F4E544A00C717C9 /* HTMLParserTests.swift in Sources */, B5C16A631F4DF77300B113CF /* HeaderFormatterTests.swift in Sources */, B5D575881F2288E2003A62F6 /* TextViewStubAttachmentDelegate.swift in Sources */, + B574F4AF1FB110850048F355 /* NSAttributedStringKeyHelperTests.swift in Sources */, F168DB861F6381A00009BD0E /* CSSParserTests.swift in Sources */, F1000CE91EAA5C720000B15B /* StringEndOfLineTests.swift in Sources */, F17BC8751F4E48FF00398E2B /* NSAttributedStringHTMLInitializerTests.swift in Sources */, @@ -1267,7 +1279,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_INCLUDE_PATHS = "$(PROJECT_DIR)/Aztec/Modulemaps/libxml2"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 1; }; name = Debug; @@ -1295,7 +1308,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_INCLUDE_PATHS = "$(PROJECT_DIR)/Aztec/Modulemaps/libxml2"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 1; }; name = Release; @@ -1315,7 +1329,8 @@ PRODUCT_BUNDLE_IDENTIFIER = com.wordpress.AztecTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -1333,7 +1348,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.wordpress.AztecTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; }; name = Release; }; @@ -1462,7 +1478,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_INCLUDE_PATHS = "$(PROJECT_DIR)/Aztec/Modulemaps/libxml2"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 1; }; name = Profiling; @@ -1481,7 +1498,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.wordpress.AztecTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; }; name = Profiling; }; @@ -1569,7 +1587,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_INCLUDE_PATHS = "$(PROJECT_DIR)/Aztec/Modulemaps/libxml2"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 1; }; name = "Release-Alpha"; @@ -1589,7 +1608,8 @@ PRODUCT_BUNDLE_IDENTIFIER = com.wordpress.AztecTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; }; name = "Release-Alpha"; }; diff --git a/Aztec/Classes/Constants/NSAttributedStringKey+Aztec.swift b/Aztec/Classes/Constants/NSAttributedStringKey+Aztec.swift new file mode 100644 index 000000000..261e9cb5d --- /dev/null +++ b/Aztec/Classes/Constants/NSAttributedStringKey+Aztec.swift @@ -0,0 +1,46 @@ +import Foundation + +// MARK: - Aztec NSAttributedString Keys +// +public extension NSAttributedStringKey { + + /// Key used to store Bold Tag Metadata, by our BoldFormatter. + /// + public static let boldHtmlRepresentation = NSAttributedStringKey("Bold.htmlRepresentation") + + /// Key used to store Color Tags Metadata, by our ColorFormatter. + /// + public static let colorHtmlRepresentation = NSAttributedStringKey("Color.htmlRepresentation") + + /// Key used to store HR Tag Metadata, by our HRFormatter. + /// + public static let hrHtmlRepresentation = NSAttributedStringKey("HR.htmlRepresentation") + + /// Key used to store Image Tag Metadata, by our ImageFormatter. + /// + public static let imageHtmlRepresentation = NSAttributedStringKey("Image.htmlRepresentation") + + /// Key used to store Italics Tag Metadata, by our ItalicFormatter. + /// + public static let italicHtmlRepresentation = NSAttributedStringKey("Italic.htmlRepresentation") + + /// Key used to store Link Tag Metadata, by our LinkFormatter. + /// + public static let linkHtmlRepresentation = NSAttributedStringKey("Link.htmlRepresentation") + + /// Key used to store Strike Tag Metadata, by our StrikeFormatter. + /// + public static let strikethroughHtmlRepresentation = NSAttributedStringKey("Strike.htmlRepresentation") + + /// Key used to store UnderlineHTMLRepresentations, by our UnderlineFormatter. + /// + public static let underlineHtmlRepresentation = NSAttributedStringKey("Underline.htmlRepresentation") + + /// Key used to store UnsupportedHTML Snippets, by our HTML Parser. + /// + public static let unsupportedHtml = NSAttributedStringKey("UnsupportedHTMLAttributeName") + + /// Key used to store VideoHTMLRepresentations, by our VideoFormatter. + /// + public static let videoHtmlRepresentation = NSAttributedStringKey("Video.htmlRepresentation") +} diff --git a/Aztec/Classes/Extensions/NSAttributedString+Analyzers.swift b/Aztec/Classes/Extensions/NSAttributedString+Analyzers.swift index d9351f6e6..4fd484f2a 100644 --- a/Aztec/Classes/Extensions/NSAttributedString+Analyzers.swift +++ b/Aztec/Classes/Extensions/NSAttributedString+Analyzers.swift @@ -14,7 +14,7 @@ extension NSAttributedString { return false } - return attribute(NSLinkAttributeName, at: beforeRange.location, effectiveRange: nil) != nil + return attribute(.link, at: beforeRange.location, effectiveRange: nil) != nil } /// Returns true if the text immediately succeding a given location contains the NSLinkAttribute. @@ -25,7 +25,7 @@ extension NSAttributedString { return false } - return attribute(NSLinkAttributeName, at: afterRange.location, effectiveRange: nil) != nil + return attribute(.link, at: afterRange.location, effectiveRange: nil) != nil } /// Returns the Substring at the specified range, whenever the received range is valid, or nil diff --git a/Aztec/Classes/Extensions/NSAttributedString+Attachments.swift b/Aztec/Classes/Extensions/NSAttributedString+Attachments.swift index 65ff6df17..342b2f9ec 100644 --- a/Aztec/Classes/Extensions/NSAttributedString+Attachments.swift +++ b/Aztec/Classes/Extensions/NSAttributedString+Attachments.swift @@ -20,9 +20,9 @@ extension NSAttributedString /// Helper Initializer: returns an Attributed String, with the specified attachment, styled with a given /// collection of attributes. /// - convenience init(attachment: NSTextAttachment, attributes: [String: Any]) { + convenience init(attachment: NSTextAttachment, attributes: [NSAttributedStringKey: Any]) { var attributesWithAttachment = attributes - attributesWithAttachment[NSAttachmentAttributeName] = attachment + attributesWithAttachment[.attachment] = attachment self.init(string: NSAttributedString.textAttachmentString, attributes: attributesWithAttachment) } @@ -52,7 +52,7 @@ extension NSAttributedString /// func enumerateAttachmentsOfType(_ type: T.Type, range: NSRange? = nil, block: ((T, NSRange, UnsafeMutablePointer) -> Void)) { let range = range ?? NSMakeRange(0, length) - enumerateAttribute(NSAttachmentAttributeName, in: range, options: []) { (object, range, stop) in + enumerateAttribute(.attachment, in: range, options: []) { (object, range, stop) in if let object = object as? T { block(object, range, stop) } @@ -69,7 +69,7 @@ extension NSAttributedString public func ranges(forAttachment attachment: NSTextAttachment) -> [NSRange] { let range = NSRange(location: 0, length: length) var attachmentRanges = [NSRange]() - enumerateAttribute(NSAttachmentAttributeName, in: range, options: []) { (value, effectiveRange, nil) in + enumerateAttribute(.attachment, in: range, options: []) { (value, effectiveRange, nil) in guard let foundAttachment = value as? NSTextAttachment, foundAttachment == attachment else { return } diff --git a/Aztec/Classes/Extensions/NSAttributedString+CharacterName.swift b/Aztec/Classes/Extensions/NSAttributedString+CharacterName.swift index b79b89531..8ec869e25 100644 --- a/Aztec/Classes/Extensions/NSAttributedString+CharacterName.swift +++ b/Aztec/Classes/Extensions/NSAttributedString+CharacterName.swift @@ -1,7 +1,7 @@ import Foundation extension NSAttributedString { - convenience init(_ characterName: Character.Name, attributes: [String:Any]?) { + convenience init(_ characterName: Character.Name, attributes: [NSAttributedStringKey: Any]?) { self.init(string: String(characterName), attributes: attributes) } } diff --git a/Aztec/Classes/Extensions/NSAttributedString+FontTraits.swift b/Aztec/Classes/Extensions/NSAttributedString+FontTraits.swift index c911fc2ff..dd7d70e96 100644 --- a/Aztec/Classes/Extensions/NSAttributedString+FontTraits.swift +++ b/Aztec/Classes/Extensions/NSAttributedString+FontTraits.swift @@ -15,7 +15,7 @@ public extension NSAttributedString { /// - Returns: True if found. /// public func fontTrait(_ traits: UIFontDescriptorSymbolicTraits, existsAtIndex index: Int) -> Bool { - guard let attr = attribute(NSFontAttributeName, at: index, effectiveRange: nil) else { + guard let attr = attribute(.font, at: index, effectiveRange: nil) else { return false } if let font = attr as? UIFont { @@ -37,7 +37,7 @@ public extension NSAttributedString { var spansRange = true // Assume we're removing the trait. If the trait is missing anywhere in the range assign it. - enumerateAttribute(NSFontAttributeName, + enumerateAttribute(.font, in: range, options: [], using: { (object: Any?, range: NSRange, stop: UnsafeMutablePointer) in @@ -75,7 +75,7 @@ public extension NSMutableAttributedString { fileprivate func modify(_ fontTraits: UIFontDescriptorSymbolicTraits, range: NSRange, enable: Bool) { - enumerateAttribute(NSFontAttributeName, + enumerateAttribute(.font, in: range, options: [], using: { (object: Any, range: NSRange, stop: UnsafeMutablePointer) in @@ -86,8 +86,8 @@ public extension NSMutableAttributedString { let newFont = font.modifyTraits(fontTraits, enable: enable) self.beginEditing() - self.removeAttribute(NSFontAttributeName, range: range) - self.addAttribute(NSFontAttributeName, value: newFont, range: range) + self.removeAttribute(.font, range: range) + self.addAttribute(.font, value: newFont, range: range) self.endEditing() }) } diff --git a/Aztec/Classes/Extensions/NSAttributedString+HTMLInitializer.swift b/Aztec/Classes/Extensions/NSAttributedString+HTMLInitializer.swift index 1c5a5077e..917706158 100644 --- a/Aztec/Classes/Extensions/NSAttributedString+HTMLInitializer.swift +++ b/Aztec/Classes/Extensions/NSAttributedString+HTMLInitializer.swift @@ -5,7 +5,7 @@ extension NSAttributedString { convenience init( withHTML html: String, - defaultAttributes: [String: Any], + defaultAttributes: [NSAttributedStringKey: Any], postProcessingHTMLWith htmlTreeProcessor: HTMLTreeProcessor? = nil) { let htmlParser = HTMLParser() diff --git a/Aztec/Classes/Extensions/NSAttributedString+Lists.swift b/Aztec/Classes/Extensions/NSAttributedString+Lists.swift index 475541af4..1e53dd622 100644 --- a/Aztec/Classes/Extensions/NSAttributedString+Lists.swift +++ b/Aztec/Classes/Extensions/NSAttributedString+Lists.swift @@ -18,7 +18,7 @@ extension NSAttributedString { var effectiveRange = NSRange() let targetRange = rangeOfEntireString guard - let paragraphStyle = attribute(NSParagraphStyleAttributeName, at: location, longestEffectiveRange: &effectiveRange, in: targetRange) as? ParagraphStyle, + let paragraphStyle = attribute(.paragraphStyle, at: location, longestEffectiveRange: &effectiveRange, in: targetRange) as? ParagraphStyle, let foundList = paragraphStyle.lists.last, foundList == list else { @@ -32,7 +32,7 @@ extension NSAttributedString { // so we need to expand the range to grab all the TextList coverage. while resultRange.location > 0 { guard - let paragraphStyle = attribute(NSParagraphStyleAttributeName, at: resultRange.location-1, longestEffectiveRange: &effectiveRange, in: targetRange) as? ParagraphStyle, + let paragraphStyle = attribute(.paragraphStyle, at: resultRange.location-1, longestEffectiveRange: &effectiveRange, in: targetRange) as? ParagraphStyle, let foundList = paragraphStyle.lists.last else { break; @@ -46,7 +46,7 @@ extension NSAttributedString { } while resultRange.endLocation < self.length { guard - let paragraphStyle = attribute(NSParagraphStyleAttributeName, at: resultRange.endLocation, longestEffectiveRange: &effectiveRange, in: targetRange) as? ParagraphStyle, + let paragraphStyle = attribute(.paragraphStyle, at: resultRange.endLocation, longestEffectiveRange: &effectiveRange, in: targetRange) as? ParagraphStyle, let foundList = paragraphStyle.lists.last else { break; @@ -72,7 +72,7 @@ extension NSAttributedString { /// func itemNumber(in list: TextList, at location: Int) -> Int { guard - let paragraphStyle = attribute(NSParagraphStyleAttributeName, at: location, effectiveRange: nil) as? ParagraphStyle + let paragraphStyle = attribute(.paragraphStyle, at: location, effectiveRange: nil) as? ParagraphStyle else { return NSNotFound } @@ -87,7 +87,7 @@ extension NSAttributedString { if NSLocationInRange(location, enclosingRange) { return numberInList } - if let paragraphStyle = attribute(NSParagraphStyleAttributeName, at: enclosingRange.location, effectiveRange: nil) as? ParagraphStyle, + if let paragraphStyle = attribute(.paragraphStyle, at: enclosingRange.location, effectiveRange: nil) as? ParagraphStyle, listDepth == paragraphStyle.lists.count { numberInList += 1 } @@ -108,7 +108,7 @@ extension NSAttributedString { /// - Returns: A TextList optional. /// func textListAttribute(atIndex index: Int) -> TextList? { - return (attribute(NSParagraphStyleAttributeName, at: index, effectiveRange: nil) as? ParagraphStyle)?.lists.last + return (attribute(.paragraphStyle, at: index, effectiveRange: nil) as? ParagraphStyle)?.lists.last } /// Returns the TextList attribute, assuming that there is one, spanning the specified Range. @@ -125,7 +125,7 @@ extension NSAttributedString { // var list: TextList? - enumerateAttribute(NSParagraphStyleAttributeName, in: range, options: []) { (attribute, range, stop) in + enumerateAttribute(.paragraphStyle, in: range, options: []) { (attribute, range, stop) in if let paragraphStyle = attribute as? ParagraphStyle { list = paragraphStyle.lists.last } diff --git a/Aztec/Classes/Extensions/NSAttributedString+Strip.swift b/Aztec/Classes/Extensions/NSAttributedString+Strip.swift index 169449649..00c384bc8 100644 --- a/Aztec/Classes/Extensions/NSAttributedString+Strip.swift +++ b/Aztec/Classes/Extensions/NSAttributedString+Strip.swift @@ -31,6 +31,6 @@ extension NSAttributedString { /// Returns true if a given instance's kind matches with a specified type. /// private func isObject(_ object: Any, kindOf type: T) -> Bool { - return type(of: object) is T + return Swift.type(of: object) is T } } diff --git a/Aztec/Classes/Extensions/NSAttributedStringKey+Conversion.swift b/Aztec/Classes/Extensions/NSAttributedStringKey+Conversion.swift new file mode 100644 index 000000000..c2318b520 --- /dev/null +++ b/Aztec/Classes/Extensions/NSAttributedStringKey+Conversion.swift @@ -0,0 +1,34 @@ +import Foundation +import UIKit + + +// MARK: - NSAttributedStringKey Helpers +// +extension NSAttributedStringKey { + + /// Converts a collection of NSAttributedString Attributes, with 'NSAttributedStringKey' instances as 'Keys', into an + /// equivalent collection that uses regular 'String' instances as keys. + /// + static func convertToRaw(attributes: [NSAttributedStringKey: Any]) -> [String: Any] { + var output = [String: Any]() + for (key, value) in attributes { + output[key.rawValue] = value + } + + return output + } + + + /// Converts a collection of NSAttributedString Attributes, with 'String' instances as 'Keys', into an equivalent + /// collection that uses the new 'NSAttributedStringKey' enum as keys. + /// + static func convertFromRaw(attributes: [String: Any]) -> [NSAttributedStringKey: Any] { + var output = [NSAttributedStringKey: Any]() + for (key, value) in attributes { + let wrappedKey = NSAttributedStringKey(key) + output[wrappedKey] = value + } + + return output + } +} diff --git a/Aztec/Classes/Extensions/NSTextingResult+Helpers.swift b/Aztec/Classes/Extensions/NSTextingResult+Helpers.swift index 274cc4b50..6d665136d 100644 --- a/Aztec/Classes/Extensions/NSTextingResult+Helpers.swift +++ b/Aztec/Classes/Extensions/NSTextingResult+Helpers.swift @@ -14,14 +14,14 @@ public extension NSTextCheckingResult { return nil } - let nsrange = rangeAt(position) + let nsrange = self.range(at: position) guard nsrange.location != NSNotFound else { return nil } let range = text.range(from: nsrange) - let captureGroup = text.substring(with: range) + let captureGroup = String(text[range]) return captureGroup } } diff --git a/Aztec/Classes/Extensions/String+EndOfLine.swift b/Aztec/Classes/Extensions/String+EndOfLine.swift index 6dfeaae32..a197ea823 100644 --- a/Aztec/Classes/Extensions/String+EndOfLine.swift +++ b/Aztec/Classes/Extensions/String+EndOfLine.swift @@ -68,7 +68,14 @@ extension String { } func isEndOfLine(at index: String.Index) -> Bool { - return index == endIndex || substring(with: index ..< self.index(after: index)).isEndOfLine() + guard index != endIndex else { + return true + } + + let range = index ..< self.index(after: index) + let slice = String(self[range]) + + return slice.isEndOfLine() } func isEndOfLine(atUTF16Offset utf16Offset: Int) -> Bool { diff --git a/Aztec/Classes/Extensions/String+Paragraph.swift b/Aztec/Classes/Extensions/String+Paragraph.swift index 64bb0370a..ac2969cfe 100644 --- a/Aztec/Classes/Extensions/String+Paragraph.swift +++ b/Aztec/Classes/Extensions/String+Paragraph.swift @@ -31,7 +31,8 @@ extension String { return true } - let endingString = substring(with: index ..< self.index(after: index)) + let endingRange = index ..< self.index(after: index) + let endingString = String(self[endingRange]) let paragraphSeparators = [String(.carriageReturn), String(.lineFeed), String(.paragraphSeparator)] return paragraphSeparators.contains(endingString) diff --git a/Aztec/Classes/Extensions/String+RangeConversion.swift b/Aztec/Classes/Extensions/String+RangeConversion.swift index 243674f37..85a85e4e3 100644 --- a/Aztec/Classes/Extensions/String+RangeConversion.swift +++ b/Aztec/Classes/Extensions/String+RangeConversion.swift @@ -113,8 +113,11 @@ public extension String { /// func utf16NSRange(from range: Range) -> NSRange { - let lowerBound = range.lowerBound.samePosition(in: utf16) - let upperBound = range.upperBound.samePosition(in: utf16) + guard let lowerBound = range.lowerBound.samePosition(in: utf16), + let upperBound = range.upperBound.samePosition(in: utf16) else + { + fatalError() + } let location = utf16.distance(from: utf16.startIndex, to: lowerBound) let length = utf16.distance(from: lowerBound, to: upperBound) @@ -150,7 +153,10 @@ public extension String { return nil } let afterIndex = index(after: currentIndex) - let after16 = afterIndex.samePosition(in: utf16) + guard let after16 = afterIndex.samePosition(in: utf16) else { + return nil + } + return utf16.distance(from: utf16.startIndex, to: after16) } @@ -160,7 +166,10 @@ public extension String { } let beforeIndex = index(before: currentIndex) - let before16 = beforeIndex.samePosition(in: utf16) + guard let before16 = beforeIndex.samePosition(in: utf16) else { + return nil + } + return utf16.distance(from: utf16.startIndex, to: before16) } diff --git a/Aztec/Classes/Extensions/UIPasteboard+Helpers.swift b/Aztec/Classes/Extensions/UIPasteboard+Helpers.swift index b4d33c48d..7805dd1a0 100644 --- a/Aztec/Classes/Extensions/UIPasteboard+Helpers.swift +++ b/Aztec/Classes/Extensions/UIPasteboard+Helpers.swift @@ -75,9 +75,9 @@ private extension UIPasteboard { /// String Initialization Options /// private struct StringOptions { - static let RTFText = [NSDocumentTypeDocumentAttribute: NSRTFTextDocumentType] - static let RTFDText = [NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType] - static let plainText = [NSDocumentTypeDocumentAttribute: NSPlainTextDocumentType] + static let RTFText: [NSAttributedString.DocumentReadingOptionKey: NSAttributedString.DocumentType] = [.documentType: .rtf] + static let RTFDText: [NSAttributedString.DocumentReadingOptionKey: NSAttributedString.DocumentType] = [.documentType: .rtfd] + static let plainText: [NSAttributedString.DocumentReadingOptionKey: NSAttributedString.DocumentType] = [.documentType: .plain] } /// Attempts to unarchive a Pasteboard's Entry into a NSAttributedString Instance. @@ -88,7 +88,7 @@ private extension UIPasteboard { /// /// - Returns: NSAttributed String with the contents of the specified Pasteboard entry, if any. /// - private func unarchiveAttributedString(fromPasteboardCFType type: CFString, with options: [String: Any]) -> NSAttributedString? { + private func unarchiveAttributedString(fromPasteboardCFType type: CFString, with options: [NSAttributedString.DocumentReadingOptionKey: Any]) -> NSAttributedString? { guard let data = data(forPasteboardType: String(type)) else { return nil } diff --git a/Aztec/Classes/Formatters/Base/AttributeFormatter.swift b/Aztec/Classes/Formatters/Base/AttributeFormatter.swift index 3e0e0be9d..0707942a6 100644 --- a/Aztec/Classes/Formatters/Base/AttributeFormatter.swift +++ b/Aztec/Classes/Formatters/Base/AttributeFormatter.swift @@ -11,7 +11,7 @@ protocol AttributeFormatter { /// Attributes to be used the Content Placeholder, when / if needed. /// - var placeholderAttributes: [String: Any]? { get } + var placeholderAttributes: [NSAttributedStringKey: Any]? { get } /// Toggles an attribute in the specified range of a text storage, and returns the new /// Selected Range. This is required because, in several scenarios, we may need to add a "Zero Width Space", @@ -29,7 +29,7 @@ protocol AttributeFormatter { /// /// - Parameter attributes: attributes to be checked. /// - Returns: the new attribute dictionary with the toggle applied. - @discardableResult func toggle(in attributes: [String: Any]) -> [String: Any] + @discardableResult func toggle(in attributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] /// Checks if the attribute is present in a given Attributed String at the specified index. /// @@ -40,7 +40,7 @@ protocol AttributeFormatter { /// - Parameter attributes: the original attributes to apply to /// - Returns: the resulting attributes dictionary /// - func apply(to attributes: [String: Any]) -> [String: Any] + func apply(to attributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] /// Apply the compound attributes to the provided attributes dictionary. /// @@ -51,14 +51,14 @@ protocol AttributeFormatter { /// - Returns: /// - the resulting attributes dictionary /// - func apply(to attributes: [String: Any], andStore representation: HTMLRepresentation?) -> [String: Any] + func apply(to attributes: [NSAttributedStringKey: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedStringKey: Any] /// Remove the compound attributes from the provided list. /// /// - Parameter attributes: the original attributes to remove from /// - Returns: the resulting attributes dictionary /// - func remove(from attributes: [String: Any]) -> [String: Any] + func remove(from attributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] /// Applies the Formatter's Attributes into a given string, at the specified range. /// @@ -70,7 +70,7 @@ protocol AttributeFormatter { /// Checks if the attribute is present in a dictionary of attributes. /// - func present(in attributes: [String: Any]) -> Bool + func present(in attributes: [NSAttributedStringKey: Any]) -> Bool func applicationRange(for range: NSRange, in text: NSAttributedString) -> NSRange @@ -85,7 +85,7 @@ extension AttributeFormatter { /// The default implementation forwards the call. This is probably good enough for all /// classes that implement this protocol. /// - func apply(to attributes: [String : Any]) -> [String: Any] { + func apply(to attributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] { return apply(to: attributes, andStore: nil) } @@ -122,7 +122,7 @@ extension AttributeFormatter { } @discardableResult - func toggle(in attributes: [String: Any]) -> [String: Any] { + func toggle(in attributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] { if present(in: attributes) { return remove(from: attributes) } else { diff --git a/Aztec/Classes/Formatters/Base/FontFormatter.swift b/Aztec/Classes/Formatters/Base/FontFormatter.swift index 81ccda35a..55749d3dc 100644 --- a/Aztec/Classes/Formatters/Base/FontFormatter.swift +++ b/Aztec/Classes/Formatters/Base/FontFormatter.swift @@ -3,12 +3,12 @@ import UIKit class FontFormatter: AttributeFormatter { - var placeholderAttributes: [String : Any]? { return nil } + var placeholderAttributes: [NSAttributedStringKey: Any]? { return nil } - let htmlRepresentationKey: String + let htmlRepresentationKey: NSAttributedStringKey let traits: UIFontDescriptorSymbolicTraits - init(traits: UIFontDescriptorSymbolicTraits, htmlRepresentationKey: String) { + init(traits: UIFontDescriptorSymbolicTraits, htmlRepresentationKey: NSAttributedStringKey) { self.htmlRepresentationKey = htmlRepresentationKey self.traits = traits } @@ -21,9 +21,9 @@ class FontFormatter: AttributeFormatter { return false } - func apply(to attributes: [String : Any], andStore representation: HTMLRepresentation?) -> [String: Any] { + func apply(to attributes: [NSAttributedStringKey: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedStringKey: Any] { - guard let font = attributes[NSFontAttributeName] as? UIFont else { + guard let font = attributes[.font] as? UIFont else { return attributes } @@ -31,28 +31,28 @@ class FontFormatter: AttributeFormatter { var resultingAttributes = attributes - resultingAttributes[NSFontAttributeName] = newFont + resultingAttributes[.font] = newFont resultingAttributes[htmlRepresentationKey] = representation return resultingAttributes } - func remove(from attributes: [String : Any]) -> [String: Any] { + func remove(from attributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] { var resultingAttributes = attributes - guard let font = attributes[NSFontAttributeName] as? UIFont else { + guard let font = attributes[.font] as? UIFont else { return attributes } let newFont = font.modifyTraits(traits, enable: false) - resultingAttributes[NSFontAttributeName] = newFont + resultingAttributes[.font] = newFont resultingAttributes.removeValue(forKey: htmlRepresentationKey) return resultingAttributes } - func present(in attributes: [String : Any]) -> Bool { - guard let font = attributes[NSFontAttributeName] as? UIFont else { + func present(in attributes: [NSAttributedStringKey : Any]) -> Bool { + guard let font = attributes[.font] as? UIFont else { return false } let enabled = font.containsTraits(traits) diff --git a/Aztec/Classes/Formatters/Base/StandardAttributeFormatter.swift b/Aztec/Classes/Formatters/Base/StandardAttributeFormatter.swift index 6bfc34f4c..c7ae2b584 100644 --- a/Aztec/Classes/Formatters/Base/StandardAttributeFormatter.swift +++ b/Aztec/Classes/Formatters/Base/StandardAttributeFormatter.swift @@ -4,16 +4,16 @@ import UIKit /// Formatter to apply simple value (NSNumber, UIColor) attributes to an attributed string. class StandardAttributeFormatter: AttributeFormatter { - var placeholderAttributes: [String : Any]? { return nil } + var placeholderAttributes: [NSAttributedStringKey: Any]? { return nil } - let attributeKey: String + let attributeKey: NSAttributedStringKey var attributeValue: Any - let htmlRepresentationKey: String + let htmlRepresentationKey: NSAttributedStringKey // MARK: - Init - init(attributeKey: String, attributeValue: Any, htmlRepresentationKey: String) { + init(attributeKey: NSAttributedStringKey, attributeValue: Any, htmlRepresentationKey: NSAttributedStringKey) { self.attributeKey = attributeKey self.attributeValue = attributeValue self.htmlRepresentationKey = htmlRepresentationKey @@ -27,7 +27,7 @@ class StandardAttributeFormatter: AttributeFormatter { return false } - func apply(to attributes: [String : Any], andStore representation: HTMLRepresentation?) -> [String: Any] { + func apply(to attributes: [NSAttributedStringKey: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedStringKey: Any] { var resultingAttributes = attributes resultingAttributes[attributeKey] = attributeValue @@ -36,7 +36,7 @@ class StandardAttributeFormatter: AttributeFormatter { return resultingAttributes } - func remove(from attributes: [String : Any]) -> [String: Any] { + func remove(from attributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] { var resultingAttributes = attributes resultingAttributes.removeValue(forKey: attributeKey) @@ -45,7 +45,7 @@ class StandardAttributeFormatter: AttributeFormatter { return resultingAttributes } - func present(in attributes: [String : Any]) -> Bool { + func present(in attributes: [NSAttributedStringKey: Any]) -> Bool { let enabled = attributes[attributeKey] != nil return enabled } diff --git a/Aztec/Classes/Formatters/Implementations/BlockquoteFormatter.swift b/Aztec/Classes/Formatters/Implementations/BlockquoteFormatter.swift index 698a6983b..9810ccace 100644 --- a/Aztec/Classes/Formatters/Implementations/BlockquoteFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/BlockquoteFormatter.swift @@ -8,34 +8,34 @@ class BlockquoteFormatter: ParagraphAttributeFormatter { /// Attributes to be added by default /// - let placeholderAttributes: [String : Any]? + let placeholderAttributes: [NSAttributedStringKey: Any]? /// Designated Initializer /// - init(placeholderAttributes: [String : Any]? = nil) { + init(placeholderAttributes: [NSAttributedStringKey: Any]? = nil) { self.placeholderAttributes = placeholderAttributes } // MARK: - Overwriten Methods - func apply(to attributes: [String : Any], andStore representation: HTMLRepresentation?) -> [String: Any] { + func apply(to attributes: [NSAttributedStringKey: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedStringKey: Any] { let newParagraphStyle = ParagraphStyle() - if let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? NSParagraphStyle { + if let paragraphStyle = attributes[.paragraphStyle] as? NSParagraphStyle { newParagraphStyle.setParagraphStyle(paragraphStyle) } newParagraphStyle.appendProperty(Blockquote(with: representation)) var resultingAttributes = attributes - resultingAttributes[NSParagraphStyleAttributeName] = newParagraphStyle + resultingAttributes[.paragraphStyle] = newParagraphStyle return resultingAttributes } - func remove(from attributes:[String: Any]) -> [String: Any] { - guard let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle, + func remove(from attributes:[NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] { + guard let paragraphStyle = attributes[.paragraphStyle] as? ParagraphStyle, !paragraphStyle.blockquotes.isEmpty else { return attributes @@ -46,15 +46,14 @@ class BlockquoteFormatter: ParagraphAttributeFormatter { newParagraphStyle.removeProperty(ofType: Blockquote.self) var resultingAttributes = attributes - resultingAttributes[NSParagraphStyleAttributeName] = newParagraphStyle + resultingAttributes[.paragraphStyle] = newParagraphStyle return resultingAttributes } - func present(in attributes: [String : Any]) -> Bool { - guard let style = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle else { + func present(in attributes: [NSAttributedStringKey: Any]) -> Bool { + guard let style = attributes[.paragraphStyle] as? ParagraphStyle else { return false } return !style.blockquotes.isEmpty } } - diff --git a/Aztec/Classes/Formatters/Implementations/BoldFormatter.swift b/Aztec/Classes/Formatters/Implementations/BoldFormatter.swift index 48aa281e7..78214a8d7 100644 --- a/Aztec/Classes/Formatters/Implementations/BoldFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/BoldFormatter.swift @@ -1,9 +1,7 @@ import UIKit class BoldFormatter: FontFormatter { - static let htmlRepresentationKey = "Bold.htmlRepresentation" - init() { - super.init(traits: .traitBold, htmlRepresentationKey: BoldFormatter.htmlRepresentationKey) + super.init(traits: .traitBold, htmlRepresentationKey: .boldHtmlRepresentation) } } diff --git a/Aztec/Classes/Formatters/Implementations/ColorFormatter.swift b/Aztec/Classes/Formatters/Implementations/ColorFormatter.swift index 5ee0c0d0d..9216f162b 100644 --- a/Aztec/Classes/Formatters/Implementations/ColorFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/ColorFormatter.swift @@ -1,11 +1,10 @@ import UIKit class ColorFormatter: StandardAttributeFormatter { - static let htmlRepresentationKey = "Color.htmlRepresentation" init(color: UIColor = .black) { - super.init(attributeKey: NSForegroundColorAttributeName, + super.init(attributeKey: .foregroundColor, attributeValue: color, - htmlRepresentationKey: ColorFormatter.htmlRepresentationKey) + htmlRepresentationKey: .colorHtmlRepresentation) } } diff --git a/Aztec/Classes/Formatters/Implementations/HRFormatter.swift b/Aztec/Classes/Formatters/Implementations/HRFormatter.swift index 5f032ccc6..660211fb4 100644 --- a/Aztec/Classes/Formatters/Implementations/HRFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/HRFormatter.swift @@ -1,11 +1,11 @@ import UIKit + class HRFormatter: StandardAttributeFormatter { - static let htmlRepresentationKey = "HR.htmlRepresentation" init() { - super.init(attributeKey: NSAttachmentAttributeName, + super.init(attributeKey: .attachment, attributeValue: LineAttachment(), - htmlRepresentationKey: HRFormatter.htmlRepresentationKey) + htmlRepresentationKey: .hrHtmlRepresentation) } } diff --git a/Aztec/Classes/Formatters/Implementations/HTMLDivFormatter.swift b/Aztec/Classes/Formatters/Implementations/HTMLDivFormatter.swift index a4b5125fe..d65738035 100644 --- a/Aztec/Classes/Formatters/Implementations/HTMLDivFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/HTMLDivFormatter.swift @@ -8,22 +8,22 @@ class HTMLDivFormatter: ParagraphAttributeFormatter { /// Attributes to be added by default /// - let placeholderAttributes: [String: Any]? + let placeholderAttributes: [NSAttributedStringKey: Any]? /// Designated Initializer /// - init(placeholderAttributes: [String: Any]? = nil) { + init(placeholderAttributes: [NSAttributedStringKey: Any]? = nil) { self.placeholderAttributes = placeholderAttributes } // MARK: - Overwriten Methods - func apply(to attributes: [String: Any], andStore representation: HTMLRepresentation?) -> [String: Any] { + func apply(to attributes: [NSAttributedStringKey: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedStringKey: Any] { let newParagraphStyle = ParagraphStyle() - if let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? NSParagraphStyle { + if let paragraphStyle = attributes[.paragraphStyle] as? NSParagraphStyle { newParagraphStyle.setParagraphStyle(paragraphStyle) } @@ -31,12 +31,12 @@ class HTMLDivFormatter: ParagraphAttributeFormatter { newParagraphStyle.appendProperty(newProperty) var resultingAttributes = attributes - resultingAttributes[NSParagraphStyleAttributeName] = newParagraphStyle + resultingAttributes[.paragraphStyle] = newParagraphStyle return resultingAttributes } - func remove(from attributes:[String: Any]) -> [String: Any] { - guard let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle, + func remove(from attributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] { + guard let paragraphStyle = attributes[.paragraphStyle] as? ParagraphStyle, !paragraphStyle.htmlDiv.isEmpty else { return attributes @@ -47,12 +47,12 @@ class HTMLDivFormatter: ParagraphAttributeFormatter { newParagraphStyle.removeProperty(ofType: HTMLDiv.self) var resultingAttributes = attributes - resultingAttributes[NSParagraphStyleAttributeName] = newParagraphStyle + resultingAttributes[.paragraphStyle] = newParagraphStyle return resultingAttributes } - func present(in attributes: [String: Any]) -> Bool { - let style = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle + func present(in attributes: [NSAttributedStringKey: Any]) -> Bool { + let style = attributes[.paragraphStyle] as? ParagraphStyle return style?.htmlDiv.isEmpty == false } } diff --git a/Aztec/Classes/Formatters/Implementations/HTMLParagraphFormatter.swift b/Aztec/Classes/Formatters/Implementations/HTMLParagraphFormatter.swift index 53caa35c4..545a6b688 100644 --- a/Aztec/Classes/Formatters/Implementations/HTMLParagraphFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/HTMLParagraphFormatter.swift @@ -8,34 +8,34 @@ class HTMLParagraphFormatter: ParagraphAttributeFormatter { /// Attributes to be added by default /// - let placeholderAttributes: [String : Any]? + let placeholderAttributes: [NSAttributedStringKey: Any]? /// Designated Initializer /// - init(placeholderAttributes: [String : Any]? = nil) { + init(placeholderAttributes: [NSAttributedStringKey: Any]? = nil) { self.placeholderAttributes = placeholderAttributes } // MARK: - Overwriten Methods - func apply(to attributes: [String : Any], andStore representation: HTMLRepresentation?) -> [String: Any] { + func apply(to attributes: [NSAttributedStringKey: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedStringKey: Any] { let newParagraphStyle = ParagraphStyle() - if let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? NSParagraphStyle { + if let paragraphStyle = attributes[.paragraphStyle] as? NSParagraphStyle { newParagraphStyle.setParagraphStyle(paragraphStyle) } newParagraphStyle.appendProperty(HTMLParagraph(with: representation)) var resultingAttributes = attributes - resultingAttributes[NSParagraphStyleAttributeName] = newParagraphStyle + resultingAttributes[.paragraphStyle] = newParagraphStyle return resultingAttributes } - func remove(from attributes:[String: Any]) -> [String: Any] { - guard let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle, + func remove(from attributes:[NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] { + guard let paragraphStyle = attributes[.paragraphStyle] as? ParagraphStyle, !paragraphStyle.htmlParagraph.isEmpty else { return attributes @@ -46,12 +46,12 @@ class HTMLParagraphFormatter: ParagraphAttributeFormatter { newParagraphStyle.removeProperty(ofType: HTMLParagraph.self) var resultingAttributes = attributes - resultingAttributes[NSParagraphStyleAttributeName] = newParagraphStyle + resultingAttributes[.paragraphStyle] = newParagraphStyle return resultingAttributes } - func present(in attributes: [String : Any]) -> Bool { - guard let style = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle else { + func present(in attributes: [NSAttributedStringKey: Any]) -> Bool { + guard let style = attributes[.paragraphStyle] as? ParagraphStyle else { return false } return !style.htmlParagraph.isEmpty diff --git a/Aztec/Classes/Formatters/Implementations/HeaderFormatter.swift b/Aztec/Classes/Formatters/Implementations/HeaderFormatter.swift index f2f4a62fe..d2f698f26 100644 --- a/Aztec/Classes/Formatters/Implementations/HeaderFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/HeaderFormatter.swift @@ -12,12 +12,12 @@ open class HeaderFormatter: ParagraphAttributeFormatter { /// Attributes to be added by default /// - let placeholderAttributes: [String : Any]? + let placeholderAttributes: [NSAttributedStringKey: Any]? /// Designated Initializer /// - init(headerLevel: Header.HeaderType = .h1, placeholderAttributes: [String: Any]? = nil) { + init(headerLevel: Header.HeaderType = .h1, placeholderAttributes: [NSAttributedStringKey: Any]? = nil) { self.headerLevel = headerLevel self.placeholderAttributes = placeholderAttributes } @@ -25,13 +25,13 @@ open class HeaderFormatter: ParagraphAttributeFormatter { // MARK: - Overwriten Methods - func apply(to attributes: [String: Any], andStore representation: HTMLRepresentation?) -> [String: Any] { - guard let font = attributes[NSFontAttributeName] as? UIFont else { + func apply(to attributes: [NSAttributedStringKey: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedStringKey: Any] { + guard let font = attributes[.font] as? UIFont else { return attributes } let newParagraphStyle = ParagraphStyle() - if let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? NSParagraphStyle { + if let paragraphStyle = attributes[.paragraphStyle] as? NSParagraphStyle { newParagraphStyle.setParagraphStyle(paragraphStyle) } @@ -45,14 +45,14 @@ open class HeaderFormatter: ParagraphAttributeFormatter { let targetFontSize = headerFontSize(for: headerLevel, defaultSize: defaultSize) var resultingAttributes = attributes - resultingAttributes[NSParagraphStyleAttributeName] = newParagraphStyle - resultingAttributes[NSFontAttributeName] = font.withSize(CGFloat(targetFontSize)) + resultingAttributes[.paragraphStyle] = newParagraphStyle + resultingAttributes[.font] = font.withSize(CGFloat(targetFontSize)) return resultingAttributes } - func remove(from attributes: [String: Any]) -> [String: Any] { - guard let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle, + func remove(from attributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] { + guard let paragraphStyle = attributes[.paragraphStyle] as? ParagraphStyle, let header = paragraphStyle.headers.last, header.level != .none else { @@ -64,17 +64,17 @@ open class HeaderFormatter: ParagraphAttributeFormatter { newParagraphStyle.removeProperty(ofType: Header.self) var resultingAttributes = attributes - resultingAttributes[NSParagraphStyleAttributeName] = newParagraphStyle + resultingAttributes[.paragraphStyle] = newParagraphStyle - if let font = attributes[NSFontAttributeName] as? UIFont { - resultingAttributes[NSFontAttributeName] = font.withSize(CGFloat(header.defaultFontSize)) + if let font = attributes[.font] as? UIFont { + resultingAttributes[.font] = font.withSize(CGFloat(header.defaultFontSize)) } return resultingAttributes } - func present(in attributes: [String: Any]) -> Bool { - guard let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle else { + func present(in attributes: [NSAttributedStringKey: Any]) -> Bool { + guard let paragraphStyle = attributes[.paragraphStyle] as? ParagraphStyle else { return false } @@ -87,14 +87,14 @@ open class HeaderFormatter: ParagraphAttributeFormatter { // private extension HeaderFormatter { - func defaultFontSize(from attributes: [String: Any]) -> Float? { - if let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle, + func defaultFontSize(from attributes: [NSAttributedStringKey: Any]) -> Float? { + if let paragraphStyle = attributes[.paragraphStyle] as? ParagraphStyle, let lastHeader = paragraphStyle.headers.last { return lastHeader.defaultFontSize } - if let font = attributes[NSFontAttributeName] as? UIFont { + if let font = attributes[.font] as? UIFont { return Float(font.pointSize) } diff --git a/Aztec/Classes/Formatters/Implementations/ImageFormatter.swift b/Aztec/Classes/Formatters/Implementations/ImageFormatter.swift index c79dab894..0db7c6b3b 100644 --- a/Aztec/Classes/Formatters/Implementations/ImageFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/ImageFormatter.swift @@ -1,16 +1,15 @@ import UIKit class ImageFormatter: StandardAttributeFormatter { - static let htmlRepresentationKey = "Image.htmlRepresentation" init() { super.init( - attributeKey: NSAttachmentAttributeName, + attributeKey: .attachment, attributeValue: ImageAttachment(identifier: NSUUID().uuidString), - htmlRepresentationKey: ImageFormatter.htmlRepresentationKey) + htmlRepresentationKey: .imageHtmlRepresentation) } - override func apply(to attributes: [String : Any], andStore representation: HTMLRepresentation?) -> [String: Any] { + override func apply(to attributes: [NSAttributedStringKey: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedStringKey: Any] { if let representation = representation { switch representation.kind { diff --git a/Aztec/Classes/Formatters/Implementations/ItalicFormatter.swift b/Aztec/Classes/Formatters/Implementations/ItalicFormatter.swift index 735d2e39d..ea886ea37 100644 --- a/Aztec/Classes/Formatters/Implementations/ItalicFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/ItalicFormatter.swift @@ -1,9 +1,8 @@ import UIKit class ItalicFormatter: FontFormatter { - static let htmlRepresentationKey = "Italic.htmlRepresentation" init() { - super.init(traits: .traitItalic, htmlRepresentationKey: ItalicFormatter.htmlRepresentationKey) + super.init(traits: .traitItalic, htmlRepresentationKey: .italicHtmlRepresentation) } } diff --git a/Aztec/Classes/Formatters/Implementations/LinkFormatter.swift b/Aztec/Classes/Formatters/Implementations/LinkFormatter.swift index 0774c1992..1a9469b3b 100644 --- a/Aztec/Classes/Formatters/Implementations/LinkFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/LinkFormatter.swift @@ -1,15 +1,14 @@ import UIKit class LinkFormatter: StandardAttributeFormatter { - static let htmlRepresentationKey = "Link.htmlRepresentation" init() { - super.init(attributeKey: NSLinkAttributeName, + super.init(attributeKey: .link, attributeValue: NSURL(string:"")!, - htmlRepresentationKey: LinkFormatter.htmlRepresentationKey) + htmlRepresentationKey: .linkHtmlRepresentation) } - override func apply(to attributes: [String : Any], andStore representation: HTMLRepresentation?) -> [String: Any] { + override func apply(to attributes: [NSAttributedStringKey: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedStringKey: Any] { if let representation = representation, case let .element(element) = representation.kind { diff --git a/Aztec/Classes/Formatters/Implementations/PreFormatter.swift b/Aztec/Classes/Formatters/Implementations/PreFormatter.swift index ad718ba72..532653cc8 100644 --- a/Aztec/Classes/Formatters/Implementations/PreFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/PreFormatter.swift @@ -12,12 +12,12 @@ open class PreFormatter: ParagraphAttributeFormatter { /// Attributes to be added by default /// - let placeholderAttributes: [String : Any]? + let placeholderAttributes: [NSAttributedStringKey: Any]? /// Designated Initializer /// - init(monospaceFont: UIFont = UIFont(descriptor:UIFontDescriptor(name: "Courier", size: 12), size:12), placeholderAttributes: [String : Any]? = nil) { + init(monospaceFont: UIFont = UIFont(descriptor:UIFontDescriptor(name: "Courier", size: 12), size:12), placeholderAttributes: [NSAttributedStringKey : Any]? = nil) { self.monospaceFont = monospaceFont self.placeholderAttributes = placeholderAttributes } @@ -25,19 +25,19 @@ open class PreFormatter: ParagraphAttributeFormatter { // MARK: - Overwriten Methods - func apply(to attributes: [String : Any], andStore representation: HTMLRepresentation?) -> [String: Any] { + func apply(to attributes: [NSAttributedStringKey: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedStringKey: Any] { var resultingAttributes = attributes let newParagraphStyle = ParagraphStyle() newParagraphStyle.appendProperty(HTMLPre(with: representation)) - resultingAttributes[NSParagraphStyleAttributeName] = newParagraphStyle - resultingAttributes[NSFontAttributeName] = monospaceFont + resultingAttributes[.paragraphStyle] = newParagraphStyle + resultingAttributes[.font] = monospaceFont return resultingAttributes } - func remove(from attributes: [String: Any]) -> [String: Any] { + func remove(from attributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] { guard let placeholderAttributes = placeholderAttributes else { return attributes } @@ -50,8 +50,8 @@ open class PreFormatter: ParagraphAttributeFormatter { return resultingAttributes } - func present(in attributes: [String : Any]) -> Bool { - let font = attributes[NSFontAttributeName] as? UIFont + func present(in attributes: [NSAttributedStringKey : Any]) -> Bool { + let font = attributes[.font] as? UIFont return font == monospaceFont } } diff --git a/Aztec/Classes/Formatters/Implementations/StrikethroughFormatter.swift b/Aztec/Classes/Formatters/Implementations/StrikethroughFormatter.swift index 9ea58fdaa..b67ba1189 100644 --- a/Aztec/Classes/Formatters/Implementations/StrikethroughFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/StrikethroughFormatter.swift @@ -1,11 +1,10 @@ import UIKit class StrikethroughFormatter: StandardAttributeFormatter { - static let htmlRepresentationKey = "Strike.htmlRepresentation" init() { - super.init(attributeKey: NSStrikethroughStyleAttributeName, + super.init(attributeKey: .strikethroughStyle, attributeValue: NSUnderlineStyle.styleSingle.rawValue, - htmlRepresentationKey: StrikethroughFormatter.htmlRepresentationKey) + htmlRepresentationKey: .strikethroughHtmlRepresentation) } } diff --git a/Aztec/Classes/Formatters/Implementations/TextListFormatter.swift b/Aztec/Classes/Formatters/Implementations/TextListFormatter.swift index 90f954280..bfd7ae0e0 100644 --- a/Aztec/Classes/Formatters/Implementations/TextListFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/TextListFormatter.swift @@ -12,14 +12,14 @@ class TextListFormatter: ParagraphAttributeFormatter { /// Attributes to be added by default /// - let placeholderAttributes: [String: Any]? + let placeholderAttributes: [NSAttributedStringKey: Any]? /// Tells if the formatter is increasing the depth of a list or simple changing the current one if any let increaseDepth: Bool /// Designated Initializer /// - init(style: TextList.Style, placeholderAttributes: [String: Any]? = nil, increaseDepth: Bool = false) { + init(style: TextList.Style, placeholderAttributes: [NSAttributedStringKey: Any]? = nil, increaseDepth: Bool = false) { self.listStyle = style self.placeholderAttributes = placeholderAttributes self.increaseDepth = increaseDepth @@ -28,9 +28,9 @@ class TextListFormatter: ParagraphAttributeFormatter { // MARK: - Overwriten Methods - func apply(to attributes: [String : Any], andStore representation: HTMLRepresentation?) -> [String: Any] { + func apply(to attributes: [NSAttributedStringKey: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedStringKey: Any] { let newParagraphStyle = ParagraphStyle() - if let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? NSParagraphStyle { + if let paragraphStyle = attributes[.paragraphStyle] as? NSParagraphStyle { newParagraphStyle.setParagraphStyle(paragraphStyle) } @@ -42,13 +42,13 @@ class TextListFormatter: ParagraphAttributeFormatter { } var resultingAttributes = attributes - resultingAttributes[NSParagraphStyleAttributeName] = newParagraphStyle + resultingAttributes[.paragraphStyle] = newParagraphStyle return resultingAttributes } - func remove(from attributes: [String: Any]) -> [String: Any] { - guard let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle, + func remove(from attributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] { + guard let paragraphStyle = attributes[.paragraphStyle] as? ParagraphStyle, let currentList = paragraphStyle.lists.last, currentList.style == self.listStyle else { @@ -60,24 +60,24 @@ class TextListFormatter: ParagraphAttributeFormatter { newParagraphStyle.removeProperty(ofType: TextList.self) var resultingAttributes = attributes - resultingAttributes[NSParagraphStyleAttributeName] = newParagraphStyle + resultingAttributes[.paragraphStyle] = newParagraphStyle return resultingAttributes } - func present(in attributes: [String: Any]) -> Bool { + func present(in attributes: [NSAttributedStringKey: Any]) -> Bool { return TextListFormatter.lists(in: attributes).last?.style == listStyle } // MARK: - Static Helpers - static func listsOfAnyKindPresent(in attributes: [String: Any]) -> Bool { + static func listsOfAnyKindPresent(in attributes: [NSAttributedStringKey: Any]) -> Bool { return lists(in: attributes).isEmpty == false } - static func lists(in attributes: [String: Any]) -> [TextList] { - let style = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle + static func lists(in attributes: [NSAttributedStringKey: Any]) -> [TextList] { + let style = attributes[.paragraphStyle] as? ParagraphStyle return style?.lists ?? [] } } diff --git a/Aztec/Classes/Formatters/Implementations/UnderlineFormatter.swift b/Aztec/Classes/Formatters/Implementations/UnderlineFormatter.swift index 33efc6838..10dd3a27a 100644 --- a/Aztec/Classes/Formatters/Implementations/UnderlineFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/UnderlineFormatter.swift @@ -1,11 +1,10 @@ import UIKit class UnderlineFormatter: StandardAttributeFormatter { - static let htmlRepresentationKey = "Underline.htmlRepresentation" init() { - super.init(attributeKey: NSUnderlineStyleAttributeName, + super.init(attributeKey: .underlineStyle, attributeValue: NSUnderlineStyle.styleSingle.rawValue, - htmlRepresentationKey: UnderlineFormatter.htmlRepresentationKey) + htmlRepresentationKey: .underlineHtmlRepresentation) } } diff --git a/Aztec/Classes/Formatters/Implementations/VideoFormatter.swift b/Aztec/Classes/Formatters/Implementations/VideoFormatter.swift index 7959d58b8..6c79f376d 100644 --- a/Aztec/Classes/Formatters/Implementations/VideoFormatter.swift +++ b/Aztec/Classes/Formatters/Implementations/VideoFormatter.swift @@ -1,15 +1,14 @@ import UIKit class VideoFormatter: StandardAttributeFormatter { - static let htmlRepresentationKey = "Video.htmlRepresentation" init() { - super.init(attributeKey: NSAttachmentAttributeName, + super.init(attributeKey: .attachment, attributeValue: VideoAttachment(identifier: NSUUID().uuidString), - htmlRepresentationKey: VideoFormatter.htmlRepresentationKey) + htmlRepresentationKey: .videoHtmlRepresentation) } - override func apply(to attributes: [String : Any], andStore representation: HTMLRepresentation?) -> [String: Any] { + override func apply(to attributes: [NSAttributedStringKey: Any], andStore representation: HTMLRepresentation?) -> [NSAttributedStringKey: Any] { if let representation = representation, case let .element(element) = representation.kind { diff --git a/Aztec/Classes/GUI/FormatBar/FormatBar.swift b/Aztec/Classes/GUI/FormatBar/FormatBar.swift index cb1df87af..2626c6a5c 100644 --- a/Aztec/Classes/GUI/FormatBar/FormatBar.swift +++ b/Aztec/Classes/GUI/FormatBar/FormatBar.swift @@ -655,10 +655,10 @@ private extension FormatBar { } let overflowTrailingConstraint = overflowToggleItem.trailingAnchor.constraint(equalTo: trailingAnchor) - overflowTrailingConstraint.priority = UILayoutPriorityDefaultLow + overflowTrailingConstraint.priority = .defaultLow let trailingItemTrailingConstraint = trailingItemContainer.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -Constants.trailingButtonMargin) - trailingItemTrailingConstraint.priority = UILayoutPriorityDefaultLow + trailingItemTrailingConstraint.priority = .defaultLow NSLayoutConstraint.activate([ overflowToggleItem.topAnchor.constraint(equalTo: topAnchor), diff --git a/Aztec/Classes/Libxml2/Converters/In/CSSParser.swift b/Aztec/Classes/Libxml2/Converters/In/CSSParser.swift index 60556a666..ad3d00018 100644 --- a/Aztec/Classes/Libxml2/Converters/In/CSSParser.swift +++ b/Aztec/Classes/Libxml2/Converters/In/CSSParser.swift @@ -59,14 +59,14 @@ class CSSParser { return CSSAttribute(name: cssAttribute) } - let name = cssAttribute.substring(to: keyValueSeparatorRange.lowerBound) + let name = cssAttribute.prefix(upTo: keyValueSeparatorRange.lowerBound) .trimmingCharacters(in: .whitespacesAndNewlines) guard keyValueSeparatorRange.upperBound != cssAttribute.endIndex else { return CSSAttribute(name: name) } - let value = cssAttribute.substring(from: keyValueSeparatorRange.upperBound) + let value = cssAttribute[keyValueSeparatorRange.upperBound...] .trimmingCharacters(in: .whitespacesAndNewlines) return CSSAttribute(name: name, value: value) diff --git a/Aztec/Classes/NSAttributedString/Attributes/HTMLRepresentation.swift b/Aztec/Classes/NSAttributedString/Attributes/HTMLRepresentation.swift index 62085a6a9..745f9f90d 100644 --- a/Aztec/Classes/NSAttributedString/Attributes/HTMLRepresentation.swift +++ b/Aztec/Classes/NSAttributedString/Attributes/HTMLRepresentation.swift @@ -59,8 +59,8 @@ class HTMLRepresentation: NSObject, NSCoding { // MARK: - HTMLElementRepresentation // class HTMLElementRepresentation: NSObject, CustomReflectable, NSCoding { - let name: String - let attributes: [Attribute] + @objc let name: String + @objc let attributes: [Attribute] init(name: String, attributes: [Attribute]) { self.name = name diff --git a/Aztec/Classes/NSAttributedString/Attributes/UnsupportedHTML.swift b/Aztec/Classes/NSAttributedString/Attributes/UnsupportedHTML.swift index 8cf59fd76..946b0a2e2 100644 --- a/Aztec/Classes/NSAttributedString/Attributes/UnsupportedHTML.swift +++ b/Aztec/Classes/NSAttributedString/Attributes/UnsupportedHTML.swift @@ -1,11 +1,6 @@ import Foundation -// MARK: - UnsupportedHTML NSAttributedString Attribute Name -// -let UnsupportedHTMLAttributeName = "UnsupportedHTMLAttributeName" - - // MARK: - UnsupportedHTML // class UnsupportedHTML: NSObject { diff --git a/Aztec/Classes/NSAttributedString/Conversions/AttributedStringParser.swift b/Aztec/Classes/NSAttributedString/Conversions/AttributedStringParser.swift index 5d78cbf64..3be764244 100644 --- a/Aztec/Classes/NSAttributedString/Conversions/AttributedStringParser.swift +++ b/Aztec/Classes/NSAttributedString/Conversions/AttributedStringParser.swift @@ -98,7 +98,7 @@ class AttributedStringParser { /// /// - Returns: Array of Node instances. /// - private func createNodes(from attributes: [String: Any]) -> [Node] { + private func createNodes(from attributes: [NSAttributedStringKey: Any]) -> [Node] { let nodes = createParagraphNodes(from: attributes) + createStyleNodes(from: attributes) return nodes.reversed().reduce([]) { (result, node) in @@ -439,7 +439,7 @@ private extension AttributedStringParser { // See here for more info: // https://github.com/wordpress-mobile/AztecEditor-iOS/issues/667 // - guard let paragraphStyle = paragraph.attribute(NSParagraphStyleAttributeName, at: 0, effectiveRange: nil) as? ParagraphStyle, + guard let paragraphStyle = paragraph.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? ParagraphStyle, paragraphStyle.properties.count > 0 else { return [ElementNode(type: .p)] @@ -456,8 +456,8 @@ private extension AttributedStringParser { /// /// - Returns: ElementNode representing the specified Paragraph. /// - func createParagraphNodes(from attributes: [String: Any]) -> [ElementNode] { - guard let paragraphStyle = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle, + func createParagraphNodes(from attributes: [NSAttributedStringKey: Any]) -> [ElementNode] { + guard let paragraphStyle = attributes[.paragraphStyle] as? ParagraphStyle, paragraphStyle.properties.count > 0 else { return [ElementNode(type: .p)] } @@ -627,7 +627,7 @@ private extension AttributedStringParser { /// /// - Returns: Style Nodes contained within the specified collection of attributes /// - func createStyleNodes(from attributes: [String: Any]) -> [ElementNode] { + func createStyleNodes(from attributes: [NSAttributedStringKey: Any]) -> [ElementNode] { var nodes = [ElementNode]() if let element = processBold(in: attributes) { @@ -655,15 +655,15 @@ private extension AttributedStringParser { return nodes } - private func processBold(in attributes: [String: Any]) -> ElementNode? { - guard let font = attributes[NSFontAttributeName] as? UIFont, + private func processBold(in attributes: [NSAttributedStringKey: Any]) -> ElementNode? { + guard let font = attributes[.font] as? UIFont, font.containsTraits(.traitBold) else { return nil } let element: ElementNode - if let representation = attributes[BoldFormatter.htmlRepresentationKey] as? HTMLRepresentation, + if let representation = attributes[.boldHtmlRepresentation] as? HTMLRepresentation, case let .element(representationElement) = representation.kind { element = representationElement.toElementNode() @@ -675,15 +675,15 @@ private extension AttributedStringParser { } - private func processItalic(in attributes: [String: Any]) -> ElementNode? { - guard let font = attributes[NSFontAttributeName] as? UIFont, + private func processItalic(in attributes: [NSAttributedStringKey: Any]) -> ElementNode? { + guard let font = attributes[.font] as? UIFont, font.containsTraits(.traitItalic) else { return nil } let element: ElementNode - if let representation = attributes[ItalicFormatter.htmlRepresentationKey] as? HTMLRepresentation, + if let representation = attributes[.italicHtmlRepresentation] as? HTMLRepresentation, case let .element(representationElement) = representation.kind { element = representationElement.toElementNode() @@ -696,11 +696,11 @@ private extension AttributedStringParser { /// Extracts all of the Link Elements contained within a collection of Attributes. /// - private func processLinkStyle(in attributes: [String: Any]) -> ElementNode? { + private func processLinkStyle(in attributes: [NSAttributedStringKey: Any]) -> ElementNode? { var urlString = "" - if let url = attributes[NSLinkAttributeName] as? URL { + if let url = attributes[.link] as? URL { urlString = url.absoluteString - } else if let link = attributes[NSLinkAttributeName] as? String { + } else if let link = attributes[.link] as? String { urlString = link } else { return nil @@ -708,7 +708,7 @@ private extension AttributedStringParser { let element: ElementNode - if let representation = attributes[LinkFormatter.htmlRepresentationKey] as? HTMLRepresentation, + if let representation = attributes[.linkHtmlRepresentation] as? HTMLRepresentation, case let .element(representationElement) = representation.kind { element = representationElement.toElementNode() @@ -724,12 +724,12 @@ private extension AttributedStringParser { /// Extracts all of the Strike Elements contained within a collection of Attributes. /// - private func processStrikethruStyle(in attributes: [String: Any]) -> ElementNode? { - guard attributes[NSStrikethroughStyleAttributeName] != nil else { + private func processStrikethruStyle(in attributes: [NSAttributedStringKey: Any]) -> ElementNode? { + guard attributes[.strikethroughStyle] != nil else { return nil } - if let representation = attributes[StrikethroughFormatter.htmlRepresentationKey] as? HTMLRepresentation, + if let representation = attributes[.strikethroughHtmlRepresentation] as? HTMLRepresentation, case let .element(representationElement) = representation.kind { return representationElement.toElementNode() @@ -741,12 +741,12 @@ private extension AttributedStringParser { /// Extracts all of the Underline Elements contained within a collection of Attributes. /// - private func processUnderlineStyle(in attributes: [String: Any]) -> ElementNode? { - guard attributes[NSUnderlineStyleAttributeName] != nil else { + private func processUnderlineStyle(in attributes: [NSAttributedStringKey: Any]) -> ElementNode? { + guard attributes[.underlineStyle] != nil else { return nil } - if let representation = attributes[UnderlineFormatter.htmlRepresentationKey] as? HTMLRepresentation, + if let representation = attributes[.underlineHtmlRepresentation] as? HTMLRepresentation, case let .element(representationElement) = representation.kind { return representationElement.toElementNode() @@ -758,8 +758,8 @@ private extension AttributedStringParser { /// Extracts all of the Unsupported HTML Snippets contained within a collection of Attributes. /// - private func processUnsupportedHTML(in attributes: [String: Any]) -> [ElementNode] { - guard let unsupportedHTML = attributes[UnsupportedHTMLAttributeName] as? UnsupportedHTML else { + private func processUnsupportedHTML(in attributes: [NSAttributedStringKey: Any]) -> [ElementNode] { + guard let unsupportedHTML = attributes[.unsupportedHtml] as? UnsupportedHTML else { return [] } @@ -808,14 +808,14 @@ private extension AttributedStringParser { /// Converts a Line Attachment into it's representing nodes. /// private func processLineAttachment(from attrString: NSAttributedString) -> ElementNode? { - guard attrString.attribute(NSAttachmentAttributeName, at: 0, effectiveRange: nil) is LineAttachment else { + guard attrString.attribute(.attachment, at: 0, effectiveRange: nil) is LineAttachment else { return nil } let element: ElementNode let range = attrString.rangeOfEntireString - if let representation = attrString.attribute(HRFormatter.htmlRepresentationKey, at: 0, longestEffectiveRange: nil, in: range) as? HTMLRepresentation, + if let representation = attrString.attribute(.hrHtmlRepresentation, at: 0, longestEffectiveRange: nil, in: range) as? HTMLRepresentation, case let .element(representationElement) = representation.kind { element = representationElement.toElementNode() @@ -830,7 +830,7 @@ private extension AttributedStringParser { /// Converts a Comment Attachment into it's representing nodes. /// private func processCommentAttachment(from attrString: NSAttributedString) -> Node? { - guard let attachment = attrString.attribute(NSAttachmentAttributeName, at: 0, effectiveRange: nil) as? CommentAttachment else { + guard let attachment = attrString.attribute(.attachment, at: 0, effectiveRange: nil) as? CommentAttachment else { return nil } @@ -842,7 +842,7 @@ private extension AttributedStringParser { /// Converts an HTML Attachment into it's representing nodes. /// private func processHtmlAttachment(from attrString: NSAttributedString) -> [Node] { - guard let attachment = attrString.attribute(NSAttachmentAttributeName, at: 0, effectiveRange: nil) as? HTMLAttachment else { + guard let attachment = attrString.attribute(.attachment, at: 0, effectiveRange: nil) as? HTMLAttachment else { return [] } @@ -866,14 +866,14 @@ private extension AttributedStringParser { /// Converts an Image Attachment into it's representing nodes. /// private func processImageAttachment(from attrString: NSAttributedString) -> ElementNode? { - guard let attachment = attrString.attribute(NSAttachmentAttributeName, at: 0, effectiveRange: nil) as? ImageAttachment else { + guard let attachment = attrString.attribute(.attachment, at: 0, effectiveRange: nil) as? ImageAttachment else { return nil } let element: ElementNode let range = attrString.rangeOfEntireString - if let representation = attrString.attribute(ImageFormatter.htmlRepresentationKey, at: 0, longestEffectiveRange: nil, in: range) as? HTMLRepresentation, + if let representation = attrString.attribute(.imageHtmlRepresentation, at: 0, longestEffectiveRange: nil, in: range) as? HTMLRepresentation, case let .element(representationElement) = representation.kind { element = representationElement.toElementNode() @@ -913,14 +913,14 @@ private extension AttributedStringParser { /// Converts an Video Attachment into it's representing nodes. /// private func processVideoAttachment(from attrString: NSAttributedString) -> ElementNode? { - guard let attachment = attrString.attribute(NSAttachmentAttributeName, at: 0, effectiveRange: nil) as? VideoAttachment else { + guard let attachment = attrString.attribute(.attachment, at: 0, effectiveRange: nil) as? VideoAttachment else { return nil } let element: ElementNode let range = attrString.rangeOfEntireString - if let representation = attrString.attribute(VideoFormatter.htmlRepresentationKey, at: 0, longestEffectiveRange: nil, in: range) as? HTMLRepresentation, + if let representation = attrString.attribute(.videoHtmlRepresentation, at: 0, longestEffectiveRange: nil, in: range) as? HTMLRepresentation, case let .element(representationElement) = representation.kind { element = representationElement.toElementNode() diff --git a/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift b/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift index 1968a3741..656db5d6d 100644 --- a/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift +++ b/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift @@ -5,11 +5,11 @@ import UIKit /// class AttributedStringSerializer { - private let defaultAttributes: [String: Any] + private let defaultAttributes: [NSAttributedStringKey: Any] // MARK: - Initializers - required init(defaultAttributes: [String: Any]) { + required init(defaultAttributes: [NSAttributedStringKey: Any]) { self.defaultAttributes = defaultAttributes } @@ -35,7 +35,7 @@ class AttributedStringSerializer { /// /// - Returns: the converted node as an `NSAttributedString`. /// - fileprivate func serialize(_ node: Node, inheriting attributes: [String:Any]) -> NSAttributedString { + fileprivate func serialize(_ node: Node, inheriting attributes: [NSAttributedStringKey:Any]) -> NSAttributedString { switch node { case let textNode as TextNode: return serialize(textNode, inheriting: attributes) @@ -56,7 +56,7 @@ class AttributedStringSerializer { /// /// - Returns: the converted node as an `NSAttributedString`. /// - fileprivate func serialize(_ node: TextNode, inheriting attributes: [String:Any]) -> NSAttributedString { + fileprivate func serialize(_ node: TextNode, inheriting attributes: [NSAttributedStringKey: Any]) -> NSAttributedString { let text = sanitizeText(from: node) @@ -81,7 +81,7 @@ class AttributedStringSerializer { /// /// - Returns: the converted node as an `NSAttributedString`. /// - fileprivate func serialize(_ node: CommentNode, inheriting attributes: [String:Any]) -> NSAttributedString { + fileprivate func serialize(_ node: CommentNode, inheriting attributes: [NSAttributedStringKey: Any]) -> NSAttributedString { let attachment = CommentAttachment() attachment.text = node.comment @@ -96,7 +96,7 @@ class AttributedStringSerializer { /// /// - Returns: the converted node as an `NSAttributedString`. /// - fileprivate func serialize(_ element: ElementNode, inheriting attributes: [String: Any]) -> NSAttributedString { + fileprivate func serialize(_ element: ElementNode, inheriting attributes: [NSAttributedStringKey: Any]) -> NSAttributedString { guard element.isSupportedByEditor() else { return serialize(unsupported: element, inheriting: attributes) @@ -127,7 +127,7 @@ class AttributedStringSerializer { /// /// - Returns: the converted node as an `NSAttributedString`. /// - fileprivate func serialize(unsupported element: ElementNode, inheriting attributes: [String: Any]) -> NSAttributedString { + fileprivate func serialize(unsupported element: ElementNode, inheriting attributes: [NSAttributedStringKey: Any]) -> NSAttributedString { let serializer = DefaultHTMLSerializer() let attachment = HTMLAttachment() @@ -139,7 +139,7 @@ class AttributedStringSerializer { // MARK: - Paragraph Separator - private func appendParagraphSeparator(to string: NSAttributedString, inheriting inheritedAttributes: [String: Any]) -> NSAttributedString { + private func appendParagraphSeparator(to string: NSAttributedString, inheriting inheritedAttributes: [NSAttributedStringKey: Any]) -> NSAttributedString { let stringWithSeparator = NSMutableAttributedString(attributedString: string) @@ -238,7 +238,7 @@ private extension AttributedStringSerializer { /// /// - Returns: an attributes dictionary, for use in an NSAttributedString. /// - func attributes(for element: ElementNode, inheriting inheritedAttributes: [String: Any]) -> [String: Any] { + func attributes(for element: ElementNode, inheriting inheritedAttributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] { guard !(element is RootNode) else { return inheritedAttributes @@ -271,9 +271,9 @@ private extension AttributedStringSerializer { /// /// - Returns: an attributes dictionary, for use in an NSAttributedString. /// - private func attributes(for htmlAttributes: [Attribute], inheriting inheritedAttributes: [String: Any]) -> [String: Any] { + private func attributes(for htmlAttributes: [Attribute], inheriting inheritedAttributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] { - let finalAttributes = htmlAttributes.reduce(inheritedAttributes) { (previousAttributes, htmlAttribute) -> [String: Any] in + let finalAttributes = htmlAttributes.reduce(inheritedAttributes) { (previousAttributes, htmlAttribute) -> [NSAttributedStringKey: Any] in return attributes(for: htmlAttribute, inheriting: previousAttributes) } @@ -290,9 +290,9 @@ private extension AttributedStringSerializer { /// /// - Returns: an attributes dictionary, for use in an NSAttributedString. /// - private func attributes(for attribute: Attribute, inheriting inheritedAttributes: [String: Any]) -> [String: Any] { + private func attributes(for attribute: Attribute, inheriting inheritedAttributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] { - let attributes: [String:Any] + let attributes: [NSAttributedStringKey: Any] if let attributeFormatter = formatter(for: attribute) { let attributeHTMLRepresentation = HTMLRepresentation(for: .attribute(attribute)) @@ -314,8 +314,8 @@ private extension AttributedStringSerializer { /// /// - Returns: A collection of NSAttributedString Attributes, including the specified HTMLElementRepresentation. /// - private func attributes(storing representation: HTMLElementRepresentation, in attributes: [String: Any]) -> [String: Any] { - let unsupportedHTML = attributes[UnsupportedHTMLAttributeName] as? UnsupportedHTML + private func attributes(storing representation: HTMLElementRepresentation, in attributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] { + let unsupportedHTML = attributes[.unsupportedHtml] as? UnsupportedHTML var representations = unsupportedHTML?.representations ?? [] representations.append(representation) @@ -324,7 +324,7 @@ private extension AttributedStringSerializer { // would mean affecting a range that may fall beyond what we expected! // var updated = attributes - updated[UnsupportedHTMLAttributeName] = UnsupportedHTML(representations: representations) + updated[.unsupportedHtml] = UnsupportedHTML(representations: representations) return updated } @@ -371,7 +371,7 @@ private extension AttributedStringSerializer { /// /// - Returns: the requested implicit representation, if one exists, or `nil`. /// - func implicitRepresentation(for element: ElementNode, inheriting attributes: [String:Any]) -> NSAttributedString? { + func implicitRepresentation(for element: ElementNode, inheriting attributes: [NSAttributedStringKey: Any]) -> NSAttributedString? { guard let elementType = element.standardName else { return nil @@ -379,11 +379,11 @@ private extension AttributedStringSerializer { if let imgElement = linkedImageElement(for: element) { var attributesWithoutLink = attributes - attributesWithoutLink[NSLinkAttributeName] = nil - attributesWithoutLink[LinkFormatter.htmlRepresentationKey] = nil + attributesWithoutLink[.link] = nil + attributesWithoutLink[.linkHtmlRepresentation] = nil let imgAttributes = self.attributes(for: imgElement, inheriting: attributesWithoutLink) - let attachment = imgAttributes[NSAttachmentAttributeName] as! ImageAttachment + let attachment = imgAttributes[.attachment] as! ImageAttachment let linkText = element.stringValueForAttribute(named: HTMLLinkAttribute.Href.rawValue) ?? "" attachment.linkURL = URL(string: linkText) @@ -402,7 +402,7 @@ private extension AttributedStringSerializer { /// /// - Returns: the requested implicit representation, if one exists, or `nil`. /// - private func implicitRepresentation(for elementType: StandardElementType, inheriting attributes: [String:Any]) -> NSAttributedString? { + private func implicitRepresentation(for elementType: StandardElementType, inheriting attributes: [NSAttributedStringKey: Any]) -> NSAttributedString? { switch elementType { case .hr, .img, .video: diff --git a/Aztec/Classes/TextKit/HTMLStorage.swift b/Aztec/Classes/TextKit/HTMLStorage.swift index 0ec8f8d33..2f758d95c 100644 --- a/Aztec/Classes/TextKit/HTMLStorage.swift +++ b/Aztec/Classes/TextKit/HTMLStorage.swift @@ -66,7 +66,7 @@ open class HTMLStorage: NSTextStorage { return textStore.string } - override open func attributes(at location: Int, effectiveRange range: NSRangePointer?) -> [String : Any] { + override open func attributes(at location: Int, effectiveRange range: NSRangePointer?) -> [NSAttributedStringKey : Any] { guard textStore.length != 0 else { return [:] } @@ -74,7 +74,7 @@ open class HTMLStorage: NSTextStorage { return textStore.attributes(at: location, effectiveRange: range) } - override open func setAttributes(_ attrs: [String : Any]?, range: NSRange) { + override open func setAttributes(_ attrs: [NSAttributedStringKey : Any]?, range: NSRange) { beginEditing() textStore.setAttributes(attrs, range: range) @@ -83,11 +83,11 @@ open class HTMLStorage: NSTextStorage { endEditing() } - override open func addAttribute(_ name: String, value: Any, range: NSRange) { + override open func addAttribute(_ name: NSAttributedStringKey, value: Any, range: NSRange) { textStore.addAttribute(name, value: value, range: range) } - override open func removeAttribute(_ name: String, range: NSRange) { + override open func removeAttribute(_ name: NSAttributedStringKey, range: NSRange) { textStore.removeAttribute(name, range: range) } @@ -125,22 +125,22 @@ private extension HTMLStorage { func colorizeHTML() { let fullStringRange = rangeOfEntireString - removeAttribute(NSForegroundColorAttributeName, range: fullStringRange) - addAttribute(NSFontAttributeName, value: font, range: fullStringRange) + removeAttribute(.foregroundColor, range: fullStringRange) + addAttribute(.font, value: font, range: fullStringRange) let tags = RegExes.html.matches(in: string, options: [], range: fullStringRange) for tag in tags { - addAttribute(NSForegroundColorAttributeName, value: tagColor, range: tag.range) + addAttribute(.foregroundColor, value: tagColor, range: tag.range) let quotes = RegExes.quotes.matches(in: string, options: [], range: tag.range) for quote in quotes { - addAttribute(NSForegroundColorAttributeName, value: quotedColor, range: quote.range) + addAttribute(.foregroundColor, value: quotedColor, range: quote.range) } } let comments = RegExes.comments.matches(in: string, options: [], range: fullStringRange) for comment in comments { - addAttribute(NSForegroundColorAttributeName, value: commentColor, range: comment.range) + addAttribute(.foregroundColor, value: commentColor, range: comment.range) } } } diff --git a/Aztec/Classes/TextKit/LayoutManager.swift b/Aztec/Classes/TextKit/LayoutManager.swift index 1602eb121..096861f80 100644 --- a/Aztec/Classes/TextKit/LayoutManager.swift +++ b/Aztec/Classes/TextKit/LayoutManager.swift @@ -21,7 +21,7 @@ class LayoutManager: NSLayoutManager { /// Closure that is expected to return the TypingAttributes associated to the Extra Line Fragment /// - var extraLineFragmentTypingAttributes: (() -> [String: Any])? + var extraLineFragmentTypingAttributes: (() -> [NSAttributedStringKey: Any])? /// Blockquote's Left Border width /// @@ -58,7 +58,7 @@ private extension LayoutManager { let characterRange = self.characterRange(forGlyphRange: glyphsToShow, actualGlyphRange: nil) // Draw: Blockquotes - textStorage.enumerateAttribute(NSParagraphStyleAttributeName, in: characterRange, options: []) { (object, range, stop) in + textStorage.enumerateAttribute(.paragraphStyle, in: characterRange, options: []) { (object, range, stop) in guard let paragraphStyle = object as? ParagraphStyle, !paragraphStyle.blockquotes.isEmpty else { return } @@ -82,7 +82,7 @@ private extension LayoutManager { return } - guard let paragraphStyle = typingAttributes[NSParagraphStyleAttributeName] as? ParagraphStyle, + guard let paragraphStyle = typingAttributes[.paragraphStyle] as? ParagraphStyle, !paragraphStyle.blockquotes.isEmpty else { return } @@ -153,7 +153,7 @@ private extension LayoutManager { let characterRange = self.characterRange(forGlyphRange: glyphsToShow, actualGlyphRange: nil) //draw html pre paragraphs - textStorage.enumerateAttribute(NSParagraphStyleAttributeName, in: characterRange, options: []){ (object, range, stop) in + textStorage.enumerateAttribute(.paragraphStyle, in: characterRange, options: []){ (object, range, stop) in guard let paragraphStyle = object as? ParagraphStyle, paragraphStyle.htmlPre != nil else { return } @@ -194,7 +194,7 @@ private extension LayoutManager { textStorage.enumerateParagraphRanges(spanning: characterRange) { (range, enclosingRange) in guard textStorage.string.isStartOfNewLine(atUTF16Offset: enclosingRange.location), - let paragraphStyle = textStorage.attribute(NSParagraphStyleAttributeName, at: enclosingRange.location, effectiveRange: nil) as? ParagraphStyle, + let paragraphStyle = textStorage.attribute(.paragraphStyle, at: enclosingRange.location, effectiveRange: nil) as? ParagraphStyle, let list = paragraphStyle.lists.last else { return @@ -271,20 +271,20 @@ private extension LayoutManager { /// Returns the Marker Text Attributes, based on a collection that defines Regular Text Attributes. /// - private func markerAttributesBasedOnParagraph(attributes: [String: Any]) -> [String: Any] { + private func markerAttributesBasedOnParagraph(attributes: [NSAttributedStringKey: Any]) -> [NSAttributedStringKey: Any] { var resultAttributes = attributes var indent: CGFloat = 0 - if let style = attributes[NSParagraphStyleAttributeName] as? ParagraphStyle { + if let style = attributes[.paragraphStyle] as? ParagraphStyle { indent = style.listIndent + Metrics.listTextIndentation } - resultAttributes[NSParagraphStyleAttributeName] = markerParagraphStyle(indent: indent) - resultAttributes.removeValue(forKey: NSUnderlineStyleAttributeName) - resultAttributes.removeValue(forKey: NSStrikethroughStyleAttributeName) - resultAttributes.removeValue(forKey: NSLinkAttributeName) + resultAttributes[.paragraphStyle] = markerParagraphStyle(indent: indent) + resultAttributes.removeValue(forKey: .underlineStyle) + resultAttributes.removeValue(forKey: .strikethroughStyle) + resultAttributes.removeValue(forKey: .link) - if let font = resultAttributes[NSFontAttributeName] as? UIFont { - resultAttributes[NSFontAttributeName] = fixFontForMarkerAttributes(font: font) + if let font = resultAttributes[.font] as? UIFont { + resultAttributes[.font] = fixFontForMarkerAttributes(font: font) } return resultAttributes diff --git a/Aztec/Classes/TextKit/MediaAttachment.swift b/Aztec/Classes/TextKit/MediaAttachment.swift index 27ff3cc00..a25d2e2d4 100644 --- a/Aztec/Classes/TextKit/MediaAttachment.swift +++ b/Aztec/Classes/TextKit/MediaAttachment.swift @@ -330,7 +330,7 @@ open class MediaAttachment: NSTextAttachment { var padding = (textContainer?.lineFragmentPadding ?? 0) if let storage = textContainer?.layoutManager?.textStorage, - let paragraphStyle = storage.attribute(NSParagraphStyleAttributeName, at: charIndex, effectiveRange: nil) as? NSParagraphStyle { + let paragraphStyle = storage.attribute(.paragraphStyle, at: charIndex, effectiveRange: nil) as? NSParagraphStyle { padding += paragraphStyle.firstLineHeadIndent + paragraphStyle.tailIndent } let width = floor(lineFrag.width - (padding * 2)) @@ -383,7 +383,7 @@ private extension MediaAttachment { self.isFetchingImage = false self.invalidateLayout(in: textContainer) - }, onFailure: { [weak self] _ in + }, onFailure: { [weak self] () in self?.isFetchingImage = false }) diff --git a/Aztec/Classes/TextKit/ParagraphStyle.swift b/Aztec/Classes/TextKit/ParagraphStyle.swift index 42b89abd1..91a43d4af 100644 --- a/Aztec/Classes/TextKit/ParagraphStyle.swift +++ b/Aztec/Classes/TextKit/ParagraphStyle.swift @@ -347,7 +347,7 @@ extension ParagraphStyle { /// clustered at the 'Right Hand Side' of the currently existant list. /// func insertProperty(_ property: ParagraphProperty, afterLastOfType type: AnyClass) { - guard let targetIndex = properties.lastIndex(where: { type(of: $0) == type }) else { + guard let targetIndex = properties.lastIndex(where: { Swift.type(of: $0) == type }) else { properties.append(property) return } @@ -359,7 +359,7 @@ extension ParagraphStyle { /// func removeProperty(ofType type: AnyClass) { for index in (0.. [String : Any] { + override open func attributes(at location: Int, effectiveRange range: NSRangePointer?) -> [NSAttributedStringKey : Any] { guard textStore.length > 0 else { return [:] @@ -257,7 +257,7 @@ open class TextStorage: NSTextStorage { endEditing() } - override open func setAttributes(_ attrs: [String: Any]?, range: NSRange) { + override open func setAttributes(_ attrs: [NSAttributedStringKey: Any]?, range: NSRange) { beginEditing() let fixedAttributes = ensureMatchingFontAndParagraphHeaderStyles(beforeApplying: attrs ?? [:], at: range) @@ -341,7 +341,7 @@ open class TextStorage: NSTextStorage { } func setHTML(_ html: String, - defaultAttributes: [String: Any], + defaultAttributes: [NSAttributedStringKey: Any], postProcessingHTMLWith postProcessHTML: HTMLTreeProcessor? = nil) { let originalLength = textStore.length @@ -380,9 +380,9 @@ private extension TextStorage { /// /// - Returns: Collection of attributes with the Font Attribute corrected, if needed. /// - func ensureMatchingFontAndParagraphHeaderStyles(beforeApplying attrs: [String: Any], at range: NSRange) -> [String: Any] { - let newStyle = attrs[NSParagraphStyleAttributeName] as? ParagraphStyle - let oldStyle = textStore.attribute(NSParagraphStyleAttributeName, at: range.location, effectiveRange: nil) as? ParagraphStyle + func ensureMatchingFontAndParagraphHeaderStyles(beforeApplying attrs: [NSAttributedStringKey: Any], at range: NSRange) -> [NSAttributedStringKey: Any] { + let newStyle = attrs[.paragraphStyle] as? ParagraphStyle + let oldStyle = textStore.attribute(.paragraphStyle, at: range.location, effectiveRange: nil) as? ParagraphStyle let newLevel = newStyle?.headers.last?.level ?? .none let oldLevel = oldStyle?.headers.last?.level ?? .none @@ -403,7 +403,7 @@ private extension TextStorage { /// /// - Returns: Collection of attributes with the Font Attribute corrected, so that it matches the specified HeaderLevel. /// - private func fixFontAttribute(in attrs: [String: Any], headerLevel: Header.HeaderType) -> [String: Any] { + private func fixFontAttribute(in attrs: [NSAttributedStringKey: Any], headerLevel: Header.HeaderType) -> [NSAttributedStringKey: Any] { let formatter = HeaderFormatter(headerLevel: headerLevel) return formatter.apply(to: attrs) } diff --git a/Aztec/Classes/TextKit/TextView.swift b/Aztec/Classes/TextKit/TextView.swift index 3dba7f759..7759fea03 100644 --- a/Aztec/Classes/TextKit/TextView.swift +++ b/Aztec/Classes/TextKit/TextView.swift @@ -151,9 +151,9 @@ open class TextView: UITextView { open let defaultParagraphStyle: ParagraphStyle var defaultMissingImage: UIImage - fileprivate var defaultAttributes: [String: Any] { - return [NSFontAttributeName: defaultFont, - NSParagraphStyleAttributeName: defaultParagraphStyle] + fileprivate var defaultAttributes: [NSAttributedStringKey: Any] { + return [.font: defaultFont, + .paragraphStyle: defaultParagraphStyle] } @@ -186,9 +186,9 @@ open class TextView: UITextView { // MARK: - Apparance Properties - /// Blockquote Blocks Border COlor. + /// Blockquote Blocks Border Color. /// - dynamic public var blockquoteBorderColor: UIColor { + @objc dynamic public var blockquoteBorderColor: UIColor { get { return layout.blockquoteBorderColor } @@ -199,7 +199,7 @@ open class TextView: UITextView { /// Blockquote Blocks Background Color. /// - dynamic public var blockquoteBackgroundColor: UIColor { + @objc dynamic public var blockquoteBackgroundColor: UIColor { get { return layout.blockquoteBackgroundColor } @@ -211,7 +211,7 @@ open class TextView: UITextView { /// Blockquote Blocks Background Width. /// - dynamic public var blockquoteBorderWidth: CGFloat { + @objc dynamic public var blockquoteBorderWidth: CGFloat { get { return layout.blockquoteBorderWidth } @@ -222,7 +222,7 @@ open class TextView: UITextView { /// Pre Blocks Background Color. /// - dynamic public var preBackgroundColor: UIColor { + @objc dynamic public var preBackgroundColor: UIColor { get { return layout.preBackgroundColor } @@ -248,11 +248,25 @@ open class TextView: UITextView { } } + + /// Returns the collection of Typing Attributes, with all of the available 'String' keys properly converted into + /// NSAttributedStringKey. Also known as: what you would expect from the SDK. + /// + open var typingAttributesSwifted: [NSAttributedStringKey: Any] { + get { + return NSAttributedStringKey.convertFromRaw(attributes: typingAttributes) + } + set { + typingAttributes = NSAttributedStringKey.convertToRaw(attributes: newValue) + } + } + + /// This property returns the Attributes associated to the Extra Line Fragment. /// - public var extraLineFragmentTypingAttributes: [String: Any] { + public var extraLineFragmentTypingAttributes: [NSAttributedStringKey: Any] { guard selectedTextRange?.start != endOfDocument else { - return typingAttributes + return typingAttributesSwifted } let string = textStorage.string @@ -304,8 +318,9 @@ open class TextView: UITextView { allowsEditingTextAttributes = true storage.attachmentsDelegate = self font = defaultFont - linkTextAttributes = [NSUnderlineStyleAttributeName: NSNumber(value:NSUnderlineStyle.styleSingle.rawValue), NSForegroundColorAttributeName: self.tintColor] - typingAttributes = defaultAttributes + linkTextAttributes = [NSAttributedStringKey.underlineStyle.rawValue: NSNumber(value:NSUnderlineStyle.styleSingle.rawValue), + NSAttributedStringKey.foregroundColor.rawValue: self.tintColor] + typingAttributesSwifted = defaultAttributes setupMenuController() setupAttachmentTouchDetection() setupLayoutManager() @@ -383,14 +398,14 @@ open class TextView: UITextView { selectedRange = NSRange(location: selectedRange.location + string.length, length: 0) } - open func pasteAndMatchStyle(_ sender: Any?) { + @objc open func pasteAndMatchStyle(_ sender: Any?) { guard let string = UIPasteboard.general.loadAttributedString()?.mutableCopy() as? NSMutableAttributedString else { super.paste(sender) return } let range = string.rangeOfEntireString - string.addAttributes(typingAttributes, range: range) + string.addAttributes(typingAttributesSwifted, range: range) string.loadLazyAttachments() storage.replaceCharacters(in: selectedRange, with: string) @@ -413,12 +428,12 @@ open class TextView: UITextView { } } - func handleShiftEnter(command: UIKeyCommand) { + @objc func handleShiftEnter(command: UIKeyCommand) { insertText(String(.lineSeparator)) } - func handleShiftTab(command: UIKeyCommand) { - guard let list = TextListFormatter.lists(in: typingAttributes).last else { + @objc func handleShiftTab(command: UIKeyCommand) { + guard let list = TextListFormatter.lists(in: typingAttributesSwifted).last else { return } @@ -427,13 +442,13 @@ open class TextView: UITextView { performUndoable(at: targetRange) { let finalRange = formatter.removeAttributes(from: storage, at: targetRange) - typingAttributes = textStorage.attributes(at: targetRange.location, effectiveRange: nil) + typingAttributesSwifted = textStorage.attributes(at: targetRange.location, effectiveRange: nil) return finalRange } } - func handleTab(command: UIKeyCommand) { - let lists = TextListFormatter.lists(in: typingAttributes) + @objc func handleTab(command: UIKeyCommand) { + let lists = TextListFormatter.lists(in: typingAttributesSwifted) guard let list = lists.last, lists.count < maximumListIndentationLevels else { insertText(String(.tab)) return @@ -444,7 +459,7 @@ open class TextView: UITextView { performUndoable(at: targetRange) { let finalRange = formatter.applyAttributes(to: storage, at: targetRange) - typingAttributes = textStorage.attributes(at: targetRange.location, effectiveRange: nil) + typingAttributesSwifted = textStorage.attributes(at: targetRange.location, effectiveRange: nil) return finalRange } } @@ -472,7 +487,7 @@ open class TextView: UITextView { // This was causing the following issue: // https://github.com/wordpress-mobile/AztecEditor-iOS/issues/462 // - typingAttributes[NSAttachmentAttributeName] = nil + typingAttributesSwifted[.attachment] = nil guard !ensureRemovalOfParagraphAttributesWhenPressingEnterInAnEmptyParagraph(input: text) else { return @@ -607,7 +622,7 @@ open class TextView: UITextView { postProcessingHTMLWith: inputTreeProcessor) if storage.length > 0 && selectedRange.location < storage.length { - typingAttributes = storage.attributes(at: selectedRange.location, effectiveRange: nil) + typingAttributesSwifted = storage.attributes(at: selectedRange.location, effectiveRange: nil) } notifyTextViewDidChange() @@ -697,12 +712,11 @@ open class TextView: UITextView { /// - Returns: A list of Formatting Identifiers. /// open func formatIdentifiersForTypingAttributes() -> [FormattingIdentifier] { + let activeAttributes = typingAttributesSwifted var identifiers = [FormattingIdentifier]() - for (key, formatter) in formatterIdentifiersMap { - if formatter.present(in: typingAttributes) { - identifiers.append(key) - } + for (key, formatter) in formatterIdentifiersMap where formatter.present(in: activeAttributes) { + identifiers.append(key) } return identifiers @@ -751,13 +765,13 @@ open class TextView: UITextView { }) if applicationRange.length == 0 { - typingAttributes = formatter.toggle(in: typingAttributes) + typingAttributesSwifted = formatter.toggle(in: typingAttributesSwifted) } else { // NOTE: We are making sure that the selectedRange location is inside the string // The selected range can be out of the string when you are adding content to the end of the string. // In those cases we check the atributes of the previous caracter let location = max(0,min(selectedRange.location, textStorage.length-1)) - typingAttributes = textStorage.attributes(at: location, effectiveRange: nil) + typingAttributesSwifted = textStorage.attributes(at: location, effectiveRange: nil) } notifyTextViewDidChange() } @@ -812,7 +826,7 @@ open class TextView: UITextView { open func togglePre(range: NSRange) { ensureInsertionOfEndOfLineForEmptyParagraphAtEndOfFile(forApplicationRange: range) - let formatter = PreFormatter(placeholderAttributes: typingAttributes) + let formatter = PreFormatter(placeholderAttributes: typingAttributesSwifted) toggle(formatter: formatter, atRange: range) forceRedrawCursorAfterDelay() @@ -829,7 +843,7 @@ open class TextView: UITextView { open func toggleBlockquote(range: NSRange) { ensureInsertionOfEndOfLineForEmptyParagraphAtEndOfFile(forApplicationRange: range) - let formatter = BlockquoteFormatter(placeholderAttributes: typingAttributes) + let formatter = BlockquoteFormatter(placeholderAttributes: typingAttributesSwifted) toggle(formatter: formatter, atRange: range) forceRedrawCursorAfterDelay() @@ -842,7 +856,7 @@ open class TextView: UITextView { open func toggleOrderedList(range: NSRange) { ensureInsertionOfEndOfLineForEmptyParagraphAtEndOfFile(forApplicationRange: range) - let formatter = TextListFormatter(style: .ordered, placeholderAttributes: typingAttributes) + let formatter = TextListFormatter(style: .ordered, placeholderAttributes: typingAttributesSwifted) toggle(formatter: formatter, atRange: range) forceRedrawCursorAfterDelay() @@ -856,7 +870,7 @@ open class TextView: UITextView { open func toggleUnorderedList(range: NSRange) { ensureInsertionOfEndOfLineForEmptyParagraphAtEndOfFile(forApplicationRange: range) - let formatter = TextListFormatter(style: .unordered, placeholderAttributes: typingAttributes) + let formatter = TextListFormatter(style: .unordered, placeholderAttributes: typingAttributesSwifted) toggle(formatter: formatter, atRange: range) forceRedrawCursorAfterDelay() @@ -868,7 +882,7 @@ open class TextView: UITextView { /// - Parameter range: The NSRange to edit. /// open func toggleHeader(_ headerType: Header.HeaderType, range: NSRange) { - let formatter = HeaderFormatter(headerLevel: headerType, placeholderAttributes: typingAttributes) + let formatter = HeaderFormatter(headerLevel: headerType, placeholderAttributes: typingAttributesSwifted) toggle(formatter: formatter, atRange: range) forceRedrawCursorAfterDelay() } @@ -898,11 +912,11 @@ open class TextView: UITextView { /// This is meant as a workaround for the "Emojis Mixing Up Font's" glitch. /// private func restoreDefaultFontIfNeeded() { - guard let activeFont = typingAttributes[NSFontAttributeName] as? UIFont, activeFont.isAppleEmojiFont else { + guard let activeFont = typingAttributesSwifted[.font] as? UIFont, activeFont.isAppleEmojiFont else { return } - typingAttributes[NSFontAttributeName] = defaultFont.withSize(activeFont.pointSize) + typingAttributesSwifted[.font] = defaultFont.withSize(activeFont.pointSize) } @@ -952,8 +966,9 @@ open class TextView: UITextView { TextListFormatter(style: .unordered) ] + let activeTypingAttributes = typingAttributesSwifted let found = formatters.first { formatter in - return formatter.present(in: typingAttributes) + return formatter.present(in: activeTypingAttributes) } guard found != nil else { @@ -986,7 +1001,7 @@ open class TextView: UITextView { /// - Parameter range: Range in which new text will be inserted. /// private func ensureRemovalOfLinkTypingAttribute(at range: NSRange) { - guard typingAttributes[NSLinkAttributeName] != nil else { + guard typingAttributes[NSAttributedStringKey.link.rawValue] != nil else { return } @@ -996,7 +1011,7 @@ open class TextView: UITextView { return } - typingAttributes.removeValue(forKey: NSLinkAttributeName) + typingAttributes.removeValue(forKey: NSAttributedStringKey.link.rawValue) } @@ -1139,7 +1154,7 @@ open class TextView: UITextView { block() - typingAttributes = previousAttributes + typingAttributesSwifted = previousAttributes } @@ -1163,9 +1178,9 @@ open class TextView: UITextView { }) let formatter = LinkFormatter() - formatter.attributeValue = url - let attributes = formatter.apply(to: typingAttributes) + formatter.attributeValue = url + let attributes = formatter.apply(to: typingAttributesSwifted) storage.replaceCharacters(in: range, with: NSAttributedString(string: title, attributes: attributes)) selectedRange = NSRange(location: finalRange.location + finalRange.length, length: 0) @@ -1205,7 +1220,8 @@ open class TextView: UITextView { undoManager?.registerUndo(withTarget: self, handler: { [weak self] target in self?.undoTextReplacement(of: originalText, finalRange: finalRange) }) - let attachmentString = NSAttributedString(attachment: attachment, attributes: typingAttributes) + + let attachmentString = NSAttributedString(attachment: attachment, attributes: typingAttributesSwifted) storage.replaceCharacters(in: range, with: attachmentString) selectedRange = NSMakeRange(range.location + NSAttributedString.lengthOfTextAttachment, 0) notifyTextViewDidChange() @@ -1254,7 +1270,7 @@ open class TextView: UITextView { self?.undoTextReplacement(of: originalText, finalRange: finalRange) }) - storage.replaceCharacters(in: range, with: NSAttributedString(string: "", attributes: typingAttributes)) + storage.replaceCharacters(in: range, with: NSAttributedString(string: "", attributes: typingAttributesSwifted)) notifyTextViewDidChange() } @@ -1298,7 +1314,7 @@ open class TextView: UITextView { } var effectiveRange = NSRange() - guard let attachment = textStorage.attribute(NSAttachmentAttributeName, at: index, effectiveRange: &effectiveRange) as? NSTextAttachment else { + guard let attachment = textStorage.attribute(.attachment, at: index, effectiveRange: &effectiveRange) as? NSTextAttachment else { return nil } @@ -1375,7 +1391,7 @@ open class TextView: UITextView { let index = maxIndex(range.location) var effectiveRange = NSRange() guard index < storage.length, - let attr = storage.attribute(NSLinkAttributeName, at: index, effectiveRange: &effectiveRange) + let attr = storage.attribute(.link, at: index, effectiveRange: &effectiveRange) else { return nil } @@ -1402,7 +1418,7 @@ open class TextView: UITextView { let index = maxIndex(range.location) var effectiveRange = NSRange() guard index < storage.length, - storage.attribute(NSLinkAttributeName, at: index, effectiveRange: &effectiveRange) != nil + storage.attribute(.link, at: index, effectiveRange: &effectiveRange) != nil else { return nil } @@ -1480,7 +1496,7 @@ open class TextView: UITextView { block(copy) performUndoable(at: range) { - storage.setAttributes([NSAttachmentAttributeName: copy], range: range) + storage.setAttributes([.attachment: copy], range: range) return range } } @@ -1577,12 +1593,20 @@ private extension TextView { HeaderFormatter(headerLevel: .h6, placeholderAttributes: [:]) ] - for formatter in formatters where formatter.present(in: typingAttributes) { - typingAttributes = formatter.remove(from: typingAttributes) + var updatedTypingAttributes = typingAttributesSwifted + var needsRefresh = false + + for formatter in formatters where formatter.present(in: updatedTypingAttributes) { + updatedTypingAttributes = formatter.remove(from: updatedTypingAttributes) + needsRefresh = true let applicationRange = formatter.applicationRange(for: selectedRange, in: textStorage) formatter.removeAttributes(from: textStorage, at: applicationRange) } + + if needsRefresh { + typingAttributesSwifted = updatedTypingAttributes + } } // MARK: - WORKAROUND: Removing paragraph styles when pressing enter in an empty paragraph @@ -1607,11 +1631,13 @@ private extension TextView { /// - Returns: `true` if we should remove paragraph attributes, otherwise it returns `false`. /// private func mustRemoveParagraphAttributesWhenPressingEnterInAnEmptyParagraph(input: String) -> Bool { + let activeTypingAttributes = typingAttributesSwifted + return input.isEndOfLine() && storage.string.isEmptyLine(at: selectedRange.location) - && (BlockquoteFormatter().present(in: typingAttributes) - || TextListFormatter.listsOfAnyKindPresent(in: typingAttributes) - || PreFormatter().present(in: typingAttributes)) + && (BlockquoteFormatter().present(in: activeTypingAttributes) + || TextListFormatter.listsOfAnyKindPresent(in: activeTypingAttributes) + || PreFormatter().present(in: activeTypingAttributes)) } @@ -1670,8 +1696,14 @@ private extension TextView { TextListFormatter(style: .unordered) ] - for formatter in formatters where formatter.present(in: super.typingAttributes) { - super.typingAttributes = formatter.remove(from: super.typingAttributes) + for formatter in formatters { + let activeTypingAttributes = NSAttributedStringKey.convertFromRaw(attributes: super.typingAttributes) + guard formatter.present(in: activeTypingAttributes) else { + continue + } + + let updatedTypingAttributes = formatter.remove(from: activeTypingAttributes) + super.typingAttributes = NSAttributedStringKey.convertToRaw(attributes: updatedTypingAttributes) let applicationRange = formatter.applicationRange(for: selectedRange, in: textStorage) formatter.removeAttributes(from: textStorage, at: applicationRange) @@ -1792,7 +1824,7 @@ extension TextView: TextStorageAttachmentsDelegate { return true } - func richTextViewWasPressed(_ recognizer: UIGestureRecognizer) { + @objc func richTextViewWasPressed(_ recognizer: UIGestureRecognizer) { guard let textView = textView, recognizer.state == .recognized else { return } diff --git a/AztecTests/Extensions/NSAttributedStringAnalyzerTests.swift b/AztecTests/Extensions/NSAttributedStringAnalyzerTests.swift index 0e6712b29..b2b044672 100644 --- a/AztecTests/Extensions/NSAttributedStringAnalyzerTests.swift +++ b/AztecTests/Extensions/NSAttributedStringAnalyzerTests.swift @@ -20,8 +20,8 @@ class NSAttributedStringAnalyzerTests: XCTestCase { /// Linkified String /// static let linkifiedString = { _ -> NSAttributedString in - return NSAttributedString(string: "WordPress iOS + Aztec", attributes: [NSLinkAttributeName: wordpressURL]) - }() + return NSAttributedString(string: "WordPress iOS + Aztec", attributes: [.link: wordpressURL]) + }(()) /// Plain Tail String /// @@ -35,13 +35,13 @@ class NSAttributedStringAnalyzerTests: XCTestCase { fullString.append(linkifiedString) fullString.append(tailString) return fullString - }() + }(()) /// NSRange indicating the linkified segment /// static let linkifiedRange = { _ -> NSRange in return fullString.foundationString.range(of: linkifiedString.string) - }() + }(()) /// Initial Link Location /// diff --git a/AztecTests/Extensions/NSAttributedStringHTMLInitializerTests.swift b/AztecTests/Extensions/NSAttributedStringHTMLInitializerTests.swift index bbb19e463..f2594a488 100644 --- a/AztecTests/Extensions/NSAttributedStringHTMLInitializerTests.swift +++ b/AztecTests/Extensions/NSAttributedStringHTMLInitializerTests.swift @@ -13,8 +13,8 @@ class NSAttributedStringHTMLInitializerTests: XCTestCase { let html = "" - let defaultAttributes = [NSFontAttributeName: UIFont.systemFont(ofSize: 14), - NSParagraphStyleAttributeName: ParagraphStyle.default] + let defaultAttributes: [NSAttributedStringKey: Any] = [.font: UIFont.systemFont(ofSize: 14), + .paragraphStyle: ParagraphStyle.default] XCTAssertNoThrow(NSAttributedString(withHTML: html, defaultAttributes: defaultAttributes)) } diff --git a/AztecTests/Extensions/NSAttributedStringKeyHelperTests.swift b/AztecTests/Extensions/NSAttributedStringKeyHelperTests.swift new file mode 100644 index 000000000..212f74aec --- /dev/null +++ b/AztecTests/Extensions/NSAttributedStringKeyHelperTests.swift @@ -0,0 +1,40 @@ +import XCTest +@testable import Aztec + +class NSAttributedStringKeyHelperTests: XCTestCase { + + /// Verifies that, given a collection of [NSAttributedStringKey: Any], `NSAttributedStringKey.convertToRaw(:)` effectively converts + /// all of the keys into Strings. + /// + func testConvertToRawReturnsANewCollectionContainingAllOfTheStringValues() { + let input: [NSAttributedStringKey: Any] = [ + .strikethroughStyle: NSUnderlineStyle.styleSingle, + .attachment: 222, + NSAttributedStringKey("Custom"): 111 + ] + + let output = NSAttributedStringKey.convertToRaw(attributes: input) + + XCTAssertEqual(output[NSAttributedStringKey.strikethroughStyle.rawValue] as! NSUnderlineStyle, .styleSingle) + XCTAssertEqual(output[NSAttributedStringKey.attachment.rawValue] as! Int, 222) + XCTAssertEqual(output["Custom"] as! Int, 111) + } + + + /// Verifies that, given a collection of [String: Any], `NSAttributedStringKey.convertFromRaw(:)` effectively converts + /// all of the keys into NSAttributedStringKey instances. + /// + func testConvertFromRawReturnsANewCollectionContainingAttributedStringKeyInstances() { + let input: [String: Any] = [ + NSAttributedStringKey.strikethroughStyle.rawValue: NSUnderlineStyle.styleSingle, + NSAttributedStringKey.attachment.rawValue: 222, + "Custom": 111 + ] + + let output = NSAttributedStringKey.convertFromRaw(attributes: input) + + XCTAssertEqual(output[.strikethroughStyle] as! NSUnderlineStyle, NSUnderlineStyle.styleSingle) + XCTAssertEqual(output[.attachment] as! Int, 222) + XCTAssertEqual(output[NSAttributedStringKey("Custom")] as! Int, 111) + } +} diff --git a/AztecTests/Extensions/NSAttributedStringListsTests.swift b/AztecTests/Extensions/NSAttributedStringListsTests.swift index ab131715c..d26c56298 100644 --- a/AztecTests/Extensions/NSAttributedStringListsTests.swift +++ b/AztecTests/Extensions/NSAttributedStringListsTests.swift @@ -255,7 +255,7 @@ extension NSAttributedStringListsTests let range = (sample.string as NSString).range(of: sampleListContents) let listParagraphStyle = ParagraphStyle() listParagraphStyle.appendProperty(TextList(style: .ordered)) - let attributes = [NSParagraphStyleAttributeName: listParagraphStyle] + let attributes = [NSAttributedStringKey.paragraphStyle: listParagraphStyle] sample.addAttributes(attributes, range: range) return sample diff --git a/AztecTests/Formatters/BlockquoteFormatterTests.swift b/AztecTests/Formatters/BlockquoteFormatterTests.swift index 53ec108b8..6c257fdd0 100644 --- a/AztecTests/Formatters/BlockquoteFormatterTests.swift +++ b/AztecTests/Formatters/BlockquoteFormatterTests.swift @@ -22,7 +22,7 @@ class BlockquoteFormatterTests: XCTestCase { let paragraphs = paragraphRanges(inString: storage) let formatter = BlockquoteFormatter() - var attributes = [String:Any]() + var attributes = [NSAttributedStringKey: Any]() attributes = formatter.apply(to: attributes) textView.storage.setAttributes(attributes, range: paragraphs[0]) formatter.toggle(in: storage, at: NSRange(location: 1, length: 1)) @@ -36,7 +36,7 @@ class BlockquoteFormatterTests: XCTestCase { let paragraphs = paragraphRanges(inString: storage) let formatter = BlockquoteFormatter() - var attributes = [String:Any]() + var attributes = [NSAttributedStringKey: Any]() attributes = formatter.apply(to: attributes) textView.storage.setAttributes(attributes, range: paragraphs[1]) formatter.toggle(in: storage, at: NSUnionRange(paragraphs[0], paragraphs[1])) @@ -51,7 +51,7 @@ class BlockquoteFormatterTests: XCTestCase { let paragraphs = paragraphRanges(inString: storage) let formatter = BlockquoteFormatter() - var attributes = [String:Any]() + var attributes = [NSAttributedStringKey: Any]() attributes = formatter.apply(to: attributes) textView.storage.setAttributes(attributes, range: paragraphs[0]) formatter.toggle(in: storage, at: NSUnionRange(paragraphs[0], paragraphs[1])) @@ -63,7 +63,7 @@ class BlockquoteFormatterTests: XCTestCase { func testToggleBlockquoteTwiceLeavesReturnsIdenticalString() { let textView = testTextView let storage = textView.storage - textView.storage.setAttributes([NSParagraphStyleAttributeName: ParagraphStyle.default], range: textView.storage.rangeOfEntireString) + textView.storage.setAttributes([.paragraphStyle: ParagraphStyle.default], range: textView.storage.rangeOfEntireString) let paragraphs = paragraphRanges(inString: storage) let formatter = BlockquoteFormatter() @@ -118,7 +118,7 @@ private extension BlockquoteFormatterTests { func existsBlockquote(for string: NSMutableAttributedString, in range: NSRange) -> Bool { var effectiveRange = NSRange() - guard let paragraphStyle = string.attribute(NSParagraphStyleAttributeName, at: range.location, effectiveRange: &effectiveRange) as? ParagraphStyle, + guard let paragraphStyle = string.attribute(.paragraphStyle, at: range.location, effectiveRange: &effectiveRange) as? ParagraphStyle, !paragraphStyle.blockquotes.isEmpty else { return false } diff --git a/AztecTests/Formatters/FontFormatterTests.swift b/AztecTests/Formatters/FontFormatterTests.swift index 4cbd5d209..1e48ee681 100644 --- a/AztecTests/Formatters/FontFormatterTests.swift +++ b/AztecTests/Formatters/FontFormatterTests.swift @@ -10,44 +10,45 @@ class FontFormatterTests: XCTestCase let italicFormatter = ItalicFormatter() func testApplyAttribute() { - var attributes: [String : Any] = [NSFontAttributeName: UIFont.systemFont(ofSize: UIFont.systemFontSize)] + var attributes: [NSAttributedStringKey : Any] = [.font: UIFont.systemFont(ofSize: UIFont.systemFontSize)] var font: UIFont? //test adding a non-existent testApplyAttribute attributes = boldFormatter.apply(to: attributes) //this should add a new attribute to it - font = attributes[NSFontAttributeName] as? UIFont + font = attributes[.font] as? UIFont XCTAssertNotNil(font) XCTAssertTrue(font!.containsTraits(.traitBold)) //test addding a existent attribute attributes = boldFormatter.apply(to: attributes) // this shouldn't change anything in the attributes - font = attributes[NSFontAttributeName] as? UIFont + font = attributes[.font] as? UIFont XCTAssertNotNil(font) XCTAssertTrue(font!.containsTraits(.traitBold)) } func testRemoveAttributes() { - var attributes: [String : Any] = [NSFontAttributeName: UIFont.boldSystemFont(ofSize: UIFont.systemFontSize)] + var attributes: [NSAttributedStringKey : Any] = [.font: UIFont.boldSystemFont(ofSize: UIFont.systemFontSize)] var font: UIFont? //test removing a existent attribute attributes = boldFormatter.remove(from: attributes) - font = attributes[NSFontAttributeName] as? UIFont + font = attributes[.font] as? UIFont XCTAssertNotNil(font) XCTAssertFalse(font!.containsTraits(.traitBold)) - attributes = [NSFontAttributeName: UIFont.boldSystemFont(ofSize: UIFont.systemFontSize)] + attributes = [.font: UIFont.boldSystemFont(ofSize: UIFont.systemFontSize)] //test removing a non-existent testApplyAttribute attributes = italicFormatter.remove(from: attributes) - font = attributes[NSFontAttributeName] as? UIFont + font = attributes[.font] as? UIFont XCTAssertNotNil(font) XCTAssertTrue(font!.containsTraits(.traitBold)) } func testPresentAttributes() { - var attributes: [String : Any] = [NSFontAttributeName: UIFont.boldSystemFont(ofSize: UIFont.systemFontSize)] + var attributes: [NSAttributedStringKey : Any] = [.font: UIFont.boldSystemFont(ofSize: UIFont.systemFontSize)] + //test when attribute is present XCTAssertTrue(boldFormatter.present(in: attributes)) //test when attributes is not present diff --git a/AztecTests/Formatters/HeaderFormatterTests.swift b/AztecTests/Formatters/HeaderFormatterTests.swift index 5e6a06b0e..b78eda259 100644 --- a/AztecTests/Formatters/HeaderFormatterTests.swift +++ b/AztecTests/Formatters/HeaderFormatterTests.swift @@ -12,8 +12,8 @@ class HeaderFormatterTests: XCTestCase { /// Sample Attributes /// - private lazy var attributes: [String: Any] = { - return [NSFontAttributeName: UIFont.systemFont(ofSize: self.defaultFontSize)] + private lazy var attributes: [NSAttributedStringKey: Any] = { + return [.font: UIFont.systemFont(ofSize: self.defaultFontSize)] }() @@ -23,11 +23,11 @@ class HeaderFormatterTests: XCTestCase { let formatter = HeaderFormatter(headerLevel: .h1, placeholderAttributes: nil) let updatedAttrs = formatter.apply(to: attributes, andStore: nil) - let updatedFont = updatedAttrs[NSFontAttributeName] as! UIFont + let updatedFont = updatedAttrs[.font] as! UIFont XCTAssert(updatedFont.pointSize == CGFloat(formatter.headerLevel.fontSize)) let removedAttrs = formatter.remove(from: updatedAttrs) - let removedFont = removedAttrs[NSFontAttributeName] as! UIFont + let removedFont = removedAttrs[.font] as! UIFont XCTAssert(removedFont.pointSize == defaultFontSize) } @@ -36,16 +36,16 @@ class HeaderFormatterTests: XCTestCase { func testDefaultFontIsPreservedWheneverTheHeaderLevelIsUpdated() { let formatterH1 = HeaderFormatter(headerLevel: .h1, placeholderAttributes: nil) let updatedH1Attrs = formatterH1.apply(to: attributes, andStore: nil) - let updatedH1Font = updatedH1Attrs[NSFontAttributeName] as! UIFont + let updatedH1Font = updatedH1Attrs[.font] as! UIFont XCTAssert(updatedH1Font.pointSize == CGFloat(formatterH1.headerLevel.fontSize)) let formatterH2 = HeaderFormatter(headerLevel: .h2, placeholderAttributes: nil) let updatedH2Attrs = formatterH2.apply(to: attributes, andStore: nil) - let updatedH2Font = updatedH2Attrs[NSFontAttributeName] as! UIFont + let updatedH2Font = updatedH2Attrs[.font] as! UIFont XCTAssert(updatedH2Font.pointSize == CGFloat(formatterH2.headerLevel.fontSize)) let removedAttrs = formatterH2.remove(from: updatedH2Attrs) - let removedFont = removedAttrs[NSFontAttributeName] as! UIFont + let removedFont = removedAttrs[.font] as! UIFont XCTAssert(removedFont.pointSize == defaultFontSize) } } diff --git a/AztecTests/Formatters/PreFormaterTests.swift b/AztecTests/Formatters/PreFormaterTests.swift index fdde9e477..ada22f5e3 100644 --- a/AztecTests/Formatters/PreFormaterTests.swift +++ b/AztecTests/Formatters/PreFormaterTests.swift @@ -10,20 +10,20 @@ class PreFormatterTests: XCTestCase { /// to the formatter's behavior. /// func testPreFormatterDoesNotLooseAttachmentAttribuesOnRemove() { - let placeholderAttributes: [String: Any] = [ - NSFontAttributeName: "Value", - NSParagraphStyleAttributeName: NSParagraphStyle() + let placeholderAttributes: [NSAttributedStringKey: Any] = [ + .font: "Value", + .paragraphStyle: NSParagraphStyle() ] - let stringAttributes: [String: Any] = [ - NSAttachmentAttributeName: NSTextAttachment(), + let stringAttributes: [NSAttributedStringKey: Any] = [ + .attachment: NSTextAttachment(), ] let formatter = PreFormatter(placeholderAttributes: placeholderAttributes) let updated = formatter.remove(from: stringAttributes) - let expectedValue = stringAttributes[NSAttachmentAttributeName] as! NSTextAttachment - let updatedValue = updated[NSAttachmentAttributeName] as! NSTextAttachment + let expectedValue = stringAttributes[.attachment] as! NSTextAttachment + let updatedValue = updated[.attachment] as! NSTextAttachment XCTAssert(updatedValue == expectedValue) } diff --git a/AztecTests/NSAttributedString/Conversions/AttributedStringParserTests.swift b/AztecTests/NSAttributedString/Conversions/AttributedStringParserTests.swift index 0df0248c8..3c5d2d6be 100644 --- a/AztecTests/NSAttributedString/Conversions/AttributedStringParserTests.swift +++ b/AztecTests/NSAttributedString/Conversions/AttributedStringParserTests.swift @@ -717,7 +717,7 @@ class AttributedStringParserTests: XCTestCase { // Store let unsupportedHTML = UnsupportedHTML(representations: [representation]) - testingString.addAttribute(UnsupportedHTMLAttributeName, value: unsupportedHTML, range: testingString.rangeOfEntireString) + testingString.addAttribute(.unsupportedHtml, value: unsupportedHTML, range: testingString.rangeOfEntireString) // Convert + Verify let node = AttributedStringParser().parse(testingString) @@ -847,9 +847,9 @@ private extension AttributedStringParserTests { /// Constants /// struct Constants { - static let sampleAttributes: [String : Any] = [ - NSFontAttributeName: UIFont.systemFont(ofSize: UIFont.systemFontSize), - NSParagraphStyleAttributeName: NSParagraphStyle() + static let sampleAttributes: [NSAttributedStringKey : Any] = [ + .font: UIFont.systemFont(ofSize: UIFont.systemFontSize), + .paragraphStyle: NSParagraphStyle() ] } } diff --git a/AztecTests/NSAttributedString/Conversions/AttributedStringSerializerTests.swift b/AztecTests/NSAttributedString/Conversions/AttributedStringSerializerTests.swift index 81bcb6186..f4599cb87 100644 --- a/AztecTests/NSAttributedString/Conversions/AttributedStringSerializerTests.swift +++ b/AztecTests/NSAttributedString/Conversions/AttributedStringSerializerTests.swift @@ -26,7 +26,7 @@ class AttributedStringSerializerTests: XCTestCase { // Test var range = NSRange() - guard let unsupportedHTML = output.attribute(UnsupportedHTMLAttributeName, at: 0, effectiveRange: &range) as? UnsupportedHTML else { + guard let unsupportedHTML = output.attribute(.unsupportedHtml, at: 0, effectiveRange: &range) as? UnsupportedHTML else { XCTFail() return } @@ -70,7 +70,7 @@ class AttributedStringSerializerTests: XCTestCase { // Test! var range = NSRange() - guard let paragraphStyle = output.attribute(NSParagraphStyleAttributeName, at: 0, effectiveRange: &range) as? ParagraphStyle else { + guard let paragraphStyle = output.attribute(.paragraphStyle, at: 0, effectiveRange: &range) as? ParagraphStyle else { XCTFail() return } @@ -172,8 +172,8 @@ class AttributedStringSerializerTests: XCTestCase { extension AttributedStringSerializerTests { func attributedString(from node: Node) -> NSAttributedString { - let defaultAttributes = [NSFontAttributeName: UIFont.systemFont(ofSize: 14), - NSParagraphStyleAttributeName: ParagraphStyle.default] + let defaultAttributes: [NSAttributedStringKey: Any] = [.font: UIFont.systemFont(ofSize: 14), + .paragraphStyle: ParagraphStyle.default] let serializer = AttributedStringSerializer(defaultAttributes: defaultAttributes) diff --git a/AztecTests/StringRangeConversionTests.swift b/AztecTests/StringRangeConversionTests.swift index 615481d6e..36350c18c 100644 --- a/AztecTests/StringRangeConversionTests.swift +++ b/AztecTests/StringRangeConversionTests.swift @@ -22,7 +22,7 @@ class StringRangeConversionTests: XCTestCase { let nsRange = nsString.range(of: wordToCapture) let range = string.range(from: nsRange) - let wordCaptured = string.substring(with: range) + let wordCaptured = String(string[range]) XCTAssertEqual(wordToCapture, wordCaptured) } @@ -35,7 +35,7 @@ class StringRangeConversionTests: XCTestCase { let nsRange = string.nsRange(fromUTF16NSRange: utf16NSRange)! let range = string.range(from: nsRange) - let wordCaptured = string.substring(with: range) + let wordCaptured = String(string[range]) XCTAssertEqual(wordToCapture, wordCaptured) } @@ -48,7 +48,7 @@ class StringRangeConversionTests: XCTestCase { let nsRange = string.nsRange(fromUTF16NSRange: utf16NSRange)! let range = string.range(from: nsRange) - let wordCaptured = string.substring(with: range) + let wordCaptured = String(string[range]) XCTAssertEqual(wordToCapture, wordCaptured) } @@ -61,7 +61,7 @@ class StringRangeConversionTests: XCTestCase { let nsRange = string.nsRange(fromUTF16NSRange: utf16NSRange)! let range = string.range(from: nsRange) - let wordCaptured = string.substring(with: range) + let wordCaptured = String(string[range]) XCTAssertEqual(wordToCapture, wordCaptured) } @@ -75,7 +75,7 @@ class StringRangeConversionTests: XCTestCase { let nsRange = nsString.range(of: wordToCapture) let index = string.indexFromLocation(nsRange.location)! - let wordCaptured = string.substring(to: index) + let wordCaptured = string.prefix(upTo: index) XCTAssertEqual("Hello ", wordCaptured) } @@ -88,7 +88,7 @@ class StringRangeConversionTests: XCTestCase { let nsRange = nsString.range(of: wordToCapture) let index = string.indexFromLocation(nsRange.location)! - let wordCaptured = string.substring(to: index) + let wordCaptured = string.prefix(upTo: index) XCTAssertEqual("Hello ", wordCaptured) } @@ -101,7 +101,7 @@ class StringRangeConversionTests: XCTestCase { let nsRange = nsString.range(of: wordToCapture) let index = string.indexFromLocation(nsRange.location)! - let wordCaptured = string.substring(to: index) + let wordCaptured = string.prefix(upTo: index) XCTAssertEqual("Hello ", wordCaptured) } @@ -114,7 +114,7 @@ class StringRangeConversionTests: XCTestCase { let nsRange = nsString.range(of: wordToCapture) let index = string.indexFromLocation(nsRange.location)! - let wordCaptured = string.substring(to: index) + let wordCaptured = string.prefix(upTo: index) XCTAssertEqual("Hello 🇮🇳 ", wordCaptured) } @@ -127,7 +127,7 @@ class StringRangeConversionTests: XCTestCase { let nsRange = nsString.range(of: wordToCapture) let location = string.location(before: nsRange.location)! let index = string.indexFromLocation(location)! - let wordCaptured = string.substring(to: index) + let wordCaptured = string.prefix(upTo: index) XCTAssertEqual("Hello", wordCaptured) } @@ -140,7 +140,7 @@ class StringRangeConversionTests: XCTestCase { let nsRange = nsString.range(of: wordToCapture) let location = string.location(before: nsRange.endLocation)! let index = string.indexFromLocation(location)! - let wordCaptured = string.substring(to: index) + let wordCaptured = string.prefix(upTo: index) XCTAssertEqual("Hello ", wordCaptured) } diff --git a/AztecTests/TextKit/TextStorageTests.swift b/AztecTests/TextKit/TextStorageTests.swift index def69ec9d..637eb4d73 100644 --- a/AztecTests/TextKit/TextStorageTests.swift +++ b/AztecTests/TextKit/TextStorageTests.swift @@ -31,7 +31,7 @@ class TextStorageTests: XCTestCase { func testFontTraitExistsAtIndex() { let attributes = [ - NSFontAttributeName: UIFont.boldSystemFont(ofSize: 10) + NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 10) ] storage.append(NSAttributedString(string: "foo")) @@ -52,7 +52,7 @@ class TextStorageTests: XCTestCase { func testFontTraitSpansRange() { let attributes = [ - NSFontAttributeName: UIFont.boldSystemFont(ofSize: 10) + NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 10) ] storage.append(NSAttributedString(string: "foo")) @@ -66,7 +66,7 @@ class TextStorageTests: XCTestCase { func testToggleTraitInRange() { let attributes = [ - NSFontAttributeName: UIFont.boldSystemFont(ofSize: 10) + NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 10) ] storage.append(NSAttributedString(string: "foo")) @@ -165,8 +165,8 @@ class TextStorageTests: XCTestCase { let finalHTML = "

\(updatedHTML)

" // Setup - let defaultAttributes = [NSFontAttributeName: UIFont.systemFont(ofSize: 14), - NSParagraphStyleAttributeName: ParagraphStyle.default] + let defaultAttributes = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 14), + NSAttributedStringKey.paragraphStyle: ParagraphStyle.default] storage.setHTML(initialHTML, defaultAttributes: defaultAttributes) @@ -285,7 +285,7 @@ class TextStorageTests: XCTestCase { var oldFont: UIFont? for i in 0 ..< storage.length { - let currentFont = storage.attribute(NSFontAttributeName, at: i, effectiveRange: nil) as? UIFont + let currentFont = storage.attribute(.font, at: i, effectiveRange: nil) as? UIFont XCTAssert(oldFont == nil || oldFont == currentFont) oldFont = currentFont } @@ -381,8 +381,8 @@ class TextStorageTests: XCTestCase { let commentString = "This is a comment" let html = "" - let defaultAttributes = [NSFontAttributeName: UIFont.systemFont(ofSize: 14), - NSParagraphStyleAttributeName: ParagraphStyle.default] + let defaultAttributes: [NSAttributedStringKey: Any] = [.font: UIFont.systemFont(ofSize: 14), + .paragraphStyle: ParagraphStyle.default] storage.setHTML(html, defaultAttributes: defaultAttributes) storage.replaceCharacters(in: NSRange(location: 0, length: 1), with: NSAttributedString(string: "")) diff --git a/AztecTests/TextKit/TextViewTests.swift b/AztecTests/TextKit/TextViewTests.swift index 5314da98d..73f98e880 100644 --- a/AztecTests/TextKit/TextViewTests.swift +++ b/AztecTests/TextKit/TextViewTests.swift @@ -55,7 +55,7 @@ class TextViewTests: XCTestCase { let paragraph = "Lorem ipsum dolar sit amet.\n" let richTextView = Aztec.TextView(defaultFont: UIFont.systemFont(ofSize: 14), defaultMissingImage: UIImage()) richTextView.textAttachmentDelegate = attachmentDelegate - let attributes = [NSParagraphStyleAttributeName : NSParagraphStyle()] + let attributes = [NSAttributedStringKey.paragraphStyle : NSParagraphStyle()] let templateString = NSMutableAttributedString(string: paragraph, attributes: attributes) let attrStr = NSMutableAttributedString() @@ -816,7 +816,7 @@ class TextViewTests: XCTestCase { textView.selectedRange = textView.text.endOfStringNSRange() textView.deleteBackward() - XCTAssertFalse(TextListFormatter.listsOfAnyKindPresent(in: textView.typingAttributes)) + XCTAssertFalse(TextListFormatter.listsOfAnyKindPresent(in: textView.typingAttributesSwifted)) XCTAssert(textView.storage.length == 0) } @@ -896,7 +896,7 @@ class TextViewTests: XCTestCase { textView.toggleOrderedList(range: .zero) textView.selectedTextRange = textView.textRange(from: textView.endOfDocument, to: textView.endOfDocument) - XCTAssertFalse(TextListFormatter.listsOfAnyKindPresent(in: textView.typingAttributes)) + XCTAssertFalse(TextListFormatter.listsOfAnyKindPresent(in: textView.typingAttributesSwifted)) } /// Verifies that a Text List gets removed, whenever the user types `\n` in an empty line. @@ -920,7 +920,7 @@ class TextViewTests: XCTestCase { XCTAssertFalse(formatter.present(in: attributedText, at: location)) } - XCTAssertFalse(TextListFormatter.listsOfAnyKindPresent(in: textView.typingAttributes)) + XCTAssertFalse(TextListFormatter.listsOfAnyKindPresent(in: textView.typingAttributesSwifted)) } /// Verifies that toggling an Unordered List, when editing an empty document, inserts a Newline. @@ -1016,7 +1016,7 @@ class TextViewTests: XCTestCase { textView.toggleUnorderedList(range: textView.selectedRange) textView.deleteBackward() - XCTAssertFalse(TextListFormatter.listsOfAnyKindPresent(in: textView.typingAttributes)) + XCTAssertFalse(TextListFormatter.listsOfAnyKindPresent(in: textView.typingAttributesSwifted)) } /// When the caret is positioned at both EoF and EoL, inserting a line separator (in most @@ -1081,7 +1081,7 @@ class TextViewTests: XCTestCase { let formatter = BlockquoteFormatter() - XCTAssertFalse(formatter.present(in: textView.typingAttributes)) + XCTAssertFalse(formatter.present(in: textView.typingAttributesSwifted)) XCTAssert(textView.storage.length == 0) } @@ -1157,7 +1157,7 @@ class TextViewTests: XCTestCase { textView.toggleBlockquote(range: .zero) textView.selectedTextRange = textView.textRange(from: textView.endOfDocument, to: textView.endOfDocument) - XCTAssertFalse(BlockquoteFormatter().present(in: textView.typingAttributes)) + XCTAssertFalse(BlockquoteFormatter().present(in: textView.typingAttributesSwifted)) } /// Verifies that Blockquotes get removed whenever the user types `\n` in an empty line. @@ -1181,7 +1181,7 @@ class TextViewTests: XCTestCase { XCTAssertFalse(formatter.present(in: attributedText, at: location)) } - XCTAssertFalse(formatter.present(in: textView.typingAttributes)) + XCTAssertFalse(formatter.present(in: textView.typingAttributesSwifted)) } /// Verifies that toggling a Blockquote, when editing an empty document, inserts a Newline. @@ -1267,7 +1267,7 @@ class TextViewTests: XCTestCase { let formatter = PreFormatter() - XCTAssertFalse(formatter.present(in: textView.typingAttributes)) + XCTAssertFalse(formatter.present(in: textView.typingAttributesSwifted)) XCTAssert(textView.storage.length == 0) } @@ -1343,7 +1343,7 @@ class TextViewTests: XCTestCase { textView.togglePre(range: .zero) textView.selectedTextRange = textView.textRange(from: textView.endOfDocument, to: textView.endOfDocument) - XCTAssertFalse(PreFormatter().present(in: textView.typingAttributes)) + XCTAssertFalse(PreFormatter().present(in: textView.typingAttributesSwifted)) } /// Verifies that Pre get removed whenever the user types `\n` in an empty line. @@ -1367,7 +1367,7 @@ class TextViewTests: XCTestCase { XCTAssertFalse(formatter.present(in: attributedText, at: location)) } - XCTAssertFalse(formatter.present(in: textView.typingAttributes)) + XCTAssertFalse(formatter.present(in: textView.typingAttributesSwifted)) } /// Verifies that toggling a Pre, when editing an empty document, inserts a Newline. @@ -1526,7 +1526,7 @@ class TextViewTests: XCTestCase { XCTAssertEqual(html, "

") textView.selectedRange = NSRange(location: NSAttributedString.lengthOfTextAttachment, length: 1) - guard let font = textView.typingAttributes[NSFontAttributeName] as? UIFont else { + guard let font = textView.typingAttributesSwifted[.font] as? UIFont else { XCTFail("Font should be set") return } @@ -1537,7 +1537,7 @@ class TextViewTests: XCTestCase { let textView = createEmptyTextViewWithNonStandardSystemFont() textView.insertText("😘") - let currentTypingFont = textView.typingAttributes[NSFontAttributeName] as! UIFont + let currentTypingFont = textView.typingAttributesSwifted[.font] as! UIFont XCTAssertEqual(currentTypingFont, nonStandardSystemFont, "Font should be set to default") } diff --git a/Example/AztecExample.xcodeproj/project.pbxproj b/Example/AztecExample.xcodeproj/project.pbxproj index 7540e5942..81d308e02 100644 --- a/Example/AztecExample.xcodeproj/project.pbxproj +++ b/Example/AztecExample.xcodeproj/project.pbxproj @@ -367,12 +367,12 @@ 607FACCF1AFB9204008FA782 = { CreatedOnToolsVersion = 6.3.1; DevelopmentTeam = PZYM8XX95Q; - LastSwiftMigration = 0800; + LastSwiftMigration = 0910; ProvisioningStyle = Manual; }; 607FACE41AFB9204008FA782 = { CreatedOnToolsVersion = 6.3.1; - LastSwiftMigration = 0830; + LastSwiftMigration = 0910; ProvisioningStyle = Manual; TestTargetID = 607FACCF1AFB9204008FA782; }; @@ -656,7 +656,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = "Aztec Example Development"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -678,7 +679,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = "Aztec Example Ad Hoc"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -698,7 +700,8 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.wordpress.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AztecExample.app/AztecExample"; }; name = Debug; @@ -713,7 +716,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.wordpress.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AztecExample.app/AztecExample"; }; name = Release; @@ -850,7 +854,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = "Aztec Example Development"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Profiling; @@ -865,7 +870,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.wordpress.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AztecExample.app/AztecExample"; }; name = Profiling; @@ -942,7 +948,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; PROVISIONING_PROFILE_SPECIFIER = "Aztec Example Alpha Distribution"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = "Release-Alpha"; @@ -962,7 +969,8 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.wordpress.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AztecExample.app/AztecExample"; }; name = "Release-Alpha"; diff --git a/Example/Example/CommentAttachmentRenderer.swift b/Example/Example/CommentAttachmentRenderer.swift index 3edfe5e9b..32abd324a 100644 --- a/Example/Example/CommentAttachmentRenderer.swift +++ b/Example/Example/CommentAttachmentRenderer.swift @@ -74,9 +74,9 @@ private extension CommentAttachmentRenderer { } func messageAttributedString() -> NSAttributedString { - let attributes: [String: Any] = [ - NSForegroundColorAttributeName: textColor, - NSFontAttributeName: textFont + let attributes: [NSAttributedStringKey: Any] = [ + .foregroundColor: textColor, + .font: textFont ] return NSAttributedString(string: defaultText, attributes: attributes) diff --git a/Example/Example/EditorDemoController.swift b/Example/Example/EditorDemoController.swift index 72432da9b..871db39cf 100644 --- a/Example/Example/EditorDemoController.swift +++ b/Example/Example/EditorDemoController.swift @@ -98,7 +98,8 @@ class EditorDemoController: UIViewController { let textField = UILabel() textField.attributedText = NSAttributedString(string: placeholderText, - attributes: [NSForegroundColorAttributeName: UIColor.lightGray, NSFontAttributeName: UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)]) + attributes: [.foregroundColor: UIColor.lightGray, + .font: UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)]) textField.sizeToFit() textField.translatesAutoresizingMaskIntoConstraints = false @@ -345,7 +346,7 @@ class EditorDemoController: UIViewController { // MARK: - Keyboard Handling - func keyboardWillShow(_ notification: Notification) { + @objc func keyboardWillShow(_ notification: Notification) { guard let userInfo = notification.userInfo as? [String: AnyObject], let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue @@ -356,7 +357,7 @@ class EditorDemoController: UIViewController { refreshInsets(forKeyboardFrame: keyboardFrame) } - func keyboardWillHide(_ notification: Notification) { + @objc func keyboardWillHide(_ notification: Notification) { guard let userInfo = notification.userInfo as? [String: AnyObject], let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue @@ -561,12 +562,12 @@ extension EditorDemoController { updateFormatBar() } - func toggleBold() { + @objc func toggleBold() { richTextView.toggleBold(range: richTextView.selectedRange) } - func toggleItalic() { + @objc func toggleItalic() { richTextView.toggleItalic(range: richTextView.selectedRange) } @@ -576,11 +577,11 @@ extension EditorDemoController { } - func toggleStrikethrough() { + @objc func toggleStrikethrough() { richTextView.toggleStrikethrough(range: richTextView.selectedRange) } - func toggleBlockquote() { + @objc func toggleBlockquote() { richTextView.toggleBlockquote(range: richTextView.selectedRange) } @@ -591,7 +592,7 @@ extension EditorDemoController { func toggleHeader(fromItem item: FormatBarItem) { let headerOptions = Constants.headers.map { headerType -> OptionsTableViewOption in let attributes = [ - NSFontAttributeName: UIFont.systemFont(ofSize: CGFloat(headerType.fontSize)) + NSAttributedStringKey.font: UIFont.systemFont(ofSize: CGFloat(headerType.fontSize)) ] let title = NSAttributedString(string: headerType.description, attributes: attributes) @@ -642,11 +643,11 @@ extension EditorDemoController { }) } - func toggleUnorderedList() { + @objc func toggleUnorderedList() { richTextView.toggleUnorderedList(range: richTextView.selectedRange) } - func toggleOrderedList() { + @objc func toggleOrderedList() { richTextView.toggleOrderedList(range: richTextView.selectedRange) } @@ -770,7 +771,7 @@ extension EditorDemoController { return nil } - func toggleLink() { + @objc func toggleLink() { var linkTitle = "" var linkURL: URL? = nil var linkRange = richTextView.selectedRange @@ -887,7 +888,7 @@ extension EditorDemoController { self.present(alertController, animated:true, completion:nil) } - func alertTextFieldDidChange(_ textField: UITextField) { + @objc func alertTextFieldDidChange(_ textField: UITextField) { guard let alertController = presentedViewController as? UIAlertController, let urlFieldText = alertController.textFields?.first?.text, @@ -900,7 +901,7 @@ extension EditorDemoController { } - func showImagePicker() { + @objc func showImagePicker() { let picker = UIImagePickerController() picker.sourceType = .photoLibrary picker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary) ?? [] @@ -1279,12 +1280,13 @@ private extension EditorDemoController richTextView.refresh(attachment) } - var mediaMessageAttributes: [String: Any] { + var mediaMessageAttributes: [NSAttributedStringKey: Any] { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = .center - let attributes: [String:Any] = [NSFontAttributeName: UIFont.systemFont(ofSize: 15, weight: UIFontWeightSemibold), - NSParagraphStyleAttributeName: paragraphStyle, - NSForegroundColorAttributeName: UIColor.white] + + let attributes: [NSAttributedStringKey: Any] = [.font: UIFont.systemFont(ofSize: 15, weight: .semibold), + .paragraphStyle: paragraphStyle, + .foregroundColor: UIColor.white] return attributes } diff --git a/Example/Example/HTMLAttachmentRenderer.swift b/Example/Example/HTMLAttachmentRenderer.swift index 1caf62c4f..d55276d6d 100644 --- a/Example/Example/HTMLAttachmentRenderer.swift +++ b/Example/Example/HTMLAttachmentRenderer.swift @@ -74,9 +74,9 @@ private extension HTMLAttachmentRenderer { } func messageAttributedString(with attachment: NSTextAttachment) -> NSAttributedString { - let attributes: [String: Any] = [ - NSForegroundColorAttributeName: textColor, - NSFontAttributeName: textFont + let attributes: [NSAttributedStringKey: Any] = [ + .foregroundColor: textColor, + .font: textFont ] let htmlAttachment = attachment as? HTMLAttachment diff --git a/Example/Example/SpecialTagAttachmentRenderer.swift b/Example/Example/SpecialTagAttachmentRenderer.swift index 2d300dab9..4fc7e6820 100644 --- a/Example/Example/SpecialTagAttachmentRenderer.swift +++ b/Example/Example/SpecialTagAttachmentRenderer.swift @@ -36,7 +36,7 @@ extension SpecialTagAttachmentRenderer: TextViewAttachmentImageProvider { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.baseWritingDirection = .leftToRight - let attributes: [String: Any] = [NSForegroundColorAttributeName: textColor, NSParagraphStyleAttributeName: paragraphStyle] + let attributes: [NSAttributedStringKey: Any] = [.foregroundColor: textColor, .paragraphStyle: paragraphStyle] let colorMessage = NSAttributedString(string: label, attributes: attributes)