/ Technical

iOS Application

1. Introduction

GoHelpFund  is a distributed platform powered by the Amazon Web Services  infrastructure and Ethereum’s network which empowers people to:

  • Donate for various charitable causes ranging from poverty to terrorism prevention, being focused on the concept of fundraising.
  • Use  the HELP token as the main currency for transactions, thus allowing  increased growth and scalability with instant transfers compared to  traditional currency.

GoHelpFund provides a faster and smarter way to donate and raise funds, allowing to facilitate change far more effectively.

2. Basic functionality

The  main functionality of the application is the possibility to donate to  several humanitarian causes, posted by NGOs like UNICEF, Red Cross, Save  The Children or by personal verified individuals.

Some of the base functionalities include:

  • dashboard with main humanitarian causes
  • popular/trending causes
  • sort the causes by different criterias (geographical area, number of views, number of stars, alphabetical order, added date etc)
  • who posted the fundraising campaign
  • how much money has been raised
  • what’s the amount needed
  • payments using smart contract
  • how many people supported the cause
  • what are the community comments
  • detailed description of the fundraising context
  • pictures & videos that better describe what’s the problem
  • ability to create a campaign
  • ability to vote and comment on a campaign

User roles:

  • Guest user: only has the “view as spectator” option available. He can check the causes, the comments and all available website data.
  • Logged in user: can comment, like/unlike posts, donate to different causes via FIAT, SMS, crypto-currency etc with a smart contract available.
  • Verified logged in user: all of the above, but he can also post a fundraising campaign.
  • Organization user: can post multiple campaigns and can manage their own campaigns.

In the following chapters, we will describe the core implementation concepts that will be used in the iOS Application.

3. Web sockets

WebSocket  is a computer communications protocol, providing full-duplex  communication channels over a single TCP connection. The WebSocket  protocol enables interaction between a web client (such as a browser)  and a web server with lower overheads, facilitating real-time data  transfer from and to the server. This is made possible by providing a  standardized way for the server to send content to the client without  being first requested by the client, and allowing messages to be passed  back and forth while keeping the connection open. In this way, a two-way  ongoing conversation can take place between the client and the server.

WebSockets  are commonly used in scenarios with rapidly or often-changed data. Web  notifications in Facebook, real-time chat in Slack, and streaming stock  prices in a trading application are good use cases for WebSockets.

All relevant campaign data (amount raised, details, comments, likes etc) will be updated real time via WebSocket communication.

We’ve analyzed 2 possibilities to integrate the real time update feature in the iOS Application:

  1. Use Amazon MQTT Protocol (supported  by AWS IoT). MQTT is a machine-to-machine (M2M)/”Internet of Things”  connectivity protocol. It was designed as an extremely lightweight  publish/subscribe messaging transport. It is useful for connections with  remote locations where a small code footprint is required and/or  network bandwidth is at a premium. In order to integrate this in the iOS  Application, AWSIoT SDK must be integrate.

The  basic idea is that the client will connect to the socket using an  unique client ID and it will subscribe to a topic (which in this case is  a campaign) on which he wants to receive updates. AWS SDK provides a  method to subscribe called “subscribeToTopic”. This method has a closure  as parameter (besides the topic & qos). The closure is used for the  message callback, so all messages posted from the server will be  received inside the callback.

2. Use our own WebSocket server.  In order to integrate this on the iOS Client, we can use the  non-blocking open-source library Starscream. Starscream is a conforming  WebSocket (RFC 6455) client library in Swift.

After connecting and setting the delegate, the messages would be receive inside websocketDidReceiveMessage delegate method. A publish/subscribe logic, similar to the one presented in the AWS scenario, could be implemented.

4. Caching /offline storage.

Relevant  data would be displayed in the iOS application even when there is no  internet connection. In order to implement the offline storage  functionality, we will use Realm.

Realm is a cross-platform mobile database solution designed specifically for mobile applications.

It’s  fast, lightweight, and simple to integrate. Most common functions such  as querying the database consist of a single line of code.

Unlike  wrappers around Core Data such as MagicalRecord, Realm does not rely on  Core Data or even a SQLite back-end. The Realm developers claim that  their proprietary data storage solution is even faster than SQLite and  Core Data.

We will use Realm Framework for caching important campaign data.

5. Networking

