Sharing data through SwiftUI Views

Passing data through SwiftUI Views

The aim of this post is just to explain three different mechanisms for passing data through SwiftUI views  presenting its pros and cons.

We have create a blank Swift UI app with three simple linked views with a navigation bar.

First and Third view share common data, but the one that is in the middle is a transition view that does not have to deal with such data.

☝️🚨Before starting needless to say that minimum iOS version for running SwiftUI is iOS13.🚨

Share via @State-@Binding

This is the first mechanism that is presented with SwitUI. Is very useful when you want to decompose a view in reusable subviews and parent and subviews share common data.

This is a simple example:

Just declare the object to share with @State wrapper in the parent view:

import SwiftUI

struct ContentView: View {
    @State private var currentPoints: Int = 0
    var body: some View {
        NavigationView {
            VStack {
                // A button that writes to the environment settings
                Button("Points @State-@Binding: \(currentPoints)") {
                    currentPoints += 1
                }
                NavigationLink(destination: IntermediateView(currentPoints: <strong>$currentPoints</strong>)) {
                    Text("Show Detail View")
                }
            }
        }
    }
}

Now you have to bind shared data through all its subviews via @Binding wrapper. In that case, the second view does not have to deal with it, but has to handle because the third view is using it.

import SwiftUI

struct IntermediateView: View {
    @Binding var currentPoints: Int
    var body: some View {
        NavigationLink(destination: PointsView(currentPoints: <strong>$currentPoints</strong>)) {
            Text("Show Detail View (again)")
        }
    }
}

And finally third view:

import SwiftUI

struct PointsView: View {
    @Binding var currentPoints: Int
    var body: some View {

        Button("Decrease  @State-@Binding \(currentPoints)") {
            currentPoints -= 1
        }
    }
}

👍 Advantage is clear, you can decompose a view in smaller and simple views and finnaly pass view model data through them.

⚠️ But when app starts to grow and starts appearing views in the middle that does not have to do with that data, as the example presented, this is totally unsuitable.

⚠️Another issue that I have found whilst I was implementing the example is that when value was changed in the third view, this was popped 🔙.

Share via environment

This is the other mechanism also for sharing data between views, it complements @State-Binding. When I say complements It means that when @State-Binding wins, Environment loses (and in the other way around).

Via environment we can share a primitive variable or a composite type, a class only, 1️⃣ further on you will see why not an struct.

Sharing a variable

First of all you need to create an Environment key that will hold the shared variable itself.

struct CurrentPointsKey: EnvironmentKey {
    static var defaultValue: Binding<Int> = .constant(0)
}

extension EnvironmentValues {
    var currentPointsValue: Binding<Int> {
        get { self[CurrentPointsKey.self] }
        set { self[CurrentPointsKey.self] = newValue }
    }
}

Just declare variable primitive to share by using @State wrapper, and inject value using .environment modifier.

struct ContentView: View {
    @State private var currentPoints: Int = 0
    <strong>@State private var currentPointsEnv: Int = 0</strong>
    var body: some View {
        NavigationView {
            VStack {
                // A button that writes to the environment settings
                Button("Points @State-@Binding: \(currentPoints)") {
                    currentPoints += 1
                }
                Button("Points @Environment: \(currentPointsEnv)") {
                    currentPointsEnv += 1
                }

                NavigationLink(destination: IntermediateView(currentPoints: $currentPoints)) {
                    Text("Show Detail View")
                }
            }
        }
        <strong>.environment(\.currentPointsValue, $currentPointsEnv)</strong>
    }
}

And finally recover value from the third subview by using @Environment wrapper:

struct PointsView: View {
    <strong>@Environment(\.currentPointsValue) var currentPointsEnv</strong>
    @Binding var currentPoints: Int
    var body: some View {

        Button("Decrease  @State-@Binding \(currentPoints)") {
            currentPoints -= 1
        }
        
        Button("Decrease @Environment: \(currentPointsEnv.wrappedValue)") {
            <strong>currentPointsEnv.wrappedValue -= 1</strong>
        }
    }
}

Shareing a class

Instead of sharing a basic primitive type, perhaps we are more interested in sharing class. In that way when its state changes all views that consumes its data will get refreshed. 🙅‍♀️ Well,  this can not be taken litarally, you only track state of attributes marked with @Published attribute.

First of all, lets create the class that we want to share:

import Combine

class AppSettings: ObservableObject {
    @Published var points = 0
}

1️⃣ The aggregated type must be a class because it has to inherit from ObserbableObject.

In the parent view is declared a class instance with the @StateObject wrapper, and is injected to child views via .environmentObject

struct ContentView: View {
    <strong>@StateObject var settings = AppSettings()</strong>
    @State private var currentPoints: Int = 0
    @State private var currentPointsEnv: Int = 0
    var body: some View {
        NavigationView {
            VStack {
                Button("Points @State-@Binding: \(currentPoints)") {
                    currentPoints += 1
                }
                Button("Points @EnvironmentObject: \(settings.points)") {
                    <strong>settings.points += 1</strong>
                }
                Button("Points @Environment: \(currentPointsEnv)") {
                    currentPointsEnv += 1
                }

                NavigationLink(destination: IntermediateView(currentPoints: $currentPoints)) {
                    Text("Show Detail View")
                }
            }
        }
        <strong>.environmentObject(settings)</strong>
        .environment(\.currentPointsValue, $currentPointsEnv)
    }
}

In any child view we can recover the class in the following way:

struct PointsView: View {
    <strong>@EnvironmentObject var settings: AppSettings</strong>
    @Environment(\.currentPointsValue) var currentPointsEnv
    @Binding var currentPoints: Int
    var body: some View {

        Button("Decrease  @State-@Binding \(currentPoints)") {
            currentPoints -= 1
        }
        
        Button("Decrease @EnvironmentObject \(settings.points)") {
            <strong>settings.points -= 1</strong>
        }

        Button("Decrease @Environment: \(currentPointsEnv.wrappedValue)") {
            currentPointsEnv.wrappedValue -= 1
        }
    }
}

