Search code examples
iosswiftbase64jsondecoder

base64encoded string returned from API call is showing nil after Data(base64encoded: data) is ran


I am very confused on this one. I had been thinking it was something wrong with my server, but after testing it seems as though using Data(base64encoded: data) is producing nil even though the data is there and is in fact base64encoded. I have tried everything I can think of and nothing is working. Here is the code I am using and the output...

do {
  print("DATA as a string: ", String(data: data!, encoding: .utf8) ?? "NONE AVAILABLE")
  let decodedData = Data(base64encoded: data!, options: .ignoreUnknownCharacters)
  print("DECODED DATA: ")
  print(decodedData as Any)
  let apiResponse = try JSONDecoder().decode(ApiConnectionResponse.self, from: decodedData!)
  completion(.success(apiResponse))
} catch {
  completion(.failure(error))
}

The output from that is...

Data as string: eyJzdWNjZXNzIjoiZmFsc2UiLCJlcnJvciI6Im1vYmlsZV9waW5fY29ubmVjdDogaW52YWxpZCBvciBleHBpcmVkIn0.

DECODED DATA: nil

So the String is a legit base64encoded string, it decodes to

{"success":"false","error":"mobile_pin_connect: invalid or expired"}

However the decoded data is nil. I don't understand, how can it be nil when the string is a base64encoded string and is not nil. I have even tried forcing the string in there like so...

let decodedData = Data(base64encoded: String(data: data!, encoding: .utf8))

Still no luck. It is throwing a fatal error, and I can see in the section at the bottom of Xcode that the data is in fact "(Data?) 92 bytes" What I can't figure out is why it is nil after running through Data()...

Any help would be greatly appreciated, I am really lost and can't figure out why I can make the string, but not the data.

The end result of this is, I need to get JSONDecoder().decode to work with the reply from the server, I think I can get that part done, if I can figure out why it is nil after the data call. Thank you.


Solution

  • You need to check your data length, divide by 3 and in case the result is not zero add one or two bytes (65) = equal sign for padding your data. Try like this:

    let decodedData = Data(base64Encoded: data + .init(repeating: 65, count: data.count % 3))
    

    extension Data {
        var base64encodedDataPadded: Data {
            let data = last == 46 ? dropLast() : self
            return data + .init(repeating: 65, count: data.count % 3)
        }
    }
    

    Playground testing:

    let data = Data("eyJzdWNjZXNzIjoiZmFsc2UiLCJlcnJvciI6Im1vYmlsZV9waW5fY29ubmVjdDogaW52YWxpZCBvciBleHBpcmVkIn0.".utf8)
    let decodedData = Data(base64Encoded: data.base64encodedDataPadded)!
    print(String(data: decodedData, encoding: .utf8)!) // "{"success":"false","error":"mobile_pin_connect: invalid or expired"}"