In the previous section, we introduced some knowledge about Xcode Playground in terms of creation, configuration, Quick Look, live view, etc. This article takes a closer look at Xcode Playground, focusing on more in-depth content such as auxiliary code, resource management, exploring packages using Playground, and Xcode projects.

The original post was posted on my blog wwww.fatbobman.com

Welcome to subscribe my public account: [Elbow’s Swift Notepad]

Auxiliary code and resources

Package structure and file additions for Xcode Playground

The Xcode Playground project does not rely on project configuration files; pages, helper code, resource files, call permissions, and so on are managed through a directory structure within the. Playground package.

Single Page

After creating a new Xcode Playground project, the default package file structure looks like this (right-click on the Playground project file and select Show package contents) :

The newly created project has only one Page. Xcode Playground will display the Page merged with the contents of the Playground project. Swift is the main code content of the current unique Page. Although Sources and Resources are shown in the Xcode navigation bar, there is no directory created for them in the.playground package because there is currently no content for either.

The Sources directory is where auxiliary code (also known as shared code) is stored. Developers typically save custom types, default methods, test snippets, the custom Quick Look mentioned above, custom live view types, and so on in Swift code files in the Sources directory.

Auxiliary code can be added in a variety of ways. You can drag and drop the code file directly from Xcode to the Sources project in the navigation bar. Or copy the code file in the Finder to the Sources directory; Or right click on Sources to create a new Swift code file directly.

The Resources directory is used to store the main Page code (Contents. Swift) as well as all kinds of resource files that need to be used in the auxiliary code, such as images, sounds, JSON, Assets, etc. You add them in the same way you add auxiliary code.

Resource files can only be saved in the Resources directory or its subdirectories, and auxiliary code can only be saved in the Sources directory or its subdirectories.

Playground will automatically create directories in the.playground package when we add content to the Sources or Resources directories.

Multiple pages

Compared to the Xcode Playground project, which has only one Page, the directory structure changes significantly when multiple pages are included.

When a new Page is added to the Playground project created above, the Playground project (NewPlaygrounds) and Page will be displayed separately. We named the original Page Page1 and the new Page Page2.

This can be seen in the Xcode navigation bar. Sources and Resources are included under the Project level (NewPlaygrounds), and Sources and Resources are also included under each Page. At this time. The structure in the playground package will look like this:

Instead of the Contents. Swift file that was in the root directory, the Pages directory has been added with two.xcplaygroundPage package files that correspond to Page names. Looking further at the Contents of the.xcPlaygroundPage package, you can see that each has a Contents. Swift (the main code file for Page).

Add auxiliary code and resource files for Page1 in Xcode, and the contents of the Page1. Xcplaygroundpage package will also change.

Note that:

  • When a new Page is added, the auxiliary code and resource files that were originally added in a single Page condition are retained in the Sources and Resources directories at the project level
  • If you delete a Page in a multi-page state, the directory structure will not return to the state (single Page) that it was when the Playground project was created

Management and invocation of auxiliary code

In Xcode Playground, each Page is treated as a separate Mini app (unrelated to each other), and each Sources directory is treated as a Module.

Take the project created above as an example:

  • Sources at the project level will be compiled into the NewPlaygrounds_Sources (project name + _Sources) module, Page1’s Sources will be compiled into the Page1_PageSources (page name + _PageSources) module.

  • Playground will be auxiliary code for Page1, implicitly importing NewPlaygrounds_Sources

  • Playground will implicitly import the NewPlaygrounds_Sources and Page1_PagesSources modules for Page1’s main code (contents.swift)

In layman’s terms, project helper code can be called in all Page helper code. In the main code of each Page, you can call the auxiliary code of the project as well as the auxiliary code of the current Page.

Because management is based on the Module, only code defined as public can be called by code that is not in this Module.

Add the following methods to the Sources helper. swift file at the project level:

import Foundation

public func playgroundName(a) -> String {
    "NewPlaygrounds"
}
Copy the code

Add the following method to Page1’s Sources directory page1code. swift:

import Foundation

public func pageName(a) -> String {
    playgroundName() + " Page1"
}
Copy the code

In the main code of Page1, the project helper code module or the public code of Page1 helper code module can be called directly:

let playgourndName = playgroundName()
let currentName = pageName()
Copy the code

Playground implicitly imports the required modules for us without having to import them ourselves. Of course, you can also manually import the corresponding modules in different code to improve understanding.

Unlike the main Page code, the secondary code does not support line-by-line execution of Playground, Quick Look, etc. Playground will finish compiling the auxiliary code (automatically) before running the main Page code.

Other things to note about auxiliary code:

  • The main or helper code of a Page cannot call helper code of another Page
  • Since each Page can be set up separately for the runtime (iOS or macOS), the helper code should be compatible with the runtime, especially if you include pages for different runtime environments in a project, it is important to ensure that the helper code for the project runs on different platforms.

Resource file organization and considerations

The resources are organized in the same directory format as the auxiliary code, divided into shareable resources for the Playground project and page-specific resources.

Resource files stored in the root directory of the project can be used by the main and auxiliary Page code of each Page. Resources stored in a Page’s Resources directory can only be used by the main and auxiliary code of the Page.