👍  Advantage is clear, no need to pass same data through its subviews, cleaner code. As you can figure it out, this data is stored in a common repository, and this has sense when you want to access to Managers, I mean, code components that handle DDBB, Notifications, Deeplinks, Authentications,…. .⚠️   But not in case that you wanted to decompose a single view in more simple ones and pass data through them. For that was designed @State-@BindingMechanism.

Conclusion

In that post I have tried to present, in a very simple way, three mechanisms for sharing data between views with SwiftUI. You can fetch the example code from here 🔗.

References:

State and Data Flow: [https://developer.apple.com/documentation/swiftui/state-and-data-flow]

EnvironmentObject: https://developer.apple.com/documentation/swiftui/environmentobject

Environment: https://developer.apple.com/documentation/swiftui/environment

XCode Build configurations. App non-functional infrastructure Part I.

Serie introduction

The aim of this post is present a way to create a XCode Build Configuration. The following posts will also present some technics that will allow you to scale your app in several directions.

As any building for being usable needs some basic facilites (gas, water, electricity) and other that make it more confortable (e.g parking lot, laundry room,…). Same happens with mobile apps, you really need to prepare some non-functional infrastructure that will allow your app easily grow and scale.

Having different development environments (debug, staging and production), setup an start up sequence, handle different country behaviour and its translations; configure event tagging …. All of then non-functional but really important to handle them pretty well for having a good app scalability  or country expansions.

Those techniques are independent from app business, very easy to implement during first phases of development, but could be really a nightmare in case of adopting them in a mature development.

Build Configuration

The aim of this post is just to present a way to prepare your app for working with different production environments.

In simple cases, such as developing and immediately executing a program on the same machine, there may be a single environment, but in industrial use the development environment (where changes are originally made) and production environment (what end users use) are separated; often with several stages in between. This structured release management process allows phased deployment (rollout), testing, and rollback in case of problems.

Common environments for app development are:

  • Development (debug): This is environment where mobile developers build, fix, refactor the app.
  • Test (alpha): When the app is enough big it is mandatory a QA team, they will validate the app against this environment.
  • Staging (beta): This is a copy of the same environment as production one. But with the exception that data that app is handling  is not real live data. This environment is used to validate  the app before an official release.
  • Production: This is where real users are playing with the app.

… of course there could be many more depending on the scope of the app.

Hands on

It has been created a new single view project, by default XCode provides two environments Development (Debug) and Production(Release). For the purpose of this post we will only deal with three environments: Development (Debug), Staging and Production(Release). Staging has to be as close as Release so we have to duplicate release.

For being accesible build configuration (environment) value to source code. Is necessary make visible configuration in the info.plist.

AppEnvironment.swift will be the component that will handle the app configuration depending on environment:

enum Environment: String {
    case debug = "Debug"
    case staging = "Staging"
    case production = "Release"

    var toString: String {
        switch self {
        case .debug: return "dev"
        case .staging: return "staging"
        case .production: return "production"
        }
    }
}

// MARK: - Private
class AppEnvironment {

    static let shared =  AppEnvironment()

    private init() { /*This prevents others from using the default '()' initializer for this class. */ }

    lazy var environment: Environment = {

        guard let uwpInfoDictionary = Bundle.main.infoDictionary,
            let uwpConfiguration = uwpInfoDictionary["Configuration"] as? String,
            let uwpBuildConfiguration = Environment(rawValue: uwpConfiguration) else {
                print("❌❌❌ No Build configuration Found: Set Debug as default ❌❌❌")
                return Environment.debug
        }
        print("⚙️⚙️⚙️    Build configuration: \(uwpBuildConfiguration)   ⚙️⚙️⚙️")
        return uwpBuildConfiguration
    }()
}

By the moment is only returning a description string containing the environment name.

But could contain, the back end base url for each environment:

 struct ConstantsBaseURL {
        static let debug = "dev-api.aws.xxx"
        static let staging = "stage-api.aws.xxx"
        static let production = "api.aws.xxx"
    }

    var baseURL: String {
        switch self {
        case .debug: return ConstantsBaseURL.debug
        case .staging: return ConstantsBaseURL.staging
        case .production: return ConstantsBaseURL.production
        }
    }

…  the log level ….

var appLogLevel: DDLogLevel {
        switch self {
        case .debug: return .debug
        case .staging, .production: return .off
        }
    }

… api key’s values, configuration filenames (e.g. Firebase, GTM),☝️whatever that will depend on environments will be handled by this component .

Update content view just to print the environment name.

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("\(AppEnvironment.shared.environment.toString)")
    }
}

Change build configuration to staging:

Finally run the project:

Last but not least, this component fully testable.

    func test_toString() {
        XCTAssertEqual(Environment.debug.toString,"dev")
        XCTAssertEqual(Environment.staging.toString,"staging")
        XCTAssertEqual(Environment.production.toString,"production")
    }

Bundle Id

It is very important that every build configuration will also have different bundle identifiers, so in that way will be posible to deploy independently apps from different environments in the same device.

Conclusion

In this post I have presented how to create another build configuration and how take control of it from source code. You can reach the repository containing the source code here.

Bitrise CI/CD for iOS (and mobile) developers

The aim of this post is just present Bitrise a CI/CD platform that will allow you to deliver (and deploy)  your ongoing app to other team members, mainly QAs and POs.

Through this post you will see how easy is to setup a xcode adhoc project and how to distribute it.

Problematic

For driving a mobile project to a good end, is necessary but not enough being a good mobile developer (iOS, Android, web-mobile…). After some years of bad and good projects I reached to the conclusion that is necessary also a good methodology (scrum), customers with clear ideas, all team members focused on their tasks and a good coutinuous integration and delivery system.

As a mobile developers, is hard to focus at the same time the app project and tune properly a CI/CD system.

Bitrise CI/CD for iOS developers is an alternative to classic platforms such as Jenkins, Travis,… that does not require a dedicate devops engineer to handle it.

What is Bitrise

