Skip to content

Instantly share code, notes, and snippets.

@jayesh15111988
Created February 9, 2023 17:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jayesh15111988/604e3e9b3630fdcb8315f8d955b4c879 to your computer and use it in GitHub Desktop.
Save jayesh15111988/604e3e9b3630fdcb8315f8d955b4c879 to your computer and use it in GitHub Desktop.
import Foundation
import SwiftUI
struct CustomStringFormat {
let formatString: String
let fragments: [FragmentType]
init(formatString: String) {
self.formatString = formatString
self.fragments = Self.fragmentizeFormattedString(formatString)
}
static func fragmentizeFormattedString(_ formatString: String) -> [FragmentType] {
return getFragments(regex: "%\\d+\\$@", inputText: formatString)
}
static func getFragments(regex: String, inputText: String) -> [FragmentType] {
var fragments: [FragmentType] = []
guard let regex = try? NSRegularExpression(pattern: regex) else {
return []
}
let results = regex.matches(in: inputText,
range: NSRange(inputText.startIndex..., in: inputText))
var previousIndex = inputText.startIndex
var currentIndex = 0
for match in results {
for range in 0..<match.numberOfRanges {
let rangeBounds = match.range(at: range)
guard let range = Range(rangeBounds, in: inputText) else {
continue
}
if range.lowerBound != inputText.startIndex {
let closedRange = previousIndex..<range.lowerBound
fragments.append(.literal(String(inputText[closedRange])))
}
fragments.append(.argument(currentIndex))
previousIndex = range.upperBound
currentIndex += 1
}
}
if previousIndex < inputText.endIndex {
fragments.append(.literal(String(inputText[previousIndex..<inputText.endIndex])))
}
return fragments
}
}
enum FragmentType {
case literal(String)
case argument(Int)
}
class Example {
init() {
let (formattedString, arguments) = Self.getFormattedStringAndArguments()
let customStringFormat = CustomStringFormat(formatString: formattedString)
let output = customStringFormat.fragments.map { fragment in
switch fragment {
case .literal(let staticText):
return Text(staticText).foregroundColor(Color.red).font(.largeTitle)
case .argument(let index):
return Text(arguments[index]).foregroundColor(Color.yellow).font(.title)
}
}.reduce(Text(verbatim: ""), +)
print(output)
}
static func getFormattedStringAndArguments() -> (String, [String]) {
return ("Going from %1$@ to %2$@. Have a happy journey passing via %3$@. Bon Voyag", ["Boston", "New York", "Providence"])
}
}
@jayesh15111988
Copy link
Author

jayesh15111988 commented Feb 9, 2023

Example,

struct Example: View {

    var body: some View {
        VStack {
            getFormattedTextView(formattedString: "Going from %1$@ to %2$@. Have a happy journey passing via %3$@. Bon Voyag", arguments: ["Boston", "New York", "Providence"])

            getFormattedTextView(formattedString: "%1$@ to %2$@. Have a happy journey passing via %3$@]", arguments: ["Boston", "New York", "Providence"])

            getFormattedTextView(formattedString: "hello %1$@ to %2$@. Have a happy journey passing via %3$@. Bon Voyag", arguments: ["Boston", "New York", "Providence"])
        }
    }

    @ViewBuilder
    func getFormattedTextView(formattedString: String, arguments: [String]) -> some View {

        let customStringFormat = CustomStringFormat(formatString: formattedString)

        let output = customStringFormat.fragments.map { fragment in
            switch fragment {
            case .literal(let staticText):
                return Text(staticText).foregroundColor(Color.red).font(.largeTitle)
            case .argument(let index):
                return Text(arguments[index]).foregroundColor(Color.yellow).font(.title)
            }
        }.reduce(Text(verbatim: ""), +)

        output
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment