On the developer conference "360 | iDev", we gave a talk about architectures that can be used together with SwiftUI. More than 60 developers joined our 4-hour workshop.
you will build What you will learn Prerequisites You will refactor an app written using a Model-View architecture to a Redux architecture as well as a MVVM approach. How to evaluate app architectures How to write an app using a Model-View architecture How to write an app using a Redux architecture How to write an app using a MVVM architecture • Xcode • Swift • Patterns Basics • Intermediate SwiftUI Knowledge Key Vocabulary • Views • MVVM • Redux • SwiftUI • Swift 360 | iDev
Introduction: Who are we? 2 • Ghulam Nasir • born in Peshawar , living in Munich . • Senior Mobile Developer at QuickBird Studios • Husband, Father of Two • gnasirky nasirky • Paul Kraft • born in Nuremberg , living in Munich . • Swift Developer at QuickBird Studios (mostly iOS with UIKit) • TUM Master’s student in Computer Science • pauljokraft pauljohanneskraft
Getting to know each other • Sadly, there is not really time for an actual introduction round, but we would like to get some insight into what your background is and how we can best conduct the workshop. • Have you already had your first contact with SwiftUI (e.g. looked at code, seen tutorials, built something)? • Have you built SwiftUI views on your own? • Have you built a SwiftUI application that has gone live? • Have you already had issues with state management in SwiftUI? • Have you struggled with your app’s architecture when using SwiftUI? • Have you ever created UIs with UIKit? 5
Asking questions during the workshop • The workshop is divided into “theory” and “exercise” parts. • During “theory” parts, feel free to write questions in Slack, but please do not interrupt the speaker unless it is really important. • During “exercise” parts, you can ask questions in Slack or Zoom, depending on your preference. Please “raise your hand” before you start with the question, we will then unmute your microphone once it is your turn. If there are many people asking questions using Zoom, feel free to use Slack instead. 6
SOLID Principles • A type should only have a single responsibility. • A type should be open for extension, but closed for modification. • Subtypes should always conform to the behavior a parent type promises. (Liskov Substitution Principle) • A type should not be forced to implement an interface it does not use. (Interface Segregation) • Entities should depend on abstractions, not concretions. (Dependency Inversion) 9
Metric: Code Overhead 11 Description Possible benefits Possible drawbacks • We do not want to write repeated code not serving a well-defined purpose. • only write as much code as needed for the given problem • increased code overhead must serve a justifiable cause • not choosing an abstraction to decrease code overhead might turn out to be the wrong decision • decreased development time • focus on more important tasks
Metric: Testability 13 Description Possible benefits Possible drawbacks • We want to be able to test code easily, both individually and as a whole • We want to be able to mock certain functionalities of the application • We want to be sure, everything works correctly • Increased development time • Increased complexity • Increased overhead • Identify bugs early • Allows for easy mocking, e.g. for demonstration
Metric: State consistency 15 Description Possible benefits Possible drawbacks • We want to always show the user the current model state without creating inconsistently displayed data • The information shown to the user should not contradict itself • State changes might contradict itself • Memory usage should be minimized • View state might not be updated when model state changes • Increased coupling • Error propagation • Great UX • Less error-prone
Metric: Code consistency 17 Description Possible benefits Possible drawbacks • We want to write consistent code according to a specific structure to allow finding a specific code piece faster • have common abstractions for similar abstraction cases • finding the right abstraction to fit most problems, but also not hinder them from being solved in a clean fashion • Higher learning curve for new developers • The consistent solution might not be the best for the given solution • Reduced maintenance costs • Reduced learning curve for existing developers
Metric: Flexibility in App 19 Description Possible benefits Possible drawbacks • We want to be able to move a view from one part of the app to the other with only minor changes of the code. • Make sure to keep dependencies to other individual parts of the application small, e.g. relying on the type of your parent view or parts of its state • Increased coupling to the whole app • • Reduced maintenance costs for UI redesigns • Decreased coupling to individual parts of the app
Metric: Flexibility across Apps 21 Description Possible benefits Possible drawbacks • We want to be able to easily reuse parts of the business logic or views in other apps - with only minor changes • Example: We want to reuse a complex view, but use a different business logic (e.g. different data source) • Example: We need a custom view for an app, but the business logic is similar to an existing app • Reuse is rarely actually needed for the complex views, since they are so specialized • Reduced development time • Less error-prone (since it worked in its previous environment)
Metric: Modularity 23 Description Possible benefits Possible drawbacks • Low coupling, High cohesion • We want code that is doing similar operations to be close to each other • We do not want to overdo it, since it will be unreadable, if you modularize too much • Steeper learning for new developers • Easy to find code related to a specific topic (for existing devs) • Less error-prone
Metric: Code proximity 25 Description Possible benefits Possible drawbacks • Code connected to a specific feature should not be spread across the whole architecture • We want to easily find out which view actions cause which Model operations • We do not want to study the whole architecture to understand a small feature • Swapping out dependencies is hard • More error-prone for consistency and compatibility issues • Easy to find code related to a specific feature and its direct consequences
SwiftUI Concepts • @State • stored view state, owned by the view specifying the state variable • @Binding • combination of getter and setter - e.g. to access State-wrapped properties, or properties of ObservableObjects • ObservableObject • protocol to be implemented by a view model - updates view, whenever its properties change • @ObservedObject • wrapper for ObservableObject to be used for updating views - initial value needs to be set when creating view • @EnvironmentObject • ObservableObject is matched instead of being provided with a default value - if it cannot find a value, your app crashes. it matches types - i.e. subclassing is not a good idea (in comparison to ObservedObject) 29
Architecture: Model-View 1. View has full access to the underlying Model. 2. View is responsible for all the processing such as fetching the required data • by using a service or • getting it from the previous view and preparing the data for use etc. X
Extracting business logic from views • General idea • Views will only contain UI code, no business logic • Possibility to change the business logic for another use case of the same UI • Some considerations for the abstraction of business logic in SwiftUI • You cannot specify a protocol with `@ObservedObject` (Swift limitation) • We will define types for input and state • In combination with generics, this will allow us to use one view with different business logic without any code changes • There are other ways around this limitation, of course • Feel free to share your solution with the group! 39
How would we integrate Redux into our app? 1. Create AppState, AppAction & AppReducer 2. Inject Store into initial view 3. Adapt initial view to use Store 46
Demo: AppAction • Let’s start with our abstraction by removing the login logic from the existing views and moving that logic into a Store. X enum AppAction { case storedLogin case login(code: String) case logout }
Demo: AppState • Then, we define a state containing the variables we need for these actions. • The LoginService is not subject to change and will be injected into the Reducer instead of being specified here. So, the dependency between the views and LoginService is replaced by a dependency of the views to the AppAction instead. X struct AppState { var githubService: GitHubService? var isLoggedIn: Bool { githubService != nil } } The GitHubService is actually needed by the subviews and cannot easily be removed (yet). Later on, it would be nice, if we could use this property as a stored property, instead of relying on the githubService property.
Demo: Reducer • A reducer takes the current state and the sent action as an input. • With that input, it does model operations (possibly asynchronous) and returns a publisher (AnyPublisher in Combine) of how the state should change based on the state and the action. X struct Change<State> { let perform: (inout State) -> Void } struct Reducer<State, Action> { typealias Change = QuickGit.Change<State> let reduce: (State, Action) -> AnyPublisher<Change, Never> }
Demo: Make use of the Store • Add @ObservedObject var store: Store<AppState, AppAction> to all views that require the store. • Inject a store “Store(initialState: AppState(), reducer: .appReducer())” to the ContentView • Remove references of the removed service, in our case LoginService. • Replace all model operations with actions sent to the store. X
ViewState MVVM 51 View Model Keep view up-to-date trigger actions based on user input ViewModel Listen to model state changes change model based on user input Input State
Model-View Redux MVVM . 66 Smaller apps Prototyping View Model View View Model Store View View Model View Model View View Model ➡ ➡ Smaller apps Fast-paced development Medium-Larger apps Heterogeneous features
• Thank you for listening! You made it through! • We will end this workshop with an open discussion, so feel free (but not obligated) to stay. Find more about us at quickbirdstudios.com Learn more about interesting topics quickbirdstudios.com/ blog Open Discussion 67 end of session