This tool is a continuous integration platform oriented to mobile developers. Is very easy to setup, it took me a few hours ⏱️. It also offers a free tier that lets you start to play with it.

Hands on

I have uploaded in github a sample single view app xcode project.

☝️The unique thing to keep in mind up to now is just be shure that project schema is shared, other wise bitrise validation will fail 🤷.

XCode - Manage Schemes

XCode, set shared schema

Be sure that app is deployable on device with cable….

Configuration

☝️Before continuing, I have to advise you that is mandatory having Apple Developer account 💲for generating a developer certifiate .p12 and the provisioning profile for deploying on a iPhone device.

Firsrt of all, create your free account  and setup your first a new app. Fill the repository url and the branch. Bitrise will validate if all is ok with the project.

Bitrise, setup app project

Once validation is ok:

Bitrise, setup xcode adhoc distribution

For the purpose of this article we will just proceed with ad-hoc distribution.

Bitrise, xcode app adhoc distribution

Last but not least!!! 😂😂😂 Now is time to provide the certificate (.p12) and the adhoc .provisioning profile.

Bitrise CI/CD for iOS - Bitrise, set ios certificate and provisioning profile

Be sure that signing is properly configured in the project, uncheck ‘Automatic manage signing’ and import the generated provisioning profiles.

Xcode set provisioning profile

Getting back to work flow configuration, select deploy workflow and define $email variable with the email list of the persons that you want to distribute the app.

Bitrise CI/CD for iOS - Bitrise set email destination

… and save

Save bitrise workflow changes

With those simple steps we are able to launch the first workflow:

Bitrise CI/CD for iOS - Launch bitrise workflow

Once finish you will receive an email, with an invitation to deploy the app in the device. Look out! the first time  you will have to install the profile:

Bitrise CI/CD for iOS - Approve bitrise profile on device

And finally open the email and install the app.

Bitrise CI/CD for iOS - Bitrise email invitation to install appBitrise CI/CD for iOS - Deployoing app from bitrise Bitrise CI/CD for iOS - Deployed bitrise app running on device

Conclusions

The point in using Bitrise CI/CD for iOS (and mobile),  is that allows developers to focus on his main goal, all in all, reach PO and QA expectations. Ideal for very small t that want to carry their app to a god destination.

 

Referneces

Other

Other iOS development posts

 

 

Moving from MVP to MVVM (an iOS architecture part VI)

The aim of this post is just to explain how improve MVP approach in view layer. MVP is a great improving respect to MVC, clarifies presenter and unleads viewcontroller responsabilites. But at the time of testing is hard, but not impossible, to perform.

MVP approach

On MVP, presenter has the following is the connection between:

  • Views: provides models to be presented in de views and collect user interaction events from views.
  • Interactor: When some data has to be fecthed from Data Layer, or some task has to be performed in Domain Layer.
  • Coordinator: When presenter has finished to perform its work and has to notifiy it to coordinator.

👍Good point is that is clear presenter responsabilites, 👎down side is that this approach is not quite ‘single responsability’ (SOLID).

Moving MVP to MVVM is same goal as was moving from MVC to MVP. All in all is dividing in classes with more concrete responsabilities and, what is more important, easier to test.

MVVM approach

Even though presenter is still interacting with 3 componets, has been unleaded significantly. Most of its previous logic in MVP has been moved to View Model component.

View Model now will be responsible of dealing with interactor.

It has one (or many) inputs that are basically the UI events that presenter bypasses from views and a unique output that informs about view model state and optionally data models for being refreshed in the view. This is view model state:


enum TransactionsListViewModelState {
    case fetching
    case fetched([String])
}

The body of view model would be:


class TransactionsListViewModel {
    var onStateChanged: ((TransactionsListViewModelState) -> Void) = { _ in /* Default empty state */ }

    // MARK: - Private attributes
    var injectedTransactionsUseCase:TransactionsUseCaseProtocol

    init(transactionsUseCase:TransactionsUseCaseProtocol = TransactionsUseCase()) {
        self.injectedTransactionsUseCase = transactionsUseCase
    }
    
    func start() {
        onStateChanged(.fetching)
        injectedTransactionsUseCase.transactions(onComplete: { [weak self] transactionIds in
            guard let weakSelf = self else { return }
            weakSelf.onStateChanged(.fetched(transactionIds))

            })
    }
}

From the callback onStateChanged the view model will report presenter about changes in the view model.

In this example presenter request view model to fetch some data via start() method. The first thing that does start() is report to presenter .fetching, so presenter can show an activity indicator. Once data is retrieved from interactor is reported to presenter .fetched state and the data for being presented. So presenter, will remove activity indicator and will set data to its views.

Testing

In view model interactor constructor one of its optional parameters is the interactor that will be used. By default is initialized by the default with the properly one, but could be injected a mocked one or emulating errors.

On the other hand presenter bypasses UI events directly to view model so is possible emlulate user interactions in the test cases.

So view model coverage could reach 100% without problems, this is something that is not so easy in MVP approach.

This is an example of unit test:


func test_fetchTransactionIds() {
        let asyncExpectation = expectation(description: "\(#function)")

        let transactionsListViewModel = TransactionsListViewModel()
        let expectedSeq:[TransactionsListViewModelState] = [TransactionsListViewModelState.fetching,
                                                   TransactionsListViewModelState.fetched([])]
        var index = 0
        transactionsListViewModel.onStateChanged = { pollResultsViewModelState in
            guard index < expectedSeq.count else {
                XCTFail()
                asyncExpectation.fulfill()
                return
            }
            // Only check state, not the associated value (that's why .rawvalue)
            guard expectedSeq[index].rawValue == pollResultsViewModelState.rawValue else {
                XCTFail()
                asyncExpectation.fulfill()
                return
            }

            index += 1
            if index == expectedSeq.count {
                asyncExpectation.fulfill()
            }
        }
        transactionsListViewModel.start()
        self.waitForExpectations(timeout: self.timeout, handler: nil)
    }

Test basically validates that sequence of states received from the view model is the expected one and also the data contained on each state.

