Trying to concatenate a string with what I think is an unicode suffix - swift

I have this app of mine that reads datamatrix barcodes from drugs using the camera.
When it does for a particular drug, I receive this string from the detector, as seen on Xcode console:
0100000000D272671721123110700XXXX\U0000001d91D1
my problem is that \U0000001d91D1 part.
This code can be decomposed on the following:
01 00000000D27267 17 211231 10 700XXXX \U0000001d 91D1"
01 = drug code
17 = expiring date DMY
10 = batch number
The last part is the dosage rate
Now on another part of the application I am on the simulator, with no camera, so I need to pass this string to the module that decomposes the code.
I have tried to store the code as a string using
let code = "0100000000D272671721123110700XXXX\U0000001d91D1"
it complains about the inverted bar, so I change it to double bar
let code = "0100000000D272671721123110700XXXX\\U0000001d91D1"
the detector analyzes this string and concludes that the batch number is 700XXXX\U0000001d91D1, instead of just 700XXXX, so the information contained from the \ forward is lost.
I think this is unicode or something.
How do I create this string correctly.

You can use string transform to decode your hex unicode characters:
let str1 = #"0100000000D272671721123110700XXXX\U00000DF491D1"#
let str2 = #"0100000000D272671721123110700XXXX\U0000001d91D1"#
let decoded1 = str1.applyingTransform(.init("Hex-Any"), reverse: false)! // "0100000000D272671721123110700XXXX෴91D1"
let decoded2 = str2.applyingTransform(.init("Hex-Any"), reverse: false)! // "0100000000D272671721123110700XXXX91D1"
You can also get rid of the verbosity extending StringTransform and StringProtocol:
extension StringTransform {
static let hexToAny: Self = .init("Hex-Any")
static let anyToHex: Self = .init("Any-Hex")
}
extension StringProtocol {
var decodingHex: String {
applyingTransform(.hexToAny, reverse: false)!
}
var encodingHex: String {
applyingTransform(.anyToHex, reverse: false)!
}
}
Usage:
let str1 = #"0100000000D272671721123110700XXXX\U00000DF491D1"#
let str2 = #"0100000000D272671721123110700XXXX\U0000001d91D1"#
let decoded1 = str1.decodingHex // "0100000000D272671721123110700XXXX෴91D1"
let decoded2 = str2.decodingHex // "0100000000D272671721123110700XXXX91D1"

The \U0000001d substring probably represents code point U+001D INFORMATION SEPARATOR THREE, which is also the ASCII code point GS (group separator).
In a Swift string literal, we can write that code point using a Unicode escape sequence: \u{1d}. Try writing your string literal like this:
let code = "0100000000D272671721123110700XXXX\u{1d}91D1"

Related

How can I convert unicode values in a string "U+XXXXXX" to the corresponding characters in Swift?

I accept from backend the following json
{
"id": "f33919f6-3554-4246-9e78-bca3a690c119",
"title": "Category3",
"slug": "category3",
"hex_up": "#eb4034",
"hex_down": "#80302a",
"emoji": "U+1F602",
"parent_id": "aa3f651b-f068-4ae1-a9d8-a18a9945b111"
}
There is a field "emoji": "U+1F602",
I need show emoji icon like 😃 in UILabel
I tried to google and found results like
let scalarValue = UnicodeScalar(emojiString)
let myString = String(scalarValue!)
Unfortunately app crashes at the second line.
Thanks for your answers.
There's no U+... syntax in Swift. (There is a \u{...} syntax that does the same thing, but it's not necessary here.)
You'll need to parse the String yourself:
func parseUnicode(_ string: String) -> String? {
guard string.hasPrefix("U+"), // Make sure it's a U+ string
let value = Int(string.dropFirst(2), radix: 16), // Convert to Int
let scalar = UnicodeScalar(value) // Convert to UnicodeScalar
else { return nil }
return String(scalar) // Convert to String
}
if let myString = parseUnicode(emoji) { ... }
Don't use ! here. The U+... string may be invalid, and you wouldn't want to crash in that case.
You can simply apply a string transform from "Hex/Unicode" to "Any" (a set of all characters):
"U+1F602".applyingTransform(.init("Hex/Unicode-Any"), reverse: false) // "😂"
or as instance properties of StringProtocol to encode/decode from/to hexa unicode:
extension StringTransform {
static let unicodeToAny: Self = .init("Hex/Unicode-Any")
static let anyToUnicode: Self = .init("Any-Hex/Unicode")
}
extension StringProtocol {
var decodingHexaUnicode: String {
applyingTransform(.unicodeToAny, reverse: false)!
}
var encodingHexaUnicode: String {
applyingTransform(.anyToUnicode, reverse: false)!
}
}
Usage:
let hexaUnicode = "U+1F602"
let emoji = hexaUnicode.decodingHexaUnicode // "😂"
let unicodeFromEmoji = emoji.encodingHexaUnicode // "U+1F602"
The reason your app crashed is due to the fact that the scalarValue you attempted to initialize is nil, and you're force-unwrapping using (!) that nil value on line 2. Rob's answer shows how to unwrap the optional safely.
You can get the emoji by using the value following the U+. So you'll need to drop the first two characters of the string. So use this code to accomplish that:
let parsedEmoji = emojiString.substring(from:2)
Now you'll convert that emoji unicode using the code below.
let emoji = String(UnicodeScalar(Int(parsedEmojiHex,radix: 16)!)!)
print(emoji)

