2

I am toying around/learning Swift and JSON atm and have problems correctly casting around my responses.

So, what I am trying to achieve is displaying a list of "posts" via Alamofire request. I have no problems displaying "first-level" json items like the post-ID or the "message", but when it comes to the author array I am kinda lost.

the json response comes in as an Array with no name, hence the first [

[
-{
__v: 1,
_id: "54428691a728c80424166ffb",
createDate: "2014-10-18T17:26:15.317Z",
message: "shshshshshshshhshshs",
-author: [
-{
_id: "54428691a728c80424166ffa",
userId: "543270679de5893d1acea11e",
userName: "foo"
}
]
} 

Here is my corresponding VC:

    Alamofire.request(.GET, "\(CurrentConfiguration.serverURL)/api/posts/\(CurrentConfiguration.currentUser.id)/newsfeed/\(CurrentConfiguration.currentMode)",encoding:.JSON)
            .validate()
            .responseJSON {(request, response, jsonData, error) in

                let JSON = jsonData as? NSArray
                self.loadPosts(JSON!)
        }

        tableView.delegate = self

        tableView.dataSource = self

    }

    func loadPosts(posts:NSArray) {
        for post in posts {
            let id = post["_id"]! as NSString!
            let message = post["message"]! as NSString!

            var authorArray = post["author"]! as? [Author]!
            println(authorArray)

            var author:Author = Author()
            author.userName = "TEST ME"

            var postObj:Post = Post()
            postObj.id = id
            postObj.message = message
            postObj.author = author
            uppDatesCollection.append(postObj)
        }
        println(self.uppDatesCollection.count)
        dispatch_async(dispatch_get_main_queue()) {
            self.tableView.reloadData()
        }
    }

my Models for Post

class Post {
    var id:String!
    var message:String!
    var createDate: NSDate!

    var author:Array<Author>!

    init () {

    }
} 

and Author

class Author {
        var id:String?
        var userId:String?
        var userName:String?

        init () {
    }

What is the best Approach here? Should you recast the returning Array as a Dictionary and then Access it via .valueforkey? Do you somehow iterate over the array to get this stuff out?

Obviously you can not say author.name = authorArray[3] as String

longbow
  • 1,593
  • 1
  • 16
  • 39

1 Answers1

2

Let's say you had Post class like so:

class Post : Printable {
    var identifier:String!
    var message:String!
    var createDate: NSDate!
    var authors:[Author]!

    var description: String { return "<Post; identifier = \(identifier); message = \(message); createDate = \(createDate); authors = \(authors)" }
}

Your JSON has only one author in it, but given that it appears to be an array in the JSON, I assume it should be an array in the object model, too, as shown above. The Author class might be defined as so:

class Author : Printable {
    var identifier:String!
    var userId:String!
    var userName:String!

    var description: String { return "<Author; identifier = \(identifier); userId = \(userId); userName = \(userName)>" }
}

I wasn't sure why you made some of your optionals implicitly unwrapped (defined with !) and others not (defined with ?), so I just made them all implicitly unwrapped. Adjust as appropriate to your business rules.

Also, let's say your JSON looked like so (I wasn't quite sure what to make of the - in the JSON in your question, so I cleaned it up and added a second author):

[
    {
        "__v": 1,
        "_id": "54428691a728c80424166ffb",
        "createDate": "2014-10-18T17:26:15.317Z",
        "message": "shshshshshshshhshshs",
        "author": [
            {
                "_id": "54428691a728c80424166ffa",
                "userId": "543270679de5893d1acea11e",
                "userName": "foo"
            },
            {
                "_id": "8434059834590834590834fa",
                "userId": "345903459034594355cea11e",
                "userName": "bar"
            }
        ]
    }
]

Then the routine to parse it might look like:

func loadPosts(postsJSON: NSArray) {
    var posts = [Post]()

    for postDictionary in postsJSON {
        let post = Post()
        let createDateString = postDictionary["createDate"] as String
        post.message = postDictionary["message"] as String
        post.identifier = postDictionary["_id"] as String
        post.createDate = createDateString.rfc3339Date()

        if let authorsArray = postDictionary["author"] as NSArray? {
            var authors = [Author]()
            for authorDictionary in authorsArray {
                let author = Author()
                author.userId = authorDictionary["userId"] as String
                author.userName = authorDictionary["userName"] as String
                author.identifier = authorDictionary["_id"] as String
                authors.append(author)
            }
            post.authors = authors
        }

        posts.append(post)
    }

    // obviously, do something with `posts` array here, either setting some class var, return it, whatever
}

And this is my conversion routine from String to NSDate:

extension String {

    /// Get NSDate from RFC 3339/ISO 8601 string representation of the date.
    ///
    /// For more information, see:
    ///
    /// https://developer.apple.com/library/ios/qa/qa1480/_index.html
    ///
    /// :returns: Return date from RFC 3339 string representation

    func rfc3339Date() -> NSDate? {
        let formatter = NSDateFormatter()

        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
        formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
        formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")

        return formatter.dateFromString(self)
    }
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Many thanks for your answer! Is there a more generic way to do this, either onboard or library based? Doing this with iterating over the Array(which I gladly implemented), theoretically I could end up with like 8x nested for functions which kinda sounds pretty ugly to me. – longbow Oct 18 '14 at 21:14
  • There are certainly things you could do in that vein, but it's hard to say without seeing an example that demonstrates the full richness of this JSON. You probably want to draw a balance between completely data-driven vs something a tad more practical. Maybe give it a go and let us know how you go. If, after a good faith effort on your part, you still have questions, post a new question here on s.o. – Rob Oct 18 '14 at 21:26