-
Here is my linkedin profile: lucas-ferreira-machado .
-
Feel free to have a look at my iOS MoviesApp repository developed using :
- Swift 5 + SwiftUI + Combine
- Clean Architecture, Dependency Injection, MVVM and Coordinator patterns
- Quick/Nimble framework for Tests
- Features modularization
Note: Current Code Coverage is 94,3% (was 42% before)
I followed this steps to improve the app project:
1. [Fix] Missing list contacts screen title. (more...)
First, I created an UI test to validate the failure. Then I fixed it in the code and the test passed successfully.
- Given that application is launched
- When the Contact List screen appears
- Then It should display the screen title "Lista de contatos"
2. [Fix] Crash when selecting first contact in the list. (more...)
UI test created to validate the failure. Then I fixed it in the code and the test passed successfully.
- Given that application is launched and the Contact List screen is displayed
- When User clicks on the first contact's name in the list
- Then It should display an dialog with the selected contact's name
3. [Feature] Create an UI test to select a legacy contact from list. (more...)
UI test created to select a legacy contact in the list. UI Test passed successfully.
- Given that application is launched and the Contact List screen is displayed
- When User clicks on a legacy contact in the list
- Then It should display an dialog with text "Você tocou no contato sorteado"
4. [Feature] Apply Clean Architecture pattern (Data, Domain, Presentation Layers + Use Cases, Repository, Data Sources, Mappers) (more...)
- ListContacts module separated between Data, Domain and Presentation layers.
- FetchContactsUseCase protocol created on Domain layer.
- NetworkPlatform module created to handle network data using HTTPClient, API, Mappers and DTO's classes.
- Repository and DataSources created to handle data from different sources (remote or local) such as REST APIs, CoreData, Firebase, Realm, etc.
Created by (Robert C. Martin, known as Uncle Bob) is an architecture that aims decoupled components, testable and easy maintenance. In that way, new features can be added quickly.
Communication flow between layers:
5. [Feature] Asynchronously loading images into table view cells (more...)
- I noticed that scrolling through the contact list was slow so I created UI test to measure and assert it.
- Given that application is launched and the Contact List screen is displayed
- When User scroll up the contact list
- Then It should take no more than 300 milli seconds
- Scrolling event time was ~ 552 milliseconds because photos of the contacts were downloaded on the main thread.
- Apple Developer Documentation recommends Asynchronously Loading Images into Table and Collection Views.
- ImageCache and ImageUrlProtocol created to store and fetch images asynchronously to make the app more responsive.
- Current Scrolling event time is ~ 24 milliseconds
- Then UI Test passed successfully.
6. [Feature] Review and improve MVVM pattern implementation (more...)
- Check legacy user ids logic removed from ListContactsViewController and moved to a new use case class (CheckLegacyUserIdsUseCase) on Domain layer.
- Create ContactCellViewModel to provide the presentation data to the ContactCell view class.
- Refactor ContactCell class. Add bindViewModel and setPhotoImage methods.
- Refactor ListContactsViewController to display presentation data and bind the events only. Any business logic was moved to ListContactsViewModel class.
Model-View-ViewModel (MVVM) is a structural design pattern that separates objects into three distinct groups:
- Models
- hold application data. They’re usually structs or simple classes.
- Views
- display visual elements and controls on the screen.
- View Models
- transform model information into values that can be displayed on a view. This behavior is traditionally implemented using a paradigm known as Functional Reactive Programming. Libraries such as ReactiveSwift and RxSwift define a standard interface for events that views may want to react to, such as button taps, incoming network data, etc. They rely heavily on the Observer pattern, in which views and/or view controllers observe/subscribe to observables and are “notified” appropriately whenever some event of interest occurs.
7. [Feature] Apply Dependency Injection pattern (more...)
- Classes were prepared to use Dependency Injection pattern.
- Dependencies are no longer initialized within classes.
- Dependencies are received in the initializer methods.
- Create ListContactsViewAssembler to assemble the module,
It's a software design pattern that implements Inversion of Control (IoC) for resolving dependencies. This pattern helps your app split into loosely-coupled components, which can be developed, tested and maintained more easily.
In the VIPER pattern its common to see the ** assembleModule method** inside the Router or AppModule class.
I usually use the SWInject framework in my projects. Swinject is a lightweight dependency injection framework for Swift.
8. [Feature] Add Integration Tests (more...)
-
Remove empty ListContactServiceTests class.
-
Create mock class for FetchContactsUseCaseOutput.
-
Add Integration Tests:
- test async fecth contacts
- test load images from network
- test cancel load image request
-
Apple Documentation: Testing Your Apps in Xcode
Integration tests examines the behavior of a larger subsystem, or combination of classes and functions. In the Arrange step of an integration test, widen the scope of real project code that’s under test, using fewer stub objects.
9. [Feature] Add Unit tests (more...)
Code Coverage: 94,3%
-
Add Unit Tests:
- Contact:
- test decode valid contact json data
- test decode invalid contact jsondata
- FetchContactsUseCase:
- test fetch contacts success
- test fetch contacts not connected to Internet
- ListContactsViewModel:
- test fetch contacts not connected to Internet.
- Contact:
-
Create Mocks:
- ContactMock
- FetchContactsUseCaseMock
- FetchContactsRepositoryMock
-
Apple Documentation: Testing Your Apps in Xcode
Each unit test should assert the expected behavior of a single path through a method or function in your project. To cover multiple paths, write one test for each scenario. For example, if a function receives an optional parameter, you’d write a test in which the parameter is nil and a test in which it takes a non-nil value. Identify the boundary cases and logical branches in your code, and write a unit test to cover each combination of these cases.
- The Clean Coder
- SOLID principles
- BDD - Behavior Driven Development
- Test strategy: Pyramid distribution of tests
- a large number of fast, well-isolated unit tests to cover your app’s logic,
- a smaller number of integration tests to demonstrate that smaller parts are connected together properly,
- and UI tests to assert the correct behavior of common use cases.
- Modularization using Pods or Swift Package Manager to separate application code into smaller modules.
- Add the Coordinator pattern to allow you to easily manage and expand the app's navigation flows.
- Use Quick + Nimble + Snapshots to generate screenshots of the tested screens and avoid any future changes impacting the UI screen layouts.
- Use the SwiftGen tool to automatically generate code and map and fail to use reources such as images, colors, fonts, and localizable strings.
- Use any code generation tool to automatically generate Mockable classes for application project. This allows you to create new unit tests quickly and efficiently.
- Use the Alamofire framework to easily make HTTP network requests.
- Use RXSwift (and RxCocoa) to implement [Functional Reactive Programming] (https://en.wikipedia.org/wiki/Functional_reactive_programming) .