Using String addingPercentEncoding to encode a string as query param

I have an input string "+20" and I am trying to pass that as query parameter in url.
So I am trying to encode the myInputString by doing
let s1 = myInputString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
But in the debugger, the string s1 still shows as '+20' instead of '%2B20'
Is there something I did wrong?
As already mentioned by matt + is a legal URL character. If you really need to encode it you would need to create your own custom urlQueryAllowed and subtract the plus sign from it:
extension CharacterSet {
static let allowedCharacters = urlQueryAllowed.subtracting(.init(charactersIn: "+"))
}
let myInputString = "+20"
let s1 = myInputString.addingPercentEncoding(withAllowedCharacters: .allowedCharacters) // "%2B20"

Format String left of multiple characters in Swift 5?

I have some Strings that vary in length but always end in "listing(number)"
myString = 9AMnep8MAziUCK7VwKF51mXZ2listing28
.
I want to get the String without "listing(number)":
9AMnep8MAziUCK7VwKF51mXZ2
.
Methods I've tried such as .index(of: ) only let you format based off one character. Any simple solutions?
A possible solution is to search for the substring with Regular Expression and remove the result (replace it with empty string)
let myString = "9AMnep8MAziUCK7VwKF51mXZ2listing28"
let trimmedString = myString.replacingOccurrences(of: "listing\\d+$", with: "", options: .regularExpression)
\\d+ searches for one ore more digits
$ represents the end of the string
Alternatively without creating a new string
var myString = "9AMnep8MAziUCK7VwKF51mXZ2listing28"
if let range = myString.range(of: "listing\\d+$", options: .regularExpression) {
myString.removeSubrange(range)
}
Another option is to split the string in parts with "listing" as separator
let result = myString.components(separatedBy: "listing").first
So to solve your issue find the code below with few comments written to try and explain each steps have taken. kindly note i have modified or arrived at this solution using this links as a guide.
https://stackoverflow.com/a/40070835/6596443
https://www.dotnetperls.com/substring-swift
extension String {
//
// Paramter inputString: This is the string you want to manipulate
// Paramter- startStringOfUnwanted: This is the string you want to start the removal or replacement from
//return : The expected output you want but can be emptystring if unable to
static func trimUnWantedEndingString(inputString: String,startStringOfUnwanted: String) -> String{
//Output string
var outputString: String?
//Getting the range based on the string content
if let range = myString.range(of: startStringOfUnwanted) {
//Get the lowerbound of the range
let lower = range.lowerBound
//Get the upperbound of the range
let upper = range.upperBound
//Get the integer position of the start index of the unwanted string i added plus one to ensure it starts from the right position
let startPos = Int(myString.distance(from: myString.startIndex, to: lower))+1
//Get the integer position of the end index of the unwanted string i added plus one to ensure it starts from the right position
let endPos = Int(myString.distance(from: myString.startIndex, to: upper))+1
//Substract the start int from the end int to get the integer value that will be used to get the last string i want to stop trimming at
let endOffsetBy = endPos-startPos
//get thes string char ranges of values
let result = myString.index(myString.startIndex, offsetBy: 0)..<myString.index(myString.endIndex, offsetBy: -endOffsetBy)
//converts the results to string or get the string representation of the result and then assign it to the OutputString
outputString = String(myString[result]);
}
return outputString ?? "";
}
}
let myString = "9AMnep8MAziUCK7VwKF51mXZ2listing28"
String.trimUnWantedEndingString(inputString: myString, startStringOfUnwanted:"listing")

