3

I'm building an app that needs to make a GET request to the API endpoint https://thecountedapi.com/api/counted using the Siesta framework. The endpoint returns a JSON array, just like an endpoint like https://api.github.com/users/ebelinski/repos, which is used in the Siesta example Github Browser. As a result, I'm trying to make my app use Siesta in the say way that one does. I create a service:

let API = Service(baseURL: "https://thecountedapi.com/api")

Then a transformer for my endpoint in application:didFinishLaunchingWithOptions:

API.configureTransformer("/counted") {
  ($0.content as JSON).arrayValue.map(Incident.init)
}

Where Incident is a struct with an initializer that takes in a JSON object.

Then in my view controller, I create a resource:

let resource = API.resource("/counted")

and in viewDidLoad:

resource.addObserver(self)

and in viewWillAppear:

resource.loadIfNeeded()

Then I have the following function in my VC to listen to changes:

func resourceChanged(resource: Resource, event: ResourceEvent) {
  print(resource.jsonArray)

  if let error = resource.latestError {
    print(error.userMessage)
    return
  }

  if let content: [Incident] = resource.typedContent() {
    print("content exists")
    incidents = content
  }

  print(incidents.count)
}

But when I run my app, I get mixed results. print(resource.jsonArray) just prints [], I have an error message Cannot parse server response, and if I set Siesta.enabledLogCategories = LogCategory.detailed, I can see the error mesage [Siesta:StateChanges] Siesta.Resource(https://thecountedapi.com/api/counted)[] received error: Error(userMessage: "Cannot parse server response", httpStatusCode: nil, entity: nil, cause: Optional(Siesta.Error.Cause.WrongTypeInTranformerPipeline(expectedType: "JSON", actualType: "__NSCFArray", transformer: Siesta.ResponseContentTransformer<SwiftyJSON.JSON, Swift.Array<TheCountedViewer.Incident….

If I comment out the whole transformer, I have some success in that print(resource.jsonArray) prints out the correct array from the endpoint. So my transformer must be wrong in some way, but I think I'm using basically the same transformer as in Github Browser:

service.configureTransformer("/users/*/repos") {
    ($0.content as JSON).arrayValue.map(Repository.init)
}

Am I missing something?

Eugene
  • 3,417
  • 5
  • 25
  • 49

1 Answers1

0

The key clue to your problem is buried in that (perhaps not ideally helfpul) log message:

Siesta.Error.Cause.WrongTypeInTranformerPipeline
  expectedType: "JSON"
  actualType: "__NSCFArray"

It’s saying that your transformer expected an input type of JSON, which makes sense — you said as much with ($0.content as JSON). However, it got the type __NSCFArray, which is the secret internal backing type for NSArray. In other words, it expected a SwiftyJSON value, but it got the raw output of NSJSONSerialization instead.

Why? The GithubBrowser project includes an NSDict/NSArray → SwiftyJSON transformer which it configures in the parsing stage. The model transformers in that project all depend on it.

To use SwiftyJSON in the same way in your project, you’ll need to include that transformer from the example project in yours:

private let SwiftyJSONTransformer =
  ResponseContentTransformer
    { JSON($0.content as AnyObject) }

And then when setting up your service:

service.configure {
  $0.config.pipeline[.parsing].add(SwiftyJSONTransformer, contentTypes: ["*/json"])
}

(Note that you might want to create the ResponseContentTransformer with transformErrors: true if you are interested in errors.)


An alternative way to use SwiftyJSON, which is not quite as pretty but requires less setup, is to manually wrap things in JSON in each individual response transformer:

service.configureTransformer("/users/*/repos") {
  JSON($0.content as AnyObject).arrayValue.map(Incident.init)
}
Paul Cantrell
  • 9,175
  • 2
  • 40
  • 48