Conclusion

This implementation leaves the presenter, usually implemented as a view-controller, implemented with the minimum logic 🥗. It forwards ↪️ the events from UI to View Model, and ask to refresh its views depending on the state received by ViewModel. Once is done 🏁, gives the token to its coordinator who will responsible for presenting another presenter.

Related Post

First chapter of an iOS architecture

References

Here you can find a sample project using this pattern.

The data layer (an iOS architecture part V)

Old library

 

The aim of this post is just to explain how to organise the data layer in an architecture. A possible definition of data layer is where the application data is being cooked and served ready for consumption to upper layers.



Data layer is fully responsible of providing data to domain layer, upper layers does not know the origin of the data ( local configuarion, local ddbb, json data, rest api…).

⛔By no means any of the data structures/classes provided by data layer could be transformed by domain or view layer components.

👉Data layer is responsible of notifying any change in the data, in that way views presenting the data affected will be invalidated and forced to fetch the data again.

Components

Data Manager

This component is the outer facade of the data layer, all the domain components will interact always with this component.

👉Data manager is responsible of requesting data to the rest of data layer components and implement all the politics related with data acquisition. Examples of those politics could be:

  • The app configuration is fetched from a rest service, when is not possible to perform such operation, then tries to recover data from persisted data.
  • Persist data remotely via rest service, when not possible data is stored locally. Later on if it is possible, data can be synchronized with the backend.
  • Depending on the criticity of the data could decide whether store data in a persisting securized area or a regular one.
  • Reset all persisted data wether is database, userdefaults or keychain. 💕Very useful if you wanted to reset the app without having to reinstall the app, or just to initialize data layer before executing unit tests.

JSON Manager

This component is the responsible to modelize data persisted in a json file.

Configuration Manager

This component is the responsible of persisting all the app settings.

Security Configuration Manager

This component is the responsible of persisting all the app settings related with the security of the app (e.g. keys, api-keys, information that could identify app user,…). Usually this component is an adaptor of keychain

 

DB Manager

This component is the responsible of persisting all massive app data. This component could be an adapter of Realm, CoreData or SQLLite.

👉As well as Data Datamanager no other outer component has to know the database technology that wraps.

⛔By no means any internal database entity should have to be exposed to the other components. DB Manager has to convert them to generic ones before providing then to the rest of the components. Following that rule you will get benefits of replacing db technology without major impact in the app.Replace DB technology without any impact on the app.

API Manager

This component is the responsible of fetching or persisting data in the backend via REST api’s. This component is usually an adaptor of URLSession or any other third party library such as Alamofire.

Conclusion

In this post we have reviewed which is the role of Data layer and its components. This post closes all the ones dedicated to this iOS Architecture.

The Domain layer (An iOS architecture part IV)

The aim of this post is just to explain how to organise the domain layer in an architecture. A possible definition of domain layer is where the application business logic is implemented, or the told in the contrary way what is nor view neither data acquisition.

The domain layer is the fuzzy invisible border between View and Data layer. But as app grows in complexity (and in lines of code) is the cornerstone of your development, so this is why is important to have this layer under control.

Components

Sequencer

The sequencer is the component that will handle the start up sequence, this is something thats starts very simple at the beginning of the implementation, but as time (and requirements) goes on could turn into a really nightmare 🎃. You can find the implementation details in the following post.

Managers

Managers is the wrapper class for accessing to a unique device services like bluetooth, maps, gyroscope… Or in case that you wanted  to centralize a behaviour in a single class, for instance, a Session Manager for controlling wether a user was logged properly or not. This class is a singleton implementation.

Up to here this is good about this component. But watch out, to start abusing them! Is quite common that will start appear singletons as mushrooms. I recommend to read the following post.

Use Cases

Use cases functions are the entry point for any functionality requested by views.  The reason for living of this component lies in:

  • Unload task from views. Helps to reduce Massive View Controllers problem.
  • Reusability. Is likely that you use the same functionality from different views.
  • TDD. You can apply unit test to this function class.

Coordinators

Coordinators controls the flow of screens presented, is the coordinator who decides which is the next screen to present, not the view any longer…

This will help to reduce the amount of code to put in the view, and also its reusability, the same view can be placed in a totally different screen flow.

Use cases and coordinators working together but not mixed

Presenters interacts with use cases when some functionality has to be performed (e.g request data from a service, try to connect to a bluetooth device, get a map pinpoints,…). But in case that presenter considers that is no more longer usable (e.g. user select dismiss button to remove the view, or selected an item for pushing another view with the item details) it has to interact with the coordinator and will be the coordinator who will decide  which is the next view to present.

Conclusion

In this post we have reviewed which is the role of Domain layer and its components.

What’s next

In the next post will be presented the Data Layer and how to retrieve data independently where is phicially stored (ddbb, service, keychain, user defaults… ).

     

Reusability in view layer (An iOS architecture part III)

The aim of this post is to reinforce the reusability in view layer, it will be presented a way to handle the fonts (and its sizes), colours, texts translations and accessibility labels (for automated UI tests).

This post is the continuation of  the view layer (an iOS architecture part ii) and part of it is based on the following presentation but supported with a sample project in case you want further investigation or adopt the technics explained in your projects.

The view layer (An iOS architecture part II)

Fonts (and its sizes)

For better folder organization we have placed the font files into ViewLayer/Resources/Fonts:
☝️Do not forget add the resource to project target:

Also do not forget to add the fonts also in the Info.plist:

Edit the Info.plist and add the following chunk of xml:

	<key>UIAppFonts</key>
	<array>
		<string>GothamRounded-Bold.otf</string>
		<string>GothamRounded-BoldItalic.otf</string>
		<string>GothamRoundedLight_21020.ttf</string>
		<string>GothamRoundedMedium_21022.ttf</string>
		<string>GothamRounded-Book.otf</string>
		<string>GothamRounded-BookItalic.otf</string>
		<string>GothamRounded-Light.otf</string>
		<string>GothamRounded-LightItalic.otf</string>
		<string>GothamRounded-Medium.otf</string>
		<string>GothamRounded-MediumItalic.otf</string>
		<string>GothamRoundedBold_21016.ttf</string>
		<string>GothamRoundedBook_21018.ttf</string>
	</array>