character extract from string in swift

I have a task to do something with a chess board. The input gives us a starting position of some chess figure. For example "b4" or "a6" or something like that. How can i decompose the input and make from it two integer numbers, like in C++:
string input;
cin>>input
int coord_x = input[0] - 'a';
int coord_y = input[1]
I cannot manage to do that in swift. I do something like:
let input : String=readLine()!
let characters = Array(input)
and then try to take the int but it doesnt work, no matter what i try...
and what type is the content of the Array in swift?
You can retrieve the c string representation like this:
let string = "a5"
let scalars = string.lowercased().cString(using: .ascii)!
let first = scalars[0]
let second = scalars[1]
It could be safer to retrieve the unicodeScalar characters instead:
let string = "a5".lowercased()
let characters = Array(string.unicodeScalars)
let first = characters[0].value - UnicodeScalar(unicodeScalarLiteral: "a").value

Inconsistent ranges of strings with greek letter

I'll show the code and the output, since it's easier to explain the issue.
Code and output in the commented lines:
let greekLetter = "β"
let string1 = greekLetter
/// string2 is the same as string1 but converted to NSString then back to String
let string2 = String(NSString(string: greekLetter))
print(string1.range(of: greekLetter)!)
/// prints: Index(_rawBits: 0)..<Index(_rawBits: 131072)
print(string2.range(of: greekLetter)!)
/// prints: Index(_rawBits: 0)..<Index(_rawBits: 65536)
The problem: A String that contains a greek letter returns a range that is different from the same String with the same greek letter that was converted to NSString and then back to String again.
Any ideas why?
Why this question is raised:
I'm doing some parsing and I need to find the range of specific string and then insert something else instead of it. Because of wrong ranges returned inserting strings in a wrong position due to wrong lower/upper bound location.
UPDATE 2:
Let's say I have a task: in a given string "β-1" change "1" to "2". And this string comes from the server.
Please look at this code sample:
let wordWithGreekLetter = "β-1"
var string1 = wordWithGreekLetter
let data = """
{ "name" : "\(wordWithGreekLetter)" }
""".data(using: String.Encoding.utf8)
struct User: Decodable {
let name: String
}
let user = try! JSONDecoder().decode(User.self, from: data!)
/// string2 is the same as string1 but decoded from the data
var string2 = user.name
let rangeOfNumberOne1 = string1.range(of: "1")!
string1.removeSubrange(rangeOfNumberOne1)
string1.insert("2", at: rangeOfNumberOne1.lowerBound)
/// RESULT: string1 = "β-2"
let rangeOfNumberOne2 = string2.range(of: "1")!
string2.removeSubrange(rangeOfNumberOne2)
string2.insert("2", at: rangeOfNumberOne2.lowerBound)
/// RESULT: string2 = "β2-"
As Rob explained in Why is startIndex not equal to endIndex shifted to startIndex position in String in Swift?, the raw bits of the index are an implementation detail, and you should not care about that value.
The actual problem is that (quote from Collection):
Saved indices may become invalid as a result of mutating operations.
so that rangeOfNumberOne1/2 may be no longer valid after you call removeSubrange() on the string.
In this particular case this may happen for string2 (which is bridged from an NSString) because removing a character may reorganize the internal storage. But this is pure speculation: what matters only is that the current code exhibits undefined behavior.
If you replace
let rangeOfNumberOne1 = string1.range(of: "1")!
string1.removeSubrange(rangeOfNumberOne1)
string1.insert("2", at: rangeOfNumberOne1.lowerBound)
by
let rangeOfNumberOne1 = string1.range(of: "1")!
string1.replaceSubrange(rangeOfNumberOne1, with: "2")
(and similarly for string2) then you'll get the same result "β-2" for both strings.