Xcode Playground might be actually useful if it doesn't crash like every 15 minutes and suck up my CPU usage
I was excited when Apple first announced Playground. After updating to the latest Xcode (at that time), I hurried to type some code in it and amazed by its instant output, then it crashed like after 10 minutes, wtf Apple?!
Playground is a great place to test out small chunk of code as you dont need simulator to see the output and the output is almost immediate. The problem with it is that it crashes really often and causes your mac fan to spin loudly.
Here are some tips to reduce Playground crashes / CPU usage, improve productivity and using Playground for testing JSON.
- Set to manually run
- Map shortcut key to execute playground
- Set to indefinite execution for asynchronous function
Xcode playground stuck in Launching Simulator or Running? Read the workaround fix here .
Set to manually run
One reason Playground eat up CPU usage is because it will auto build and run your code every few seconds / keystroke. To reduce CPU usage, simply turn off auto run by selecting Manually Run like this :
Then you can click the same button (▶️) again to run the code when you have finished typing. This should reduce crash frequency in Playground and also lower the CPU usage.
Map shortcut key to execute playground
Don't like to move mouse around and prefer to use keyboard shortcuts? By default, Xcode doesn't have a shortcut key to execute playground code, you can assign a shortcut key to it like this :
-
Select Xcode > Preferences
-
Select Key Bindings, search for Execute Playground and input the shortcut key you want
Now you can run the code in Playground by pressing the shortcut key you have just set.
Map ⌘R key to execute playground
You might be used to press ⌘R to run Xcode project code and want to use back the same key to execute Playground code as well.
Sadly, an error will occur when you try to map ⌘R to Execute Playground as this key has already been mapped to "Run" :
You can get around this error by using the macOS system keyboard shortcut setting to override it.
-
Open System Preferences > Keyboard
-
Select Shortcuts
-
Select App Shortcuts, then click "+". Select
Xcode
for the Application, typeExecute Playground
for the Menu Title (must follow the text exactly, else it might not work), then input⌘R
as the keyboard shortcut.
You should see the 'Execute Playground' shortcut key created for Xcode like this :
Now you can execute the Playground code by pressing ⌘R just like in normal Xcode Project!
As of Xcode 9, assigning ⌘R to Execute Playground
won't conflict with Run
as typical Xcode project dont have the Playground execute button and Playground dont have the run button of Xcode Project. You can stop worry about key conflict (for now).
Set to indefinite execution for asynchronous function
I often use Playground to test JSON response received from a web API, it's really fast to test without having to open simulator.
Let's say we have a URLSession code to get Car JSON data like this in Playground:
After executing it, you will notice that the response data is not shown in the console log, but when you try it in a Xcode project, it works! 😫
This is because by default, Playground will stop running when it reach the last line of the code. After executing the last line task.resume()
, Playground will stop running immediately, even before the JSON response is received, hence the completion handler of the URLSession will not get executed and the output didnt show.
Here's a visual explanation:
To make Playground wait for the JSON response to return, we can add some code to tell the Playground to keep on running until we stop them :
import UIKit
import Foundation
// Import this to modify Playground setting
import PlaygroundSupport
// Tell playground to keep on running until we click the stop button
PlaygroundPage.current.needsIndefiniteExecution = true
struct Car: Decodable {
let name: String
let horsepower: Int
}
...
Now execute the Playground code again and Playground will keep on running, wait for the JSON response to arrive and parse it:
Remember to click the Stop button when you have retrieved the JSON response to conserve CPU usage.
Setting PlaygroundPage.current.needsIndefiniteExecution = true
is really helpful when you want to run asynchronous function which the result will not return immediately in the Playground.
Using code to stop the playground execution
Update: 15 May 2018
Scott has commented about using PlaygroundPage.current.finishExecution()
to stop the execution of the playground programmatically so that we don't have to remember to click the Stop button. Thanks Scott!
We can instruct the Playground to stop executing by adding PlaygroundPage.current.finishExecution()
inside the completion handler. This will cause the playground to stop running after parsing the received json or after encountering error while parsing json.
// Get car json data
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let car = try decoder.decode(Car.self, from: data)
print("car name is \(car.name)")
print("car horsepower is \(car.horsepower)")
} catch let error {
print("Failed to decode JSON:", error)
}
// stop the playground execution
PlaygroundPage.current.finishExecution()
})