Playground driven development in Swift

Khoa Pham
Khoa Pham
Apr 19, 2018 · 12 min read
Image for post
Image for post

The need to quickly tweak UI

For iOS, we need to continuously tweak the UI according to the design to make it fit small sized handheld devices. That process involves us changing code, compiling, waiting, checking, then changing code and much more…Tools like Flawless App helps to easily compare between result on iOS app and Sketch design. But the real pain lies in the compiling part, which takes the most time, and that is even worse with Swift. It makes us less efficient to do quick iteration. It looks like the compiler is mining Bitcoin secretly while pretending to compile 😅

If you work with React, you know that it is just merely UI representation of state UI = f(state).You get some data, you build a UI to represent it. React has hot reloader and Storybook which make it super fast to do UI iterations. You make some changes and see the result immediately. You also get a complete overview of all the possible UIs for each state. You know you want the same thing in iOS!

Playground

I wasn’t very convinced at first, and I saw lots of complains about slow or unresponsive Playground. But after seeing Kickstarter iOS app using Playground to faster their styling and development process, it impressed me a lot. So I started using it successfully in some of the apps. It is not rerendering immediately like React Native or Injection App, but hopefully it will be better over the years 😇

Or at least it depends on the development community. The scenario with Playground is that we only style one screen or component at a time. That forces to think carefully about dependencies, so I can just import a particular screen and iterate on that in Playground.

Custom framework in Playground

Image for post
Image for post

Creating app framework

The demo for this article is an iOS project that uses CocoaPods to manage dependencies. For the time of this post, it is Xcode 9.3 and Swift 4.1.

Let’s walk through steps on making Playground work with project that uses CocoaPods. There are also some good practices.

Step 1: Adding a pod

Create a new project, let’s call it UsingPlayground. The app shows some kind of confetti particles 🎊. There are many options to adjust the way those particles show up, and I choose Playground to iterate on that.

For this demo, we will use CocoaPods to fetch one dependency called Cheers because we want something fun. Cheers helps show fancy confetti effect if you want to congratulate users with some achievements.

Create a Podfile with UsingPlayground as app target:

Step 2: Using the pod in your app project

This is very straightforward, just to make sure the pod works. Write some code to use the Cheers:

Build and run the project to enjoy a very fascinating confetti 🎊

Step 3: Adding a CocoaTouch framework

In your workspace select the UsingPlayground project and add new CocoaTouch framework. This is the framework that contains our app code. Let’s call it AppFramework.

Image for post
Image for post

Now add the source files you want to test into this framework. For now, just check file ViewController.swift and add it to the AppFramework target.

Image for post
Image for post

For this simple project, there is only one ViewController.swift . If this file references code from other files, you need to add related files to the AppFramework target too. This is how you should be clever about dependencies.

Step 4: Adding files to AppFramework

Playground is no magic. You need to compile your AppFramework for every time you change the code, otherwise the changes won’t be reflected in your Playground. If you don’t mind slow compile time, you can add all files to your AppFramework . Simply expanding group folders, selecting and adding files to the target takes a lot of time. Not to mention that if you select both folder and files, you won’t be able to add them to your target. You can only add files to your target.

Image for post
Image for post

A quicker way is to go to Compile Sources under your AppFramework target Build Phase . Here all files are expanded automatically for you, all you need to do is to select them and click Add .

Image for post
Image for post

Step 5: Public

Open access and public access enable entities to be used within any source file from their defining module, and also in a source file from another module that imports the defining module. You typically use open or public access when specifying the public interface to a framework.

Step 6: Adding pod to AppFramework

Now run pod install again. In some rare cases, you need to run pod deintegrate and pod install to start from a clean slate.

Step 7: Adding a Playground

Image for post
Image for post
Image for post
Image for post

Step 8: Enjoy

Playground is best for testing our framework independently or our app. Select MyPlayground and type the code below. Here we tell liveView to render our ViewController:

Sometimes you want to test a piece of the pod you want to use. Create a new Playground Page called CheersAlone. Here you just need to import Cheers.

Image for post
Image for post

Let’s use liveView of PlaygroundPage to display a live view. Remember to toggle Editor Mode so you can see Playground result. And 🎉

