<![CDATA[Stories by Juantri Jiménez on Medium]]> https://medium.com/@Juantri?source=rss-cd48b029b443------2 https://cdn-images-1.medium.com/fit/c/150/150/2*e485R4HBu-nIwl-UUgrghA.jpeg Stories by Juantri Jiménez on Medium https://medium.com/@Juantri?source=rss-cd48b029b443------2 Medium Wed, 30 Oct 2024 04:11:53 GMT <![CDATA[One or multiple packages?]]> https://tech.new-work.se/one-or-multiple-packages-ce24a59c653b?source=rss-cd48b029b443------2 https://medium.com/p/ce24a59c653b Wed, 08 Nov 2023 10:27:50 GMT 2023-11-08T10:27:50.011Z Well…it depends
Photo by Kelly Sikkema on Unsplash

Swift Package Manager is not just a dependency manager, it is also a tool that allows you to organize your project into modules and create a package structure with which organize your frameworks and dependencies.

If CocoaPods has always been your dependency manager, you should know that all modules and third party frameworks are listed in the Podfile, so any framework can access to any module or third-party framework by the podspec. But, with SPM, we can control this access and create a package structure that “defines” access rules to the shared code.

Normally, a modular project has different kinds of modules depending on their purpose, and we might organize them into the following categories:

  • Dependencies (third-party frameworks)
  • Feature frameworks
  • Services frameworks (APIClient, tracker, formatters, etc)
  • Testing frameworks
  • UI frameworks

Depending on the project you can organize it in a multiple ways, but in this article I will explain the following one:

  • Testing frameworks should just depend on the system tools and maybe in some third-party frameworks (like a snapshot test framework), but they never can depend on features, services or UI code.
  • UI frameworks should depend on our testing frameworks and maybe some third-party frameworks (like animation frameworks), but they never can depend on our core code.
  • Services frameworks can need third-party frameworks but we should try to inject them instead of adding them as dependencies. These frameworks should depend on our testing frameworks, but they can never depend on the feature code.
  • Feature frameworks will need all our frameworks and they can also need some third-party frameworks, but they should always be injected.
  • Third-party frameworks will be added directly to the main target, where we can configure and inject them into the needed frameworks.
Packages graph

Third party frameworks

Let’s create an empty package file for all the third-party frameworks.

First of all is add all the third-party frameworks in the dependencies property. Now we need a target where link all the dependencies so we create one with an empty file to make Xcode compile and resolve the dependencies. Once we have the target, we can create the product and link it into the main target.

We have created a wrapper where all the third-party dependencies will be located. This way, we can control everything: dependency version, whether the dependency should be compiled (maybe depending on the environment), etc.

Dependencies package

But there are also other ways to solve this:

  • You can add the third-party frameworks directly into the main target without a package file.
  • You can create a package file and one product and target per dependency, but the dependencies list of the main target can be huge.

Once the project has access to the dependencies, we can configure and inject them into the frameworks that are needed.

Internal modules

Now it’s time for our frameworks. We will create four packages as we mentioned before: Features, Services, Testing, and UI. Each of them will have a product that contains all the targets in the file, so we add just one product into the dependencies of the main target.

Folder structure and project linking

The main idea is that any feature framework should not be a dependency of any of the rest of our frameworks. So, if we connect the packages as we defined in the packages graph image, we ensure that nothing depends on the feature frameworks but the main target and avoid cyclic dependencies.

Packages definitions

Package modifier

In a package, we can define as many products and targets as we want, but the relation between them is what we should control to avoid unnecessary dependencies.

Swift 5.9 gives us a new access modifier called “package”. It allows access to the code to just the targets defined in the package file.

Conclusion

Organizing the code into modules and these into different packages with a dependency order established by you is a good practice because you can control the dependencies of your modules, favoring DI, testing, and scalability.


One or multiple packages? was originally published in New Work Development on Medium, where people are continuing the conversation by highlighting and responding to this story.

]]>
<![CDATA[Measuring Testing Times]]> https://tech.new-work.se/measuring-testing-times-dbc38d8e479b?source=rss-cd48b029b443------2 https://medium.com/p/dbc38d8e479b Mon, 13 Feb 2023 08:12:35 GMT 2023-02-13T08:12:35.923Z Testing improvements
Photo by testalizeme on Unsplash

What Is A Slow Test?

There is no simple way to define a slow test, since it depends on the type of test, the complexity of the project, what are you testing, and many factors. But you can define time ranges to work on and know if a test is slow compared to the whole (or total time).

There are multiple types of tests but in mobile, there are 3 main ones: unit tests, integration tests, and snapshots tests.

Unit tests

Unit tests are typically automated tests written and run by software developers to ensure that a section of an application (known as the “unit”) meets its design and behaves as intended. https://en.wikipedia.org/wiki/Unit_testing

These tests are the fastest because they only check the logic of a part of the code, dependencies are mocked, they don’t need UI, etc.

If we have a small project in which the tests last half a second each, we might not give it relevant, but when the number of tests increases because the project is larger and more complex, this time value is not scalable, so we could say that a unit test that lasts more than half a second is slow.

I do not mean that half a second is an optimal time for a unit test, on the contrary, it is still a long time, but it serves as a concept to define the slowness of a test.

Integration tests

Integration testing is the phase in software testing in which individual software modules are combined and tested as a group. https://en.wikipedia.org/wiki/Integration_testing

These are the longest time consuming tests as they verify if the flow of the app works correctly. This flow can be of variable duration since it will depend on what you want to test, so it is not possible to define an optimal time range.

Snapshot tests