Moya (https://github.com/Moya/Moya) is Network Abstration layer between  application code and Alamofire for Swift. It has many functions like  custom Providers, Plugins and other. It’s classes can easily be  sub-classed in order to add the needed features.

Some awesome features of Moya:

  • Compile-time checking for correct API endpoint accesses.
  • Lets you define a clear usage of different endpoints with associated enum values.
  • Treats test stubs as first-class citizens so unit testing is super-easy.

More details on the API implementation of the networking layer of GoHelpFund iOS App will come in a separate article.

6. Layout using storyboards

We will use separate storyboard for every screen or very small flows. The problems with using bigger storyboard are:

  • Source control:  Storyboard merging conflicts are very difficult to solve, so simply  working in separate storyboards will make the team life easier.
  • Storyboard file become heavy and hard to navigate in
  • We need to assign the storyboard ID for every ViewController, that is error-prone: you need to hard-code this ID every time you want to use the ViewController in code

We will use the same name for storyboard file and associated viewController subclass. This will simplify the naming convention.

We  will move the instantiation of the storyboard and the view controller  inside the a UIViewController subclass and use a static method to  initialize it with storyboard. We will do this in order to avoid using  this pattern in each view controller.

We can avoid hard-typing the storyboard name and use the className. let storyboard = UIStoryboard(name: String.className(self), bundle: nil)

Since we will use a single screen per storyboard, the code will look like this:

class HomeViewController: UIViewController {
   static func storyboardInstance() -> HomeViewController? {                     let storyboard = UIStoryboard (name: String.className(self),                                             bundle: nil)
       return storyboard.instantiateInitialViewController() as? HomeViewController      
       }
}

Now, when you need to initialize this viewController, it will be a single-liner:

let homeViewController = HomeViewController.storyboardInstance()

We will not overload the project with storyboard segues. Since the  navigation will be done from code, it is very easy to initialize the  next view controller from code.

guard let nextViewController = NextViewController.storyboardInstance()
  else { return }
navigationController?.pushViewController(nextViewController,                                                       animated: true)

For the tab approach, we will be using storyboard refferences, as described here. https://code.tutsplus.com/tutorials/ios-9-staying-organized-with-storyboard-references--cms-24226

7. MVVM (Model — View — ViewModel) architecture for presentation layer

MVVM  is proposed by John Gossman in 2005. The main purpose of the MVVM is to  move the data state from the View to the ViewModel. The data flow in  MVVM could be drawn as the following figure:

According  to the definition, the View consists of only visual elements. In the  View, we only do things like layout, animation, initializing UI  components, etc. There’s a special layer between the View and the Model  called the ViewModel. The ViewModel is a canonical representation of the  View. That is, the ViewModel provides a set of interfaces, each of  which represents a UI component in the View. We use a technique called  “binding” to connection UI components to ViewModel interfaces. So, in  MVVM, we don’t touch the View directly, we deal with business logic in  the ViewModel and thus the View changes itself accordingly. We write  presentational things such as converting Date to String in the ViewModel  instead of the View. Therefore, it becomes possible to write a simpler  test for the presentational logic without knowing the implementation of  the View.

In  general, the ViewModel receives the user interaction from the View,  fetches data from the Model, then process the data to a set of  ready-to-display properties. The View updates itself after observing the  change of the ViewModel. That’s the whole story of the MVVM.

Specifically,  for MVVM in iOS development, the UIView/UIViewController represent the  View. The responsabilities of this component are:

  • Initiate/Layout/Present UI components.
  • Bind UI components with the ViewModel.

On the other hand, in the ViewModel, we do:

  • Write controller logics such as pagination, error handling, etc.
  • Write presentational logic, provide interfaces to the View

You  might notice that the ViewModel is kinda complex. Some solutions  include adding a ViewModel Designer, Builder classes and Flow  Coordinators or Routers.

More details and code examples with MVVM implentation from GoHelpFund iOS Application will be added in a separate article.

8. Dependency injection

In  software engineering, dependency injection is a technique whereby one  object (or static method) supplies the dependencies of another object. A  dependency is an object that can be used (a service). An injection is  the passing of a dependency to a dependent object (a client) that would  use it. The service is made part of the client’s state. Passing the  service to the client, rather than allowing a client to build or find  the service, is the fundamental requirement of the pattern.

Dependency  injection is one form of the broader technique of inversion of control.  As with other forms of inversion of control, dependency injection  supports the dependency inversion principle. The client delegates the  responsibility of providing its dependencies to external code (the  injector). The client is not allowed to call the injector code. It is  the injecting code that constructs the services and calls the client to  inject them. This means the client code does not need to know about the  injecting code. The client does not need to know how to construct the  services.

Dependency  injection is an essential tool when it comes to making code more  testable. Instead of having objects either create their own dependencies  or access them as singletons, it’s the idea that everything an object  needs in order to do its work should be passed in from the outside. This  both makes it easier to see what exact dependencies a given object has,  and it also makes testing a lot simpler — since dependencies can be  mocked in order to capture and verify state & values.

As  the number of dependencies for a given object grows, initializing it  can become quite a chore. Making code testable is nice, but it’s really  too bad if it has to come with the cost of having huge initializers.

The  main reason why we often end up having big constructors when using  dependency injection is because we need to pass dependencies around in  order to use them later.

In  order to avoid singletons & also avoid passing dependencies between  classes, we will use a factory that we could simply ask to create our  dependencies.

Factories  give the ability to fully decouple the usage and creation of an object.  This enables many objects to have a much loosely coupled relationship  with their dependencies, which really helps in situations when you want  to refactor or change things.

We’ll  be defining a protocol for our factory, which will enable us to easily  create any view controller that we need in our app, without actually  knowing anything about its dependencies or its initializer.

protocol ViewControllerFactory {  
   func makeCampaignListViewController() -> CampaignListViewController  

   func makeCampaignDetailsViewController(for campaign: Campaign)
       -> CampaignDetailsViewController
}

We’ll also create additional factory protocols for creating our view controllers’ dependencies as well. For example, we can create one for the list of campaigns data source.

protocol CampaignListLoaderFactory {  
   func createCampaignListLoader() -> CampaignListLoader
}

The CampaignListViewController will have a single dependency, the Factory.

class CampaignListViewController: UITableViewController {
// Here we use protocol composition to create a Factory type that includes all the factory protocols that this view controller needs.

typealias Factory = CampaignListLoader & ViewControllerFactory

private let factory: Factory

// We can now lazily create our CampaignListLoader using the injected factory.

private lazy var loader = factory.makeMessageLoader()  

   init(factory: Factory) {    
       self.factory = factory    
       super.init(nibName: nil, bundle: nil)
   }
}

The „List” doesn’t have to know about the „Details” dependencies.

We’ll  also create a dependency container which conform to our factory  protocols, which will enable us to inject it as a factory to our various  view controllers and other objects.

extension DependencyContainer: ViewControllerFactory {
func makeCampaignListViewController() -> CampaignListViewController {       return CampaignListViewController(factory: self)}

func makeCampaignDetailsViewController(for campaign: Campaign)
-> CampaignViewController {
   return CampaignDetailsViewController(campaign: campaign)
   }
}

extension DependencyContainer: CampaignListLoaderFactory{
func createCampaignListLoader() -> CampaignListLoader {
   return CampaignListLoader(networkManager: networkManager)
   }
}

Since we  will inject our dependency container as an implementation of the  factories needed for our objects, and since those objects will hold a  strong reference to their factory — there’s no need for us to store the  container anywhere else.

Conclusion:

Setting  up your dependency injection using factory protocols and containers can  be a great way to avoid having to pass multiple dependencies around and  having to create complicated initializers.

Since  we have defined all of our factories as protocols, we can easily mock  them in tests by implementing a test-specific version of any given  factory protocol.

9. Unit testing

Unit  testing doesn’t just apply to software. It is a broader engineering  term that can apply to any domain where there’s something composed of  parts, each of which needs to do a particular job.

You  can unit test your car by taking it apart and running diagnostics on  each of the components. Maybe you want to test the oil filter’s ability  clean up some filthy oil, or perhaps you want to test the seal on the  gas cap. Each of those is a test that you can perform on the individual  units which make up your car.

A good unit test needs to have the following things.

  • An isolated component that you are testing. If you test more than one thing together, that’s an integration test.
  • A  particular behavior that you are testing. It sort of goes without  saying that this behavior needs be related to the component you are  testing.
  • A  success and failure condition. Kind of a no-brainer. With unit tests,  there’s no such thing as partially successful. When run, each unit test  must either succeed or fail.

We will use the give-when-then formula.

Given-When-Then  is a style of representing tests — or as its advocates would  say — specifying a system’s behavior using SpecificationByExample. It’s  an approach developed by Dan North and Chris Matts as part of  Behavior-Driven Development.

  • The given  part describes the state of the world before you begin the behavior  you’re specifying in this scenario. You can think of it as the  pre-conditions to the test.
  • The when section is that behavior that you’re specifying.
  • Finally the then section describes the changes you expect due to the specified behavior.

More details and code examples with Unit Testing from GoHelpFund iOS Application will be added in a separate article.

iOS Application
Share this

Subscribe to GoHelpFund Blog