Image for post
Image for post

There is a button in the bottom panel of Xcode. That’s where you can toggle between Automatically Run and Manual Run behaviour. And you can stop and start the Playground by yourself. Pretty neat 🤘

Bridging header

If you need a review of how to use bridging header, read Swift and Objective-C in the Same Project.

Just make sure the path to the header is correct:

Image for post
Image for post

Step 1: Import Bridging Header

Using bridging headers with framework targets is unsupported

The solution is to refer to that Bridging-Header.h inside your AppFramework.h

Image for post
Image for post

Step 2: Public header

Include of non-modular header inside framework module

For this to work, you need to add the Bridging-Header.h to the framework, and declare it as public. A search on SO shows this quote

Public: The interface is finalized and meant to be used by your product’s clients. A public header is included in the product as readable source code without restriction.

Private: The interface isn’t intended for your clients or it’s in early stages of development. A private header is included in the product, but it’s marked “private”. Thus the symbols are visible to all clients, but clients should understand that they’re not supposed to use them.

Project: The interface is for use only by implementation files in the current project. A project header is not included in the target, except in object code. The symbols are not visible to clients at all, only to you.

So, select Bridging-Header.h and add it to AppFramework and set visibility as public:

Image for post
Image for post

If you go to Build Phases of AppFramework you will see the 2 header files there.

Image for post
Image for post

Now, select scheme AppFramework and hit Build, it should compile without any errors.

Fonts, localised strings, images and bundle

Here I use Anchors for convenient and declarative Auto Layout 🤘. It is also for showing later how Swift Playground can handle any number of frameworks.

Now, select the app scheme UsingPlayground and hit build and run. The app should look like below, and of course it can pick up the right image and localised string.

Image for post
Image for post

Let’s see if our Playground can recognise these assets. Create a new page in MyPlayground called Resource and type the following

Wait a bit for the Playground to finish running. Oops. Things are not so great in the Playground, it does not recognise the images and localised strings 😢

Image for post
Image for post

Resources folder

Main bundle

Before you can locate a resource, you must first specify which bundle contains it. The Bundleclass has many constructors, but the one you use most often is main. The main bundle represents the bundle directory that contains the currently executing code. So for an app, the main bundle object gives you access to the resources that shipped with your app.

If your app interacts directly with plug-ins, frameworks, or other bundled content, you can use other methods of this class to create appropriate bundle objects.

// Get the app's main bundle
let mainBundle = Bundle.main

// Get the bundle containing the specified private class.
let myBundle = Bundle(for: NSClassFromString("MyPrivateClass")!)

Step 1: Adding resources to AppFramework target

Image for post
Image for post

Step 2: Specifying bundle

Change the code in ResourceViewController to

Every time we change code in AppFramework, we need to recompile it. This is important. Now open the Playground, it should pick up the right assets.

Image for post
Image for post

What about custom font?

Download a free font called Avengeance and add this font to both our app and AppFramework target.

Add the code to specify font in ResourceViewController, remember to recompile AppFramework :

And tada, both your app and Playground can see the custom font 🎉

Image for post
Image for post

Device size and trait collection

And AppEnvironment, which is like a stack to change dependencies and app properties, like bundle, locale and language. See one example about Signup screen:

Couldn’t lookup symbols

error: Couldn’t lookup symbols:__T06Cheers9CheerViewCMa__T012AppFramework14ViewControllerCMa__T06Cheers8ParticleO13ConfettiShapeON__T06Cheers6ConfigVN

Problem with symbol lookup means that Playground can’t find your code. It may be because your class is not exposed as public, or you forget to add files to AppFramework target. Or the referenced pods is not visible in AppFramework Framework search path , …

Version 1.5.0 brings static library support, also changes in modular header. In the mean time, the demo switches back to CocoaPods 1.4.0, you can take a look at UsingPlayground demo.

In the terminal, type bundler init to generate Gemfile. Specify 1.4.0 for cocoapods gem:

Now run bundler exec pod install to run pod commands from CocoaPods 1.4.0. The problem should be solved.

Where to go from here

Flawless iOS

🍏 Community around iOS development, mobile design, and…

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store