Archive for January 2017 | Monthly archive page
Highcharts Support in a Native iOS App
There are many options available to display charts within your [native] iOS apps. You could write one yourself (why?) or use one of the many third party libraries that are available . Check out this GitHub repo for a list of charting libraries in swift/ ObjC.
However, if you are interested in using Highcharts within your app, then read on . Highcharts is a popular JS charting engine and you can learn more about it at http://www.highcharts.com.
This post will demonstrate how you can load a Highcharts based chart into a iOS native app. We will use the example charts available as part of the Highcharts library. A more interesting and complete example that demonstrates how you can dynamically load chart data is available at https://github.com/rajagp/iOS_Highcharts_Sample.
1) Download the sample xcode app project from http://www.priyaontech.com/wp-content/uploads/2017/01/HighchartDemo.zip. This is a simple single view application that will load a .html file into a WKWebView.
2) Download the Highcharts library from http://www.highcharts.com/download
3) Once downloaded , unzip its contents and examine it. You should see contents list similar to screenshot below
4) Navigate to the “examples” folder
5) Copy the “index.htm” file from any of the examples into your project. Make sure you copy it into your project
6) Your project should look something like this
7) In your xcode project, open and review the the index.htm file that you just copied over. It contains a JS function that creates a chart using the Highcharts chart API as defined in http://api.highcharts.com/highcharts/chart. The chart data and options are defined statically.
1 2 3 4 5 6 7 8 9 10 |
$(function() { // Create the chart Highcharts.chart('container', { chart: { type: 'column' }, title: { text: 'Browser market shares. January, 2015 to May, 2015' }, ……… |
8) Now, open the “ChartViewController.swift” file and review it. This is a simple view controller that manages a WKWebview and loads the index.html into the web view.
Look for the “loadContainerPage” function . This is where the index.htm is loaded.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
fileprivate func loadContainerPage() { print(#function) // Loads the index.html that is the base page for loading the chart guard let filePath = Bundle(for: ChartViewController.self). path(forResource: "index", ofType: "htm") else{ print("Could not locate index.htm") return } do{ let currFrame = self.view.frame; let content = tryString(contentsOfFile:filePath) let formattedContent = String.init(format: content,currFrame.size.width,currFrame.size.height) let_= self.webView?.loadHTMLString(formattedContent, baseURL: URL.init(fileURLWithPath: Bundle(for: ChartViewController.self).bundlePath)) } catch{ print("Failed to load contents of index.html") } } |
9) That’s it ! Run the app and you should see the sample chart load …
A complete example that demonstrates how you can dynamically load chart data is available at https://github.com/rajagp/iOS_Highcharts_Sample.
MVC, MVP and MVVM Patterns on iOS
A software pattern is a template for solving a repeatable problem. Any user facing client app can be broadly composed of three main layers –
· The Presentation / UI Layer: Layer that a user interacts with.
· The Business Logic Layer: Layer that handles the data model and interactions for local/remote databases
· The Application Logic Layer : The glue between the Presentation and Business Logic Layers.
The Model View Controller (MVC):
The MVC is a well-known architectural pattern used for building iOS Apps.
· The View , which is generally not passive, corresponds to the Presentation Layer
· The Model , corresponds to the Business Logic Layer
· The Controller sits between the View and the Model , corresponds to the Application Logic Layer.
However, the controller becomes the catch-all for everything that isn’t exactly a UI control or a data model, leading to the “Massive View Controller” issue.
Two popular alternatives to the MVC pattern are the MVP and MVVM patterns, both of which are essentially variants of the MVC pattern.
The Model View Presenter (MVP):
The MVP defines the following
· The View , which is generally passive, corresponds to the Presentation Layer
· The Model , corresponds to the Business Logic Layer and is identical to the MVC pattern
· The Presenter sits between the View and the Model , corresponds to the Application Logic Layer.
A View is typically associated with one Presenter.
In iOS, the interaction between the View and Presenter can be implemented using the Delegation Pattern (https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html). The Delegation Pattern allows one object to delegate tasks to another object. In iOS, the pattern is implemented using a Protocol. The Protocol defines the interface that is to be implemented by the delegate.
In the MVP pattern, the View and Presenter are aware of each other. The Presenter holds a reference to the view it is associated with.
The PresenterProtocol defines the base set of methods that any Presenter must implement. Applications must extend this Protocol to include application specific methods.
1 2 3 4 |
protocol PresenterProtocol: class{ func attachPresentingView(_view:PresentingViewProtocol) func detachPresentingView(_view:PresentingViewProtocol) } |
The PresentingViewProtocol defines the base set of methods that View must implement. By providing default implementation of the methods in this interface, the conformant view does not have to provide its own implementation. This interface can be extended to define application specific methods.
1 2 3 4 5 6 |
protocol PresentingViewProtocol: class{ func dataStartedLoading() func dataFinishedLoading() func showErrorAlertWithTitle(_title:String?, message:String) func showSuccessAlertWithTitle(_title:String?, message:String) } |
The Model View View Model (MVVM) :
The MVVM defines the following
· The View , which is generally passive, corresponds to the Presentation Layer
· The Model , corresponds to the Business Logic Layer and is identical to the MVC pattern
· The View Model sits between the View and the Model , corresponds to the Application Logic Layer.
The key aspect of the MVVM pattern is the binding between the View and the View Model. In other words, the View is automatically notified of changes to the View Model.
In iOS, this can be accomplished using Key-Value-Observer (KVO) Pattern. In the KVO Pattern (https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html) , one object is automatically notified of changes to the state of another object. In Objective C, this facility is built into the run-time. However, it’s not as straightforward in Swift. One option would be to add the “dynamic” modifiers to properties that need to be dynamically dispatched. However, this is limiting as objects now need to be of type NSObject. The alternate is to simulate this behavior by defining a generic type that acts as a wrapper around properties that are observable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public struct DynamicType<T> { typealias ModelEventListener = (T)->Void typealias Listeners = [ModelEventListener] private var listeners:Listeners= [] var value:T? { didSet{ for(_,observer) inlisteners.enumerated() { if let value = value{ observer(value) } } } } mutating func bind(_listener:@escaping ModelEventListener) { listeners.append(listener) if let value = value{ listener(value) } } } |
The View can “bind” an appropriate callback to the ViewModel by using the “bind” method on the DynamicType. Every time the property value changes, the callback is automatically invoked.
Code and More:
A sample app that showcases the implementation of the MVC, MVP and MVVM pattern is available on GitHub at https://github.com/rajagp/iOS_MVC_MVP_MVVM_SampleApp
Slides from a related talk on supporting the MVP and MVVM patterns in iOS apps can be downloaded from iOS_MVP_MVVM_Patterns .