Subash
Subash

Reputation: 1000

MVVM architecture using swift to fetch data and update UITableView

I am completely new to MVVM architecture. I just started researching it and this is my first try at writing code using MVVM architecture. I need to download comments from the server and display them in a table view. My view is implemented using storyboard Below is my implementation of other classes.

SECommentsViewController.swift

import UIKit

class SECommentsViewController: SEViewController, UITableViewDataSource {



    @IBOutlet weak var tableView: UITableView!
    var mediaObject: PFObject?
    var viewModel = SECommentsViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()

        viewModel.fetchCommentsForMedia(mediaObject) { (comments, error) in
            self.tableView.reloadData()
        }
    }

    // MARK: - UITableViewDataSource

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewModel.comments.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("SECommentCell", forIndexPath: indexPath) as! SECommentCell
        let object = viewModel.comments[indexPath.row]
        if let username = (object.objectForKey("fromUser") as? PFUser)?.objectForKey("userId") as? String {
            cell.usernameButton.setTitle(username, forState: UIControlState.Normal)
        }
        if let profileImageURLString  = ((object.objectForKey("fromUser") as? PFUser)?.objectForKey("image") as? PFFile)?.url {
            if let profileImageURL = NSURL(string: profileImageURLString) {
                cell.profileImageView.sd_setImageWithURL(profileImageURL)
            }
        }
        if let commentText = object.objectForKey("text") as? String {
            cell.commentLabel.text = commentText
        }

        return cell
    }
}

SECommentsViewModel.swift

import UIKit

class SECommentsViewModel: NSObject {

    var comments = [PFObject]()

    func fetchCommentsForMedia(mediaObject: PFObject?, completion: (comments: [PFObject]?, error: NSError?) -> Void) {
        if let media = mediaObject {
            let commentsQuery = PFQuery(className: "SEActivity")
            commentsQuery.whereKey("image", equalTo: media)
            commentsQuery.whereKey("type", equalTo: "comment")
            commentsQuery.findObjectsInBackgroundWithBlock({ (objects: [PFObject]?, error: NSError?) in
                if let theObjects = objects {
                    self.comments = theObjects
                }
                completion(comments: objects, error: error)
            })
        }
        else {
            completion(comments: nil, error: nil)
        }
    }
}

SECommentCell.swift

import UIKit

class SECommentCell: UITableViewCell {

    @IBOutlet weak var profileImageView: UIImageView!
    @IBOutlet weak var usernameButton: UIButton!
    @IBOutlet weak var commentLabel: UILabel!
    var commentUser: PFUser?
}

I feel like this is no different than MVC where the network code is in ViewModel rather that ViewController. So I think I might not be doing it right. I would appreciate if you could let me know if I'm going in the right direction or not and if there are any suggestions.

Thank you in advance.

Note: I was very impressed by the article on protocol oriented MVVM but had no idea on how to get started on my particular example. Click here to see the article

Upvotes: 4

Views: 3720

Answers (1)

tskippe
tskippe

Reputation: 165

I recommend using the ReactiveCocoa library (https://github.com/ReactiveCocoa/ReactiveCocoa) which let you among many other functions listen to signals.

Then you can place all your network calls in the ViewModel as you are doing, but instead of using callbacks, use a SignalProducer.

So in your ViewModel you could do something like this:

func fetchData() -> SignalProducer<[PFObject], NSError> {
    return SignalProducer { event, disposable in
        // Your network code here, and on completion / error do
        event.sendNext(array)
        event.sendCompleted()
        // On error do
        event.sendError(error)
    }
}

Also, rather than accessing your comments array directly from the view, implement methods like

func getDataSourceCount()
func getItemAtIndexPath()

Upvotes: 4

Related Questions