RetroVisor helps you with inspecting UIViews in your unit tests.

Overview

RetroVisor

RetroVisor helps you with inspecting UIViews in your unit tests. You do test views in unit tests, right? Right?

Usually, I mark subview properties of my custom views as private, which I think is the right practice, only exposing what is really needed to use a certain view. This makes testing said views a bit more complex, since there is no way to access those private members in the Unit Test target. This is where RetroVisor comes to help. It adds a new property on UIView and subclasses, called elements, which contains all the views subviews (recursively, skipping class names starting with _ or UIKit private classes) and allows for Collection like operations on these elements. It also provides matching and filtering helper functions, which help you find the specific view you are looking for more easily.

Retro What?

RetroVisor works by recursively searching a given UIView's subviews for views that match a certain criteria. Additionally it provides (very crude) functionality for simulating tapping on said views.

It is trying to mimic the behavior of XCUIElement by providing an interface for querying a view for its sub-elements

It supports:

  • finding views with text that contains a given string, i.e.:
// an array of views, which contain the text 'abc'
let viewsThatContainAbc = myViewController.view.elements.containing("abc")
  • finding views with text that exactly matches a given string, i.e.:
// an array of views, which display the text 'abc'
let viewsThatMatchAbc = myViewController.view.elements.matching("abc")
  • finding views that are of specific type:
// an array of UIButtons that are included in a view
let buttons = myViewController.view.elements.of(UIButton.self)
  • finding views that match a custom predicate:
// an array of UILabels which have red text color
let redLabels = myViewController.view.elements.of(UILabel.self).matching { label in
  label.textColor == .red
}

// OR

// specify which types you want to match by specifying the closure parameter type 
let doneButton = myViewController.view.elements.matching { (button: UIButton) in
  button.title(for: .normal) == "Done" && 
    button.isEnabled == true
}
  • tapping on a button:
// tap on a Button labeled 'done'
let doneButton = myViewController.view.elements.of(UIButton.self).matching("done").first
doneButton?.tap()

Note that tapping is implemented a bit clumsiliy, as sending UIControl Action events is not really supported by iOS without a running UIApplication object.

  • tapping on a view (which has a tap gesture recognizer attached):
// tap on a custom view
let label = myViewController.view.elements.of(UILabel.self)["Tap here for more info"].first
label?.tap()

Oh yes, it also supports subscripting on the elements property. A subscript means RetroVisor will search for a view with an exact text match, i.e. it is equivalent to calling view.elements.matching(text).

Using RetroVisor with your own UIView subclasses

The fastest way is to specify the type you are looking for in the matcher predicate

let customSubview = view.elements.matching { (subview: MyCustomViewClass) in
  true
}.first

But RetroVisor also provides Protocols through which it can search for text in the custom UI elements. These protocols are TextFindable, OptionalTextFindable and ImplicitTextFindable. Extend your custom UIView subclass with one of these protocols in your Unit Tests and RetroVisor will be able to search for it via the exposed text property.

extension CustomView: TextFindable {
  public var text: String {
    "I am a custom view"
  }
}

// In your XCTestCase
let customView = view.elements.containing("custom").first // finds the CustomView

RetroVisor uses these protocols under the hood for some common UIKit classes. Here is an example on how it extends UIButton to make it searchable by text

extension UIButton: OptionalTextFindable {
  public var text: String? {
    title(for: state)
  }
}

Running

To try out RetroVisor, clone the repo, create the project and install pods

$git clone ...
$xcodegen
$pod install
$open *.xcworkspace

You can also add it to your own project as a development pod. PLEASE only include it in your test target! It contains code that will not pass AppStore review.

"PathToWhereYouDownloadedRetroVisor" end end ">
target "MyApp" do
  target "MyAppTests" do
    pod 'RetroVisor', :path => "PathToWhereYouDownloadedRetroVisor"
  end
end

You can also install it through CocoaPods

pod 'RetroVisor'

Contributing

Contributions are very welcome, in form of Pull Requests. The project could also do with more unit tests for more specialized cases.

License

RetroVisor is available under the MIT license.

You might also like...
Create an easy to peek SwiftUI View to showcase your own data, catalog, images, or anything you'd like.
Create an easy to peek SwiftUI View to showcase your own data, catalog, images, or anything you'd like.

Create an easy to peek SwiftUI View to showcase your own data, catalog, images, or anything you'd like.

Are you sure the chemical compounds of your daily use are 100% safe? Use Chem-Aware, identify them right now!
Are you sure the chemical compounds of your daily use are 100% safe? Use Chem-Aware, identify them right now!

View Project On Devpost: Built With: PubChem's REST API How To Install Chem Aware: Prerequiste: Latest Version of Xcode and Simulators installed The a