The helper for the font sizes is placed at ViewLayer/Resources/Styles:

For each presenter we have defined an structure that gathers the fonts that is using, this approach lets you have centralised in a unique file the font definitions for WHOLE app:

struct ArchTempFonts {
    
    struct MostPopular {
        static let PlaceCountryFont   = TextStyle.h20Book.font
        static let BoteUpButtonFont   = TextStyle.h36Book.font
    }
    
    struct LatestPlace {
        static let CountryFont  = TextStyle.h36Book.font
        static let PlaceFont =  TextStyle.h20Book.font
    }
}

I let the reader to download the sample project and review the rest of structures contained in the file for further understanding.

Finally for setting the file in the outlet is as simple as:

lblName.font = ArchTempFonts.LatestPlace.PlaceFont

And the result is the following:

Colors

For colors we will use the same approach as in fonts, define a helper in Styles folder:

As well as in fonts  we will define the palette of colours used as well as the palette-dolors used by each presenter:

struct ArchTempColors {
    
    private struct Palette {
        static let Grey = UIColor(rgbaValue: 0x888888FF)
        static let Brown = UIColor(rgbaValue: 0xC48764ff)
        static let Crimsom = UIColor(rgbaValue: 0xce2124ff)
        static let Water = UIColor(rgbaValue: 0x5085DEff)
    }
    
    struct MostPopular {
        static let PlaceCountryFontColor   = ArchTempColors.Palette.Grey
        static let BoteUpButtonFontColor   = ArchTempColors.Palette.Brown
    }
    
    struct LatestPlace {
        static let CountryFontColor  = ArchTempColors.Palette.Crimsom
        static let PlaceFontColor    = ArchTempColors.Palette.Water
    }
}

Finally for setting the color in the @IBOutlet:

lblName.textColor = ArchTempColors.LatestPlace.PlaceFontColor

And the result is the following:

Texts

We have just created a string extension for automatically handle the translation depending on the project language bundle:

extension String {
    var localized: String {
        
        if let _bundle = self.getCurrentBundle() {
            return NSLocalizedString(self,
                                     tableName: nil,
                                     bundle: _bundle,
                                     value: "",
                                     comment: "")
        } else {
            return ""
        }
    }

For avoiding the problem of text identifiers during execution time, we use R.Swift so during compilation time we will know if resource really exists or not. This is also very useful for images, cell reusability, segue identifiers… In that way you will avoid bugs related with identifiers that do not match with the resource. 🚑 Really, do not hesitate in incorporate this pod in your projects!!!!

Finally for set the texts is as easy as:

self.tabBar.items?[0].title = R.string.localizable.home.key.localized//"_Home"
self.tabBar.items?[1].title = R.string.localizable.mostPopular.key.localized//"_Most Popular"

Even that constants has 6 elements in the keypath, 5 of them are always constant: R.string.localizable.<textId>.key.localized

Accessibility labels

iOS developer will not get direct benefit in setting accessibility labels to @IBOutlets, but when there  is a test team for handling UI tests we have to facilitate their job. In return we will have quick and precise bug reporting. All in all this will save you time in middle-long term.

As well as in fonts and colors, we will create a new module that will concentrate all accessibility constants:

With the following structure:

struct Accessibility {
    
    struct MostPopular {
        static let PlaceCountry   = "MostPopular.PlaceCountry"
        static let VoteUpButton   = "MostPopular.VoteUpButton"
    }
    
    struct LatestPlace {
        static let Country  = "LatestPlace.Country"
        static let Place =  "LatestPlace.Place"
    }
}

Finally for applying accessibility to @IBOutlet:

lblCountry.isAccessibilityElement = true
lblCountry.accessibilityIdentifier = Accessibility.LatestPlace.Country

Conclusion

With this and previous post we consider the view layer of the architecture already presented. If you have any doubt, and you can download the source sample project from here.

What’s next

In the next post will be presented the Domain Layer and how View Layer interacts with it using UseCases (Interactors in Viper) and Coordinators.

References:

 

     

The view layer (An iOS architecture part II)

This is the continuation of the following post, the aim of the view layer (an iOs Architecture part II), is just to explain in detail how view layer will be built.

The view layer

When you were working on development tasks related purely related with view just shift your mind to the following schema:

The schema is almost MVP, almost because in some concrete cases does not fit (we will see later on). But the main components are views and presenters, and its role is practically the same

Views

Views are the classes responsible of drawing the information in the screen of device. The input of this class is the model to printout and the output the events that view can not handle.

The events can be handled via:

  • Delegate protocol.
  • Callbacks.

In the implementation project I have used callbacks.

For instance if a view hast to print out a list of persons, as soon as view receives an array of persons will print out the list. When user taps on one of the persons, view catches the event, but does not know what to do with it, so bypasses the event to its parent presenter.

Composite views 🙄. Is quite common that a view will be complex enough to be splited in subviews. In the previous example, figure it out that now we want to print out also a header and a footer on the persons lists. The view will have access to its subviews, and as soon as receives the model from the presenter it will bypass also the models to its subviews. When one of its subviews will receives a user action, depending on the situation, will handle it or will bypass it to its parent presenter.

Heterogeneous Composite Views😣. I will start directly with the example, figure it out the home view of a very famous application like Spotify, LinkedIn, Facebook… In the same screen is presented several sections each one presenting information not related with the other sections.

Could your tell the number of lines for this presenter? Who would be the super-hero in case that some change would be required in the home screen? This is a clear smelt that MVP is not enough for such implementation🤔.

In case of such kind of complex views or in case that we want to reuse the same section views in two (or more) different screens the pattern that we use is MVPP (or MVP+). The idea is as simple as a view will be the container of another presenter (with its own views).

Every (sub)presenter will fetch data from business and will pass it to its views, this will help a lot in reducing the size of the main (super)presenter. Another great advantage of this pattern is that allows to reuse the presenter-view in some other screen.

 

Presenters

Presenter is the abstract representation of a screen app, it has direct access to its views. Its role is quite clear fetch/pass data from/to Business (Domain and Data) layer on one hand and in the other transfer information between views and handle the events reported by views.

For a presenter its views are black boxes, at no moment the presenter knows if what is being drawn is a tableview, a collection view, a pageviewcontroller…. It transfer Models  to view, and wait for the events that view can not handle.

As well as in views, there are some events that even a presenter can not handle, for instance if user presses a button for dismissing the app screen view. In such case the presenter will bypass the event to its coordinator, … but this will be explained in Domain layer (part III).

Implementation

The Presenter will be implemented as a subclass of a UIViewController, whilst for View we have two alternatives UIView or UIViewController. I prefer to use UIViewController, mostly because is easier to control the lifecycle of the view. But If you do not have to, there’s no problem in using UIView.

The project app just present two screens, and heterogeneous composite view and a simple view.

Home screen is a presenter of presenters and the second one is just a presenter. As you can see popular presenter has been reused in the home, this is why is so powerful this model presenter plus protocol.

Simple presenter (Popular).

The presenter is subclassed from UIViewController this allows us to represent it in the storyboard. The view could be subclassed from:

  • UIView. UIViews is the class implementation for handling views, so why not…
  • UIViewController. With UIViewController we have the extra that we can handle the view lifecycle much better than with a UIView.

In the story board the view is embedded in the presenter with a container view:

This is the implementation for the presenter:

class MostPopularP: UIViewController {
    
    // MARK: - Private attribues
    var mostPopularV: MostPopularV?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
       
       self.setupPresenter()
        self.fetchMostPopular()
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    
        if (segue.identifier ==  R.segue.mostPopularP.mostPopularV.identifier) {
            mostPopularV = segue.destination as? MostPopularV
            mostPopularV?.onVoteUp = { _ in
                PlacesUseCase.shared.voteUpMostPopular()
            }
            
        }
    }
    
    // MARK: - Private/Internal   
    func fetchMostPopular() {
        guard let _mostPopularV = self.mostPopularV else {
            return
        }
        _mostPopularV.place  =  PlacesUseCase.shared.mostPopular()
        
    }
    
    func setupPresenter() {
        self.tabBarItem = UITabBarItem(title: "_Popular",
                                       image: nil ,
                                       selectedImage:  nil)
    }
    
}

In the prepare segue is where is handled the event that is coming from the view, and is bypassed to UseCase (… but this is domain part II).

🚫View could also call the UseCase and the compiler did not complain, but this is totally forbidden in this architecture🚫. Remember 3-tier layer philosophy.

And the view simply:

class MostPopularV: UIViewController {
    
    // MARK: - IBOutlet
    @IBOutlet weak var imgPlace: UIImageView!
    @IBOutlet weak var lblName: UILabel!
    @IBOutlet weak var btnVoteUp: UIButton!
    
    // MARK: - Callbacks
    var onVoteUp:((Void)->Void) = { _ in }
    
    // MARK: - Public attributes
    var place:Place?{
        didSet {
            self.refreshView()
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.setupView()
    }
    
    // MARK : - Target methods
    func btnVoteUpAction(sender: UIButton!) {
        self.onVoteUp()
    }
     
    // MARK: - Private/Internal
    func setupView() {
        btnVoteUp.setTitle("_Vote Up!",for: .normal)
        btnVoteUp.addTarget(self, action: #selector(btnVoteUpAction), for: .touchUpInside)
    }
    
    func refreshView() {
        guard let _place = self.place else { return }
        
        lblName.text = _place.name + " - " + _place.country
        
        let spinnerActivity = MBProgressHUD.showAdded(to: imgPlace, animated: true)
        spinnerActivity.removeFromSuperViewOnHide = true
        spinnerActivity.bezelView.color = UIColor.clear
        spinnerActivity.bezelView.style = .solidColor
        _place.getImage { [weak self] (image, _ ) in
            guard let weakSelf = self else { return }
            DispatchQueue.main.async {
                spinnerActivity.hide(animated: true)
                weakSelf.imgPlace.clipsToBounds = true
                weakSelf.imgPlace.contentMode = .scaleAspectFill
                weakSelf.imgPlace.image = image
            }
        }
    }
    
}

Print out the model and send back the button event.

Composite presenter (Home).

The Home presenter is a presenter of presenters, the unique case that has to do the superpersenter is just show/hide the sub presenters. But in the case of the implementation both are presented. The storyboard shows pretty clear the super-presenter (at the top), its container view that will hold the subcontainers for placing the presenter for the latest popular place and the most popular place.

The home presenter implementation:

class HomeP: UIViewController {
    
    // MARK: - Private attribues
    var homeV: HomeV?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.setupPresenter()
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        
        if (segue.identifier ==  R.segue.homeP.homeV.identifier) {
            homeV = segue.destination as? HomeV
        }
    }
    
    // MARK: - Private/Internal
    func setupPresenter() {
        self.tabBarItem = UITabBarItem(title: "_Home",
                                       image: nil ,
                                       selectedImage:  nil)
    }
   
}

And its view container:

class HomeV: UIViewController {
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == R.segue.homeV.mostPopularP.identifier {
            
            _ = segue.destination as? MostPopularP
            
        } else if segue.identifier == R.segue.homeV.latestPlaceP.identifier {
            
            _ = segue.destination as? LatestPlaceP
        }
    }
}

Conclusion

In this post I have presented how to structure view layer using presenters and views. Finally the idea has been supported using a example project, the source code can be found here.

What’s next…

View layer is not finished yet, in the next post I will present how to handle font types (and its sizes), colours, accessibility labels (for automated testing) and localised texts (for translations).

     

An iOS app architecture (Part I)

An iOS app architecture

The aim of this post (and the followed ones) is to expose an iOS app architecture that I have been using for developing iOS apps during last two years. In the end is not strictly one of the known ones: VIPER, MVVM, MVP…, but obviously with many commons concepts.

