Why we really need MVCVM not just MVVM in iOS

I don’t know why it took this long, but it finally clicked.

I’ve been creating view models for my views but I’ve been quite confused as to what to call the larger class that embodies the view models for the entire view. Let's look at the example below.

ViewController -> ViewControllerViewModel

    DetailView -> DetailViewModel

    TableView -> [CellViewModel]

    GraphView -> GraphViewModel

The ViewControllerViewModel will encapsulate the view models for all the other smaller views.  However, in the past, I would also have this ViewControllerViewModel handle fetching data, updating the view model, telling the view to update itself.  Although it split my UIViewController code in half, it still could have a better separation of concerns.  So let's look at each of it's responsibilities and see if we can split them up a bit better.

The view model simply transforms the model data into something consumable by the view. The funny thing is that you’re going to need a larger container for all your little view models, especially when you’re dealing with tables views and collection views.  This larger view model is well, still a view model!  Just like a larger model made up of many smaller models is still a model!

Now we get a little into controller land when we think about the fact that the larger view models are often the ones constructing the smaller view models.  For instance, in a UITableView, you’re going to need to take the data you get from the models and coerce it into the view model.  It seems to follow that you could have a method on each of the smaller view models that helps coerce this data just like you do for model objects being coerced from JSON.

So you really should be able to just call the following:

let cellViewModels = models.map{cellViewModel($0)}

That’s super simple.  So there remains a question, where do you handle the effects that the input has on the data?

Up until now, I’ve passed the result of any picker to the view controller then to the view model and let it take care of the rest.  Holy crap that’s a waste.

You simply need to have your date picker and metric picker delegates to be a separate controller that the view has access to.  This controller than can manipulate the ViewModel and then tell the view to update itself.

I really think this pattern is more like MVCVM.

Should a VM update a display? Certainly not, that’s a controller's job.

Should a VM hit the web and then create itself? Nope, still a controller’s job

Should a VM update itself when the view registers user input? Nope the controller should get that input, update the ViewModel and then update the view.

This allows view models to be reusable with their views.  The views and the vie w models are coupled, but they can be used anywhere, as long as you have a controller to coordinate them.

The last piece is that any view should be able to be instantiated by doing the following:

DetailView(viewModel:detailViewModel)

Simple enough right? So thinking about the view structure from before.

ViewController
    DetailView
    TableView
    GraphView

If we want a view that is setup like that using the MVCVM convention, it might look a little bit like the following:

class AwesomeVC: UIViewController {
    var controller:Controller!
      
    override func viewDidLoad( ) {
        super.viewDidLoad( )
        loadData( )   
    }

    func loadData( ) {
        spinner.start( )
        controller.loadData( ).then {
            self.spinner.stop( )
        }.error {
            self.spinner.stop( )
            presentErrorMessage(error)
        }
    }
}

protocol Controller {
    var viewDelegate:ViewDelegate {get set}
    func loadData( ) -> Promise<Void>
}

class AwesomeController: Controller {
    var vcvm:ViewControllerViewModel?
    var viewDelegate:ViewDelegate

    init(viewDelegate:ViewDelegate) {
        self.viewDelegate = viewDelegate        
    }

    func loadData() -> Promise<Void> {
        return getModel( ).then { model in
            vcvm = ViewControllerViewModel(model: model)
            Dispatch.async {
                viewDelegate.updateView( )
            }
        }
    }
}

struct ViewControllerViewModel {
    var detailViewModel:DetailViewModel
    var cellViewModels:[CellViewModel]
    var graphViewModel:GraphViewModel    

    init(model: model) {
        detailViewModel = DetailViewModel(model: model)
        cellViewModels = model.array.map{cellViewModel($0)}
        graphViewModel = GraphViewModel(model: model)
    }
}

It's not using reactive bindings, it simply would use a delegate to update the view.  Also I'm using promises instead of closures to handle callbacks. Curious to hear your thoughts on this pattern.

Never Lose Your Error Messages Again

Tell me if this has happened to you.

You're diligently finishing a feature (and damn is it a great feature).  And you go to run the simulator, things are looking great and then BOOM.  Simulator crashes, error registers to the console, and your day is ruined.

Well not really, right? Because at least you have an error statement. Oh, and it's a simple one.  "Array index out of bounds".  Cool, glad we decided to check for those with guards.

You search the code base and there are exactly 5 different places that error shows up in the code base. D'oh!  Well at least it's narrowed down your code search from 30,000 lines to 5.  But we can do better than that right? 

Maybe we could just write the class name at the beginning of our errors.  That would make it easier to find at least.  Well what happens when the class name changes?  Huh.

Never fear, simple swift feature is here!  You can quickly print the name of the class in all your error messages with the following snippet:

print("\(type(of:self)): Array index out of bounds")

If you haven't had a chance to use Type(of:), go ahead and give it a whirl.  And never guess where a print statement comes from again.

What's your favorite way to track down error messages?

Code Injection for Xcode 8 in CollectionViews and TableViews

Screen Shot 2017-02-25 at 1.30.35 PM.png

