0

I have a JSON file I have downloaded that I am trying to .flatMap into an array of structs I have created, but the .flatMap is not initializing the data into the struct.

A parsed version of the JSON file is below, which would represent one struct in the array:

[["eventUri": <null>, 
"wgt": 266482320, 
"source": {
    dataType = news;
    title = WAFB;
    uri = "wafb.com";
}, 
"uri": 885629090, 
"body": A bunch of text, 
"lang": eng, 
"dateTime": 2018-06-12T06:52:00Z, 
"date": 2018-06-12, 
"sim": 0, 
"isDuplicate": 1, 
"title": Now a candidate, Romney appears to embrace Trump presidency,
"time": 06:52:00, 
"url": http://www.wafb.com/story/38391905/now-a-candidate-romney-appears-to-embrace-trump-presidency, 
"dataType": news]]

This is the struct, with the shared instance of structs, as well as the flatMap method. All initializer subscripts are named according to the keys of the JSON:

struct Article {

    static var sharedInstance: [Article] = [Article]()

    // MARK: Properties

    var dataType = ""
    var date = ""
    var eventUri = ""
    var isDuplicate = false
    var sourceName = ""
    var lang = ""
    var sim = 0.0
    var time = ""
    var articleTitle = ""
    var image: UIImage? = nil

    // MARK: Initializer
    init?(dictionary: [String:AnyObject]) {

        // GUARD: Do all dictionaries have values?
        guard
            let dataType = dictionary[EventRegistry.ParameterValues.dataType] as? String,
            let date = dictionary[EventRegistry.ParameterValues.date] as? String,
            let eventUri = dictionary[EventRegistry.ParameterValues.eventUri] as? String,
            let isDuplicate = dictionary[EventRegistry.ParameterValues.isDuplicate] as? Bool,
            let source = dictionary[EventRegistry.ParameterKeys.source] as? [String:Any],
            let sourceName = source[EventRegistry.ParameterValues.sourceName] as? String,
            let lang = dictionary[EventRegistry.ParameterValues.lang] as? String,
            let sim = dictionary[EventRegistry.ParameterValues.sim] as? Double,
            let time = dictionary[EventRegistry.ParameterValues.time] as? String,
            let articleTitle = dictionary[EventRegistry.ParameterValues.articleTitle] as? String

            // If not, return nil
            else { return nil }

        // Otherwise, initialize values
        self.dataType = dataType
        self.date = date
        self.eventUri = eventUri
        self.isDuplicate = isDuplicate
        self.sourceName = sourceName
        self.lang = lang
        self.sim = sim
        self.time = time
        self.articleTitle = articleTitle
    }

    static func articlesFromResults(_ results: [[String:AnyObject]]) -> [Article] {

        return results.flatMap(Article.init)
    }

}

And this is my call to initiate the flatMap method, which returns no result:

func getEventRegistrySearchResults(completionHandlerForSearchResults: @escaping (_ articles: [Article]?, _ errorString: String?) -> Void) {

taskForGetEventRegistrySearchResults(method: "", parameters: ["" : (Any).self]) { (result, error) in

    if error != nil {
        print("There was an error getting articles")
    }

    if let result = result {

        if let articles = result["articles"] as? [String:AnyObject] {

            if let articleResults = articles["results"] as? [[String:AnyObject]] {
                Article.sharedInstance = Article.articlesFromResults(articleResults)
                print(Article.sharedInstance.count) // Returns 0
                }
            } else {
                completionHandlerForSearchResults(nil, "There was a problem")
            }
        }
    }
}

Printing "articleResults" here gives me the JSON file above, but when I try to .flatMap it into my array of structs, it does not initialize any of them. What am I missing?

Pigpocket
  • 449
  • 5
  • 24
  • As far as I’m aware, you cannot call `results.flatMap(Article.init)` like that. You would need to use a closure that takes each element from the JSON array (referenced by `$0` for example) and initialises an `Article` from it. Even trying `results.flatMap(Article(dictionary: $0))` might work. – Chris Jun 12 '18 at 07:48
  • Also, there are some problems with the JSON formatting. – Chris Jun 12 '18 at 08:20
  • What you've written above is not JSON. It's the `description` of a dictionary (which is a different format, and not legal JSON, and not intended to be parsed). It's not clear what `taskForGetEventRegistrySearchResults` returns or how it generates it. I'm guessing it's using the older NSJSONSerialization, but it's not clear what `parameters` is in this case. I would start by putting a breakpoint on the `return nil` line in your init and see which entry is malformed for your expectation. – Rob Napier Jun 12 '18 at 13:45

1 Answers1

0

If I were you, I'd implement the Swift 4 Codable protocol in my Article struct, and then I'd decode the array using JSONDecoder, like this

let decoder = JSONDecoder()
let articles = try! decoder.decode(Array<Article.self>, for: results)

So your articles struct would look like this:

struct Article: Codable {
    var dataType: String
    var date: Date
    var eventUri: URL
    var isDuplicate: Bool
    var sourceName: String
    var lang: String
    var sim: Double
    var time: String
    var articleTitle: String
    var image: UIImage? = nil

    static func articlesFromResults(_ results: [[String : Any]]) -> [Article] {
        let decoder = JSONDecoder()
        let articles = try! decoder.decode(Array<Article.self>, for: results)
    }
}

Now, that leaves an issue with the image property, since UIImage doesn't conform to Codable, but you can look here for a solution How to conform UIImage to Codable?

ElFitz
  • 908
  • 1
  • 8
  • 26