Swift & Fika 2018 – John Sundell: The Lost Art of System Design

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Applause] Wow I've never been preceded by a song before in a conference this is something new my name is John hi everybody hallo alle Hoopa there's a true pleasure to be here in my home country of Sweden for the first time ever giving a talk at a conference I do a bunch of blogging like Jack mentioned I run Swift by sindell which is a weekly blog and a podcast about Swift developments and I'm also an iOS developer as well and today I want to talk to you about system design and how I believe that it's become a little bit of a lost art in our iOS and Swift developer communities now we don't talk so much about system design I think but there is one thing that we do talk about quite a lot and that is architecture app architecture and more specifically what is the best app architecture what is the single best complete solution for all apps that always works never has any problems and we will never write a single bug again right well today I'm hoping to give you my answer to this question but it's not an easy question to answer so before we do I think we need to start breaking it up a little bit just like with any hard problem so before we start talking about what the best app architecture is I think we need to start talking about what is our app architecture to begin with because many different people have many different definitions of this term some people they see it more as their design pattern like MVC or mvvm while some people more talk about the frameworks they use like rx for example or maybe the way they work like functional programming or TDD test-driven development but of course architecture is not something that we as software developers invented it's been around for ages so we actually need to go one level deeper and talk about what is architecture even and what does it kind of stand for now when most people think about architecture they don't think about apps they don't think about rx whiffed they don't think about VIPRE they think about buildings such as this one the Burj Khalifa is the largest or list building in the world or they might think about buildings like Apple Park the roundest building in the world or something like that but these are of course examples a very extravagant building so maybe we should take it a little bit closer to where we are and we can look at the building we're in right now Nora Lawton which actually used to be a school before it was a conference center fun fact now these buildings here are very different they serve different purposes and they're used for different things but they all have an architecture and I think it's interesting to look at buildings I mean I'm not a building experts but you can kind of look at what is important for building architecture well the first thing is that the architecture can have serves the purpose of the building that it's functional that a conference center works as a conference center it wouldn't be so much fun for me being here speaking in a super tiny room in an office building so you have different requirements and different functions but we also want things to look pretty right when we walk around our cities and we see our buildings we want them to be elegant and nice and this is true for our code as well we want our code to be nice to work with and look and be nice to handle but the most important aspect of any kind of architecture perhaps is the fact that it's going to be robust we don't want to build a building that's going to collapse in two years that wouldn't be a very good investment and the same thing also is true for apps I mean they might not last for thousands of years like buildings but we don't want them to be completely unmaintainable three months from now so the funny thing is that by looking at architecture in general there's a lot of similarities between building buildings or furniture or apps whatever you might be building architecture is really fundamental so in the iOS community then why are we so hung up around design patterns everyone's always talking about design patterns and what's the latest one you know we have MVC mvvm Viper mvvm see it seems like the further along in time we go the more letters we get so maybe like a couple of years we'll have like 15 letter design pattern combinations I don't know but the million-dollar question here is is a design pattern equivalent to an architecture is a design pattern all we need to create a good architecture for our apps well to use the building analogy one final time if we look at buildings again we can kind of see that they have design patters too you have towers you have offices you have conference centers in many different others but of course the office that most of us work in probably don't look like Apple Park so buildings are also very different even though they might use the same design pattern so it's the same true for apps well I think so and to kind of prove it to you first of all if I were to ask every single person in this room to build an app using the MVC design pattern so I would tell all of you please go ahead and build an MVC app I am pretty sure that we would end up with maybe even 250 different architectures or at least once that look very very different but why is that well if you think about it if you take MVC in particular and we just break it down let me see what it really just tells us is that in our app we should have models we should have views and we should have controllers and it kind of defines some rules around how those should interact with each other but that's about it really so when we have some types of classes it can be really tricky to figure out kind of where they fit in for example let's take a look at UI application is that a model is it a view or a controller it's kind of controller because it controls the lifecycle of the app but it's also a UI kit class so it's kind of a view layer thing is it a model it contains information about the app what about something like user notification center it's kind of a model in the sense that it's a data base for push notifications but it's also kind of a controller like objects seems true for Notification Center as well and here's perhaps the most tricky one of them all URL session URL session some people would argue is part of the networking layer so yet another layer altogether but does that mean that we have an MVC networking app well almost all apps do networking right so these can be quite tricky for us as third-party developers to kind of deal with in terms of a design pattern as our only kind of guideline and it turns out we are not alone Apple they have also a huge problem to categorize these classes and I have proof of that you know what it is they're all single turns and that means we don't have to worry about architecture we can access them from anywhere we can access them from a view from a model from a controller just URL session got shared boom you're good to go so a design pattern is awesome it's great to have a good design pattern as a starting point but I don't think it's a complete solution for an architecture we need something else we need a complement to our design pattern and I would argue the dad is system design and today I want to talk to you about that how it relates to design patterns in architecture design system design is kind of a way to answer the question of where do you put something a new class a new type a new function where does it go and how does it relate to the other things that are already in your app and today I want to give you my top three practical tips on how to design good systems for iOS apps so hopefully you'll get some inspiration for something you could apply to your apps as well and I want to kick it off too by talking about decomposition decomposition is basically a fancy way of saying breaking things up so you might have heard about composition putting things together well this is pretty much the opposite so before we can compose types together in to form new functionality we need to break things up I think that almost every developer on the planet would argue that breaking things up is kind of a good thing no one wants to work as with an app or a system as one big blob of things we don't want to deal with it in like one big file that would be kind of hard instead we want to break things up into smaller pieces so that they're easier to manage and easier to work with that way we can take one single one of these pieces we can work on it we can add new features to it and we can test it in isolation as well but this is of course a lot easier said than done especially when we deal with UI development so let's take a look at an example here we're building a podcast app and we have a screen for displaying one show with a list of episodes with a standard iOS approach we might just be able to say well you know what this is this is a podcast view controller and it contains all of the UI to display this podcast view and it contains all of the features needed to do so so it will contain a header list actions at the bottom and it will perform all of the tasks that we need to render this information so that would include things like data loading model loading image loading and extracting data from that model and rendering it and handling user interactions and everything that goes with it so this of course is an approach that had a lot of people using and including myself and sometimes it's fine sometimes it's not a problem but we usually end up with these really really big classes doing it this way because just look at this list it's so many things that we need to do and usually just gross over time and we end up with these massive view controllers so decomposition is one way of addressing this problem so what we're going to do in this case is we're gonna start by splitting up the UI we're gonna take all of the parts of this UI and make them into a separate piece separate building block and in this case we're going to use child view controllers this is something that is becoming more and more popular in the iOS community and has actually been around since iOS 5 you've been able to add a view controller as a child to another view controller just like you do with UI views so it's really really powerful but just splitting things up is not a complete answer either because then we might just move code around we haven't improved the system design we haven't improved our architecture so we also need to take a look at what are these individual parts actually doing well if we take a look at the header first we can see that it needs to load some data to display the title and things like that it probably to cache that data and extract that data from the model and then do some image loading in this case because it displays and then and for the list we have a similar set of tasks but at the bottom there we need to handle cell taps so we have to conform to UI tableview delegates and for the actions view controller well it's also fairly similar except in the bottom there we have to handle button taps so what we can see here is that we have a lot of common functionality we have a lot of things that we need to share between all of these separate view controllers and if we were to just decompose without designing it well we would probably end up with a lot of code duplication so how does this relate to system design what we're going to do here is we're going to take all of these four things that are common for all these few controllers and design a little micro system for this so what we're going to do we're gonna move the data loading and the caching into something called shared services so here we'll have a network service and a caching service and the reason they are different from the bottom two is that these are more generic things that we can reuse across our app so here we have system-wide services that can be used for any different screen that can be used for any part of the app because they're not really domain-specific but on the HA on the bottom half there we have our podcast loader and this case we is a podcast view model we could also use a podcast presenter or a podcast logic controller it really is the same thing from a system design perspective it's all about splitting things up and in this case we're talking about domain-specific classes the main specific functionality that specifically relates to loading and displaying podcasts and splitting things up this way enables us to reuse as much of our code as possible without having each layer have to be aware of specific implementation details so what we've done now is we've taken this big blob that was the podcast view controller that probably was like thousand plus lines of code and we split it up into many different parts so now instead we have multiple pieces of the UI that can all independent it operate independently but we have shared services and shared classes that they can use both the main specific and more general so we don't have to duplicate our code and the cool thing is that we've done this now on a view level but we can do this on an app level as well so we can take all of this code and put it into a podcast kit into a domain-specific framework we can do the same thing for other domains whether that's features or things like analytics and we can model those as frameworks and that way we have made also decomposition on an app level as well but before we can do this I think it's very important to start from the ground up and kind of look at what different parts do we have in the different domains and the different features in our app so decomposition what it's all about is separating each task so that they can be performed maintained and tested in isolation and shall queue controllers and services view models you have logic controllers and many different other tools they're really great tools for doing this and for splitting up big view controllers and again you can pick out of which one you want to use and which one fits your app the best and finally what you've seen we've done here is visualizing this thing we've visualized all of these different parts of a new feature before we started to code and that can really save us a lot of time because we can spot problems early on we can spot circular dependencies and we can spot a bugs that might happen just by drawing things out so that is decomposition now let's talk about locks and keys now you might be thinking here that I'm gonna do another talk about encryption but I'm not because honestly it's way better at that than I am but here I'm actually talking about dependency management what is locks and keys have to do with the pendency management well let's let's take a look so there's a piece of code or a style of code that I've seen in so many different apps and I'm sure you have as well you have these guard statements in different parts of the app where you are unwrapping an optional somewhere but it's not really optional you kind of need it so you create as guard statement in this case we're unwrapping a user optional from a user manager singleton and if we fail we trigger an assertion but this is all good if you are in the context where the user is optional but then you wouldn't really put the assert in there but if you are in a context for example in this case we want to show a friends list for the current user this guard statement doesn't make any sense it's really awkward because what's what's the point of this assertion it's code that we're not even thinking will ever be executed and who wants to maintain and write code that will never be run no one right so how can we get rid of these kind of awkward optionals in our code base well we might be thinking this is hold all have to do with Singleton's let's get rid of the singleton let's make a dependency injected user manager and use that as the property instead and that is an improvement but it doesn't solve our core issue the fact that user is an optional because the user can either be logged in or not and our code is not really adapted for that so here comes locks and keys what we're going to do is we're going to define our system and make some system design to actually define a lock that says that in order to open this lock you need a user the user opened this lock so the user is the key the user model is the key and that way we can kind of split our codebase up into two distinct parts one part of our code base it's guaranteed that there will not be a user a user does not exist in this part of the codebase and in the other part of the code base the user is guaranteed to exist that way we don't need these nasty optionals that are not really optional to begin with and in the other part of our code base we can just not have to worry about users at all so that way when we build features like a user manager or friends list or an inbox or something like that we can safely assume that we have a user we don't have to write these guards thickness anymore so how can that work in practice there are many different ways to solve it in this case I'm going to use the factory pattern I really love the factory pattern because it provides us a way to define objects that create other objects we can move the creation of some complex objects and dependencies into separate classes which is also pretty nice so let's take a look at how that would work in this case we're gonna start with a root Factory this is going to be the root factory that can create objects without any locks it doesn't require and in dependencies so here we can make things like our image loader because it just requires networking no specific model or something like that we can make our login controller so that were able to log into the app and here comes the interesting parts we can make other factories and here comes our lock so we create a method here called make user bound factory for user and it returns a new factory which is bound to a specific logged in user so what happens is that when the routes factory creates a login controller it injects itself into that login controller and the login controller can safely retain the root factory because factories do not retain the objects that they create what that means is that when the login controller has a user model when the user has logged in it can use that as the key to unlock the user bound factory and when it gets that back it can use that new factory to create new objects that are dependent on our user that means that when we say make user manager make friends list or make home.you controller they can all have a non-optional user to deal with which is really really nice and the cool thing is we can continue with this design further along so when we have the user bound factory it can also inject itself into the controllers it creates and that way those controllers can then make more controllers and everything is pretty cool so we can make a detail controller or a sender percenter again with a guarantee that the user always exists so locks and keys is great to keep in mind when designing systems and it's the idea that an object shouldn't be created until all of its the required dependencies are available it doesn't really make sense to create a user manager without a user so let's wait until we have one by using a required object or model as the key to unlock this lock then we'll be able to create much more predictable system design and again drawing things out and visualizing it it's really key here factories can be a great abstraction it's not the only abstraction but it's a great one in order to make it more easy to get an overview of where our objects are created we don't have to guess where the user manager is created we don't have to debug it we can see that it's created in a single place and that is locks and keys and for the final topic I want to talk about abstractions abstractions is really really kind of the bread and butter of system design and architecture and picking the right abstractions it's really important in order to achieve a good design now when we are working with iOS apps it's very very common that we feel like we're going in circles it can kind of feel like we have written the same table view a million times before we have written the same collection view we have written the same networking code it's all very familiar to us and that's just because we've been there before right and this is not necessarily a bad thing but it can be a bit repetitive after a couple of times so one common example of this is lists we are usually creating a lot of different lists in our apps we might have a message list we might have lists of friends or a list of comments or settings something like that and it's very common to have to re-implement UI tableview datasource and UI tableview delegate over and over again now this is not a criticism to apple's system design or apple's api design because these these actually I think UI tableview is is really greatly designed because it gives us so much flexibility and so much power while still utilizing things like celery use to make performance easy but when we have a situation like this we have many controllers many classes that are using the same type of functionality it's usually a sign that it's time for some form of abstraction and they're really two key ways that we can define abstractions in Swift and the first one I want to talk about is generics generics is really powerful but it can sometimes be easy also to stumble upon kind of your own leg and sometimes end up with really really tricky code so you have to be a little bit careful about how you deploy generics but let's take a look at this example in particular about lists and see how we can use generics to create a nice abstraction for our list in our app what we're going to do is we're going to define a list view controller which is a view controller that renders a list and just using a UI tableview and in this case here we're going to add two generic types a model loader and an action handler and the power of this and the reason I'm doing it this way is that by using these generic constraints and these generic types we enable custom specializations that are concrete so what that means is that our list view controller is not a base class it's not a base controller it doesn't need to know everything about all the different models and all the different types we have in our app it just needs to know how to render a list and by saying that the model loaders model and the action handlers model should always be the same we can make some really nice assumptions about that when we are building our table view data source and our table view delegates and by making the class final we are also signaling to ourselves in the future and our co-workers that this is not a base class it's instead meant to be composed and used as it is instead of using inheritance so how do we use this class then and how does it help us in terms of system design so the way we're going to use it is that we're going to create a list view controller for each use case and we're going to supply a specific concrete implementation of the model loader and of the action handler so we're gonna say that for the message list view controller we are loading messages and we are handling message actions and that's really cool because that way we can have domain-specific implementations again with a more generic abstraction that is built on top of we can do the same thing for other things as well and really the only thing here that's different is the concrete implementations of how we load our models and how we load our action handlers and that way we can separate the domain-specific knowledge in the domain-specific code from the more generic one of course it's impossible to talk about abstractions in Swift without talking about protocols protocols are super powerful in terms of creating abstractions and they can be really great in terms of system design as well so we already used protocols now when we created our generic abstraction we use protocols for the list model loader and for the list action handler but protocols are also really powerful in other senses as well let's say that we were building a user manager again and it needs to talk to a database in this case we just have one class that talks to our database it's just for user login information it's very simple so we say you know what we can actually use a concrete type here we don't need to create a protocol we don't need to create an abstraction we can juice use it as it is and that might be true but as our use case grows and as we have more different types that need to interact with this database for example we might have notes in our app or movies if it's a movie application for example it becomes a little bit trickier and in terms of the design of our app and the design of our system we're kind of starting to paint ourselves into a corner because this database here it becomes a stop block in case we ever want to change our mind and move things around and if there's something that all developers all designers and all product managers like to do it's change our minds right so how can we make that easier well using protocols if we instead define our database as a protocol as the API and just have these classes depend on that protocol we end up with a much more flexible and more future-proof solution what that means is that we can have multiple implementations we can I want four core data for example if that's what we're currently using to define our database but we can also do things like mock this database in our tests and have a way more predictable implementation for testing we can also do things like use a mock as a development tool and have an in-memory database in order to always have a fresh date when we're launching the app which can speed up development a lot and finally if we all want to change our minds and move to another database for example realm we can do that without updating any of our other code and this is really the power of system design where we have thought a little bit of head we haven't over engineered things we've just defined better relationships between all of these parts and that way we can use we move a lot faster so abstractions that's all about having a great tool to create boundaries between various parts of our app each with its own scope and its own area of responsibility and by using things like generics and protocols they can let you define custom abstractions that really reduce the duplication of code and enable us to be much more flexible of course it's important to use these wisely to not have everything be generic or everything be a protocol but to use it to create these boundaries between the different parts and then finally it's a perfectly fine to be inspired by other techniques other platforms and other design patterns as long as we stay consistent I think you can use presenters in your code without using MVP as your design pattern you can use factories without changing your design pattern it's all about picking the right abstractions for your use case and trying it out and staying consistent throughout the system so now that we've gone through these things these three tips I think it's about time we'd come back to our original question what is really the best app architecture well for me the way I see it is that I think the best app architecture is an architecture that was decomposed into separate focused components for easier reuse and maintainability we don't have massive classes with too much responsibility we have small building blocks that you can reuse and repurpose an architecture that uses locks and keys to restrict global access to different objects and to remove non-optional optionals to make things more predictable and to require some keys to unlock certain objects I also think it's an architecture that uses abstractions wisely to decouple all of its subsystems to avoid repeating code and to do it in a consistent manner but more most important of all I think I think is that the best app architecture for your app is the architecture you've create to fit your project by combining standard patterns that people know about and techniques that have recognition but with custom system design where you define the relationship between your objects in a consistent and nice manner and hopefully this talk has inspired you to do just that for your app and hopefully more people in the community can rediscover the lost art of system design thank you very much [Applause] if you want to find me online you can find me on Twitter I am at Johnson Dell my website is Swift bison delcom you can also find me on the stack trace podcast and I have a bunch of open source projects on github as well thank you again [Applause]
Info
Channel: Swift & Fika
Views: 18,821
Rating: undefined out of 5
Keywords: Swift, iOS Development, Programming, Technology
Id: ujOc3a7Hav0
Channel Id: undefined
Length: 29min 51sec (1791 seconds)
Published: Thu Sep 27 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.