肘子的 Swift 记事本

Exploring Xcode Playground (Part 1)

Published on

Get weekly handpicked updates on Swift and SwiftUI!

In the same year that Swift language was released (2014), Apple integrated Playground function into Xcode. Compared to standard Xcode projects, Playground launches faster, is lighter, and is widely used for Swift language learning, framework API testing, quick data processing, collecting inspirations, and many other purposes. This series will introduce a large number of tips on using Xcode Playground, involving stability, third-party libraries, resource management, asynchronous processing, document annotation, etc., allowing you to explore Playground and make it a useful tool in your work and learning.

Creation and Configuration

.playground vs .playgroundbook

The Playground project created in Xcode is saved as a package with a .playground suffix (the files can be viewed by displaying the package contents). .playground can be directly opened in Xcode and Swift Playgrounds version 3.x or higher.

.playgroundbook is a package format unique to Swift Playgrounds. Compared to .playground, it contains many unique features, which are mainly used to improve the experience of Swift Playgrounds in education and entertainment. .playgroundbook format can only be opened in Swift Playgrounds.

For more information about the latest Swift Playgrounds 4, please read Swift Playgrounds 4: Entertainment or Productivity.

The tips introduced in this series mainly focus on Xcode Playground (.playground). Most of the tips are applicable to both Xcode and Swift Playgrounds. Tips that apply only to a specific platform will be clearly marked.

How to create a Playground project

Creating a Playground project in Xcode

In Xcode, click on File -> New -> Playground to create an Xcode Playground project in .playground format.

https://cdn.fatbobman.com/image-20211223162302918.png

Playground provides several preset templates. The system settings for the template selection interface (iOS, macOS) only affect the template code and do not automatically set the Page’s runtime environment.

https://cdn.fatbobman.com/image-20211223164000220.png

Xcode can open Playground projects and also add Playground projects to a Project or Workspace (helpful for testing SPM or calling custom types in a Project).

Creating a Playground project in Swift Playgrounds

In Swift Playgrounds 4, you can directly create an Xcode-compatible Playground project (.playground). Click on “View All” at the bottom of the homepage and select Xcode Playground.

https://cdn.fatbobman.com/image-20211224160807063.png

Note: The Playground button at the bottom of the homepage creates a playgroundbook.

https://cdn.fatbobman.com/image-20211223161945374.png

Projects created in Swift Playgrounds are saved by default in the Playgrounds directory on iCloud Drive. Try not to edit a project at the same time on both Xcode on macOS and Swift Playgrounds on iPad, as it can cause version conflicts.

How to create multiple Playground Pages

Playground encourages developers to focus on one topic at a time by distributing topics across different Pages to help organize code and corresponding resources.

By default, a new Playground project only has one Page (in Single Page mode, the Page and Playground project will be displayed together in the left navigation bar). Each Page can have its own corresponding live view.

https://cdn.fatbobman.com/image-20211223164606421.png

In Xcode, you can create a new Playground Page by using the File menu or right-clicking on the Playground project in the navigation bar.

https://cdn.fatbobman.com/image-20211223170027028.png

https://cdn.fatbobman.com/image-20211223170047358.png

When there is more than one Page, the Playground project and the Pages will be displayed separately.

In Swift Playgrounds 4, click the “Edit” button in the sidebar to enter the editing mode, and click the ”+” button to create a new Page.

https://cdn.fatbobman.com/image-20211223175608008.png

It is possible to adjust the order of Pages and modify the name of each Page (which not only helps with identification, but also makes it easier to quickly jump between different Pages).

In single Page mode, there is only one set of Sources and Resources directories in the project. In multi Page mode, in addition to the Sources and Resources directories under the project root directory, each Page also has its own Sources and Resources directories.

https://cdn.fatbobman.com/image-20211223170801239.png

Each Page should be treated as an independent Module, and the code in Page A cannot be called by Page B.

How to Debug Code

Playground does not provide the ability to set breakpoints, but it can meet some debugging needs by specifying the execution end point or single-stepping.

In Xcode, click the execution button on the left side of the code line number (the button needs to be blue) to specify the current execution endpoint.

https://cdn.fatbobman.com/image-20211223180328839.png

Click the blue execute button after the current ending position to continue execution. Click the execute button below the code editing area to execute all the code again.

