Scripting in Swift: Key characters, or what character can’t be key

In SearchKey, the user edits a String which is then embedded into text to make a text property list, which in turn forms the data of an extended attribute.

Embedding the string is simple:
var theStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"\">\n<plist version=\"1.0\">\n<string>" + theDStr + "</string>\n</plist>"

The snag is that the contents of the String theDStr must be valid XML, and not cause a parse error in that property list. If theDStr happens to contain a character like &, <, or >, then the character gets mangled, and/or a parse error results making the property list invalid.

What we need is a quick (and dirty) way to replace all occurrences of those three characters with their XML escaped forms. One simple possibility is
func convertToXMLEsc(string: String) -> String {
var newString1 = string
let char_dictionary0 = [
"&" : "&amp;",
"<" : "&lt;",
">" : "&gt;"
for (unescaped_char, escaped_char) in char_dictionary0 {
newString1 = string.replacingOccurrences(of: unescaped_char, with: escaped_char, options: NSString.CompareOptions.literal, range: nil)
} return newString1 }

There are lots of better ways to do this, I’m sure, but this is transparent, simple, easy to write in reverse if you need to go the other way, and it’s readily expandable to cover any problem characters you may encounter.

The snag with this is that it immediately generates a runtime error whenever it encounters a <. Although not obvious (to me, at least), the reason for that is that the code is trying to use the < symbol as a dictionary key, which is not permissible. I’m sure that this is made clear somewhere in the Swift Language Reference, although I haven’t been able to spot it yet.

My workaround for this is to perform substitution in two steps. First to use convertToXMLEsc() to get rid of the other characters:
let theCStr = convertToXMLEsc(string: theContent)
then to convert < into &lt; using
let theDStr = theCStr.replacingOccurrences(of: "<", with: "&lt;")
which is permissible as it doesn’t use < as a dictionary key.

From there, generating a binary property list is relatively straightforward:
let theSData = theStr.propertyList()
if PropertyListSerialization.propertyList(theData, isValidFor: PropertyListSerialization.PropertyListFormat.binary) {
do {
theData = try theSData, format: PropertyListSerialization.PropertyListFormat.binary, options: 0)
} catch {
} }
return theData

And yes, trying to write this in HTML was even more of a pig than working this out.