Me, Carlos

Coding

New York, NY

03/03/2019





Swift

Swift 5.0

Singletons

In my latest post, I talked about the use of constants and how they can help optimize performance.

Today, we are going to go over Singletons.

When one needs only a single instance of an object, using a Singleton is very useful. It is one of the many standard design patterns.

I am going to use a non-existent stock market app as an example. This app will gather information from an API, to collect the data needed to display stock information such as price, price history, traded volume, etc. As an extra feature, this app will also display the top movers, gainers and losers.

Where to find data

Finding and collecting data is essential for the existence of any app that wants to provide that information. Thankfully, there is a large number of APIs that provide all sorts of data, and many of them are free.

The chosen API to provide market information today is IEX Trading and there are a lot of others who provide the same data, but what drove me to choose them is that they did not require any information from me to use their API, although their developer section says that IEX API will sunset non-Investors Exchange (IEX) data on June 1, 2019.

You can find a list of APIs that provide all sorts of information here.

After heading over to the IEX developer section to take a look at how their API works, I have quickly made this Singleton class in Swift.

private let _APIClientSharedInstance = APIClient()

class APIClient: NSObject {

    // Info: https://iextrading.com/developer/docs/#getting-started

    class var shared: APIClient {
        return _APIClientSharedInstance
    }

    /** The standart HTTP methods */
    enum APIClientMethod: String {
        case get = "GET"; case post = "POST"
    }

    /** The URLs to request API data */
    struct EndPoints{
        static let base:String = "https://api.iextrading.com/1.0"
        /** Gets the URL for Stock Chart */
        static func stockChart(code:String, lengh:String?) -> String{
            var premade:String = "\(APIClient.EndPoints.base)/stock/\(code)/chart"
            if let postmade = lengh { premade.append("/\(postmade)") }
                return premade
            }
        /** Gets information from Company, given the code (i.e. name, CEO, sector, logo) */
        static func companyInfo(code:String) -> String{
            return "\(APIClient.EndPoints.base)/stock/\(code)/company"
        }
        /** Gets the Movers (Most active, Gainers and Losers of the day */
        static func getMovers(type:MoversType) -> String{
            return "\(APIClient.EndPoints.base)/stock/market/list/\(type.rawValue)"
        }

        enum MoversType:String{
            case mostactive; case gainers; case losers;
        }
    }

    func performRequestWith(address:String, onCompletion completionHandler: ((Error?, NSDictionary?) -> ())?) {

        let config = URLSessionConfiguration.default
        config.requestCachePolicy = .reloadIgnoringLocalCacheData
        let session = URLSession(configuration: config)

        let urlString = address

        guard let url = URL(string: urlString) else {
            NSLog("INVALID URL: \(urlString)")
            completionHandler?(NSError(domain: "local", code: 500, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]), nil)
            return
        }

        let request = NSMutableURLRequest(url: url)
        request.httpMethod = APIClientMethod.get.rawValue

        let task = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in

        if let error = error {
            DispatchQueue.main.async(execute: {
                  print(" ---- ERROR   ------")
                  completionHandler?(error, nil)
            })
            return
        }
        // Data is NSDictionary
        if let data = data, let json = (try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions(rawValue: 0))) as? NSDictionary {
            DispatchQueue.main.async(execute: {
                completionHandler?(nil, json)
            })
            return
        }
        // Data is NSArray
        if let data = data, let json = (try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions(rawValue: 0))) as? NSArray {
            DispatchQueue.main.async(execute: {
                  completionHandler?(nil, NSDictionary(object: json, forKey: "data" as NSCopying))
            })
            return
        }
        if let httpResponse = response as? HTTPURLResponse {
            if httpResponse.statusCode == 201 || httpResponse.statusCode == 204 {
            DispatchQueue.main.async(execute: {
                completionHandler?(nil, nil)
            })
            return
            }
        }
        let error = NSError(domain: "local", code: 500, userInfo: [NSLocalizedDescriptionKey: "An unknown error has occurred. Please try again."])
            DispatchQueue.main.async(execute: {
                completionHandler?(error, nil)
            })
            return
        })
        task.resume()
    }
}

The connection to the API is set. It is time to test it, and possibly print out the info on a TextView.

As my last post I made a demo involving UILabel and UITableView, this time around I am going to use macOS as a target for my app.

Using EndPoints

let symbol = "AAPL"
let address = APIClient.EndPoints.stockChart(code: symbol, lengh: nil)
APIClient.shared.performRequestWith(address: address) { (error, dictionary) in

    if let error = error{ print("ERROR: \(error.localizedDescription)") }

    // Create an empty array to add the data
    var chartArray:[Chart] = []
    if let jsonArray = dictionary?.value(forKey: "data") as? [[String:Any]]{
        for item in jsonArray{
            let chart = Chart(dictionary: item)
            chartArray.append(chart)
            self.loggerTextView.string.append("\(chart.description)")
        }
    }

    if !chartArray.isEmpty{
        company.chart = chartArray
    }
}

After testing it, and seeing the results show up on a TextView, I wanted to see an actual chart, so with a bit more coding, I drew the chart on an NSView.

Swift

The chart shows the stock price (white line) with short (yellow) and long (green) Moving Average indicators, while the semi-circles indicate good statistics to buy (green), or sell (red) a stock.

If you would like to see how I got to draw the chart, and perhaps more details about making apps of this type, you should check out my repository on GitHub.