These are similar to unit tests but instead of checking the logic, they verify if the visuals are matching the reference images of that view. These tests are also fast as the unit tests as it is a unit test that loads a view. In this case, we could also define a time range for the execution of these tests.

How to identify slow tests?

In this article, we are going to analyse the unit and snapshot test times we have in the XING app in the iOS project.

To know how to make such an analysis, let’s use the Pareto rule: approximately 80% of the consequences come from 20% of the causes.

We are going to apply this rule to the results obtained in the xcresult file when launching the tests of our project. This file shows us a lot of information, but we are going to keep the information of the targets launched in the tests, the test classes of each target, the tests of each class, and how long each test has taken.

With this information of the times and ordering it from those that take the longest to those that take the least, we can make a graph with the Pareto rule:

Unit tests time graph
Snapshot tests time graph

In both graphs, we can see that the tests that take the longest, represent more than 50% of the total time spent running all the tests even if they are a minimum number of tests. Thanks to this graph we have been able to identify which are the slowest and now we can focus on improving the times of these tests and thus spend less time launching them locally or in the CI.

Analysing the snapshot tests a little more in detail, we have seen that the total duration of the execution of the tests is approximately 1 minute, and looking at the sum of the tests that last the longest we see that they are 33 seconds, more than half the time, so if we improve those 6 tests, we could reduce by half the execution times of the capture tests.

As explained above, the duration of integration tests depends on what is being tested. But if we find tests that last much longer than the average of the others, we could analyse if smaller tests can be done to test in parts of this large flow.

Conclusion

The ideal test execution time representation would be a diagonal line, which would mean that all tests take the same time to run and there is no time accumulated by slow tests. Since the ideal never exists, it would be best to get a line as close to that linear behaviour as possible.


Measuring Testing Times was originally published in New Work Development on Medium, where people are continuing the conversation by highlighting and responding to this story.

]]>
<![CDATA[DI in a Modularized Project in Swift]]> https://tech.new-work.se/di-in-a-modularized-project-in-swift-339fc242b4df?source=rss-cd48b029b443------2 https://medium.com/p/339fc242b4df Fri, 16 Sep 2022 14:22:47 GMT 2022-09-16T14:22:47.775Z +10 years project, Objective-C + Swift, UIKit + SwiftUI…
Photo by jesse orrico on Unsplash

More than two years ago the new version of the Xing app was released to a small percentage of users and every month the percentage was increasing until a few months ago when the app was 100% rolled out. Now is the time to clean old code, refactor, improve compiling times, improve the dependency injection, etc.

More than 30 developers spread across approximately 20 teams are constantly working on our project. Teams are divided into Platform team, Feature teams and Design team:

  • Platform team is in charge of the common parts of the project, architectural decisions, managing the crashes of the app, etc.
  • Feature teams create the features of the app and maintain them.
  • Design team creates the UI components of the app.

Each feature team has as many frameworks to develop the feature as it needs, so counting the common frameworks, the feature ones and the external dependencies, our project contains more than 100 dependencies.

Because of that, the dependency graph of the project is huge and the compiling time for a clean build is extremely high, so we decided that after this big rollout it was time to clean the project.

What do we want to achieve?

We have 3 main objectives:

  • Improve compilation time. A clean and build takes over 8 min.
  • Improve dependency injection. With over 100 development frameworks, there are multiple dependencies between them. The average of dependencies that a framework has is 24.
  • Once we have a clear dependency graph and a better DI we want to move to Swift Package Manager instead of CocoaPods

Steps to achieve it

1. Identify common frameworks

The first step was to identify the common frameworks (the ones that have common logic, components, etc) that should be cleaned or refactored. Once we have these frameworks, we have to organize code (avoid helpers frameworks), check if code belongs to this framework or should be moved to another one or a new one, check if some code should be converted to Swift…

We have some internal tools to generate the graph dependency of the project using the .podspec.json format that CocoaPods has, which allows us to see those relations in Graphviz format, so our first idea was to only count the number of frameworks we have in every graph version. We listed the number of dependencies we have in each framework and we obtained something like this:

2 — Coordinators
2 — FeatureA1
2 — FeatureB2
2 — Keys
12 — Internal Data
18 — Internal manager
22 — Tracking
30 — CommonFrameworkA
35 — FeatureD
41 — FeatureE
48 — FeatureF

We realized that some common frameworks that contain a huge number of dependencies were also a dependency of some feature frameworks, so the feature ones were importing also the dependencies of the common frameworks. Because of that, we decided to focus on these common frameworks and try to refactor them.

2. Improve the DI

Normally, the first step when the app starts is initialising the third-party frameworks with their tokens and properties, and then using them in the project. When the project is big and you have many feature frameworks, things start to become more complex because maybe you need to access any feature from any other framework so you have to inject the third-party frameworks into them and you need a way to communicate your feature frameworks.

To solve this issue and be able to access any framework from them, we use a framework called Bridge.

Framework Bridge is a singleton with a weak reference of each feature framework of our project and it works with protocols. So it does not depend on any framework and can be added as a dependency.

We can differentiate between two steps for managing the DI of the project: the DI of third-party / services frameworks and the DI of the feature frameworks. The idea is to initialize all these third-party / services frameworks and create an “Environment” for the app. Then, with this environment, we initialize all the feature frameworks to set up the “Bridge”, so everything is accessible from all parts of the app, and finally, the app runs.

Process of initializing the dependencies and the app

DI for external dependencies

Let’s start with the code.

Managing the external dependencies can be done in many ways but we will going to manage them with a wrapper framework. So each third-party framework will have its own wrapper framework.

By doing this, we force that the third-party framework is just the dependency of this wrapper framework and is not spread through the project. And as we mentioned before, we are going to create an environment for the app that manages all the external dependencies, so, using SPM, the ExternalDependencies.package could be something like this:

Package.swift
  • Environment framework

As we have one dependency, the struct is easy:

AppEnvironment.swift

What is this AppboyTracker?

This is the public struct of the environment that is accessible by the app and is the wrapper of the third-party framework.

AppboyTracker.swift

The AppboyProtocol is the one that Braze will conform to, so we can use Braze in our wrapper.

  • Appboy framework
AppBoyConnection.swift

First, Appboy implements our protocol so we can use it in the tracker.
Second, one static func to initialize the tracker with Appboy.
We have created mocks because testing and launching the app are useful.

And this is how we use it:

AppDelegate+AppEnvironmentSetup.swift
AppDelegate.swift

DI for internal dependencies

We are going to try to create an example of how a complex app could be: some feature frameworks, the bridge to communicate between the features and a component framework. So the package is something like this:

Package.swift

For managing the dependencies we are going to create protocols with which you can work inside the framework but are implemented out of it. With this step, you will remove dependencies and improve compilation times in the framework as well as the main project. Also, you will be able to test everything because you will be able to mock things.

  • FeatureA framework

This is how our FeatureA framework entry point looks like:

FeatureAEntryPoint.swift

What is this FeatureAProtocol?

This is the public protocol that contains the methods that are available to access of FeatureA framework and it is located in the bridge framework.

FeatureAProtocol.swift

So in the main target, we initialize the FeatureA:

FeatureAAdapter.swift

We do the same process for the rest of the feature frameworks. We inject them the dependencies they need and use them to set up the bridge:

BridgeConnections.swift

Once the bridge is set, we can access the features like this:

Example of how to use the Bridge

One step more

If you can inject all dependencies of a framework, you will be able even to create an “example” app of just your framework. You would launch your framework mocking everything and you will not need to launch the complete app. This step will allow you to save a lot of time because you will not need to compile your project every time.

3. The transition from CocoaPods to SPM

This step could be the most difficult one because you need to have a clear dependency graph in your project, avoid dependencies between third-party frameworks and development frameworks, not have objc and swift code in the same framework…

But, since you have cleaned the dependencies between frameworks, the transition to SPM will be easy. If you are able to remove the dependencies of your feature frameworks to the third-party ones or common ones, you could work with both framework managers at the same time because development frameworks and third-party ones are linked by the main target.

How it worked on our project

Going back to the list obtained in the first step, we focused on one common framework that contains a lot of dependencies and was used by many feature frameworks:

  • First, we organized the basic code that could be in different frameworks and we made it accessible through the bridge.
  • Then, we removed some dependencies that could be accessed through the bridge.
  • Finally, we improved the DI of the feature frameworks that were using this framework as a dependency.

This is the dependency graph of one of these feature frameworks that had the dependency of the common one before the changes:

FeatureA dependency graph before refactor

After some refactors and improvements, the final result is this:

FeatureA dependency graph after refactor

Conclusions

The result was great: we reduced 1/3 of the compiling times of the project. Also, we realized that the complexity of the project improved and was reduced one half. If you want to know more about how to measure the complexity of your project, here you have the post.

This work was done just in one common framework, we are still working on this because there are much more. Also, some teams are improving the DI of their frameworks so the project is improving and we hope that in the next months we will start moving from CocoaPods to SPM

You can find the project of this post here


DI in a Modularized Project in Swift was originally published in New Work Development on Medium, where people are continuing the conversation by highlighting and responding to this story.

]]>
<![CDATA[Generic Network Service With Async/Await]]> https://medium.com/@Juantri/generic-network-service-with-async-await-87c398d0552d?source=rss-cd48b029b443------2 https://medium.com/p/87c398d0552d Fri, 19 Aug 2022 08:33:48 GMT 2022-08-19T08:40:12.771Z Use generics and simplify the process

In this article I will show you how to make a simple and generic network layer for your iOS application without having to write a lot of code every time you make a request using Swift concurrency: async/await.

Following the SOLID principles, I work with a data source in the data layer where I do the requests but I do not like to implement the requests every time I start a new project, so I decided to move the network layer into an independent file to make it testable and reusable.

Playground

The playground with the code can be found here, but I will explain the files and how the example works. First of all, we can see in the playground that we have all the code related to the Network Service in the Sources folder:

Sources folder
  • APIError: it contains a custom error enum.
  • HTTPMethod: it contains an enum with all the HTTP requests types.
  • NetworkService: it contains the main logic for making the requests.

Network Service

The main class conforms to a protocol to allow mocking of the class when injecting it to the data source for tests or even for development. We can see that there is an init method that asks for the base URL, the default headers (in case needed) and the URL session (in case we want to mock it to test the Network Service). In a private extension we have the three main actions:

  • Create the request
  • Do the request
  • Handle the errors

How to use it

Our example will be done with this public API: https://gorest.co.in/. We will use just use the users endpoint for testing our Network Service.

  1. We declare the endpoints we want to use and the user object.
  2. We create the data source where we define our actions. The API allows us to do multiple actions but we are just use the typical ones like get one or multiple users, create one user or delete one user.
  3. Join all the pieces.
Implementation of the Network Service
]]>
<![CDATA[Part 6: How to work in an easy and fast way]]> https://medium.com/jeff-tech/part-6-how-to-work-in-an-easy-and-fast-way-7a0c4eecd7a?source=rss-cd48b029b443------2 https://medium.com/p/7a0c4eecd7a Sat, 08 Aug 2020 08:59:44 GMT 2020-08-08T09:00:40.605Z
Photo by Ken Wyatt on Unsplash

From an App to a Super App

Do not reinvent the wheel

