Go Help Fund iOS Application

GoHelpFund
11 min readMar 23, 2018

--

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 & ViewControllerFactoryprivate 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 artcile.

You can find us at https://gohelpfund.com

--

--