After entering new code, you can use the Shift-Return method to let Playground execute the code that has not yet been executed up to this line. This method will be very useful when you do not want to repeatedly execute long-time-consuming code segments or want to keep data stable (network data or random data), such as machine learning.

In single-step mode, there is no need to reset the execution for code whose first line has a blue execute button if you modify it. If you modify code lines that have already been executed (the first line is gray), you must reset Playground (click the execute button below the code editing area) to reflect the changes you made.

Swift Playgrounds does not provide a function to set the end of execution, but it provides a setting for single-step execution. Click the instrument button at the bottom of the screen to set the debugging mode.

https://cdn.fatbobman.com/image-20211223180913086.png

Improving the stability of running under Xcode (Xcode Only)

Setting the running environment

In Xcode, you can set the running environment for the Playground in the Playground Settings on the right side.

https://cdn.fatbobman.com/image-20211223144432779.png

Setting the runtime environment to macOS when there is no dependency on iOS framework code can reduce instability caused by iOS simulator errors. When there are multiple Playground Pages, each Page can be individually configured with its corresponding runtime environment.

https://cdn.fatbobman.com/image-20211223144916673.png

When there are multiple Playground Pages, clicking on the project name at the top allows you to set a unified runtime environment for all Pages.

https://cdn.fatbobman.com/image-20211223144934347.png

Swift Playgrounds is only compatible with Playground projects running on iOS.

Change the running mode to manual

When the running mode is set to automatic, the system will automatically run the code and display the results every time you modify the code. Automatic mode works well when the code is simple and has less content, but once the code becomes lengthy and complex, automatic running mode will consume more system resources and may lead to unstable running.

https://cdn.fatbobman.com/image-20211223150747157.png

By long-pressing the execute button below the code editing area, you can choose between two modes.

In Xcode’s settings, you can assign suitable shortcuts for Playgrounds to improve your operational efficiency.

https://cdn.fatbobman.com/image-20211223151240337.png

How to view the result pane

Playground in Xcode has a unique display area called the result pane, which not only displays the current value and history of each line of code, but also displays information about the number of calls, file information, and more.

https://cdn.fatbobman.com/image-20211224091253473.png

For example, in the above figure, line 55 shows the size information of the image, line 57 shows the value of y in the current line, and line 59 shows the number of times the current line is executed in the loop.

https://cdn.fatbobman.com/image-20211224091811968.png

When the mouse approaches the screen icon on the right, an eye icon will appear. Clicking the eye icon will display the Quick Look content corresponding to that line of code. Clicking the screen icon will display the Quick Look content in the code editing area (inline display).

https://cdn.fatbobman.com/image-20211224091947472.png

Quick Look content can be switched between Latest value, Value history, and Graph (the number of switchable modes will vary depending on the type).

https://cdn.fatbobman.com/image-20211224092212674.png

Operations for Quick Look in Swift Playgrounds are similar to those in Xcode, and the code execution efficiency can be improved by disabling the “Enable Results” option.

How to create custom Quick Look

Apple has provided Quick Look support for many system types in Playground. By making other system types (mainly focused on newer APIs) and our custom types meet the CustomPlaygroundDisplayConvertible protocol, Quick Look support can be provided.

For example, the new AttributedString introduced at WWDC 2021 does not currently support Quick Look, but by converting it to NSAttributedString in playgroundDescription, the correct Quick Look can be displayed directly in Playground.

The following image shows the situation where CustomPlaygroundDisplayConvertible protocol is not satisfied. The Quick Look of AttributedString is in the Dump style of the structure.

https://cdn.fatbobman.com/image-20211224142839306.png

Apple has provided a proper Quick Look implementation for NSAttributedString, which converts AttributedString into NSAttributedString for better display performance.

Swift
extension AttributedString: CustomPlaygroundDisplayConvertible {
    public var playgroundDescription: Any {
        NSAttributedString(self)
    }
}

https://cdn.fatbobman.com/image-20211224142915994.png

PlaygroundSupport

What is PlaygroundSupport?

PlaygroundSupport is a framework that is specifically designed to extend Playground. It provides features such as sharing data, managing real-time views, and controlling Playground running modes.

To import the framework into the code of the required Playground Page, use import PlaygroundSupport.

How to get the result of an asynchronous execution (Swift Playgrounds Only)