To create a new view, sometimes we have to create a lot of files: View Controller, View Model, Router, etc. Those files are always equal at beginning and we loose much time creating and setting up them.

In the last episode of How to create a Super App and not die trying, we are going to talk about Templates. Templates allows us to save time and create views in an easy and fast way.

Templates

There are much templates in Xcode:

Those default templates are in the path: /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates

If we open the path we can find two folders: File Templates and Project Templates. The templates of the image are inside File Templates folder and we are going to use some of them to create ours, but first, let’s talk about them.

Each template must has at least 3 files:

  • TemplateInfo.plist: is the responsible for describing the template.
  • TemplateIcon.png: is the icon shown in Xcode new file dialog.
  • __FILEBASENAME__.swift: the actual template file what will be created. Template file can be any other type: .xib, .h, .m, .storyboard…

The plist file has the info of the template. There are some mandatory parameters:

  • Description: a brief description of the template file
  • DefaultCompletionName: the default name of file (without extension).
  • Summary: a brief description of the template file

And also, there are some optional parameters:

  • MainTemplateFile: is the list of files that our template has. The first file of the list is the file that Xcode opens when the template has been created..
  • Options: determine the options what you can configure in the first step of creating a new File.

Let’s create our template

We talk previously about the default templates and their path, but we are going to add our custom templates in another path: ~/Library/Developer/Xcode/Templates. In this path we have to create a File Templates folder, where we are going to create a new folder called: Swift MVVM Router.xctemplate.

Now, go to template’s default folder, open Source folder, copy the items of Swift File.xctemplate and paste them inside our folder. We have to do the same with a xib file, so open User Interface folder and copy the xib file of View.xctemplate folder in our folder. With these files we have the minimun to start, so open our template folder and let’s create our template. We need one file for our View Controller, other for our View Model and another one for our Router, so we have to duplicate the swift file two times and rename them:

  • ___FILEBASENAME___ViewController.swift
  • ___FILEBASENAME___ViewModel.swift
  • ___FILEBASENAME___Router.swift

Also, change the xib file name to: ___FILEBASENAME___ViewController.xib

The next step is configure the plist file. This file is where we give the properties to the template: name of the template, description, the relationship between the files, etc.

Open the plist file and add the following values to the properties:

  • DefaultCompletionName: MVVM Router Swift.
  • Description: Generates a View Controller, View Xib, ViewModel and Router.
  • Summary: Templates for Swift using MVVM pattern with Routers.

Now we are going to add some keys that set the relationship between the files and other properties:

  • MainTemplateFiles: the list of files that our template has. The first file of the list is the file that Xcode opens when the template has been created.
  • Options: determine the options what you can configure in the first step of creating a new File.

The last part is add the code to our files:

Juantri94/MVVM-Router-Template

Getting a bit deeper in the files, we can see many variables with underscore:

  • ___FILEBASENAMEASIDENTIFIER___
  • ___VARIABLE_productName___
  • ___FILEBASENAME___

When we add the template, Xcode asks us the Class Name (configured in the plist file as productName). This productName variable gives the value to ___VARIABLE_productName___ and ___FILEBASENAME___, for example: if in one file we have the following code: ___VARIABLE_productName___ViewModel, and we call the class Test the result is TestViewModel .

The variable ___FILEBASENAMEASIDENTIFIER___ is the name of the file, for example: if the Class name is Test, our View Controller file will be called TestViewController.

Now, open Xcode and create a new file ;)

Some info:
https://www.rubydoc.info/github/bfoz/xcode-ruby/Xcode/Template
https://help.apple.com/xcode/mac/9.0/index.html?localePath=en.lproj#/dev7fe737ce0

Conclusions

You can find the source code of the example on GitHub.

A huge project has a lot of challenges and processes. During these stories I have tried to resume some of them in an easy way with examples to help developers to do a Super App. Of course, these tutorials are a posibility among many more.


Part 6: How to work in an easy and fast way was originally published in Jeff Tech on Medium, where people are continuing the conversation by highlighting and responding to this story.

]]>
<![CDATA[Part 5: How to reduce and reuse views]]> https://blog.devgenius.io/part-5-how-to-reduce-and-reuse-views-f9d9f3b676e7?source=rss-cd48b029b443------2 https://medium.com/p/f9d9f3b676e7 Sat, 01 Aug 2020 10:12:04 GMT 2020-08-12T11:18:31.870Z
Photo by Марьян Блан | @marjanblan on Unsplash

Form an App to a super App

Part 5: How to Reduce and Reuse Views

Reduce, recycle and reuse

When you are developing a huge project, you sometimes repeat code and views because you do not have enough time and you prefer to do copy and paste. As I told you in the previous post Setting the App in Frameworks, we decided at Jeff to move the business models in to frameworks to center all the business logic into a common place. Because of that we noticed that we have a lot of views and logic repeated so we decided to create a common framework.

For this new episode of How to create a Super App and not die trying, we are going to talk about how to reduce the number views of the project and how to create a configurable common view by different ways to reuse it all over the project.

We are going to continue developing our School Project and we are going to explain how we are managing, in Jeff iOS team, the common views and methods. To explain the process we are going to create a new framework where we set up all common views and methods and we are going to create a common view and manage it in the different frameworks we have.

School Dean

The first step is to add the dean of our school. It will be the common place of our project where we are going to have the common classes and views, so we are going to create the dean framework: DeanFramework. To do that we follow the steps of my previous post Setting the App in Frameworks, so let’s start!

Create the framework following the post and call it DeanFramework. Do not create a home and detail views, only the framework. After that, you have to create the DeanApp (also follow the steps of the post). When you finish, you would have something like this:

