Exploring Xcode Playground (Part 1)
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.
A Chinese version of this post is available here.
Don’t miss out on the latest updates and excellent articles about Swift, SwiftUI, Core Data, and SwiftData. Subscribe to fatbobman’s Swift Weekly and receive weekly insights and valuable content directly to your inbox.
In light of the fact that my blog, Fatbobman’s Blog, now offers all articles in English, starting from April 1, 2024, I will no longer continue updating articles on Medium. You are cordially invited to visit my blog for more content.
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.
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.
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.
Note: The Playground button at the bottom of the homepage creates a playgroundbook
.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
Quick Look content can be switched between Latest value, Value history, and Graph (the number of switchable modes will vary depending on the type).
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.
Apple has provided a proper Quick Look implementation for NSAttributedString, which converts AttributedString into NSAttributedString for better display performance.
extension AttributedString: CustomPlaygroundDisplayConvertible {
public var playgroundDescription: Any {
NSAttributedString(self)
}
}
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.
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
print("Hello world")
}
Import PlaygroundSupport
and set needsIndefiniteExecution
to true.
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.
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.
Real-time view supports both SwiftUI views and UIKit (AppKit) views and view controllers. SwiftUI views need to be set using setLiveView.
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:
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).
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 “Exploring Xcode Playground (Part 2)”, we will focus on topics such as SPM, resource management, assistant code, and document annotations.
If you found this article helpful or enjoyed reading it, consider making a donation to support my writing. Your contribution will help me continue creating valuable content for you.
Donate via Patreon, Buy Me aCoffee or PayPal.
Want to Connect?
@fatbobman on Twitter.