diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..05bd97e --- /dev/null +++ b/Package.resolved @@ -0,0 +1,43 @@ +{ + "object": { + "pins": [ + { + "package": "Down", + "repositoryURL": "https://github.com/johnxnguyen/Down", + "state": { + "branch": null, + "revision": "f34b166be1f1db4aa8f573067e901d72f2a6be57", + "version": "0.11.0" + } + }, + { + "package": "iosMath", + "repositoryURL": "https://github.com/tophatmonocle/iosMath", + "state": { + "branch": null, + "revision": "8c65dab2295100c02d9d844f6dc5646ab9d57b42", + "version": "1.1.2" + } + }, + { + "package": "SnapKit", + "repositoryURL": "https://github.com/SnapKit/SnapKit", + "state": { + "branch": null, + "revision": "d458564516e5676af9c70b4f4b2a9178294f1bc6", + "version": "5.0.1" + } + }, + { + "package": "SwiftRichString", + "repositoryURL": "https://github.com/tophatmonocle/SwiftRichString", + "state": { + "branch": "master", + "revision": "e0b72d5c96968d7802856d2be096202c9798e8d1", + "version": null + } + } + ] + }, + "version": 1 +} diff --git a/Package.swift b/Package.swift index 20e07fa..1184145 100644 --- a/Package.swift +++ b/Package.swift @@ -12,6 +12,7 @@ let package = Package( .package(name: "Down", url: "https://github.com/johnxnguyen/Down", .upToNextMajor(from: "0.11.0")), .package(name: "SnapKit", url: "https://github.com/SnapKit/SnapKit", .upToNextMajor(from: "5.0.1")), .package(name: "iosMath", url: "https://github.com/tophatmonocle/iosMath", .upToNextMajor(from: "1.1.1")), + .package(name: "SwiftRichString", url: "https://github.com/tophatmonocle/SwiftRichString", .branch("master")) ], targets: [ .target( @@ -20,6 +21,7 @@ let package = Package( .product(name: "Down", package: "Down"), .product(name: "SnapKit", package: "SnapKit"), .product(name: "iosMath", package: "iosMath"), + .product(name: "SwiftRichString", package: "SwiftRichString"), ], path: "Source", exclude: ["Info.plist"] diff --git a/Podfile b/Podfile index 2071f20..0669229 100644 --- a/Podfile +++ b/Podfile @@ -12,6 +12,7 @@ target 'RichTextView' do pod 'iosMath', :git => 'https://github.com/tophatmonocle/iosMath.git' pod 'SwiftLint' pod 'SnapKit' + pod 'SwiftRichString', :git => 'https://github.com/tophatmonocle/SwiftRichString.git' end # This works around a unit test issue introduced in Xcode 10 / Cocoapod diff --git a/Podfile.lock b/Podfile.lock index 5d7a364..557b980 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -15,6 +15,7 @@ PODS: - Quick (4.0.0) - SnapKit (5.0.0) - SwiftLint (0.32.0) + - SwiftRichString (3.7.2) DEPENDENCIES: - Down @@ -24,6 +25,7 @@ DEPENDENCIES: - Quick - SnapKit - SwiftLint + - SwiftRichString (from `https://github.com/tophatmonocle/SwiftRichString.git`) SPEC REPOS: trunk: @@ -38,11 +40,16 @@ SPEC REPOS: EXTERNAL SOURCES: iosMath: :git: https://github.com/tophatmonocle/iosMath.git + SwiftRichString: + :git: https://github.com/tophatmonocle/SwiftRichString.git CHECKOUT OPTIONS: iosMath: :commit: 7518e763c44a82202ec36e518c5ab4b412adbdae :git: https://github.com/tophatmonocle/iosMath.git + SwiftRichString: + :commit: e0b72d5c96968d7802856d2be096202c9798e8d1 + :git: https://github.com/tophatmonocle/SwiftRichString.git SPEC CHECKSUMS: Down: 8babe0cafe145de188d2efa76ea938a11a5fdc6f @@ -53,7 +60,8 @@ SPEC CHECKSUMS: Quick: 6473349e43b9271a8d43839d9ba1c442ed1b7ac4 SnapKit: fd22d10eb9aff484d79a8724eab922c1ddf89bcf SwiftLint: 009a898ef2a1c851f45e1b59349bf6ff2ddc990d + SwiftRichString: d61b807db1f8834c26fee92b6dd3e1be2dc5af03 -PODFILE CHECKSUM: fd6952a9b41a9a41bb7e6fb036901d60f1134d46 +PODFILE CHECKSUM: ecc4996936f36f5e9a7ebeadfeac1dec9b7ffc17 -COCOAPODS: 1.11.2 +COCOAPODS: 1.10.1 diff --git a/RichTextView.podspec b/RichTextView.podspec index c56bd77..3e36b6c 100644 --- a/RichTextView.podspec +++ b/RichTextView.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'RichTextView' - s.version = '3.2.2' + s.version = '3.3.0' s.summary = 'iOS Text View that Properly Displays LaTeX, HTML, Markdown, and YouTube/Vimeo Links.' s.description = <<-DESC This is an iOS UIView that Properly Displays LaTeX, HTML, Markdown, and YouTube/Vimeo Links. Simply feed in an input @@ -29,5 +29,6 @@ string with the relevant rich text surrounded by the appropriate tags and it wil s.dependency 'Down' s.dependency 'iosMath' s.dependency 'SnapKit' + s.dependency 'SwiftRichString' s.swift_version = '5.0' end diff --git a/RichTextView.xcodeproj/project.pbxproj b/RichTextView.xcodeproj/project.pbxproj index cf18186..bcdaf91 100644 --- a/RichTextView.xcodeproj/project.pbxproj +++ b/RichTextView.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ @@ -33,6 +33,10 @@ 78E2681121A4B16900C9B2DE /* RichTextViewUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78E2680F21A4B16300C9B2DE /* RichTextViewUITests.swift */; }; 78E2681421A4B75800C9B2DE /* RichTextView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 781488E92194E6B600DED7A1 /* RichTextView.framework */; }; E0D33D3F751E4903439BC745 /* Pods_RichTextViewUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 82CE0BC0D9A50B53001050A4 /* Pods_RichTextViewUITests.framework */; }; + F28C888827C9725D003990CC /* HTMLStyleBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28C888427C9725D003990CC /* HTMLStyleBuilder.swift */; }; + F28C888927C9725D003990CC /* DynamicAttributesResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28C888527C9725D003990CC /* DynamicAttributesResolver.swift */; }; + F28C888A27C9725D003990CC /* HTMLStyleParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28C888627C9725D003990CC /* HTMLStyleParams.swift */; }; + F28C888B27C9725D003990CC /* HTMLRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28C888727C9725D003990CC /* HTMLRenderer.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -89,6 +93,10 @@ ADEC9B28C5BC453A6A45AD55 /* Pods-RichTextView.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RichTextView.release.xcconfig"; path = "Target Support Files/Pods-RichTextView/Pods-RichTextView.release.xcconfig"; sourceTree = ""; }; BD74F48D045A1BFAEE356BA5 /* Pods-RichTextViewUnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RichTextViewUnitTests.debug.xcconfig"; path = "Target Support Files/Pods-RichTextViewUnitTests/Pods-RichTextViewUnitTests.debug.xcconfig"; sourceTree = ""; }; DE6831B3A5825165B8A981E0 /* Pods-RichTextView.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RichTextView.debug.xcconfig"; path = "Target Support Files/Pods-RichTextView/Pods-RichTextView.debug.xcconfig"; sourceTree = ""; }; + F28C888427C9725D003990CC /* HTMLStyleBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLStyleBuilder.swift; sourceTree = ""; }; + F28C888527C9725D003990CC /* DynamicAttributesResolver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicAttributesResolver.swift; sourceTree = ""; }; + F28C888627C9725D003990CC /* HTMLStyleParams.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLStyleParams.swift; sourceTree = ""; }; + F28C888727C9725D003990CC /* HTMLRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLRenderer.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -149,6 +157,7 @@ 7886C3B521A3540F00C11817 /* Constants */, 78322F97223C39B400C3D599 /* Delegates */, 7886C3AD21A33AE800C11817 /* Extensions */, + F28C888327C9725D003990CC /* HTML Rendering */, 781488ED2194E6B600DED7A1 /* Info.plist */, 781488EC2194E6B600DED7A1 /* RichTextView.h */, 781488F42194E6D600DED7A1 /* RichTextView.swift */, @@ -275,6 +284,17 @@ name = Frameworks; sourceTree = ""; }; + F28C888327C9725D003990CC /* HTML Rendering */ = { + isa = PBXGroup; + children = ( + F28C888427C9725D003990CC /* HTMLStyleBuilder.swift */, + F28C888527C9725D003990CC /* DynamicAttributesResolver.swift */, + F28C888627C9725D003990CC /* HTMLStyleParams.swift */, + F28C888727C9725D003990CC /* HTMLRenderer.swift */, + ); + path = "HTML Rendering"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -545,10 +565,14 @@ files = ( 7886C3AF21A33AFD00C11817 /* StringExtension.swift in Sources */, 7886C3B721A3541D00C11817 /* RichTextViewConstants.swift in Sources */, + F28C888827C9725D003990CC /* HTMLStyleBuilder.swift in Sources */, 7823D04521BACCBC0041CD51 /* WKWebViewGenerator.swift in Sources */, 78D1DC3721A49486004CA0F1 /* NSMutableAttributedStringExtension.swift in Sources */, + F28C888A27C9725D003990CC /* HTMLStyleParams.swift in Sources */, 7823D04621BACCBC0041CD51 /* UITextViewGenerator.swift in Sources */, + F28C888927C9725D003990CC /* DynamicAttributesResolver.swift in Sources */, 781488F52194E6D600DED7A1 /* RichTextView.swift in Sources */, + F28C888B27C9725D003990CC /* HTMLRenderer.swift in Sources */, 7886C3AB21A319E000C11817 /* LatexParserProtocol.swift in Sources */, 78D1DC3521A47B23004CA0F1 /* RichDataType.swift in Sources */, 78148940219536DF00DED7A1 /* RichTextParser.swift in Sources */, diff --git a/Source/HTML Rendering/DynamicAttributesResolver.swift b/Source/HTML Rendering/DynamicAttributesResolver.swift new file mode 100644 index 0000000..3cd7959 --- /dev/null +++ b/Source/HTML Rendering/DynamicAttributesResolver.swift @@ -0,0 +1,42 @@ +// +// DynamicAttributesResolver.swift +// +// +// Created by Maicon Brauwers on 2022-02-24. +// + +import UIKit +import SwiftRichString + +class DynamicAttributesResolver: XMLDynamicAttributesResolver { + + func styleForUnknownXMLTag(_ tag: String, to attributedString: inout SwiftRichString.AttributedString, attributes: [String: String]?, fromStyle: StyleXML) { + } + + func applyDynamicAttributes(to attributedString: inout SwiftRichString.AttributedString, xmlStyle: XMLDynamicStyle, fromStyle: StyleXML) { + let finalStyleToApply = Style() + xmlStyle.enumerateAttributes { key, value in + switch key { + case "color": // color support + finalStyleToApply.color = Color(hexString: value) + case "style": + // THIS IS ALL HORRIBLE AND SHOULD BE REFACTOR USING A STRING SCANNER OR REGEX!!! + if value.starts(with: "color: rgb(") { + let substr = value.suffix(from: "color: rgb(".endIndex).dropLast() + let rgbValues = substr.split(separator: ",") + if rgbValues.count == 3, + let red = Float(rgbValues[0].trimmingCharacters(in: .whitespacesAndNewlines)), + let green = Float(rgbValues[1].trimmingCharacters(in: .whitespacesAndNewlines)), + let blue = Float(rgbValues[2].trimmingCharacters(in: .whitespacesAndNewlines)) { + finalStyleToApply.color = Color(red: CGFloat(red)/255, green: CGFloat(green)/255, blue: CGFloat(blue)/255, alpha: 1.0) + } + } + + default: + break + } + } + + attributedString.add(style: finalStyleToApply) + } +} diff --git a/Source/HTML Rendering/HTMLRenderer.swift b/Source/HTML Rendering/HTMLRenderer.swift new file mode 100644 index 0000000..6ee5a9a --- /dev/null +++ b/Source/HTML Rendering/HTMLRenderer.swift @@ -0,0 +1,33 @@ +// +// HTMLRenderer.swift +// +// +// Created by Maicon Brauwers on 2022-02-22. +// + +import UIKit +import SwiftRichString + +/* + * Renders an HTML string into a NSAttributedString by using the SwiftRichString dependency + */ + +class HTMLRenderer { + + var cachedStyles: [HTMLStyleParams: StyleXML] = [:] + static let shared = HTMLRenderer() + + func renderHTML(html: String, styleParams: HTMLStyleParams) -> NSAttributedString { + let style: StyleXML + + if let cachedStyle = self.cachedStyles[styleParams] { + style = cachedStyle + } else { + style = HTMLStyleBuilder().buildStyles(styleParams: styleParams) + cachedStyles[styleParams] = style + } + + let htmlReplacingBr = html.replacingOccurrences(of: "
", with: "\n") + return htmlReplacingBr.set(style: style) + } +} diff --git a/Source/HTML Rendering/HTMLStyleBuilder.swift b/Source/HTML Rendering/HTMLStyleBuilder.swift new file mode 100644 index 0000000..c781cf5 --- /dev/null +++ b/Source/HTML Rendering/HTMLStyleBuilder.swift @@ -0,0 +1,84 @@ +// +// HTMLStyleBuilder.swift +// +// +// Created by Maicon Brauwers on 2022-02-24. +// + +import SwiftRichString + +// This class is responsible to create the styles that are using to style the HTML tags + +struct HTMLStyleBuilder { + + // swiftlint:disable function_body_length + func buildStyles(styleParams: HTMLStyleParams) -> StyleGroup { + + let baseStyle = Style { + $0.font = styleParams.baseFont + $0.minimumLineHeight = 28 + } + + let h1Style = Style { + $0.font = styleParams.h1Font + $0.traitVariants = .bold + } + + let h2Style = Style { + $0.font = styleParams.h2Font + $0.traitVariants = .bold + } + + let h3Style = Style { + $0.font = styleParams.h3Font + $0.traitVariants = .bold + } + + let italicStyle = baseStyle.byAdding { + $0.traitVariants = .italic + } + + let boldStyle = baseStyle.byAdding { + $0.traitVariants = .bold + } + + let superStyle = Style { + $0.font = styleParams.baseFont.font(size: 8.0) + $0.baselineOffset = 20.0 / 3.5 + } + + let subStyle = baseStyle.byAdding { + $0.font = styleParams.baseFont.font(size: 8.0) + $0.baselineOffset = -20.0 / 3.5 + } + + let codeStyle = baseStyle.byAdding { + $0.backColor = Color(red: 249/255, green: 249/255, blue: 249/255, alpha: 1.0) + $0.color = Color(red: 0.69, green: 0.231, blue: 0, alpha: 1) + } + + let groupStyle = StyleXML( + base: baseStyle, + [ + "p": baseStyle, + "div": baseStyle, + "h1": h1Style, + "h2": h2Style, + "h3": h3Style, + "i": italicStyle, + "em": italicStyle, + "italic": italicStyle, + "bold": boldStyle, + "strong": boldStyle, + "b": boldStyle, + "sub": subStyle, + "sup": superStyle, + "span": baseStyle, + "code": codeStyle + ] + ) + groupStyle.xmlAttributesResolver = DynamicAttributesResolver() + return groupStyle + } + // swiftlint:enable function_body_length +} diff --git a/Source/HTML Rendering/HTMLStyleParams.swift b/Source/HTML Rendering/HTMLStyleParams.swift new file mode 100644 index 0000000..a28a337 --- /dev/null +++ b/Source/HTML Rendering/HTMLStyleParams.swift @@ -0,0 +1,22 @@ +// +// HTMLStyleParams.swift +// +// +// Created by Maicon Brauwers on 2022-02-24. +// + +import UIKit + +public struct HTMLStyleParams: Hashable { + let baseFont: UIFont + let h1Font: UIFont + let h2Font: UIFont + let h3Font: UIFont + + public init(baseFont: UIFont, h1Font: UIFont, h2Font: UIFont, h3Font: UIFont) { + self.baseFont = baseFont + self.h1Font = h1Font + self.h2Font = h2Font + self.h3Font = h3Font + } +} diff --git a/Source/RichTextView.swift b/Source/RichTextView.swift index 394e164..6997fa5 100644 --- a/Source/RichTextView.swift +++ b/Source/RichTextView.swift @@ -41,6 +41,8 @@ public class RichTextView: UIView { textViewDelegate: RichTextViewDelegate? = nil, customAdditionalAttributes: [String: [NSAttributedString.Key: Any]]? = nil, frame: CGRect, + shouldUseOptimizedHTMLParsing: Bool = false, + htmlStyleParams: HTMLStyleParams? = nil, completion: (([ParsingError]?) -> Void)? = nil) { self.input = input self.isSelectable = isSelectable @@ -51,7 +53,9 @@ public class RichTextView: UIView { textColor: textColor, latexTextBaselineOffset: latexTextBaselineOffset, interactiveTextColor: interactiveTextColor, - customAdditionalAttributes: customAdditionalAttributes + customAdditionalAttributes: customAdditionalAttributes, + shouldUseOptimizedHTMLParsing: shouldUseOptimizedHTMLParsing, + htmlStyleParams: htmlStyleParams ) self.textColor = textColor self.textViewDelegate = textViewDelegate @@ -79,6 +83,8 @@ public class RichTextView: UIView { latexTextBaselineOffset: CGFloat? = nil, interactiveTextColor: UIColor? = nil, customAdditionalAttributes: [String: [NSAttributedString.Key: Any]]? = nil, + shouldUseOptimizedHTMLParsing: Bool = false, + htmlStyleParams: HTMLStyleParams? = nil, completion: (([ParsingError]?) -> Void)? = nil) { self.input = input ?? self.input self.richTextParser = RichTextParser( @@ -87,7 +93,9 @@ public class RichTextView: UIView { textColor: textColor ?? self.textColor, latexTextBaselineOffset: latexTextBaselineOffset ?? self.richTextParser.latexTextBaselineOffset, interactiveTextColor: interactiveTextColor ?? self.richTextParser.interactiveTextColor, - customAdditionalAttributes: customAdditionalAttributes ?? self.richTextParser.customAdditionalAttributes + customAdditionalAttributes: customAdditionalAttributes ?? self.richTextParser.customAdditionalAttributes, + shouldUseOptimizedHTMLParsing: shouldUseOptimizedHTMLParsing, + htmlStyleParams: htmlStyleParams ) self.textColor = textColor ?? self.textColor self.subviews.forEach { $0.removeFromSuperview() } diff --git a/Source/Text Parsing/RichTextParser.swift b/Source/Text Parsing/RichTextParser.swift index d0c5559..f873f75 100644 --- a/Source/Text Parsing/RichTextParser.swift +++ b/Source/Text Parsing/RichTextParser.swift @@ -8,7 +8,9 @@ import Down import UIKit +import SwiftRichString +// swiftlint:disable file_length class RichTextParser { // MARK: - Constants @@ -49,6 +51,8 @@ class RichTextParser { let latexTextBaselineOffset: CGFloat let interactiveTextColor: UIColor let customAdditionalAttributes: [String: [NSAttributedString.Key: Any]]? + let shouldUseOptimizedHTMLParsing: Bool + let htmlStyleParams: HTMLStyleParams? // MARK: - Init @@ -57,13 +61,17 @@ class RichTextParser { textColor: UIColor = UIColor.black, latexTextBaselineOffset: CGFloat = 0, interactiveTextColor: UIColor = UIColor.blue, - customAdditionalAttributes: [String: [NSAttributedString.Key: Any]]? = nil) { + customAdditionalAttributes: [String: [NSAttributedString.Key: Any]]? = nil, + shouldUseOptimizedHTMLParsing: Bool = false, + htmlStyleParams: HTMLStyleParams? = nil) { self.latexParser = latexParser self.font = font self.textColor = textColor self.latexTextBaselineOffset = latexTextBaselineOffset self.interactiveTextColor = interactiveTextColor self.customAdditionalAttributes = customAdditionalAttributes + self.shouldUseOptimizedHTMLParsing = shouldUseOptimizedHTMLParsing + self.htmlStyleParams = htmlStyleParams } // MARK: - Multi-Purpose Functions @@ -127,7 +135,7 @@ class RichTextParser { if attributes.isEmpty || attributes[.attachment] != nil { return } - let specialDataSubstring = specialDataTypesString.string[ + let specialDataSubstring: String = specialDataTypesString.string[ max(range.lowerBound, 0).. ParserConstants.RichTextWithErrors { + if self.shouldUseOptimizedHTMLParsing, let htmlStyleParams = self.htmlStyleParams { + return self.getRichTextWithHTMLAndMarkdownHandledV2(fromString: mutableAttributedString, htmlStyleParams: htmlStyleParams) + } let inputString = mutableAttributedString.string let inputStringWithoutBreakingSpaces = inputString.replaceTrailingWhiteSpaceWithNonBreakingSpace().replaceLeadingWhiteSpaceWithNonBreakingSpace() let inputStringWithoutCommonEditorTags = self.removeCommonEditorTags(from: inputStringWithoutBreakingSpaces) @@ -220,6 +231,27 @@ class RichTextParser { return (finalOutputString, nil) } + private func getRichTextWithHTMLAndMarkdownHandledV2( + fromString mutableAttributedString: NSMutableAttributedString, + htmlStyleParams: HTMLStyleParams) -> ParserConstants.RichTextWithErrors { + + // Cleanup on the string + + let inputString = mutableAttributedString.string + + // Markdown to HTML + + guard let inputAsHTMLString = try? Down(markdownString: inputString).toHTML([.unsafe, .hardBreaks]) else { + return (mutableAttributedString.trimmingTrailingNewlinesAndWhitespaces(), [ParsingError.attributedTextGeneration(text: inputString)]) + } + + // Renders the HTML into a NSAttributedString + + let renderedAttributedString = HTMLRenderer.shared.renderHTML(html: inputAsHTMLString, styleParams: htmlStyleParams) + + return (renderedAttributedString, nil) + } + private func getParsedHTMLAttributedString(fromData data: Data) -> NSAttributedString? { var attributedString: NSAttributedString? let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [ @@ -398,7 +430,8 @@ class RichTextParser { let interactiveElementTagName = ParserConstants.interactiveElementTagName let interactiveElementID = input.string.getSubstring(inBetween: "[\(interactiveElementTagName) id=", and: "]") ?? input.string let interactiveElementText = input.string.getSubstring(inBetween: "]", and: "[/\(interactiveElementTagName)]") ?? input.string - let attributes: [NSAttributedString.Key: Any] = [.link: interactiveElementID].merging(input.attributes(at: 0, effectiveRange: nil)) { (current, _) in current + let attributes: [NSAttributedString.Key: Any] = [ + .link: interactiveElementID].merging(input.attributes(at: 0, effectiveRange: nil)) { (current, _) in current } let mutableAttributedInput = NSMutableAttributedString(string: interactiveElementText, attributes: attributes) return mutableAttributedInput @@ -418,3 +451,4 @@ class RichTextParser { return mutableAttributedInput } } +// swiftlint:enable file_length diff --git a/UnitTests/Extensions/StringExtensionSpec.swift b/UnitTests/Extensions/StringExtensionSpec.swift index 3d9e2e7..addaab2 100644 --- a/UnitTests/Extensions/StringExtensionSpec.swift +++ b/UnitTests/Extensions/StringExtensionSpec.swift @@ -147,17 +147,17 @@ class StringExtensionSpec: QuickSpec { context("Subscripts") { it("correctly subscripts a closed range of type Int") { let initialString = "Wow I love RichTextView!" - let substring = initialString[1...3] + let substring: String = initialString[1...3] expect(substring).to(equal("ow ")) } it("correctly subscripts an open range of type Int") { let initialString = "Wow I love RichTextView!" - let substring = initialString[5..<8] + let substring: String = initialString[5..<8] expect(substring).to(equal(" lo")) } it("correctly subscripts when it is just a single Int") { let initialString = "Wow I love RichTextView!" - let substring = initialString[6] + let substring: String = initialString[6] expect(substring).to(equal("l")) } } diff --git a/UnitTests/Text Parsing/RichTextParserSpec.swift b/UnitTests/Text Parsing/RichTextParserSpec.swift index ef8adb6..d8e22b4 100644 --- a/UnitTests/Text Parsing/RichTextParserSpec.swift +++ b/UnitTests/Text Parsing/RichTextParserSpec.swift @@ -42,7 +42,7 @@ class RichTextParserSpec: QuickSpec { it("succesfully returns an NSAttributedString with the custom link property from a basic interactive element") { let output = self.richTextParser.extractInteractiveElement(from: NSAttributedString(string: MarkDownText.basicInteractiveElement)) let attributes: [NSAttributedString.Key: Any] = [ - .link: "123", + .link: "123" ] let expectedAttributedString = NSAttributedString(string: "This is an interactive element", attributes: attributes) expect(output).to(equal(expectedAttributedString)) @@ -50,7 +50,7 @@ class RichTextParserSpec: QuickSpec { it("succesfully returns an NSAttributedString with the custom link property from a complex interactive element") { let output = self.richTextParser.extractInteractiveElement(from: NSAttributedString(string: MarkDownText.complexInteractiveElement)) let attributes: [NSAttributedString.Key: Any] = [ - .link: "123", + .link: "123" ] let expectedAttributedString = NSAttributedString(string: "element", attributes: attributes) expect(output).to(equal(expectedAttributedString))