I finally got sick enough of my load times 😭 in Xcode that I went looking for a solution.

I had heard about this code injection plugin for Alcatraz which looked promising.  But to my dismay, I soon realized that all plugin support had been dropped for Xcode 8.  Sigh.  But wait! What's this here?  A code injection app for mac?  Let's investigate.

John Holdsworth, the creator of the code injection plugin, has released a code injection app that can interact with the Xcode simulator when both are running at the same time.  Fantastic!  I downloaded and installed it.  Also, this fantastic code injection intro video was attached to the site, but later taken down. I wish it hadn't because I had to go looking for it again.

Does Code Injection Work in Production Code?

My only problem was that a struggled a bit to integrate the code injection app into my workflow.  Like most iOS developers, I'm working with UICollectionViews and UITableViews on a daily basis.  Could I use the code injection app there?  That's where I really needed it.

As it turns out the answer is yes!  I made a quick little video to show how easy it is, but you can also see the take-aways below.

How to use Code Injection:

  1. Install and run the injection app from here.
  2. In any subclass of NSObject (like UIViewController) add the function below.
  3. Compile and run your app
  4. Once the app is loaded you can change the UI code in your table or collection
  5. Press (Command + S) to save
  6. Press (Control + =) to run the injection script
func injected() {
    collectionView.reloadData()
}

A few things to be careful about:

  1. If you're using a tableview, all you have to do is change the injection code to tableview.reloadData().
  2. Make sure to run the injection script while the file that contains the injected() function is open in the Xcode window.
  3. You can change files other than the one you're working on and still have your UI Code update from the injection script.

And there you have it! I'm a huge fan now that I've saved myself nearly 30 seconds of compile time for every UI change I make.  A big thank you to John Holdsworth for making such a great app.

Setting UIButton Title and Title Color "The Right Way" in Swift 3

I wanted to write this short post as a reminder to myself.  Every time I set a UIButton color or title I fail at first because I try to do the following:

//This is incorrect, don't do this
let button = UIButton()
metricButton.titleLabel.text = "My Amazing Button"
button.tintColor = .blue

I always forget about the setters that include the control state.  This is how you should actually do it:

//Setting UIButton color and title the right way
let button = UIButton()
button.setTitle("My Amazing Button", for: .normal)
button.setTitleColor(.blue, for: .normal)

Perhaps now I'll finally remember the right way to do it, and I hope you will too!

WWDC: How to quickly become a iOS expert on StackOverflow

If you want to be a top notch iOS developer, you need a strong reputation on Stack Overflow (SO).

Why?

  1. It teaches you how to ask great questions
  2. It takes ads off perhaps the most important site you’ll use every day
  3. It allows you to leave comments and up vote so you get the most out of SO
  4. It makes sure you know how to use SO like a champ
  5. It sets you apart from the crowd and let’s people know you care about your craft
  6. It allows you to directly contribute to your team by setting bounties on hard to answer questions

But getting to 1000 rep on stack overflow isn’t as easy as it used to be.  Most of the simple questions are answered, and it’s a slog only getting 10 points for every answer you post.  That’s if (and only if) you get up-voted or accepted as the answer. Let’s say 50% of the time your answers get points, that’s 200 answers you’ll have to post to get 1k in rep!

So what’s a iOS developer to do?

Well there is a very simple strategy I used to get to 1k rep by answering Swift questions.  And with WWDC around the corner, it’s the best time of year to put it into practice.  I’ll break it down into 4 steps.

1) Pick a new Apple API and become an expert in it

Every June, Apple rolls out a fancy new SDK that no one knows how to use.  So if you watch the WWDC videos on an API you’re really interested in, and then start playing around with the technology, you’ll know that API better than 90% of developers out there.  This happens because most people don’t have the time to play with all the new technologies that Apple rolls out.

I did this with UIStackViews.  I thought this technology was a godsend for iOS developers because it brought a completely new way to handle auto layout to iOS. Fortunately, most people couldn’t start using the technology because it was only supported by devices running iOS 9 and newer.  So I watched the videos on Stack Views from WWDC, built a few apps with it, and then supplemented them by watching the tutorial videos on RayWenderlich.com (It’s only $19 a month and worth every penny).  Equipped with this new knowledge I moved on to step 2.

2) Stake out the tag for your API and answer every question that gets posted

From the SO homepage, navigate to tags and type the name of the new API you’ve mastered.  Roll over the tag and you’ll be able to favorite the tag by clicking the star and subscribe via email or rss.  Then each day for the next month, check SO in the morning and answer all the questions that have been posted.  Or simply answer them as the show up in your email.

3) Format Your Answers for Maximum Utility

The most important part of this strategy is the format of your answers.  Make sure you do the following:

1) Make sure you are answering the exact question asked

A lot of people fail to get up-voted on SO simply because they don’t focus on answering the question that was asked.

2) Include screenshots or example code when possible to make your answer clearer

We’re visual creatures.  Have you every tried to implement a piece of code that was only explained in text? It’s a pain!  So make the Original Poster's (OP's) life easier by including screenshots and code.