Playground didn’t separate project resources from Page resources when executing the Page code. Instead, he created a directory for each Page to aggregate resources and created links (surrogates) for the resources available for that Page. Therefore, Playground cannot support both resources if the project resource file has the same name as the Page-specific resource file.

Because Playground aggregates all the currently page-accessible resources into a single directory, both project and Page-specific resources can be accessed using bundle. main in the main and secondary Page code.

The following code can get a summary directory of available resources for Page1:

let url = Bundle.main.url(forResource: "pic", withExtension: "png")
Copy the code

Name. json is a resource for Page1 and pic. PNG is a resource for the project. Are all grouped together (so that if there is a duplicate name, normally only the content from the exclusive resource can be used).

Assets files (.xcassets) are slightly special. Each Page can support only one asset. If the Page does not have Assets in its own resources, the Page can use Assets in the project resources. If the Page resource contains Assets, they are ignored regardless of the Assets name in the project resource.

Playground currently has a Bug (at least in Xcode 12, Xcode 13) dealing with resource file renaming and deletion. If you rename a resource file in Xcode, Playground creates a substitute for the new name in the directory where the substitute is stored, but does not remove the original name. If the resource file is deleted, the corresponding proxy file is not deleted. As a result, files can be retrieved even if the resource name is different from the name called in the code (the original name is still used in the code). Currently, there is no way to reset the directory. If necessary, you can locate the directory and manually delete invalid files.

In Swift Playground, there is no way to add Resources for each Page individually; all Resources are placed in the Resources directory in the project layer. If you do need to add resources for a single Page, you can add them in Xcode or finder and open them in Swift Playground.

Playground preprocesses (compiles) resources in certain formats, such as.xcassets and.mlModel, and the processed resources can be configured and managed directly in Playground.

How to use localization files (mainly for Swift Playgrounds)

Similar to SPM for localization management, you only need to create the required language directory (for example, en.lproj, zh-cn. Lproj) in the resource file directory, and then add the string files and resource files of the corresponding language to the directory.

When Swift Playgrounds executes the Page code, it invokes the correct resource based on the current system Settings.

Xcode Playground does not provide convenient locale Settings for the runtime environment. Developers can use the UITraitCollection to set up the iOS emulator in the Xcode Playground to a certain extent.

How do I test Core Data code

If you want to learn and test various functions of Core Data in Playground, you should pay attention to the following:

  • Playground is not supported.xcdatamodeldFormat of a configuration file. You need to create a Core Data project in Xcode and edit what you need.xcdatamodeldAfter the file, compile the project. Will compile in the package.momdCopy to Playground’s resources directory

  • Playground does not support automatic generation of managed object definitions. You can generate the corresponding code in the Xcode project using the Create NSManagedObject Subclass and copy the code into the auxiliary code for the Playground (you can also write it by hand if the definition is not complicated).

The document

Add renderable annotation documents to your code

Compared to standard Xcode projects, Playground can render specific annotation documents in the main Page code.

Adding a renderable annotated document to our Playground is as simple as adding: after the standard annotation identifier.

import Foundation

/*: # Title ## Title2 ### Title3 * Line 1 * Line 2 */

//: **Bold** *Italic*