A Simple way help you drop or drag your source (like UIImage) between different App.

A Simple way help you drop or drag your source (like UIImage) between different App.

FlutterNativeDragAndDrop - A package that allows you to add native drag and drop support into your flutter app
FlutterNativeDragAndDrop - A package that allows you to add native drag and drop support into your flutter app

native_drag_n_drop A package that allows you to add native drag and drop support

FocusSpace - A time-management tool to help you stay focus with your friends
FocusSpace - A time-management tool to help you stay focus with your friends

FocusSpace 🏆 ElleHacks2022 - (Telus) First Place 👩 Developers Manyi Cheng(@man

The Git interface you've been missing all your life has finally arrived.
The Git interface you've been missing all your life has finally arrived.

GitUp Work quickly, safely, and without headaches. The Git interface you've been missing all your life has finally arrived. Git recently celebrated it

A usermanager written in swift 3.0 saves you from hassle of saving your active user session.

SwiftUserManager A usermanager written in swift 3.0 saves you from hassle of saving your active user session. Call api and give the json to MOProfile

ExpoMod - a small application tool that lets you quickly setting up your computer for presentations / exhibitions
ExpoMod - a small application tool that lets you quickly setting up your computer for presentations / exhibitions

ExpoMod is a small application tool that lets you quickly setting up your computer for presentations / exhibitions. Or simply having useful shortcut to not being distract and keep awake your computer.

Displays your HomeKit temperature sensors in your menu bar
Displays your HomeKit temperature sensors in your menu bar

Temperature Glance Displays your HomeKit temperature sensors in your menu bar Screenshot Note This is a very simple app that I made for myself but dec

Owner
Vid Tadel
iOS, MacOS, Linux experience with: C,C++,Obj-C,Swift
Vid Tadel
Runtime Mobile Security (RMS) 📱🔥 - is a powerful web interface that helps you to manipulate Android and iOS Apps at Runtime

Runtime Mobile Security (RMS) ?? ?? by @mobilesecurity_ Runtime Mobile Security (RMS), powered by FRIDA, is a powerful web interface that helps you to

Mobile Security 2k Dec 29, 2022
This is BouncyBall, from Develop in Swift Explorations Unit 3, completed into Part 3, where the onTapped function gets a little squirrelly

This is BouncyBall, from Develop in Swift Explorations Unit 3, completed into Part 3, where the onTapped function gets a little squirrelly. Use this version when you add a function to be called when the funnel is tapped and taps aren't registered in the simulator.

Teaching Develop in Swift 0 Nov 20, 2021
ReactorKit + Unit Test

left7 현재 세계에서 상영중인 목록을 보여주는 ReactorKit + CleanArchitecture를 도입한 개인 프로젝트입니다. Directory Tree ├── Left7 │ ├── Application │ ├── Utility │ ├── Prese

LIMJISEONG 3 Nov 15, 2022
A repository that demonstrates the difficulty to run async tests with Xcode 13.2 beta on pre iOS-15 simulators

A repository that demonstrates the difficulty to run async tests with Xcode 13.2 beta on pre iOS-15 simulators This demonstration uses an iOS 13.7 sim

Gwendal Roué 0 Nov 1, 2021
You can monitor your APIs and websites on your menubar. Gives you status code 🎉 Cool & good

Hope not. Monitor your APIs and websites on your menubar. For macOS. Right now! YyeeeHav!

Steven J. Selcuk 10 Nov 29, 2022
Tutorials from sparrowcode.io website. You can add new, translate or fix typos. Also you can add your apps from App Store for free.

Tutorials from sparrowcode.io website. You can add new, translate or fix typos. Also you can add your apps from App Store for free.

Sparrow Code 31 Jan 3, 2023
Tutorials from sparrowcode.io website. You can add new, translate or fix typos. Also you can add your apps from App Store for free.

Страницы доступны на sparrowcode.io/en & sparrowcode.io/ru Как добавить свое приложение Добавьте элемент в json /ru/apps/apps.json. Если ваше приложен

Sparrow Code 30 Nov 25, 2022
MQTagged provides a Tagged structure which helps differentiating between values of the same type.

MQTagged provides a Tagged structure which helps differentiating between values of the same type.

Miquido 2 Jun 29, 2022
An Xcode Source Editor Extension that helps navigating to many places easier

XcodeWay ❤️ Support my apps ❤️ Push Hero - pure Swift native macOS application to test push notifications PastePal - Pasteboard, note and shortcut man

Khoa 543 Dec 1, 2022
Quotes shows you famous quotes to, hopefully, give you enlightment

"Quotes" shows you famous quotes to, hopefully, give you enlightment! You can also save/favorite the quotes that you liked to review later or show to your friends!

Filipe Kunioshi 1 Mar 7, 2022