 Sample iOS App - A collection of examples and patterns for Unit Testing, UI Testing, handling Result/Optionals, writing documentation

Overview

BookStore

👉 한글 버전

See new releases and search for programming books from IT Bookstore API

This is a sample app to practice using Result type, stubbing network request for unit tests, separating functionalities into frameworks, and writing Swift documentation.

How to run

> cd BookStore
> open BookStore.xcodeproj

Run!

Contents

App Features

What's New

A simple UITableView with cells and modal presentation for a detailed page.

Search

  1. As a user types in the keyword, the search text is "debounced" for a fraction of second for better performance and user experience. See Debouncer.

  2. Search results are paginated and provides infinite scroll.

Result type in Swift 5

Out of the box, you have to switch on the Result instance to access the underlying success instance or the error instance.

switch result {
case .success(let response):
  //do something with the response
case .failure(let error):
  //handle error
}

However, I think switch statements are too wordy. I added success and catch method to Result type. So it can be chained like this.

searchResult.success { response in
  //do something with the response
}.catch { error in
  //handle error
}

Even cleaner, like this.

result.success(handleSuccess)
      .catch(handleError)
      
func handleSuccess(_ result: SearchResult) { ... }
func handleError(_ error: Error) { ... }

Stubbing Network Requests for Unit Tests

Generally, it is not a good idea to rely on the actual network requests for unit tests because it adds too much dependency on tests. One way to stub networking is to subclass URLProtocol.

1. Subclass URLProtocol

See MockURLProtocol

2. Configure URLSession with your mock URLProtocol

let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [MockURLProtocol.self]

//Use this URLSession instance to make requests.
let session = URLSession(configuration: config) 

4. Use the configured URLSession instance just as you would.

session.dataTask(with: urlRequest) { (data, response, error) in
  //Stubbed response
}.resume()

UI Testing with Stubbed Network Data

The above method(as well as the famous OHHTTPStubs) doesn't work for UI testing because the test bundle and the app bundle (XCUIApplication) are loaded in separate processes. By using Swifter, you can run a local http server on the simulator.

First, change the API endpoints during UI testing with launchArguments in your hosting app.

//In XCTestCase,
override func setUp() {
  app = XCUIApplication()
  app.launchArguments = ["-uitesting"]
}

//In AppDelegate's application(_:didFinishLaunchingWithOptions:)
if ProcessInfo.processInfo.arguments.contains("-uitesting") {
  BookStoreConfiguration.shared.setBaseURL(URL(string: "http://localhost:8080")!)
}

Then stub the network and test the UI with it.

0) XCTAssert(app.staticTexts["9781788476249"].exists) XCTAssert(app.staticTexts["$44.99"].exists) } }">
let server = HttpServer()

func testNewBooksNormal() {
  do {
    let path = try TestUtil.path(for: normalResponseJSONFilename, in: type(of: self))
    server[newBooksPath] = shareFile(path)
    try server.start()
    app.launch()
  } catch {
    XCTAssert(false, "Swifter Server failed to start.")
  }
        
  XCTContext.runActivity(named: "Test Successful TableView Screen") { _ in
    XCTAssert(app.tables[tableViewIdentifier].waitForExistence(timeout: 3))
    XCTAssert(app.tables[tableViewIdentifier].cells.count > 0)
    XCTAssert(app.staticTexts["9781788476249"].exists)
    XCTAssert(app.staticTexts["$44.99"].exists)
  }
}

Using Frameworks for independent functionalities

Separating your app's functions into targets has several advantages. It forces you to care about dependencies, and it is good for unit tests since features are sandboxed. However, it may slow down the app launch (by little) due to framework loading.

BookStoreKit is responsible for fetching and searching books data from IT Bookstore API.

Networking is a wrapper around URLSession for making HTTP requests and parsing response.

Writing a documentation comment

Swift's API Design Guidelines suggest you write a documentation comment for every declaration. Writing one can have an impact on the design.

1. Write

Reference this document for markup formatting.

2. Check out the result

In Xcode's autocompletion

and Show Quick Help (option + click)

Getting Rid of IUOs

IMHO Implictly unwrapped optional is a potential threat to code safety and should be avoided as much as possible if not altogether. An example of two methods to get rid of them from where they are commonly used.

Make IBOutlets Optional

IBOutlets are IUOs by Apple's default. However, you can change that to Optional types. You may worry that making IBOutlets Optionals may cause too many if lets or guards, but that concern may just be overrated. IBOutlets are mostly used to set values on them, so optional chaining is sufficient. In just few cases where unwrapping is added, I will embrace them for additional safety of my code.

Using lazy instantiation

For the properties of UIViewController subclass, IUO can be useful but it's still dangerous. Instead, I use unspecified. It generates a crash upon class/struct usage so it can be spotted fast during development, and most importantly no more IUOs.