We are going to to develop two common views: an alert and a rating popup. Inside DeanFramework create a folder and call it Views , inside of it create two more folders for the alert and the rating popup and call them Alert and Rating. In every folder we have to create our views, so create the view controller, view model, router and view as I explained in the previous post. You will have something like this:

Create a Common View: An Alert

We start with the alert. The first thing is to think what the alert does and how it will communicate it. We create a protocol to allow the communication between the alert and the view that implements it:

Remember that the protocols and views must be public for access them outside of the framework

As the alert is configurable, the texts for the labels and buttons will be passed them over to the constructor as parameters to configure the view. The code for our Alert View is the following:

Juantri94/SchoolProjectPart4

Now is time to check if the alert is working properly, so open DeanApp and add a button on Main.storyboard to open it. I created two buttons: one of them opens the alert with one button and the other one opens the alert with a long body and two buttons.

When we click in any button of the alert, the action is done by the delegate (the view controller of the main view). If we add a print function we obtain the following result:

The alert is working so it is time to use it in our School project!

Using the alert

Now we have to create the podspec as I told you in the previous post Setting the App in Frameworks. Follow the steps and add the framework as a pod into SchoolProject. Open School Home and add a button to open the alert.

Create a Reusable View: A Rating Popup

Let’s create something more complex. Think you want to rate the school and the student, this is a common action but maybe each rating has a different logic, for example when you rate the school you have to send some specific data about the school, but when you rate the student you have to send only the student mark. We are going to develop a reusable view that cans do different actions depending on the situation.

Go back to our DeanFramework and open Rating view. We are going to create a rating view with a title, a description, a list with options and two buttons, send and close. As before, we have to create the protocol for the view. This protocol must has the two principal actions of the view: rate and close (the two button actions), so the class that implements it will have the logic associated to the rating process.

The code for our rating popup:

Juantri94/SchoolProjectPart4

Once we have the view, we need a class that implements the protocol of the rating popup, so go to DeanApp an create a new file called RatingExample. Make the class implements the protocol and add the logic of rating, for example:

Open main.storyboard and add a button that opens the popup:

Launch the example app, select an option, write a comment and send the rating:

Great! You have it!

Rating the school and student

Time to get deeper and use our rating popup in the main project. Let’s start with the student framework. Open student project and add Dean dependency in podfile (remember to add in podspec too). We need a class to implement the protocol of the rating popup, so we are going to create a class called StudentRating and make it delegate of the protocol. You can create it in a folder called Domain in the root folder of the framework. Inside of this class add the following code:

Now open the student detail view and add a button to open the rating popup. In the view model you have to create the delegate for the rating popup and pass it over to the router as a parameter in order to init the view. As before, launch the example app, select an option, write a comment and send the rating:

One step more! Let’s rating the school!

We have to do the same as in StudentProject. We have to do the same steps like before: add the pod in the podfile and create a class that implements the rating protocol. I have created a class called SchoolRating inside a new folder called Domain. Let’s implement the protocol as in StudentFramework:

As before, we have to create a button in School home view, init the delegate and pass it over to the router as a parameter in order to init the view. Launch the app and rate the school!

More Options

There are a lot of ways to front facing the problem of reuse and reduce views:

  • Common view with same logic always: alert example.
  • Common view with different logic: rating popup example.
  • Common view element, for example a map: you can create the view with the logic that the map does. Another example could be a banner: a view composed of an icon, a title and a body, you can create a view and pass the data in the init method.
  • Common logic in different views: one option is to create a base view model with all the logic and the view model of the views inherit it.

Conclusions

You can find the source code of the example on GitHub.

With this tutorial I have tried to explain the most difficult options of reuse views that I have founded while I have been developing. The main idea is to create a core or a common point where we can put together all those views or logics and reuse them. For example, we can add common files like validators, extensions, etc that we are going to use throughout the entire project.

As a detail, we can put the Style framework, explained in the previous post, in DeanFramework and have the styles in the common views. Also, we can configure them for been different depending on the StudentFramework or the SchoolProject.

The next post will be the last one of the saga From an app to a Super App and I am going to share a template to make easy the creation of new views.


Part 5: How to reduce and reuse views was originally published in Dev Genius on Medium, where people are continuing the conversation by highlighting and responding to this story.

]]>
<![CDATA[Part 4: How to manage dependencies in an App with frameworks]]> https://blog.devgenius.io/part-4-how-to-manage-dependencies-in-an-app-with-frameworks-a59119e7d34f?source=rss-cd48b029b443------2 https://medium.com/p/a59119e7d34f Sat, 18 Jul 2020 08:53:00 GMT 2020-08-12T19:05:53.615Z
Photo by Karim MANJRA on Unsplash

From an app to a Super App

Together as one

As I told you in the first post of this saga, at Jeff we have been through a lot of changes and one of them was creating a Design System team to manage the new colors, components, etc. If you want to know more about it, you can read this article.

In this new post of How to create an App and not die trying, we are going to explain how we are managing, in Jeff iOS team, the new design features with an example. To explain the process we are going to create another framework that provides our existing project a common functionality for styles of the School App, so we are going to set common colors, button styles, etc.: we are going to create a Style Framework.

Work between frameworks

Styles framework

The first step is to create the new framework: Style Framework. Let’s go to Xcode -> File -> New -> Project and name it Style Framework. Create it in the folder of the project, at the same level as School Project and Student Framework.

Now, we create a folder called StyleFiles where we put the config files for our styles: StyleConstants and StyleButton. Also, we are going to create another folder called Extensions where we put the extensions for colors and buttons. The framework folder structure is the following:

First we are going to create the colors:

