Search code examples
swiftstringreplaceemojiunicode-string

Is it possible to write a Swift function that replaces only part of an extended grapheme cluster like 👩โ€👩โ€👧โ€👧?


I want to write a function that could be used like this:

let ๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ = "๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง".replacingFirstOccurrence(of: "๐Ÿ‘ง", with: "๐Ÿ‘ฆ")

Given how odd both this string and Swift's String library are, is this possible in Swift?


Solution

  • Using the range(of:options:range:locale:) the solution became quite concise:

    extension String {
        func replaceFirstOccurrence(of searchString: String, with replacementString: String) -> String {
            guard let range = self.range(of: searchString, options: .literal) else { return self }
            return self.replacingCharacters(in: range, with: replacementString)
        }
    }
    

    This works by first finding the range of searchString within the instance, and if a range is found the range is replaced with replacementString. Otherwise the instance just returns itself. And, since the range(of:) method returns as soon as it finds a match, the returned range is guaranteed to be the first occurrence.

    "221".replaceFirstOccurrence(of: "2", with: "3")                // 321
    "๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ".replaceFirstOccurrence(of: "\u{1f469}", with: "\u{1f468}") // ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ
    

    *To clarify, the last test case converts woman-woman-girl-boy to man-woman-girl-boy.