In older versions of Xcode (this issue has been resolved in Xcode 12 and Xcode 13) and in Swift Playgrounds, Playground does not wait for the return result of asynchronous code by default. It ends the execution after completing all code calls. To terminate the running status after obtaining the result of asynchronous execution, set the Playground to infinite execution mode.

Executing the following code in Swift Playgrounds will not get the print result.

Swift
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
    print("Hello world")
}

Import PlaygroundSupport and set needsIndefiniteExecution to true.

Swift
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
    print("Hello world")
}

Each Page needs to be set separately and cannot be set at the end of the code.

How to execute async/await code

This section does not require PlaygroundSupport support, but is placed here for the sake of proximity to the “How to Get the Result of Asynchronous Execution” chapter.

When using new async/await code in Playground, you need to import _Concurrency to run the code normally.

Swift
import _Concurrency

Task {
    try await Task.sleep(nanoseconds: 2_000_000_000)
    print("Hello world")
}
  • To execute the above code in Swift Playgrounds, needsIndefiniteExecution needs to be set. *

How to create a live view

You can use a live view to add interactivity to your Playground, experiment with different user interface elements, and build custom elements. By importing PlaygroundSupport and setting the live view of the current page to your custom view or view controller, you can add an interactive live view to your Playground Page.

https://cdn.fatbobman.com/image-20211224105528272.png

Real-time view supports both SwiftUI views and UIKit (AppKit) views and view controllers. SwiftUI views need to be set using setLiveView.

Swift
import PlaygroundSupport
import UIKit

let lable = UILabel(frame: .init(x: 0, y: 0, width: 200, height: 100))
lable.text = "Hello world"
lable.textAlignment = .center
lable.textColor = .red

//PlaygroundPage.current.setLiveView(lable) UIKit 视图,两种设置方法都可以
PlaygroundPage.current.liveView = lable

After setting up a live view, Playground automatically sets needsIndefiniteExecution to true.

If you want to terminate execution through code, you can use PlaygroundPage.current.finishExecution().

In Xcode, you can also customize the Touch Bar using PlaygroundPage.current.liveTouchBar.

How to display other type instances in a live view

Any type that conforms to the PlaygroundLiveViewable protocol can be set as a live view.

The following code allows UIBezierPath to be displayed directly in the live view:

https://cdn.fatbobman.com/image-20211224140536980.png

Swift
import PlaygroundSupport
import UIKit

let path = UIBezierPath()
var point = CGPoint(x: 0, y: 0)
path.move(to: point)
point = CGPoint(x: 100, y: 0)
path.addLine(to: point)
point = CGPoint(x: 200, y: 200)
path.addLine(to: point)

extension UIBezierPath: PlaygroundLiveViewable {
    public var playgroundLiveViewRepresentation: PlaygroundSupport.PlaygroundLiveViewRepresentation {
        let frame = self.bounds
        let view = UIView(frame: frame)
        view.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 1)
        let layer = CAShapeLayer()
        layer.path = self.cgPath
        view.layer.mask = layer
        return .view(view)
    }
}

PlaygroundPage.current.liveView = path

Accessing the Playground’s Shared Directory (playgroundSharedDataDirectory)

playgroundSharedDataDirectory points to a directory where resources can be shared among Playground Pages.

If the Playground Page is set to macOS mode, the contents of this directory can be shared among Pages of different Playground projects in macOS mode. If the Playground Page is running in iOS mode, the contents of this directory can only be shared among Pages of the same Playground project in iOS mode (each Playground project has its own corresponding iOS simulator).

Swift
import PlaygroundSupport
import AppKit

let url = playgroundSharedDataDirectory.appendingPathComponent("playground1.png")
if let data = try? Data(contentsOf: url) {
    _ = NSImage(data: data)
}

On macOS, the directory is the Shared Playground Data subdirectory under the user’s Documents directory. The system does not create this directory automatically and needs to be created manually.

The playgroundSharedDataDirectory is mainly used to store data that is required by multiple Playground projects on macOS. Within a single Playground project, data can be shared between pages through the project’s Resource directory.

Summary

In ”Part 2”, we will focus on topics such as SPM, resource management, assistant code, and document annotations.

I'm really looking forward to hearing your thoughts! Please Leave Your Comments Below to share your views and insights.

Fatbobman(东坡肘子)

I'm passionate about life and sharing knowledge. My blog focuses on Swift, SwiftUI, Core Data, and Swift Data. Follow my social media for the latest updates.

You can support me in the following ways