1

Edit: I was able to pin down the issue to a MUCH more concentrated field. Although this post here isn't necessarily wrong with its assumptions, Swift 4 base64 String to Data not working due to special character is much more clear and has a Playground example.

I have a string that has to be be serialized into a Dictionary in Swift 4. The app lets users upload data (JSON serialized as Data) and download it later. For the latter, the app does the following with the downloaded data (dlData)

if let rootDict = NSKeyedUnarchiver.unarchiveObject(with: dlData) as? Dictionary<String, Any> {
    if let content = rootDict["C"] as? String {
        if let data = content.data(using: .utf8, allowLossyConversion: true){
            let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:Any]
             ...
        } else {
            print("DATA DIDNT WORK") //gets printed with his data
    }

Pretty much every time this has worked fine but recently a user has contacted me that on his iPhone there isn't any data showing up. I've added the else path and that's where it goes. It doesn't seem to be able to convert this particular string to Data

When I print() the string, copy the console output and then hardcode it into the method, it works just fine. The string is valid JSON (validated with 3 different online validators), and the JSONSerialization also works. But not in the "live" environment where it uses the downloaded data instead of the hardcoded print()-representation

Where I think the issue might be is that the Xcode console "cleans up" the string and "bad" characters that might be in it which is why copy-pasting makes it work but a direct download does not. The only weird thing I can spot in the print()ed string is the replacement character (the � symbol).

dlData > rootDict   > content > data > json 
Data   > Dictionary > string  > Data > Dictionary 

Isn't the best possible chaining for this task but I am not in the position to change the infrastructure of this system. And because it does work for at least 95% of the users, I think it should work for all of them.

I've tried doing replacingOccurrences(of: "�", with: "?") but this doesn't affect the string, probably because in the actual string this is no "�" but something else and the "�" only gets displayed because the console doesn't know how else to put it.

I've come across this blog https://natrajbontha.wordpress.com/2017/10/12/replacement-character-in-json-data/ but I would actually prefer to do the cleaning up only when the conversion has already failed once.

The original character at this spot is the American flag emoji and my users could live without having the latest emojis in there but generally, I want emojis to be displayed so replacing all of them isn't a choice.

I've just tried the same in the Playground and it results in the same.

//b64String is dlData but in base64
let decodedData = Data(base64Encoded: b64String)! //works
if let unarchivedDictionary = NSKeyedUnarchiver.unarchiveObject(with: decodedData) as? Dictionary<String, Any> { //works
    if let dF = unarchivedDictionary["C"] as? String { //works
        print(dF) //prints
        if let data = dF.data(using: .utf8, allowLossyConversion: true) { //fails
            print(data)
        } else {
            print("NO DATA") //prints
        }
    }
}
user2875404
  • 3,048
  • 3
  • 25
  • 47
  • Can you check what will happen when `allowLossyConversion: false`? – user28434'mstep Sep 26 '18 at 15:41
  • Same issue without that argument, with that argument set to true as well as false – user2875404 Sep 26 '18 at 16:03
  • When you say "When I print() the string," which string, and how are you printing it? From your description, the replacement character is likely in `dlData` itself. Do you have the raw `dlData` available? "The only weird thing I can spot in the print()ed string is the replacement character (the � symbol)." This is really the heart of the issue. Where does that symbol appear, and at what point does it enter the system? – Rob Napier Sep 26 '18 at 16:11
  • Hey, sorry that was indeed unclear - I mean the `content` variable. That's what I print. I have the dlData on my computer. It's a base64 string, about 6kb long and doing `let decodedData = Data(base64Encoded: b64String)!` I get a Data object that I can without issues cast to a Swift Dictionary because it is one. In the Swift Dictionary the key "C" contains the JSON String that I also want to make to a Dictionary. But because it's JSON I need to convert it to Data first which fails. I think if I could replace the symbol somehow then it could easily be converted. – user2875404 Sep 26 '18 at 16:28
  • I've added a Playground example and I am now trying to make a minimal example of the dlData – user2875404 Sep 26 '18 at 16:33
  • The code can only fail if the JSON string is **not** UTF8 encoded. The option `allowLossyConversion` is pointless if the string is UTF8 encoded because `data(using: .utf8)` being called on an UTF8 string is always lossless and cannot fail. – vadian Sep 26 '18 at 16:37
  • Hello, if you want you can check my new post here, the issue is cut down to 10 characters now: https://stackoverflow.com/questions/52524382/swift-4-base64-string-to-data-not-working-due-to-special-character – user2875404 Sep 26 '18 at 19:03
  • Given your new question you should probably delete this one. – rmaddy Sep 26 '18 at 19:04

0 Answers0