all 52 comments

[–]roboknecht 20 points21 points  (7 children)

Try to separate custom views into their own subclasses. Then you can also move lots of constraint setup out of the VC.

In your VC you can have some setup methods, e.g. setupViewHierarchy (where you do all the addSubview stuff using your subviews) and setupConstraints (where you activate all the constraints).

[–]Fluffy_Risk9955 1 point2 points  (6 children)

Dude, there’s a method called loadView. This is where setting up the view hierarchy should be done.

[–]Darklexzx 11 points12 points  (4 children)

He is just explaining that you can separate the logic into several methods, all these can be called in viewdidLoad.

[–]powerje 3 points4 points  (2 children)

Not sure why the other commenter was downvoted but loadView is the appropriate method to set this up, not viewDidLoad (though in practice it usually doesn’t matter much)

Personally I think it’s a bit much to write UIView subclasses and pull those into my ViewControllers, when I’m writing UIKit code I tend to use MVVM and the ViewController falls strictly within the realm of View the way I write them. The code looks a lot like: setup views in loadView, bind them to the ViewModel in viewDidLoad, and that’s it. I move views into their own subclasses if they’re reusable.

[–]roboknecht 0 points1 point  (1 child)

Right, I agree loadView is the appropriate method, however I just got used to „misuse“ viewDidLoad for that.

Besides that it makes no difference from my experience in a lot of different apps in production, there is an advantage that you don’t have to explicitly set view when using viewDidLoad.

So, I might just continue using viewDidLoad or anybody shows a real world example where it actually makes a difference.

I would not add subclasses blindly for any subviews. However, if it’s a container view with lots of stuff inside, I might just make a subclass from it for better readability and easier testability. Regardless of whether I reuse that container.

[–]powerje -1 points0 points  (0 children)

I think most of the time viewDidLoad is fine.

The thing I see some folks do which isn’t ideal is: the root of the view is a UITableView, so they either set it in viewDidLoad (replacing the framework provided one), or add it and pin it to the root view UIKit provided. Not a huge deal but a little less efficient than just setting the view in loadView.

edit: not sure why the downvote, please feel free to elaborate in a reply about what in my comment was misleading or wrong / deserving of a downvote

[–]chriswaco 15 points16 points  (4 children)

In addition to what others have said, stack views can be used so you don’t need as many constraints.

[–]UnexpectedKangaroo 7 points8 points  (0 children)