//Inside a viewcontroller
lazy var bookStore: BookStoreService = unspecified()
You might also like...
InputMethodKit Sample App with macOS11, Xcode13, Swift5.5 in 2021.

What is this? This is a sample implementation of IMKit App with Swift/SwiftUI. Working Environment macOS 11.5 Swift 5.5 Xcode13 (beta) Procedure to ma

A sample app that will display some airport information using MVVM pattern

Simulator.Screen.Recording.-.iPhone.11.-.2021-11-15.at.14.27.41.mp4 AirportDisplayApp A sample app that will display some airport information using MV

This sample app use the Star Wars public api to show a list of characters

StarWars-MVVM In this sample app, I use the Star Wars public api to show a list of characters from the Star Wars movie series. There are two goals I w

Apple ExtensionFoundation/ExtensionKit sample app
Apple ExtensionFoundation/ExtensionKit sample app

TextTransformer: an ExtensionKit sample app This year's WWDC introduced many new APIs, two of which caught my attention: ExtensionFoundation and Exten

Sample projects for using .NET and Swift with SourceGear Bridge

bridge-samples-swift This repo contains various sample projects which show using .NET and Swift with SourceGear Bridge. (Note that SourceGear Bridge i

REDUX like architecture sample for iOS

perdux_sample_swiftUI REDUX like architecture sample for iOS (target 14.*) Motivation: to share reactive architecture approach with single direction d

Sample Code for WWDC21

WWDC21 Sample Code Accessibility Create Accessible Experiences for watchOS Creating Accessible Views WWDC21 Challenge: Large Text Challenge WWDC21 Cha

UIKit Chat List Sample
UIKit Chat List Sample

When I tried to make a UI like this, I faced a problem. When keyboard appears, TableView doesn't scroll automatically. I wanted to make the feature tableview scrolls automatically so I tried to make this project. Below images describe the difference without feature and with feature properly.

Comments
  • Fix Unspecified Method, To Track File/Line

    Fix Unspecified Method, To Track File/Line

    Hi. I am very impressed with your project. Thank you for sharing a good project and I would like to suggest a small improvement.

    I fix unspecified method in Unspecified.swift to track where the assertion occurred. This change make you find bugs more fast. I hope to be approved, and have a nice day.

    opened by kcharliek 0
Owner
Soojin Ro
Swift & iOS & Flutter
Soojin Ro
IOS Swift : Explain Higher-order Function Examples

IOS Swift : Explain Higher-order Function Examples

Aya Baghdadi 0 Feb 20, 2022
Complete SwiftUI Docs with Examples

?? SwiftOnTap Complete SwiftUI Docs with Examples. Get Started | How It Works | Add Docs Get Started To get started, Go to swiftontap.com ?? . Press /

null 553 Dec 29, 2022
Simple examples that help you get started with Appwrite + Apple with SwiftUI (=❤️)

Appwrite's Apple Playground ?? Appwrite playground is a simple way to explore the Appwrite API & Appwrite Apple SDK. Use the source code of this repos

Appwrite 28 Nov 22, 2022
Simple examples that help you get started with Appwrite + Swift for Server (=❤️)

Playground for Swift Simple examples that help you get started with Appwrite + Swift (= ❤️ ) This is Appwrite server side integration with Swift. For

Appwrite 24 Dec 10, 2022
Combine explanation + examples

Combine Playground These repository contains a Xcode Playground that goes over the basics of Combine and presents usage examples for the main componen

Andrés Pesate 2 Dec 29, 2021
📏 A set of advanced compositional layouts for UICollectionView with examples

compositional-layouts-kit If you like the project, please give it a star ⭐ It will show the creator your appreciation and help others to discover the

Astemir Eleev 378 Dec 27, 2022
Sample app to demonstrate the integration code and working of Dyte SDK for iOS, using Swift

docs-template by dyte ADD_DESCRIPTION_HERE Explore the docs » View Demo · Report Bug · Request Feature Table of Contents About the Project Built With

Dyte 8 Nov 26, 2021
A sample iOS app built using the Clean Swift architecture

Main Purpose is to create a simple project for Clean-Swift This project wrote with using Clean-Swift and MVP Design Pattern

Eyup Cimen 8 Oct 24, 2022
Sample app to demonstrate using Firebase features

Photoram Sample app to demonstrate using Firebase features Uses: Firebase authentification for login and registrations Firebase storage to keep image

Anna Zharkova 3 Jun 4, 2021
This is a sample project that supplements the tutorial written over at my blog on 'Building a music recognization app in SwiftUI with ShazamKit'

Shazam-Kit-Tutorial This is a sample project that supplements the tutorial written over at my blog on 'Building a music recognization app in SwiftUI w

Swapnanil Dhol 7 Mar 17, 2022