From 8ac9efbacbdef5b03fb05cabc0b1914c068b5c22 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 13 Jul 2023 18:09:04 -0300 Subject: [PATCH 1/6] re-did regexes with swift regex --- .../ProcessedLocalizedConfiguration.swift | 1 + RevenueCatUI/Helpers/Variables.swift | 65 ++++++++++++------- 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/RevenueCatUI/Helpers/ProcessedLocalizedConfiguration.swift b/RevenueCatUI/Helpers/ProcessedLocalizedConfiguration.swift index 80522441ed..f3e781a657 100644 --- a/RevenueCatUI/Helpers/ProcessedLocalizedConfiguration.swift +++ b/RevenueCatUI/Helpers/ProcessedLocalizedConfiguration.swift @@ -1,6 +1,7 @@ import RevenueCat /// A `PaywallData.LocalizedConfiguration` with processed variables +@available(iOS 16.0, macOS 13.0, tvOS 16.0, *) struct ProcessedLocalizedConfiguration: PaywallLocalizedConfiguration { var title: String diff --git a/RevenueCatUI/Helpers/Variables.swift b/RevenueCatUI/Helpers/Variables.swift index c899a8f6c2..67ddb27363 100644 --- a/RevenueCatUI/Helpers/Variables.swift +++ b/RevenueCatUI/Helpers/Variables.swift @@ -1,5 +1,6 @@ import Foundation import RevenueCat +import RegexBuilder @available(iOS 16.0, macOS 13.0, tvOS 16.0, *) extension PaywallData.LocalizedConfiguration { @@ -20,45 +21,64 @@ protocol VariableDataProvider { } +struct VariableMatch { + let variable: String + let range: Range +} + + /// Processes strings, replacing `{{variable}}` with their associated content. +@available(iOS 16.0, macOS 13.0, tvOS 16.0, *) enum VariableHandler { + private static func extractVariables(from expression: String) -> [VariableMatch] { + let variablePattern = Regex { + Capture { + OneOrMore { + "{{ " + OneOrMore(.word) + " }}" + } + } + } + + return expression.matches(of: variablePattern).map { match in + let (_, variable) = match.output + return VariableMatch(variable: String(variable), range: match.range) + } + } + static func processVariables( in string: String, with provider: VariableDataProvider ) -> String { + let matches = extractVariables(from: string) var replacedString = string - let range = NSRange(string.startIndex..., in: string) - let matches = Self.regex.matches(in: string, options: [], range: range) - - for match in matches.reversed() { - let variableNameRange = match.range(at: 1) - if let variableNameRange = Range(variableNameRange, in: string) { - let variableName = String(string[variableNameRange]) - let replacementValue = provider.value(for: variableName) - - let adjustedRange = NSRange( - location: variableNameRange.lowerBound.utf16Offset(in: string) - Self.pattern.count / 2, - length: string.distance(from: variableNameRange.lowerBound, - to: variableNameRange.upperBound) + Self.pattern.count - ) - let replacementRange = Range(adjustedRange, in: replacedString)! - replacedString = replacedString.replacingCharacters(in: replacementRange, with: replacementValue) - } + for variableMatch in matches { + let replacementValue = provider.value(for: variableMatch.variable) + + let adjustedRange = NSRange( + location: variableMatch.range.lowerBound.utf16Offset(in: string) - Self.startPattern.count, + length: string.distance(from: variableMatch.range.lowerBound, + to: variableMatch.range.upperBound) + + Self.startPattern.count + + Self.endPattern.count + ) + let replacementRange = Range(adjustedRange, in: replacedString)! + + replacedString = replacedString.replacingCharacters(in: replacementRange, with: replacementValue) } return replacedString } - private static let pattern = "{{ }}" - // Fix-me: this can be implemented using the new Regex from Swift. - // This regex is known at compile time and tested: - // swiftlint:disable:next force_try - private static let regex = try! NSRegularExpression(pattern: "\\{\\{ (\\w+) \\}\\}", options: []) + private static let startPattern = "{{ " + private static let endPattern = " }}" } +@available(iOS 16.0, macOS 13.0, tvOS 16.0, *) extension String { func processed(with provider: VariableDataProvider) -> Self { @@ -67,6 +87,7 @@ extension String { } +@available(iOS 16.0, macOS 13.0, tvOS 16.0, *) private extension VariableDataProvider { func value(for variableName: String) -> String { From 1a15401dfb7437c345b051664e2a0d758b540e92 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 13 Jul 2023 18:36:56 -0300 Subject: [PATCH 2/6] fixes to regex --- RevenueCatUI/Helpers/Variables.swift | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/RevenueCatUI/Helpers/Variables.swift b/RevenueCatUI/Helpers/Variables.swift index 67ddb27363..2e2a58f67e 100644 --- a/RevenueCatUI/Helpers/Variables.swift +++ b/RevenueCatUI/Helpers/Variables.swift @@ -33,12 +33,12 @@ enum VariableHandler { private static func extractVariables(from expression: String) -> [VariableMatch] { let variablePattern = Regex { - Capture { - OneOrMore { - "{{ " + OneOrMore { + "{{ " + Capture { OneOrMore(.word) - " }}" } + " }}" } } @@ -57,25 +57,12 @@ enum VariableHandler { for variableMatch in matches { let replacementValue = provider.value(for: variableMatch.variable) - - let adjustedRange = NSRange( - location: variableMatch.range.lowerBound.utf16Offset(in: string) - Self.startPattern.count, - length: string.distance(from: variableMatch.range.lowerBound, - to: variableMatch.range.upperBound) - + Self.startPattern.count - + Self.endPattern.count - ) - let replacementRange = Range(adjustedRange, in: replacedString)! - - replacedString = replacedString.replacingCharacters(in: replacementRange, with: replacementValue) + replacedString = string.replacingCharacters(in: variableMatch.range, with: replacementValue) } return replacedString } - - private static let startPattern = "{{ " - private static let endPattern = " }}" - + } @available(iOS 16.0, macOS 13.0, tvOS 16.0, *) From 50a9665c151fae68bee8231b4b315509850c17a6 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 13 Jul 2023 18:42:27 -0300 Subject: [PATCH 3/6] small fixes --- RevenueCatUI/Helpers/Variables.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RevenueCatUI/Helpers/Variables.swift b/RevenueCatUI/Helpers/Variables.swift index 2e2a58f67e..ecca68c625 100644 --- a/RevenueCatUI/Helpers/Variables.swift +++ b/RevenueCatUI/Helpers/Variables.swift @@ -55,9 +55,9 @@ enum VariableHandler { let matches = extractVariables(from: string) var replacedString = string - for variableMatch in matches { + for variableMatch in matches.reversed() { let replacementValue = provider.value(for: variableMatch.variable) - replacedString = string.replacingCharacters(in: variableMatch.range, with: replacementValue) + replacedString = replacedString.replacingCharacters(in: variableMatch.range, with: replacementValue) } return replacedString From 3391a9290f487104eff8402fdaefd9ebd9e83f40 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 13 Jul 2023 18:47:01 -0300 Subject: [PATCH 4/6] reorganized the code --- RevenueCatUI/Helpers/Variables.swift | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/RevenueCatUI/Helpers/Variables.swift b/RevenueCatUI/Helpers/Variables.swift index ecca68c625..cc6036a59f 100644 --- a/RevenueCatUI/Helpers/Variables.swift +++ b/RevenueCatUI/Helpers/Variables.swift @@ -31,6 +31,21 @@ struct VariableMatch { @available(iOS 16.0, macOS 13.0, tvOS 16.0, *) enum VariableHandler { + static func processVariables( + in string: String, + with provider: VariableDataProvider + ) -> String { + let matches = extractVariables(from: string) + var replacedString = string + + for variableMatch in matches.reversed() { + let replacementValue = provider.value(for: variableMatch.variable) + replacedString = replacedString.replacingCharacters(in: variableMatch.range, with: replacementValue) + } + + return replacedString + } + private static func extractVariables(from expression: String) -> [VariableMatch] { let variablePattern = Regex { OneOrMore { @@ -48,21 +63,6 @@ enum VariableHandler { } } - static func processVariables( - in string: String, - with provider: VariableDataProvider - ) -> String { - let matches = extractVariables(from: string) - var replacedString = string - - for variableMatch in matches.reversed() { - let replacementValue = provider.value(for: variableMatch.variable) - replacedString = replacedString.replacingCharacters(in: variableMatch.range, with: replacementValue) - } - - return replacedString - } - } @available(iOS 16.0, macOS 13.0, tvOS 16.0, *) From e0e1b2ff28b0545ac5573352d435f3360b1760f0 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 13 Jul 2023 18:49:36 -0300 Subject: [PATCH 5/6] swiftlint --- RevenueCatUI/Helpers/Variables.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/RevenueCatUI/Helpers/Variables.swift b/RevenueCatUI/Helpers/Variables.swift index cc6036a59f..bf0d6560f9 100644 --- a/RevenueCatUI/Helpers/Variables.swift +++ b/RevenueCatUI/Helpers/Variables.swift @@ -1,6 +1,6 @@ import Foundation -import RevenueCat import RegexBuilder +import RevenueCat @available(iOS 16.0, macOS 13.0, tvOS 16.0, *) extension PaywallData.LocalizedConfiguration { @@ -26,7 +26,6 @@ struct VariableMatch { let range: Range } - /// Processes strings, replacing `{{variable}}` with their associated content. @available(iOS 16.0, macOS 13.0, tvOS 16.0, *) enum VariableHandler { From 0b1b5cee3c077730d20f1dfd9b63c88e36959764 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Fri, 14 Jul 2023 12:01:33 -0300 Subject: [PATCH 6/6] improvements from PR comments --- RevenueCatUI/Helpers/Variables.swift | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/RevenueCatUI/Helpers/Variables.swift b/RevenueCatUI/Helpers/Variables.swift index bf0d6560f9..edb0898138 100644 --- a/RevenueCatUI/Helpers/Variables.swift +++ b/RevenueCatUI/Helpers/Variables.swift @@ -21,11 +21,6 @@ protocol VariableDataProvider { } -struct VariableMatch { - let variable: String - let range: Range -} - /// Processes strings, replacing `{{variable}}` with their associated content. @available(iOS 16.0, macOS 13.0, tvOS 16.0, *) enum VariableHandler { @@ -34,7 +29,7 @@ enum VariableHandler { in string: String, with provider: VariableDataProvider ) -> String { - let matches = extractVariables(from: string) + let matches = Self.extractVariables(from: string) var replacedString = string for variableMatch in matches.reversed() { @@ -62,6 +57,13 @@ enum VariableHandler { } } + private struct VariableMatch { + + let variable: String + let range: Range + + } + } @available(iOS 16.0, macOS 13.0, tvOS 16.0, *)