Obsidian

Obsidian

Questrade Client for iOS

Why

Dissatisfaction

After signing up and using Questrade's Trading platform I wanted a good mobile client that I could use. Sadly the one they have was just a poorly executed web app that was very slow and frustrating to use. So I decided to see if I could make my own.

Learn & Grow

Secondly I was interested in building a oAuth 2 based API from scratch and learning new iOS paradigms.

Learning

iOS Localization

Using Apple's built-in number and date formatters, stack views, controls, and NSLocalizedStrings allows easy and effective localization of UI.

UI Testing Automation

Building UITests to easily get an overview of all UI in all languages and device sizes.

class ObsidianUITests: XCTestCase {
    let app = XCUIApplication()
    
    var isLoggedOut = true
    
    func makeSureAppIsLoggedIn() {
        if isLoggedOut {
            app.images["AppIcon"].press(forDuration: 0.3);
            isLoggedOut = false
        }
    }
    
    func testPositions() {
        makeSureAppIsLoggedIn()
        
        let button = app.tables.children(matching: .cell).element(boundBy: 1).buttons["PositionPriceLabel"]
        let expectation = XCTNSPredicateExpectation(predicate:NSPredicate(format: "exists == true"), object: button)
        wait(for: [expectation], timeout: 1)
        button.tap()

        app.otherElements["Graph View"].press(forDuration: 1)
        snapshot("01-SymbolModalGraph", timeWaitingForIdle: 0)
        
        app.navigationBars["Title"].buttons["DismissModal"].tap()
        snapshot("02-Positions", timeWaitingForIdle: 0)
    }
}

UI Themeing

Harnessing the power of protocols and the UIResponder chain to add a themeing system to UIKit.

struct UITheme {
    let statusBarStyle: UIStatusBarStyle
    let keyboardAppearance: UIKeyboardAppearance
    let barStyle: UIBarStyle
    let blurEffectStyle: UIBlurEffect.Style
    let tintColor: UIColor
    let scrollViewIndicatorStyle:UIScrollView.IndicatorStyle
    
    init(withBarStyle barStyle: UIBarStyle, tintColor: UIColor) {
        self.barStyle = barStyle
        self.blurEffectStyle = barStyle == .black ? .dark : .light
        self.keyboardAppearance = barStyle == .black ? .dark : .light
        self.tintColor = tintColor
        self.statusBarStyle = barStyle == .black ? .lightContent : .default
        self.scrollViewIndicatorStyle = barStyle == .black ? .white : .black
    }
    
    func setAppearances() {
        UINavigationBar.appearance().barStyle = barStyle
        UITabBar.appearance().barStyle = barStyle
        UIToolbar.appearance().barStyle = barStyle
        UISearchBar.appearance().barStyle = barStyle
        ThemedEffectView.appearance().effect = UIBlurEffect(style: blurEffectStyle)
        UIScrollView.appearance().indicatorStyle = scrollViewIndicatorStyle
        UITextField.appearance().keyboardAppearance = keyboardAppearance
        UISearchBar.appearance().keyboardAppearance = keyboardAppearance
    }
}

Swift API for Questrade

Using keychain storage, Codable structs, and generics, to make a lightweight, secure, powerful, and easy to use native Swift API.

See on GitLab
// First setup the keychain store for your app
let keychain = AuthKeychainStore(service: "Questrade", account: "Obsidian", data: [:])

// since the init could throw errors you will have to wrap it in a do catch
do {
    // Init iOSQuestAuth class with the keychain store
    let auth = try iOSQuestAuth(keychainStore: keychain)
    
    // Init QuestAPI with the authorizer
    let api = QuestAPI(authorizor: auth)
    
    // Optionally set a singleton to access the API
    QuestAPI.shared = api
} catch let err {
    // handle error
}

Automated App Store Distribution

Using fastlane to automate tedious tasks of capturing, uploading, and localizing App Store metadata.

See Fastlane
platform :ios do
    lane :test_beta do
        ensure_git_status_clean

        increment_build_number(
            build_number: latest_testflight_build_number + 1,
            xcodeproj: "Obsidian.xcodeproj"
        )

        commit_version_bump(xcodeproj: "Obsidian.xcodeproj")
        add_git_tag
        push_to_git_remote

        build_app(
            scheme: "Obsidian",
            workspace: "Obsidian.xcworkspace",
            include_bitcode: true,
            output_directory: "./fastlane/app"
        )

        upload_to_testflight
    end
end

Outcome

A public Questrade API written in Swift, marketing site, and published Questrade client app. Expect continued improvements to the design and functionality of Obsidian.

Check it out if you live in Canada and use Questrade as a broker!

View Site Get on App Store

Let's Work Together

And make something beautiful!

Projects

BeeBusy

BeeBusy

Spa and Salon Booking App for iOS

Read More