The colors are on an extension to access them easily and it is public because we have to access them from other frameworks.

As we are going to create a design for the buttons of the project, we have to think that they will have a common design: the same border radius, border width, etc., but we can configure them in different ways, different styles. So, the second step is to create the constants values of our buttons on Style Constants file. For example:

Once we have the constants, we configure the button with them and with the style we want, so we create a public enum with the different styles that a button has and the methods that return the properties depending on the style:

Finally, we use the enum to create a method where we configure our buttons, so we create a public extension for buttons to allow setup them outside this framework. We add a “setup” method where we pass the style we want to automatically configure the button. The method sets the properties of the style and other common properties of the button, for example: borders, corners, if button is enabled, etc.

We have our framework ready to use.

Let’s create an application inside the framework for using it and check that everything is correct. Go to Style Framework project, add an App target and call it Style App. Add the Style Framework to the app. If any doubt, the steps are the same as in Setting the App in Frameworks post. Open Main.storyboard and create two buttons, set them a width of 150 and a height of 50, embedded them into a stack view and center them in the middle of the view. The view has to be something like this:

Now create the outlets of the buttons in the view controller. The next step is to configure the buttons with our styles, so open ViewController.swift and import Style Framework. In viewDidLoad method, call our button method to configure the buttons:

Launch the app and see the results:

Student with style

We have our framework prepared to be used so let’s create the podspec file for Style Framework and use it in Student Framework. To create the podspec you can follow the same instructions as in the previous post: Setting the App in Frameworks.

Now we have the podspec file, let’s add it as a pod into Student Framework. For that we have to create the podfile for the framework running “pod init” on the framework folder in the terminal. Now add the pod to the pod file and run “pod install”. After the installation we have the workspace, so open it and compile the framework scheme. Now we have the project ready to use the new Style Framework!

Open StudentHomeViewController file and import the framework, create the outlet of the detail button and configure it.

Launch the Student App and check the button style.

Now the detail: Student Framework has a dependency on another framework, so we have to put it on the podspec file:

School with style

Now, let’s do the same stuff in the School Project. Add the pod of Styles in the School podfile and run “pod install” in the terminal. Open the project and compile it. The last step is to do the same as before in the Student Framework: open SchoolHomeViewController file and import the framework, create the outlet of the student button and configure it. Run the School App.

It looks nice!

Working with external dependencies

It is normal to use more than one pod, for example Alamofire for API calls, Lottie for animations, etc. How our app is going to be a “super app”, maybe we will have more than one dependency, so we are going to explain some problems we had during the development process with external dependencies.

We are going to add Alamofire and Google Maps to our projects (Student and School project), the first one to both of them, and Google Maps only for Student Framework.

Let’s open Student Framework and add Alamofire pod in the podfile. Run “pod install” and compile the project. Now we can import Alamofire and use it in the Student Framework. Let’s create an example for it:

We have used a public API to do the test. We import Alamofire in StudentHomeViewModel and add this example, now we call it from StudentHomeViewController in didLoadView system method, so if we launch the Student App we can see the results:

Remember to add the Alamofire dependency in the Student podspec. Go to School Project and do the same, add in the podfile of School Project the Alamofire dependency and run “pod install”. Compile the project and add the same Alamofire function example in School App to check that we can use the pod inside the project and launch the app.

Great job!

Some problems with external frameworks

Now we are going to talk about a pod that creates some problems: Google Maps. We add Google Maps pod in the Student Framework Podfile and run the Student App. If we see Xcode terminal, we will have a lot of messages telling us that there are classes implemented in both Framework and App and Xcode is telling you that one of them will be used but it is undefined.

objc[2423]: Class GMSIndoorTileCoordsGenerator is implemented in both …/StudentFramework.framework/StudentFramework (0x10ff708b8) and …/StudentApp.app/StudentApp (0x10e521990). One of the two will be used. Which one is undefined.

This problem can create a crash in your app. For example, with the Google Maps framework the app crashes because when init the framework in AppDelegate file, you are initializing one of the two classes, so when you load a map, maybe you are loading the one that is initialized or the other one, and if it is loading the no initialized the app crashes.

This is a CocoaPods problem and there are some issues about it:
https://github.com/CocoaPods/CocoaPods/issues/7155
https://github.com/CocoaPods/CocoaPods/issues/7126

So to solve this problem we can use the following workaround: https://github.com/CocoaPods/CocoaPods/issues/7126#issuecomment-399395611

We have to change “Pods-SampleApp” by “Pods-StudentApp” and “Pods-SampleLib” by “Pods-StudentFramework” and run “pod install” on terminal. This will remove the duplicated files of the App and if we run the app we do not have this problem anymore.

Remember to add Google Maps pod dependency in the podspec file of Student Framework and indicate that the framework is static with the following code:

spec.static_framework = true

If not you will have the following error: “The ‘Pods-SchoolProject’ target has transitive dependencies that include statically linked binaries”

CocoaPods problem:
https://github.com/CocoaPods/CocoaPods/issues/7234

What’s next?

You can find the source code of the example on GitHub.

We have created a new framework for our project to set up color buttons. You can set up everything you want like text fields, labels, etc. Also we have reused the same framework for different projects and it can be very useful to reuse code.

In the next post, we will create a reusable view and we will use it in the different frameworks to see how it works depending on the moment that the app uses it.


Part 4: How to manage dependencies in an App with frameworks was originally published in Dev Genius on Medium, where people are continuing the conversation by highlighting and responding to this story.

]]>
<![CDATA[Part 3: How to manage environments in to an App with frameworks]]> https://medium.com/jeff-tech/part-3-how-to-manage-environments-in-to-an-app-with-frameworks-1a57800be700?source=rss-cd48b029b443------2 https://medium.com/p/1a57800be700 Fri, 01 May 2020 11:06:03 GMT 2020-07-18T08:56:23.353Z
Photo by Mantas Hesthaven on Unsplash