What is an architecture?

From wikipedia: Software architecture refers to the high level structures of a software system, the discipline of creating such structures, and the documentation of these structures. These structures are needed to reason about the software system. Each structure comprises software elements, relations among them, and properties of both elements and relations.[1] The architecture of a software system is a metaphor, analogous to the architecture of a building.[2]

In practice a software architecture allow to absorb the product owner requirements without increasing your app source code complexity. If does not, start to refactor before is too late.

Outlining the architecture

As a professional painter we have to outline the available canvas and decide which functionaly will be implemented in each part.

Once done, now is crystal-clear which part will be occupied by fingers, palm and wrist.

Moving to software engineering, I am talking about 3-tier architecture model, this model has been used for in other technologies (e.g web, client-server). It splits the app in 3 layers (View, Domain, Data), its philosophy is very clear:

  • Each layer has a very concrete purpose
  • Each layer has a defined interface for requesting its implemented services.
  • A layer can only interact with its adjacent one.

Rule number I. Visualize mentally that each component (and its subcomponents) from your architecture as an apartment in a building. A component can only interact with its immediate upper and lower components floor. If you want to go from 4th floor to 2nd one, you have go through 3rd.

For instance if we want to present ddbb query result in a view:

🚫 🙅 Forbidden. Access directly from a view to a database handler to make a request. Comming in to iOS context, figure out that you get the realm ddbb handler and implements the query from the ViewController that will present the results.

✅The approach. Define one interface method in the Data and Domains layer that returns the requested data (e.g. in form of array) to the view, so View layer unique responsibility is draw the data returned by domain layer.

With this simplistic example, Domain layer does nothing apart from calling Data layer, you would think that this layer is redundant, but truly believe me it is not. If some data processing has to be done, Domain layer is the place.

From this rule you will get the following benefits:

  • Use TDD for validating Domain and Data layers with unit tests
  • Reduce the number of code-lines in the view (Massive ViewControllers problem).

Rule II. No-shared responsibility. Every component has a concrete responsibility and no other component can perform it, but the designated one.

For instance in our app we have different screens and each one is presented information retrieved from a rest service.

🚫 🙅 Forbidden. Implement in every view the service call for retrieve the data to present.

✅The approach. Place all the service calls in a component, as this component is retrieving data, it will be placed in the Data layer. So the view will request to domain the rest data, and domain will request to data layer, finally the data will be returned to view via domain.

From this rule you will get the following benefits:

  • Use TDD for validating a component with a concrete responsibility.
  • In case that some component fails (e.g rest api’s), we have to review a file, not bunch of scattered code in all project.
  • Reduce the number of code-lines in the view (Massive ViewControllers problem).

The architecture

As you can see in the architecture block schema, there are from 5 up 7 seven levels, so is essential that each component only interacts with its upper and lower components.

iOS app architecture

The exception that confirms the rule

There is one case that Rule II will be overridden, and is the case when two (or more view are presenting some information) and this information is updated/deleted from Data Layer. In this case the presenters get subscribed to data events broadcasted by Data Manager, and when presenter gets the notification then fetchs for the latest data to present.

iOS app architecture

Implementation

iOS app architecture

Basically the implementation is an almost empty project, with the basic folder organisation for holding different layers ant its components that matches with architecture presented:

We have added a very basic app start up sequence, is very important to keep this part of code under a tight control, leave only necessary code in AppDelegate.swift:

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        InitAppSequencer.shared.start()

        return true
    }
}

InitAppSequencer is the responsible for the start up sequence, this class dispatches the different operations needed during start up app sequence.

import Foundation

class  InitAppSequence {

    static let shared =  InitAppSequence()
    fileprivate let operationQueue = OperationQueue()

    init() {}

    func start() {
        self.regularInitialSequence()
    }

    func regularInitialSequence() {

        let presentMainAppOperation = PresentMainAppOperation()
        let operations = [presentMainAppOperation]
        operationQueue.addOperations(operations, waitUntilFinished: false)
    }
}

Up to this moment the unique task (operation) that app must do is just present the main screen.

import Foundation
import UIKit

class PresentMainAppOperation: ConcurrentOperation {

    override init() {
        super.init()
    }

    override func main() {
        DispatchQueue.main.async {
            let mainMVP = MainMVP.instantiate(fromAppStoryboard: .main)
            let appDelegate  = UIApplication.shared.delegate as! AppDelegate
            appDelegate.window!.rootViewController = mainMVP
            self.state = .finished
        }
    }
}

If you want to know more about sequencers you can find in my pot post Design iOS app start up sequence (with Operations)

Pods

The pods that highly recommend and will improve dramatically the quality of your code are:

  • R.Swfit. Get strong typed, autocompleted resources like images, fonts and segues in Swift projects
  • Swiftlint. A tool to enforce Swift style and conventions

You can download the code used in this post from here.

Conclusion

In this post has been presented the layered structured of this architecture and the rules that governs the communication between each component.

What’s Next

In the next posts will be presented in detail: View, Domain and Data layer.

References

 

     

Design iOS app start up sequence (with Operations)

The aim of this post is just to propose a design pattern for implementing the start up sequence for an iOS App. When Coordinator pattern is not enough for your app needs, then  is time to move to a upper level of abstraction.

The reality

Lets pretend that we want to develop an iOS that allows us to compose music and  connect to a bluetooth speaker to play our creations. At the beginning the start up sequence coordinator just presents the main screen. But as the sprints are passing by, the product owner ask to implement the following requirements during the start up sequence:

  • Autoconnect to default speaker. During start up process, the app  tries to autoconnect to last connected speaker. When the app has never been connected to one, is presented a coordinator with an assistant for scanning and connect to any surrounding bluetooth speaker.
  • Force update. The app checks from a service whether can continue or require a user to update the app.
  • Tutorial. When the app has a new release or is the first time app execution is presented the tutorial slider.
  • Country/Language detection. In the first time app execution is set the language according device automatically, if language is not available then is presented to user a list with the languages supported by the app.
  • Push notifications. When the user presses on the push notification alert message, the app is started up presenting a view showing more details about the notification. On dismissing the view, appears the main app view.
  • Present a customized splash screen with an animation.
  • AutoLogin/Login/Create account. When user was previously logged in, the app request a rest service for a token_id that will be used in further rest api’s calls. Otherwise is presented an assistant for entering credentials or create a new account.
  • …Last but not least, start up sequence can not take much longer (no more than 3  secs.), when no user interaction is required.