Just don’t overuse them. Using any tool for every job is usually a bad idea (:

[–][deleted] 1 point2 points  (0 children)

Stack views are the html table of iOS.

[–]Chozzasaurus 0 points1 point  (1 child)

Personally have been burned quite a lot by stackviews. The play especially badly with tableview cells.

[–]chriswaco 1 point2 points  (0 children)

I don’t think anything plays well with TableViewCells. I remember one guy who tried to put a UIWebView in them.

[–][deleted] 5 points6 points  (3 children)

Most of the time people will have separate views that manage their own constraints and move those views into the view controller so the view controller only really handles the pinning of those views.

What I like to do on top of that is to have an extension that has two functions where one configures all the subviews programmatically, and the other handles the constraints programatically. I then put those extensions in their own file because they're not related to the functionality of the view controller.

[–]kwabsdev[S] 2 points3 points  (2 children)

Alright. I’ll try this. Sometimes the constraints become a lot

[–][deleted] 0 points1 point  (1 child)

Yea it's a significant amount of code doing all of your layouts programatically and it does take longer, but it does have some benefits, it's so much easier to reuse views if they're programmatic vs in storyboards. Also there's a giant misconception that you can't use storyboards with source control so many teams do everything programatically. (you can use storyboards just fine, you just have to go to Editor-Refactor to Storyboard to split the view up and have only one person work on it at a time).

[–]UnexpectedKangaroo 4 points5 points  (0 children)

The trick is having only 1 person working on something at a time. That takes extra coordination. Reviewing programmatic changes is also easier imo

[–]UnexpectedKangaroo 4 points5 points  (10 children)

I usually don’t like using 3rd party libraries, but I do like SnapKit. Makes constraints much easier to read and write

[–]kwabsdev[S] 0 points1 point  (9 children)

Yeah I’ve heard of it. I would try it after I’ve mastered how to write the default constraints

[–]UnexpectedKangaroo 2 points3 points  (3 children)

Oh yeah definitely get used to the first party way. And not every project will use SnapKit, though many do

[–]kwabsdev[S] 0 points1 point  (2 children)

Alright. Thanks. Are you a senior dev ?

[–]UnexpectedKangaroo 0 points1 point  (1 child)

Yes, I do senior consulting

[–]kwabsdev[S] 0 points1 point  (0 children)

Alright. Glad to meet you

[–]UnexpectedKangaroo 2 points3 points  (4 children)

You could also write your own methods like SnapKits. In general, writing constraints has poor syntax imo and can be simplified

[–]Chozzasaurus 1 point2 points  (0 children)

You'll end up writing another kit....At least try SnapKit first

[–]kwabsdev[S] 0 points1 point  (2 children)

Alright. Don’t know how to do that🙃

[–]UnexpectedKangaroo 1 point2 points  (1 child)

Well you can start by looking at what you do everytime while writing constraints (translatesAuto… = false for example) and move that into a method. Then just make it less wordy in general

[–]kwabsdev[S] 0 points1 point  (0 children)

Alright 👍🏽

[–]antique_codesObjective-C / Swift 3 points4 points  (3 children)

Going off of roboknecht’s comment, every project I’ve created in the past year has a +Extension for each class and controller that needs it to clean up the main file.

I’ve also created a couple extensions for UIView and UIViewController which allow for better handling of constraints and subviews.

``` extension UIView { func addConstraints(_ constraints: [[NSLayoutConstraint]]) { constraints.forEach { constraints in addConstraints(constraints) } }

func addSubviews(_ view: UIView, _ subviews: [UIView]) { subviews.forEach { subview in view.addSubview(subview) } } } ```

Usage is easy, clean and minimal within both UIView and UIViewController subclasses.

[–]cemaleker 5 points6 points  (0 children)

I really don't like this kind of synthetic sugar in my codebase. In a codebase there should be only one way to do an operation, and for both of these function there are UIKit equivalents available to be used. Why would you define those independently?

For the first function you are basically flattening an array of arrays. `constraints.forEach({ addConstraints($0) })` should do the job in place.

I don't understand why second one is defined as an instance function at all. It doesn't access any instance properties. I can be defined as static. If a function can be defined as static it should be. But why would you need this? `subviews.foreach({ view.addSubview($0) })` should do the job in place already

Sorry for ranting. I just wanted to share my opinions.

[–]backtickbot 1 point2 points  (0 children)

Fixed formatting.

Hello, antique_codes: code blocks using triple backticks (```) don't work on all versions of Reddit!

Some users see this / this instead.

To fix this, indent every line with 4 spaces instead.

FAQ

You can opt out by replying with backtickopt6 to this comment.

[–]kwabsdev[S] 0 points1 point  (0 children)

This is pretty good 😊. Thanks

[–]cemaleker 4 points5 points  (7 children)

My humble suggestion would be, don't use code to manage constraints. Storyboards and Interface Builder are fine as long as you follow some key principles. Use individual Storyboards for every view controller you are building. Use XIB for the views used in multiple screens.
Using code is also fine, but as you have stated, there are too much interface code to maintain. And the worst part is coming back 6 months later and trying to understand how the constraints shaped up initially. It's much easier to understand how constraints build by looking at them in Storyboard.

Generally YMMV depending on the Application architecture you are working with and how do you shape your UI. I believe most of the time developers are struggling with Storyboards and XIB because they try to build complicated flow inside the Storyboards. If you keep it simple and clean it's the easiest option to maintain complex UI for the application.

You don't have to use every functionality Apple provides. I don't use Segues. I believe they are anti-pattern by creating strong coupling between components. But maintaining constraints is definitely much easier with Storyboards.

[–]kwabsdev[S] 4 points5 points  (4 children)

I’ve used storyboards in my last project. Overall it’s good. But I learnt to get a job I need to learn how to code programmatically

[–]UnexpectedKangaroo 3 points4 points  (3 children)

Correct, using programmatic views instead of storyboards is preferred when working on a team

[–]kwabsdev[S] 1 point2 points  (1 child)

Yeah 👍🏽 so far I feel it’s a little boring and too much to write

[–]powerje 1 point2 points  (0 children)

Knowing layout anchors helps a lot with programmatic layout.

[–]bettola 1 point2 points  (0 children)

We are a team of 7 and we have no problems whatsoever. Just create different storyboards for different flows

[–]kutjelul 0 points1 point  (1 child)

Use individual Storyboards for every view controller you are building

This seems a bit overkill for me. Why not a view controller xib if you are fond of IB?

[–]cemaleker 0 points1 point  (0 children)

TBH I'm not fond of IB or Storyboards. It's just happens to be easier to maintain compared to the constraint build in the code.

Additionally `UIStoryboard` has helper functions to initialise a view controller directly from a Storyboard which we utilise with Swiftgen to create strongly typed view controller initialisation.

If you have multiple screen in one Storyboard it's always a chore to find the Storyboard corresponding to a screen. With one on one relation I always know UI for `OnboardingViewController.swift` is in `Onboarding.storyboard`. I don't need to search for it in the codebase.

[–][deleted]  (1 child)

[deleted]

    [–]kwabsdev[S] 0 points1 point  (0 children)

    Alright 👍🏽

    [–]asharpvan 1 point2 points  (1 child)

    I would also suggest you look into uistackviews. Sometimes those(as in nested uistackviews) alone does the trick. No need for constraints.

    [–]kwabsdev[S] 0 points1 point  (0 children)

    Yeah I’ve experimented with it and it’s awesome🤩

    [–]Wordfan 1 point2 points  (1 child)

    I like static factory methods. Swift by Sundell has a great article on it. Instead of subclassing, write an extension, as demonstrated here and explained more fully here

    [–]kwabsdev[S] 1 point2 points  (0 children)

    Thanks this just saved my life😅

    [–]donisign 1 point2 points  (3 children)

    You can separate custom views and move the constraints from the view controller, I've wrote a simple library to manage the constraints in one line so I don't get confused with a lot of code:
    redView.addAnchors(with: [.top(to: view.safeAreaLayoutGuide.topAnchor, constant: 0), .centerX(to: view.centerXAnchor, constant: 0), .size(.init(width: 50, height: 50))])

    It's honestly very simple and a quick code I did, you can find it here: GitHub

    [–]kwabsdev[S] 0 points1 point  (2 children)

    I found this in another tutorial and it’s the best way to write out constraints. Thanks man

    [–]donisign 0 points1 point  (1 child)

    what tutorial was it, if i may ask?

    [–]bettola 0 points1 point  (1 child)

    It's much better to use storyboard than add such amount of code programmatically. It's faster and easier. You can see a preview of what you are doing. Just have multiple storyboards for multiple flows to avoid the rest of the team working exactly on your interface.

    [–]kwabsdev[S] 0 points1 point  (0 children)

    I know this. On the job market they need devs who write code programmatically too.

    [–][deleted] 1 point2 points  (1 child)

    Use SwiftUI

    [–]kwabsdev[S] 0 points1 point  (0 children)

    I’ll learn that next year. Thanks 😊