From an app to a Super App

Adapting like a chameleon

During the development process, it is very important to know which data you are working with, so you have to have development environments within the project. Xcode init the project with Debug and Release environments by default, but you can add as many environments as you want and configure them.

For this new episode of How to create a Super App and not die trying, we are going to talk about environments: how to configure them within our project, how to configure them in our frameworks and how to configure the environmental framework in our project.

We are going to continue developing our School Project and create environments using the configuration options of the project and using the compiler custom flags that Swift provides us. This is not the only way of creating an environment, there are more ways of creating and managing environments like XCConfig (Xcode Configuration File), but in this post we are not going to explain them or to talk about the differences between them.

How to create, configure and use environments into a framework

Open Student Framework and select the project, go to Info tab and in the Configurations section we can see the Xcode default environments: Debug and Release. Let’s create a new one duplicating Debug environment. We will call it Prerelease.

Student Framework Configurations

Now, open Build Settings tab, look for ‘Swift Compiler — Custom Flags’ and open the variable ‘Active Compilation Conditions’, where all the configurations are available. We have to give a name to those environment variables:

Student Framework Swift Compiler Custom Flags
Active Compilation Conditions is a build setting which allows us to define our flags without the need to prefix them.

The next step is to open StudentHomeViewModel, where we are going to create a method to print in console the environment we are using at this time.

We call this method from StudentHomeViewController on the system method didLoadView(). To check the environments, we have to modify the StudentApp scheme and select the environment we want to launch.

Student App Scheme

If we launch the Student App selecting the different environments we obtain the following message on the console of Xcode:

How to create, configure and use environments into the main project

Now that we have our framework ready to work with environments, we have to configure our main project to work in the same way. The point we want to achieve is that we want to have one app for each environment, so we are going to create different targets on the School App. By this way, each target will be a different environment and it will be easy to work with them.

A target specifies a product to build and contains the instructions for building the product from a set of files in a project or workspace.

First step is to create the Prerelease environment like we did before in the Student Framework. Go to Project -> Info -> Configurations and duplicate Debug. Like before, open Build Settings, look for ‘Swift Compiler — Custom Flags’ and give the same names to the environment variables in ‘Active Compilation Conditions’.

To check the environments, we have to repeat the same process of adding a method in the SchoolHomeViewModel, so repeat the process explained before by adding the same code as in StudentHomeViewModel.

Manage School environment

The second step is to create a target for each environment. Click the right mouse button over the School Project target and duplicate it. Change the name to SchoolProjectDebug.

Create a new one for Prerelease target repeating the process and name it SchoolProjectPrerelease. Xcode has created the new Info.plist files for these two new targets outside the project folders so we have to move them to the correct folder. Pick those two new files, move them to the same path as the original Info.plist file and rename the files to InfoDebug.plist and InfoPrerelease.plist:

School Project folder

Now you have to indicate on the project where those files are for the new targets: select SchoolProjectDebug target, go to Build Setting tab and search “Info.plist file”. Set the current path for the files. Do the same for Prerelease target.

School Project Build Settings

The next step is to configure the targets for managing the correct environment. School Project uses Release config, SchoolProjectDebug uses Debug config and SchoolProjectPrerelease uses Prerelease config.

School Project Scheme

As we have created new targets, we have to add them to the pod file to install the dependencies, so the pod file will be like this:

School Project Podfile

Let’s work together

Now that all is ready, run ‘pod install’ on the terminal and launch the School Project Debug scheme. If you navigate through the app, the terminal will print the messages configured in the School App and in the Student Framework:

When we launch the Prerelease scheme and navigate through the app, the Student Framework prints in Xcode terminal in incorrect environment:

This is because when we install the pod, CocoaPods does not maintain our project settings: if we go to Pods project in the project navigator, open Student Framework and search ‘Swift Compiler — Custom Flags’ in Build Setting, we can see that the configuration disappeared. To solve this, we have to add the following code inside our pod file:

School Project Podfile
Be careful of managing correctly the configuration names.

This code looks for the targets of the app pods and gives a value for the configuration variable according to the configuration that we have put. Once we have this, we run ‘pod deintegrate’ and ‘pod install’ again on the Terminal, relaunch the Prerelease Scheme and get the expected result.

Finally, we launch the Release environment and we have the following result on the Xcode console:

What’s next

You can find the source code of the example on GitHub.

We have created different targets for managing different environments for our project and for our framework, and we have connected them maintaining the same environment. As I explained at the beginning of the post, this is an example of doing that. If we want to manage URLs for each environment or keys and tokens, it is more secure to use XCConfig files because the keys and tokens are not on the code.

In the next post we will see how to manage dependencies between developing frameworks and manage dependencies between our frameworks and others libraries.


Part 3: How to manage environments in to an App with frameworks was originally published in Jeff Tech on Medium, where people are continuing the conversation by highlighting and responding to this story.

]]>
<![CDATA[Part 2: Setting the App in Frameworks]]> https://medium.com/jeff-tech/part-2-setting-the-app-in-frameworks-353d07c86f45?source=rss-cd48b029b443------2 https://medium.com/p/353d07c86f45 Tue, 14 Apr 2020 18:52:26 GMT 2020-05-01T15:46:30.010Z
Photo by STIL on Unsplash

From an app to a Super App

Laying the foundations