3) Make the answer exhaustive by explaining the why behind your answer

Stack overflow is a great place to learn but barely anyone teaches.  Thoughtful answers that include why not just how get the most up votes.  When you include why, then it’s likely people who are asking the question won’t have to look it up again because they’ll have learned more than just how to copy and paste code.

At first, your answers may not get up voted much, but that’s part of the plan, this is a bit of a long play.  Over the course of the year, people are going to start using the API you became an expert in.  As more people use it, more people will look up your answers and click up-vote.

Here's an example of one of my thorough posts on Stack Overflow.

Now, if there questions you know people are going to ask, but haven’t yet, use step 4.

4) Post your own API question and answer it thoroughly

This is kind of like step 2 but it includes a preemptive strike. 😉 When you keep running into a problem as you use the new API and find ways around the problems by reading the documentation, you’re in a great position.  You can actually post that as a question and answer it yourself.  You’ll have to wait 3 days to accept your answer.  And make sure your answer is exhaustive, because other people are likely to answer your question as well.  You want to be able to accept your own answer in good conscience.

This strategy is almost twice as effective as step 2 if you can pull it off correctly because now people can up vote your question and your answer.  Sweet!

Here's an example post I made using the iOS Charts API.

So once you’ve gone on an answering spree for a month, you should be able to sit back and watch your rep roll in.  If you take a look at the graph of my rep, you’ll notice that I shot up to 500 pretty fast once I figured out this strategy, and then my rep increases more gradually.  That’s when I just answered a question here and there and people started discovering my answers to the new API questions.

Wrapping It Up

The secret sauce in this technique is to get super specific knowledge on a small part of a new technology.  There are rewards for the people who specialize, 1k in stack overflow rep is just one of them. Happy coding! 

Hacksgiving! Running a #GiveFirst HackFest for Nonprofits this Thanksgiving

Sometimes when an idea's time has come it literally bursts forth into the world and you'll have a hard time controlling it.  The #GiveFirst HackFest has definitely been one of those ideas.

Not to long ago, I was sitting on my coach, pondering how to make a contribution with my iOS skills.  I thought of the Boulder Food Rescue (a local charity that donates unused food to families who need it).  They really were doing something I believed in.  So I did some research and BAM! there it was on their website.  They needed a way to communicate with their volunteers more effectively.  To be exact, they needed a mobile app!  I was in business.

I signed up and attended one of their intro sessions, and while I was there listening an idea came to me.  I had been attending the wonderful hackathons at QuickLeft and having a great time.  I'd won 3rd place at the last two :-).  But at the end we would just ditch what we built.  What if you could get that many hackers to work on real tech problems for nonprofits?  I could do so much more than build one app for one nonprofit.  I could help facilitate building 10 apps for nonprofits over a single weekend!

I didn't sleep well that night, I was so excited about the idea.  The very next day I went to work at TechStars, and excited about the idea I pitched it to Julie Penner, the program manager.  Julie is not one to drag her feet when she hears a good idea.  The minute I told her about it she said, "That's a great idea!  Let's call it the GiveFirst Hackfest, and hold it right before Thanksgiving!"

"Um, what?" I was dumbstruck.  I had been thinking of doing this maybe 6 months down the road.  Nothing like a kick in the ass from Julie to get your projects moving.  No wonder she's so good at mentoring young companies.

We just put our landing page up last week and we've already had 25 people RSVP!  If you'd like to participate you can RSVP here.

I'm still a bit shocked at how fast this has all come together.  It's a grand experiment and I can't wait to see what we're able to accomplish with it.

 

Source: https://givefirsthackfest.splashthat.com/

Creating a floating graph reader with iOS-Charts

IOS-Charts is by far the best chart drawing framework I've used to date (I've only used PNChart so far so that's not saying too much).  Out of the box you get custom animations, touch events, and fill effects.  It's enough to make a developer swoon.

That said iOS-Charts and I had a little trouble getting to know one another.  The entire documentation is written for android developers in Java.  Luckily, I had switched to Swift from Objective-C earlier that week, so reading the docs was super easy.  That is, until I got to my floating graph reader...

You see that nice 3.0 with impressions and a date attached to it?  Well it slides with your finger over the graph and changes it's values accordingly.  Really, not a hard thing to implement if your graph has good docs.  Unfortunately, mine didn't.

So if you want to create something similar, here are the steps to do it:

  1. In the file you are implementing the graph, be sure to include ChartViewDelegate
  2. Then implement the chartValueSelected method.
  3. If you then use the getMarkerPosition method, you'll be able to center your "marker" wherever you want.

Here's some sample code:

markerView is simply the UIView that contains three labels. It's the 3.0, impressions, and Apr you see in the picture. This acts as my "floating graph reader" for the graph.

The markerPosition is simply a CGPoint so you can use the x and y values to reposition your floating graph reader.  Hopefully, this saves you the hours I spent looking for the getMarkerPosition method.  I honestly had to scroll through all the methods of a chartView to find it, because (as I said before) iOS-Charts lacks documentation.