5

I'm trying to get to the url of thumbnail_images from the JSON response I got as shown in this picture below: enter image description here So in order to reach to the url I made a class called Posts in which

class Posts: NSObject {

    var title: String?
    var excerpt: String?
    var content: String?
    var thumbnail_images: [ThumbImages] = []

    init(dict: [String: AnyObject])
    {
        super.init()

        self.title = dict["title"] as? String
        self.excerpt = dict["excerpt"] as? String
        self.content = dict["content"] as? String

        if let postImage = dict["thumbnail_images"] as? [String: AnyObject] {

            for img in postImage  {
                self.thumbnail_images.append(ThumbImages(dict: img))
            }
        }
    }
}

On the line self.thumbnail_images.append(ThumbImages(dict: img)) I get this error:

Cannot convert value of type '(key: String, value: AnyObject)' to expected argument type '[String : AnyObject]'

That is it for my questions but I think it's relevant to share more code as in order to reach to url I had to make to more classes ThumbImages and MedLargeImage. Which has the following code:

class ThumbImages: NSObject {

    var medium_large: [MedLargeImage] = []

    init(dict: [String: AnyObject]) {

        super.init()

        if let imgURL = dict["medium_large"] {

            for iURL in imgURL as! [AnyObject] {
                medium_large.append(MedLargeImage(dict: iURL as! [String : AnyObject]))
            }
        }
    }

As you can see in the screenshot. So finally I'm in the room where I can get the URL:

class MedLargeImage: NSObject {

    var imageUrl: String?

    init(dict: [String: AnyObject]) {

        super.init()
        self.imageUrl = dict["url"] as? String

    }
}

UPDATE:

If I change if let postImage in Posts class to:

if let postImage = dict["thumbnail_images"] as? [[String: AnyObject]] {

            for img in postImage  {
                self.thumbnail_images.append(ThumbImages(dict: img))
            }
        }

The error is gone but for the if condition never gets true.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Chaudhry Talha
  • 7,231
  • 11
  • 67
  • 116
  • 1
    You treat `postImage` as an array but it's a dictionary. That's why you get this tuple when looping, you get tuples of keys and values from the dictionary. – Eric Aya Sep 24 '17 at 15:36
  • @Moritz If I change it to `if let postImage = dict["thumbnail_images"] as? [[String: AnyObject]]` it works but `if let` doesn't move to run `for` command. – Chaudhry Talha Sep 24 '17 at 15:46
  • 1
    Don't use `[String:AnyObject]` as JSON dictionary representation in Swift 3 at all. A JSON dictionary is `[String:Any]` because all JSON dictionary values are (Swift) value types. Moreover, looking at your current implementation for `Posts`, I see no reason to inherit from `NSObject`. – Dávid Pásztor Sep 24 '17 at 15:52

3 Answers3

8
  • First of all classes are supposed to be named in singular form Post / ThumbImage. The datasource array e.g. posts contains Post instances and each Post instance contains an array thumbnailImages of ThumbImage instances. These semantics make it easier to understand the design.
  • Second of all JSON dictionaries in Swift 3+ are [String:Any]
  • Third of all according to the initialization rules the super call must be performed after initializing all stored properties.

The value of key thumbnail_images is a dictionary containing dictionaries. The error occurs because you are using the array enumeration syntax which treats the dictionary as an array of tuples.

The dictionary can be parsed to use the key as size name and the value to pass the parameters.

I have no idea why you are using NSObject subclasses but I'm sure you have your reasons.


This is the Thumbnail class

class ThumbImage: NSObject {

    let size: String
    var url : URL?
    let width : Int
    let height : Int

    init(size : String, parameters: [String: Any]) {

        self.size = size
        if let urlString = parameters["url"] as? String {
            self.url = URL(string: urlString)
        }
        self.width = parameters["width"] as? Int ?? 0
        self.height = parameters["height"] as? Int ?? 0
        super.init()
    }
}

and this the Post class

class Post: NSObject {

    var title: String
    var excerpt: String
    var content: String
    var thumbnailImages = [ThumbImage]()

    init(dict: [String: Any])
    {
        self.title = dict["title"] as? String ?? ""
        self.excerpt = dict["excerpt"] as? String ?? ""
        self.content = dict["content"] as? String ?? ""
        super.init()
        if let images = dict["thumbnail_images"] as? [String: [String:Any] ]  {
            for (key, value) in images {
                thumbnailImages.append(ThumbImage(size: key, parameters: value))
            }
        }
    }
}
vadian
  • 274,689
  • 30
  • 353
  • 361
1

You are seeing that error message because you are looping over postImage as if it's an array, when it's actually a dictionary. In your ThumbImages class initialiser, you are expecting to take in a dictionary. However, since you are looping through the postImage dictionary, this returns you tuples in key value pairs instead.

Hence when you try to pass in this tuple into the ThumbImages initialiser, the compiler complains that it is unable to convert your tuple into a dictionary.

Passing in the dictionary immediately will resolve the error:

if let postImage = dict["thumbnail_images"] as? [String: AnyObject] {
    self.thumbnail_images.append(ThumbImages(dict: postImage))
}
Adam Haafiz
  • 218
  • 1
  • 8
0

You cannot pass tuple as dictionary directly. instead use this: let dicFromArray = [tuple.key :tuple.value]