If an sprint takes 2 weeks, Could you figure out how messy was the start up coordinator passed four months? … yes it was 😨

Why a coordinator is not enough (for this case)

A Coordinator, as well known as Router in VIPER,  is a great pattern for removing presenter the knowledge about which is the next presenter to show (Great… less lines in the ViewController 🎉). The presenters logic flow now is concentrated in a single class and second presenters can be reused by other flows. There are two articles that talks about coordinators, I highly recommend them (Coordinators Redux and An iOS Coordinator pattern)

But times goes by and oncoming features are integrated in the app start up sequence, the number of lines will start to grow and code become harder to understand. Not using coordinator pattern would have been hundred times worst 🤡.

This pattern does not fit in our start up sequence, basically because we were forcing to put in the same flow tasks that could be splited in different flows and in some cases can be launched in parallel. Is clear that I need a another point of view.

Design iOS app start up sequence

From now on, for avoiding naming mistakes we will call Sequencer to our brand new supercoordinator.

REQUIREMENTS

First of all,  we have to split the start up sequence in independendent isolated operations. The start up sequence does not have to be unique, depending on the context we have several dependency graphs.

TASKS DEPENDENCY SCHEMA

First time execution

s1

This is the worst case scenario because all operations must be serialised, the unique operations that can be parallelised are Present Splash (PS) and Force Update (FU). Fetch Configuration depends on PS because in case that did not exist country configuration for country device is presented a UI menu, so it is mandatory to wait until PS finishes for using the UI.

The rest of operations, login (LOG), tutorial (TUT), connect first speaker (CFS) must be serialised because requires user input.

Forze update (FU) has no dependency, because when update is required it will abort all the operation sequence.

Regular start up sequence

In this scenario: the country, the auto login information and also there is a known speaker to connect is known by the app, so all those operations can be parallelised.

s2

This is the best case scenario up to 5 operations can be launched in parallel.

Push notification start up sequence

s0
The unique difference between this an previous scenario is that with that using that pattern you can replace easily replace Tutorial (TUT) operation by Present push information (PPI) operation.

OPERATIONS LAUNCHER

Operations (NSOperations previously also known) is a mechanism for parallelizing tasks on iOS. Is very useful when the app has to do a lot of massive and repetitive taks, like for example fetch images from a server and present them on a collection view. This really removes the dust from device cpu cores 🚀.

Another great advantage that provide us Operations is that is direct to define depenendencies between tasks, so tasks will be executed in the order defined by the dependency graph.

An operation can be cancelled so in case that Force update requires to update the app, it can cancel the rest of operations and take control of UI.

🖐️ I highly recommend to view Advanced NSOpetations (WWDC 2015)

IMPLEMENTATION

This is the implementation of first time execution:

class  StartSequencer {

    static let shared =  StartUpAppCoordinator()

    fileprivate let operationQueue = OperationQueue()

    private init() {} //This prevents others from using the default '()' initializer for this class.

    func start() {
        
        let presentSplashOperation = PresentSplashOperation()
        let forceUpdateOperation = ForceUpdateOperation()
        let fetchAppConfiguration = FetchAppConfiguration()
        let loginOperation = loginOperation()
        let fetchFirstSpeakerOperation = FetchFirstSpeakerOperation()
        let presentTutorialOperation = PresentTutorialOperation()
        let presentMainAppOperation = PresentMainAppOperation()
        
        let operations = [presentSplashOperation,
                          forceUpdateOperation,
                          fetchAppConfiguration,
                          loginOperation,
                          presentTutorialOperation,
                          fetchFirstMachineOperation,
                          presentMainAppOperation]
        
        // Add operation dependencies
        fetchAppConfiguration.addDepenendy(presentSplashOperation)
        loginOperation.addDependency(fetchAppConfiguration)
        fetchFirstSpeakerOperation.addDependency(loginOperation)
        presentTutorialOperation.addDependency(fetchFirstSpeakerOperation)
        presentMainAppOperation.addDependency(presentTutorialOperation)
        
        operationQueue.addOperations(operations, waitUntilFinished: false)
    }
    
    

}

This is incomplete, because the point is to dynamically build the task dependency depending on the scenario that app starts up.

An operation would be something like this:

import Foundation
import UIKit

class PresentTutorialOperation: ConcurrentOperation {

    override init() {
        super.init()
    }

    override func main() {
        
        if ConfigurationManager.shared.isFirstTimeInLifeAppExecution() ||
            ConfigurationManager.shared.isFirstTimeAfterSoftwareUpdateExecution() {
            
            ConfigurationManager.shared.setLastExecutedToBundleVersion()
            DispatchQueue.main.async {
                TutorialCoordinator.shared.start(completion: { [weak self] _ in
                    guard let weakSelf = self else { return }
                    weakSelf.state = .Finished
                })
            }
        } else {
            self.state = .Finished
        }
    }
}

For understanding concurrent operation, please take a look at Ray Wenderlich video AsyncOperations

TESTING

For validating the sequencer is as easy as creating a unit test that validates the dependencies between operations in the deferents scenarios that app can start up.

And for validating an operation, just add completion block to the operation. The test will launch the operation, and the unit test will be executed in the completion block. In case of tutorial the unit test will validate is (or not) the topViewController, and DataManager has knowledge that has been presented and does not have to be presented next time (unless the app will be updated to upper version)

Conclusion

We have tried to demonstrate the approach presented is more accurate than Coordinator pattern when we want to sequence (and paralelize) something as heterogenous as a start up sequence.

References