The Swift notepad] / / : [elbow (https://www.fatbobman.com)

/ / :! PNG width="400" height="209"

/ * : / / code snippet func test () - > Stirng {print (" Hello ")} * /

print("Hello world")
Copy the code

In Xcode, set whether to enable document rendering by clicking on Render Documentation on the right.

When enabled, the above code will look like this:

The use of images within Assets in document standards is not currently supported.

Rendering documents in Swift Playgrounds will always be enabled and cannot be turned off.

For more information on renderable annotation code, see Apple’s official documentation.

How do I navigate between multiple pages

In the case of multiple pages, navigation between pages can be achieved through annotations in the main code of the Page.

Before and after the navigation

The following code allows you to jump back and forth by navigation bar order.

//: [Previous](@previous)

import Foundation

var greeting = "Hello, playground"

//: [Next](@next)
Copy the code

The rendered state

Click Previous in Page3 to jump to Page2. But clicking Next will not change (because Page3 is the last page).

Navigate to the specified Page

You can directly specify the Page name to jump to the specified Page

import Foundation

var greeting = "Hello, playground"

//: [Page1](Page1)

//: [Page2](Page2)
Copy the code

How to hide code (Swift Playgrounds Only)

Swift Playground has extremely strong entertainment and educational attributes, and provides several special marking methods to enhance its ability in courseware making and presentation. Initially these annotations were only for playgroundbook, but now they’re available for playground.

The purpose of the hidden code is to show only the code that the user needs to know in the code area of the Swift Playground. Hide any other code that is temporarily unnecessary to the user (it will still be executed, just not displayed).

//#-hidden-code
import SwiftUI
import PlaygroundSupport
var text = "Hello world"
let view = Text(text).foregroundColor(.red)
PlaygroundPage.current.setLiveView(view)
//#-end-hidden-code

text = "New World"
Copy the code

The code above shows only the last line of code in Swift Playground. The code between //#-hidden-code and //#-end-hidden-code will be hidden.

How to set editable code area (Swift Playgrounds Only)

By setting editable areas in the Page code, the user can only modify the code in the specified edit area.

//#-hidden-code
import SwiftUI
import PlaygroundSupport
var text = "Hello world"
let view = Text(text).foregroundColor(.red)
PlaygroundPage.current.setLiveView(view)
//#-end-hidden-code

//#-editable-code
text = "New World"
//#-end-editable-code
// Modify the font
view.font(.title)
Copy the code

Use //#-editable code and //#-end-editable-code to set the editable area.

The user can only modify the code in the rectangular box. The code outside the area can be edited, such as view.font(.title) below, but cannot be modified.

Hiding code and setting up modification areas can be very useful in creating interactive documents, so hopefully Xcode Playground will support these annotations sooner rather than later.

Explore packages and projects using Xcode Playground

Starting with Xcode 12, Apple has taken Playground and Xcode collaboration to a whole new level. With a deep integration between the two, Xcode Playground makes it easy to call and test code and resources in the SPM library, Xcode Project, and WorkSpace.

Playground in SPM

The library developer added the Playground project to the SPM-managed library, providing interactive documentation and examples to help users quickly learn how to use the library.

In the WWDC presentation, Apple’s Playground developers hope that in the future Swift third-party libraries will come with an interactive document based on Playground.

Adding Playground to the library is as simple as adding the Playground project (.playground) anywhere.

Precautions for use:

  1. You need to import the library file in the Playground code

  2. Only code marked public in the library can be called

  3. Resources in the library cannot be called

  4. You cannot use code in a library to call resources in a library

  5. Before executing Playground code, select the correct Target (Target should match the environment Playground is set to run in)

  6. Enable Build Active Scheme to automatically compile library files when switching Target

Point 4 is a little harder to understand than the others. Playground did not set the correct resource Bundle for the library even though it compiled the library first when executing the Page code, and an error was reported if the code in the library tried to call the library resources. Currently only applies to code that does not call library resource files.

Playground with Project

Precautions for use:

  1. Without Import App Types enabled, the project must be imported to call the publicly callable code in the project (public)
  2. With Import App Types enabled, code in the project can be called without importing the project (non-private)
  3. You can call third-party packages imported in your project
  4. Do not directly use the resources in the project
  5. Resources in a project can be obtained indirectly by calling code in the project that uses project resources
  6. Before executing Playground code, select the correct Target (Target should match the environment Playground is set to run in)
  7. Enable Build Active Scheme to automatically compile library files when switching Target
  8. Make sure the current Target is compiled before executing the Playground code

Compared with Playground in SPM, the differences include:

  • When Import App Type is enabled, you can use the code in the project directly (without public)
  • You can import other third-party packages used in the current Target.
  • Resources in a project can be invoked indirectly through code in the project

In the following figure, the MyPlayDemo project contains the following code (methods, variables are not public) :

import Foundation
import UIKit

func abc(a) {
    print("abc")}let a = 100

// Read an image from project Assets
func getProjectImage(a) -> UIImage? {
    UIImage(named: "abc")}Copy the code

In Playground, instead of importing the project name, you can use the code in the project directly (with import App Types enabled).

PlaygroundPackageDemo is a Package added to the current Target and can also be imported directly into Playground.

Playground with WorkSpace

Sometimes, you might want to create Playground in your workspace to test multiple projects or frameworks.

Tips for using Playground in WorkSpace:

  1. Only one project’s code in the workspace can be executed per Page
  2. Each Page can import packages that have been compiled in the workspace and are compatible with the current Page runtime environment (packages can be imported from different projects)
  3. Resources in a project cannot be used directly
  4. Resources in a project can be accessed indirectly through code in the project
  5. Only code with public permissions can be called (public)
  6. Before executing the current Page code, ensure that the project and library imported by the current code have been compiled
  7. Before executing the code for the current Page, switch Target to the compatible Target of the project that the current code imports

In the figure above, there are two projects in WorkSpace (DemoiOS 13 and MyPlayDemo).

Page1 imported MyPlayDemo project, and MyPlayDemo’s dependency PlaygroundPackageDemo, DemoiOS13, And SwiftUIOverlayContainer (a dependency of the DemoiOS 13 project).

You can only execute code in one project (but you can execute code for dependencies in another project).

Playground in SPM, Projcet, and WorkSpace are not in conflict, and you can directly implement Playground projects at any level.

Use third-party libraries in Swift Playgrounds

Swift Playground does not support adding third-party libraries directly to.playground. However, partial support for third-party libraries can be achieved by copying code from the Source directory of the third-party library into the Sources directory of Playground.

This mode applies only to third-party libraries that do not use library resources.

In the figure above, copy the Plot library code into Playground’s project Sources directory. All pages can be called directly to the Plot API without import.

conclusion

Don’t underestimate Xcode Playground, it’s far more capable and efficient than you can imagine.

Hopefully this article has helped you play with Xcode Playground.

The original post was posted on my blog wwww.fatbobman.com

Welcome to subscribe my public account: [Elbow’s Swift Notepad]