0

My use case is storing drawing data. I have defined a Codable data object in Swift, something like this:

class DrawingData : Codable {
  var startTime : Double?
  var pointsOverTime : [String : CGPoint] = [:]
  var templateLine : [CGPoint]
}

(it turns out, objects like CGPoint have a very nice Codable implementation, as you will see below). In my Java 8 Lambda function, I have defined the object this way:

public class DrawingData {
  Double startTime;
  HashMap<String, Double[][]> pointsOverTime;
  Double[][] templateLine;
}

To encode, on the iOS side, I would love to just do this:

func sendToLambda(drawingData : DrawingData) {
  let request = AWSLambdaInvokerInvocationRequest()
  request.payload = drawingData
  ...

but in this case, I get an NSInvalidArgumentException: Invalid type in JSON write. OK, no problem, I thought, I will encode the object:

let encoder = JSONEncoder()
let data = try encoder.encode(drawingData)
let json = String(data: data, encoding: .utf8)

At first, I tried request.payload = data but this gives me the same NSInvalidArgumentException error as above. When I try request.payload = json it actually sends it to the Lambda, but then I receive a deserialization error from Lambda:

com.fasterxml.jackson.databind.JsonMappingException: 
Can not instantiate value of type [simple type, class com.(...).DrawingData] from String value ...

Now here's the part that really gets me. I copy the String value (from the above error), convert escaped quotes to quotes, and paste the exact string as a test object in the Lambda console, and it works just fine! (I also tried modifying my json string in my code using .replacingOccurrences(of: "\\\"", with: "\"") - no dice).

So it seems the problem lies either with a) the Swift JSONEncoder and/or String.init(data:) or else b) something about the Jackson configuration that is part of the AWS Lambda environment. I'm not sure how to figure out which one it is, or what the best way to fix it would be.

I'll likely submit a request for the AWS iOS SDK to accept a Codable object as a payload, but in the meantime, any suggestions how to work around this? Any alternatives I could try? Thanks in advance!

Edit: as suggested, here is a snippet of the JSON as printed to my XCode console:

{\"startTime\":1612896264.1828671,
\"templateLine\":[[115.53672736136741,335.1095896791432],
[1055.5423897415162,193.17949493333694]],\"pointsOverTime\":
{\"363899.94621276855\":[880.5,145.5],\"209530.83038330078\":
[242,347.5],\"331470.9663391113\":[843,156.5],\"175115.10848999023\":
[187,369.5],\"156777.85873413086\":[173,373],\"209379.9114227295\":
[242,347.5],\"281613.826751709\":[698,197.5],\"265787.12463378906\":
[606.5,224],\"347551.8226623535\":[868.5,148.5],\"174979.92515563965\":
[187,369.5],\"0\":[172.5,373],\"156646.9669342041\":
[173,373],\"224991.7984008789\":[331,313]...
John Nimis
  • 586
  • 3
  • 15
  • can you add the json that is being rejected? It may give a clue. – flanker Feb 09 '21 at 20:03
  • @flanker I added a snippet from the json as you suggested. As I said above, copying and pasting this data (after un-escaping quotes) into a test event in the Lambda console works just fine. – John Nimis Feb 09 '21 at 20:54

1 Answers1

0

I solved this after finding this answer, which led me to use the following code on the iOS side:

extension Encodable {
  func asDictionary() throws -> [String: Any] {
    let data = try JSONEncoder().encode(self)
    guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else {
      throw NSError()
    }
    return dictionary
  }
}

After calling this on my Codable object, I had something I could assign as the payload:

let payloadObject = drawingData.asDictionary()
request.payload = payloadObject

Then by printing that object to the XCode console, I was able to determine exactly how to structure my Lambda request object in Java.

Another method that could have helped was to implement RequestStreamHandler (instead of RequestHandler) in my Lambda function, and just deserialize manually using GSON or something similar. But I'm really glad to be able to leverage the Codable protocol and POJOs as models.

John Nimis
  • 586
  • 3
  • 15
  • 1
    Nice solution. The problem with the benefits of Codable is we forget all that is possible using the older JSONSerialisation. – flanker Feb 10 '21 at 14:28