In the second part of this series: How to create a Super App and not die trying, we are going to learn how to split an app into frameworks. Why? In the previous post we talked about Organizing the app because at Jeff we now have more than one business model so we decided to split the project into modules to manage the app in an easy way, so each module is a business model and all of them are connected in the same app.

PROS

  • It is an easy way to develop an independent app.
  • It gives you the opportunity to release an app with only the business model you want.
  • It helps the development because each developer is inside an independent app.

CONS

  • You need a common module with the common features of the app.

To explain the process we are going to create a project: School Project, where we gonna have the main app: School App. In this project we are going to add as a pod the framework we are going to create: Student Framework, that it will be a development pod.

The main project

The first step is to create a Xcode project single view App. We are going to call it School Project. Let’s create a folder for our Home view from which we are going to navigate to our framework.

School project folders and files

As we said in the first post, we use MVVM with routers, so we instantiate the router of the home on SceneDelegate file and launch the app to check it works.

SceneDelegate.swift

Let’s create a Framework

Our framework is going to be called Student Framework. This is the pod we are going to use in our School App, but for developing it we are going to create an independent app: Student App, that is going to be an App Target inside Student Project. When we finish the framework and check that it is working on the Student App, we will import it to the main app as a pod. Here we have an explanation of how we are going to use our framework:

Student Framework

Now is the time for the first framework. Let’s create it! Go to File -> New -> Project -> Framework. We call it Student Framework. We are going to save it at the same folder level as the School Project for example:

Let’s create a folder: Views, with another two folders: Home and Detail. Inside each folder, we create the files for the views:

Student framework folders and files

Add a label in Student Detail view to identify the controller when it is shown. Here we have an important issue: in it the controllers with the correct bundle. If we don’t, the app will crash when we try to load the view.

SchoolHomeViewController.swift init method

Let’s create a button in the home view to show the detail:

SchoolHomeViewController.swift button method
SchoolHomeViewModel.swift method
SchoolHomeRouter.swift method

Student App

Once we have our framework prepared, it is time to use it and for doing that we are going to create an App inside Student project. Selecting the project on Xcode, we add a template for a new target: Single View App, and we call it Student App.

Create Single View App Target in Student Framework

When we create it, the App files appear on the project navigator. Now we have to add the Student Framework to the App: select StudentApp target -> General -> Frameworks, Libraries and Embedded Content, and add the framework.

Adding Student Framework in Student App

Select Student App as an active scheme and compile it. Open SceneDelegate file and import the framework to initialize the App. When we try to access the Student Home to initialize the App, we can not do so because it is not public. To solve that, go to Student Framework and create a folder called Interactor, where we will create a class that interacts with School Project. This class will be in charge of communicating the external App that is using the Framework with the Framework. So create a new swift file and call it StudentInteractor.

Creating StudentInteractor.swift file

This class will be public for access from out of the Framework. Inside it, we are going to create a public variable to get the StudentHomeViewController. The variable will be a UIViewController, because if it is StudentHomeViewController type, we must do the claas as public, and what we want is to maintain the framework privacy. The class would be as follows:

StudentInteractor.swift

After that, compile the Framework scheme and now we can access the HomeViewController from the project. We initialize the App with a UINavigationController, whose root view controller is the home, to permit the navigation to the Framework detail.

SceneDelegate.swift (Student App)

Launch the App and see the results. Now we can see the application launch the home of Student Framework and from there we can navigate to the detail.

Student Framework Podspec

The next step is to load our Student Framework into the School App. We are going to use Cocoapods to create a development pod. Open the Terminal and navigate to the project folder, after that, write “pod spec create StudentFramework” and launch it. The Framework folder will be as follows:

We open the podspec file created with any text editor and configure it. By default there are many parameters configured but we have to add a description, the license, specify the source of the pod (as it is a development framework we will put: :path => ‘.’ ), the resources and classes that we want to be accessible and those that are not. We’ll also indicate that we want the folder hierarchy to be maintained. After finishing the podspec, in the terminal we go to the School project folder and do “pod init”. We open the pod file created and add our framework.

School Project Podfile

We make a “pod install” and open the workspace of the project, where the corresponding folder of the pods will have been generated and inside it one that contains the development pods. We compile the project to check that everything is correct. Now we go to the SchoolHomeViewController file, where we create a button that opens the Student home. We will do the same steps as in the Student Home view to go into detail. Finally, in the SchoolHomeRouter file, we will import the framework to be able to access and navigate to its home page.

Launch the app and see the result.

Gif of the result of School App

What’s next?

You can find the source code of the example on GitHub.

We have created our own framework, an app to develop the framework and we have accessed the framework from the main application. So much for this second part of How to create a super app and not die trying. Following these steps we can create all kinds of frameworks, either to have only logic or to have views that are reused throughout the application we are developing. In the next post we will see how to create development environments both in the project and in the different development frameworks we have.


Part 2: Setting the App in Frameworks was originally published in Jeff Tech on Medium, where people are continuing the conversation by highlighting and responding to this story.

]]>
<![CDATA[From an App to a Super App]]> https://medium.com/jeff-tech/from-an-app-to-a-super-app-89ae5084e8eb?source=rss-cd48b029b443------2 https://medium.com/p/89ae5084e8eb Tue, 14 Apr 2020 18:39:31 GMT 2022-06-08T16:05:13.573Z
Photo by Luca Bravo on Unsplash

How to create a Super App and not die trying

Developing an App is always difficult, but developing a Super App is much more. Here I list a series of articles about how we have moved from an App to a Super App at Jeff: what problems we had, how we managed them and an example project of how we pass from a simple App to a Super App to understand the process.


From an App to a Super App was originally published in Jeff Tech on Medium, where people are continuing the conversation by highlighting and responding to this story.

]]>