大多数浏览器和
Developer App 均支持流媒体播放。
-
使用 Xcode Playgrounds 创建原型
通过使用 Xcode Playgrounds 创建原型来加速功能开发,无需重建和重启项目来验证更改。我们将向你展示如何在项目或软件包中使用 Playground 来帮助你在各种场景中尝试代码,并仔细查看返回的值,包括复杂的结构和用户界面元素,以便在新功能集成到你的项目之前进行快速迭代。
章节
- 3:00 - Exploring the data model and inspecting UI snapshots
- 14:26 - Becoming familiar with a new package
- 18:07 - Using the Live View
- 22:03 - Running the updated project
资源
相关视频
WWDC20
-
下载
♪ ♪
Dariusz:嗨 我是 Dariusz 是 Xcode Playgrounds 团队的工程师 我想为你展示 Xcode Playgrounds 的改进 是如何让项目中 开发新功能的原型变得更简单 在很多情况下 Xcode Playgrounds 都能改善工作流程 首先 当你想创建新功能 或只是想对代码做点小改动时 Xode Playgrounds 可以让你跳过重建 和重新启动项目的过程 还可以极大地简化执行 原本难以实现的代码 例如在购物 App 中 生成订单摘要的逻辑 当然 这样的代码也需要测试验证 不过 Xcode Playgrounds 也可以开发测试 而且它还是在项目中引入依赖项之前 尝试代码的绝佳环境
我会为你展示 Xcode Playgrounds 在项目中起到的作用 我一直在开发一款小型 App 来帮我进行野生动物摄影 这款 App 目前在帮我追踪 已经发现和拍摄的野生动物物种 我希望这款 App 在野生动物摄影中 帮我做一件重要的事 寻找野生动物
我想为清单视图 引入一个新的标签页
我已经开始实现这个视图 我对这个 UI 也很满意 每种鸟类都有一个复选框 可以帮我追踪进展 可是清单上有 2000 多种鸟 根本看不出来有什么进展 这个清单能缩小一点就好了
于是我只能调整自定义的 ChecklistView 中的 birdsToShow 属性 目前 它只是创建了一个 针对北美配置的 BirdProvider 类型 并返回整个大陆上 找到的所有鸟类物种 为了避免频繁地重建和重启项目 然后导航到 ChecklistView 查看我的进展 我会尝试在 Xcode Playground 中调整此代码
我要在项目中添加一个新的 Playground
这是个 iOS App 所以我会使用 iOS 模板 我在过滤栏中输入“playground” 然后选择 Blank Playground
我可能尝试几次后 就不需要这个 Playground 了 所以就保留默认名称 “MyPlayground”
我打算删掉 Playground 中的默认内容
为了更加快速迭代 我会长按底部工具栏的运行按钮 切换到菜单中的 “Automatically run”选项
这样一来 只要我停止更改 Playground 就会自动执行整个代码 请注意 添加到项目中的 Playground 默认启用两个设置: Build Active Scheme 和 Import App Types 确保在每次 Playground 执行前 先构建活动方案 并自动导入 App 目标模块 这样一来 使用项目中 定义的类型更加便捷 我可以关闭检查器 为 Playground 提供更多空间
我们从声明一个 BirdProvider 实例开始 就像我们在 ChecklistView 的 birdsToShow 属性中看到的那样
编辑器右侧的结果边栏显示了 此声明生成的 Playground 结果 我可以使用内联结果切换 来查看更多详细信息
内联结果显示了 BirdProvider 实例的详细信息 及其两个属性: 鸟类数组和规定区域 在 Xcode 15 中 每行还有一个类型信息标签 显示类型的简短摘要 并且我可以使用每行的工具提示 查看更多详细信息
例如 工具提示显示 BirdProvider 类型 来自我的 App 模块 区域枚举是在该结构体中定义的 我们展开数组行 来看看关于鸟类的更多细节
注意 当我与内联结果视图交互时 Xcode 15 会突出显示生成结果的源代码 在这里 视图显示了分配给 birdProvider 常量的值 这样更容易理解显示的值
我们来进一步查看数组元素
我们看到了 区域和鸟类数组属性的清晰摘要 但默认情况下 表示每种鸟的行 只显示出数组索引的信息 这是因为自定义 Bird 类型没有定义描述
我们可以通过让 Bird 类型 符合 CustomStringConvertible 协议 来改进这一点 我可以在 Playground 或其 Sources 目录中 添加一个扩展 两个选项只会影响 Playground 范围内的 Bird 类型 而且我们也不因此在其它地方 比如调试器中 获得新描述 所以我才会在定义 Bird 类型的 文件中添加扩展
有了新的描述定义 每行应该都会显示 鸟的俗称和学名 我们回到 Playround 看看新描述是如何起作用的
在 Playground 自动执行模式下 重新打开时 Playground 会自动重新执行 我们再次展开 鸟类数组行来查看描述
这样好多了 现在不用展开就清楚 这些行表示什么了 让我们来看看 Bird 类型的其他属性
我已经找到 并拍摄了其中的一些鸟类 可以看到其中一些 已经具有照片属性 比如这个大西洋海雀
点击这一行 照片就会在新的 基于分屏浏览的 UI 中显示 我就能够看到对象的结构以及预览
默认情况下 当我点击 自定义 Bird 类型行时 没有这样的预览 能看到这张照片就太好了 因为我对所有鸟类的名字 都不太熟悉 为了实现这一点 我可以使用 CustomPlaygroundDisplayConvertible 协议 正如协议名称所示 这种一致性只影响 Playground 中的表示 所以我这次会在 Playground 的 Sources 目录中添加扩展
我会导入 App 模块 来访问 Bird 类型 并添加一个简单的扩展 将照片属性作为 playgroundDescription 返回
请注意 我在返回语句中把 photo 显式地转换为 Any 如果不这么操作 编译器会警告我正在丢失 该值为可选值的重要信息 在这种情况下还可以接受 因为 Xcode Playgrounds 处理可选值的方式是仅为 playgroundDescription 属性中 不返回 nil 的对象 创建自定义描述 我们回到 Playground 中 查看新的描述
在 Xcode 15 中 遵循 CustomPlaygroundDisplayConvertible 类型 返回的 playgroundDescription 会在分屏浏览中 与对象的结构一起展示
现在已经有照片的鸟类 会快速显示照片 无需展开行 这样一来 处理 此自定义类型的大型集合更加容易
但我今天想把重点放在 那些还没有照片的鸟类上 我们来关闭这个内联结果并过滤掉 所有已经有照片的鸟类
可以看到 这两个边栏注解看起来有点不同 这是因为在新行中有多个表达式 我可以点击新的控件来查看 每个表达式的熟悉摘要
把鼠标悬停在 内联结果上会突出显示其源代码范围
这清楚地表明该数组是 分配给 birdsToFind 常量的结果 而 true 是传递给 filter 函数的闭包产生的最新值
结果边栏显示 我还有 超过 1800 种鸟类没有拍照 数量相当惊人 虽然这可能是最终目标 但我希望专注在一小类上 来降低目标数字 比如猫头鹰 六月是去拍摄猫头鹰的好时机 但这不是本次讲座中要讨论的话题 我们来过滤掉其他族群的鸟类
这个数组现在 只有五个元素 更令人振奋 我想用这个小鸟群体来 尝试一下我的 自定义 ChecklistView 为此 我会创建一个 ChecklistView 的实例 并逐一添加鸟类
我们来打开 ChecklistView 的内联结果
作为一个 UIView 子类 它也显示了一些属性和快照 我可以切换到 Value History 模式 此模式也使用 基于分割视图的新 UI
我可以看到 ChecklistView 在每次循环迭代中的样子
它还帮我发现了视图中的问题 只有一种鸟类 我却在标题中错用“birds”来称呼 我可以调整 新 String Catalog 中 定义的字符串来解决这个问题
第一行好像是我的 Checklist View 标题中使用的行 我会打开上下文菜单 然后选择 Vary By Plural
这样做后 受影响行的状态 就会变为“NEEDS REVIEW” 我会把此字符串改为单数形式
要进一步了解有关 新 String Catalog 的信息 请观看讲座“发现 String Catalogs” 我们回到 Playground 看看更改后的 清单是什么样
现在 每个循环迭代中的标题都是正确的 我现在可以关闭这个内联结果了
我的 ChecklistView 应该可以用了 我会快速把用来缩小鸟类列表的 代码带到我的项目中
我要把这三行复制到我的 ChecklistView 中的 birdsToShow 属性中
我还会添加一个带有新的、 更小的鸟类数组的返回语句
在重新运行我的项目之前 我想再创建一个功能原型 可以看到自定义 Checklist View 中的 每一行都有一个展开按钮 在列表中选择一行后 我会打开一个简单的地图视图 现在还没太大作用 但我想抓取 所选鸟类最近的观测数据 并在地图上显示出来 因此 我得调整一下 ChecklistView 中的 sightingsToShow(for bird:) 函数 我往项目中添加了 一个依赖项来帮我实现这个想法 有了 BirdSightings 软件包 很容易就能 从公民科学网站上获取数据 因为人们在这个网站上发布观测结果
我没用过那个软件包 所以不太熟悉它的 API 还好软件包中包含了一个 Playground 形式的文档 展示了一些示例
这个方法很好 可以让用户 尝试里面提供的 API 看来需要向 fetchSightings 函数 提供两个参数: 要查找的鸟类和位置的代码 我可以执行这个 Playground 来了解预期的结果
我们回到 Playground 试用一下
我可以关闭导航器 为编辑区域让出更多空间
在调用函数之前 需要添加两个 import 语句 我会导入 CoreLocation 框架来使用坐标 导入 BirdSighting 框架来使用 API
对于函数参数 可以直接先从清单中选择第一种鸟
看来我们要查找短耳鸮了 请注意 我在这里使用了强制解包 我不太担心 Playgrounds 环境中的 错误处理 但把代码带入实际项目时 需谨记错误处理的重要性
至于位置 大部分时候我可能会用当前位置 但如果能够提供具体坐标 会对测试代码和规划路线很有帮助 让我们试试看在 Apple Park 周围能找到什么
在引入网络调用前 我会切换到 Playground 手动执行模式 以免进行不必要的调用 此类请求可能需要一些时间 而我希望尽快迭代新功能 为此 我会再次调出 底栏菜单 然后选择 Manually Run
我现在可以完全控制 需要执行的代码部分 我们来添加数据抓取代码
源编辑器侧边栏中的控件 显示执行这两行代码 不会重新执行 上面已经执行过的代码 我们来执行新行代码 看看能否得到数据
很走运 的确有观测数据 我们看一下第一个 应该是最新的
看来最近在长门岩保护区 观测到了短耳鸮 我对该地区 所有观鸟热点地区都不太熟悉 希望能在 SightingMapView 中 查看观测数据 我们用获取到的 观测数据进行初始化
对于地图视图 这样复杂的 UI 元素 我们可以使用 Playgrounds 实时视图 来查看大型的完全交互式预览 首先需要导入 PlaygroundSupport 框架来使用它
我准备设置实时视图 并执行 Playground
我在文件顶部 添加了 import 语句 修改了 Playground 已经执行过的部分 因此源编辑器侧边栏的控件显示 需要重新执行整个文件 我会失去上次执行的结果 但在这里应该不会有问题 所以我继续执行 Playground
我不记得有哪个离 Apple Park 很近的岛屿 实时视图是完全交互式的 我可以稍微缩小视图 来了解我们的位置 像在 iPhone 模拟器中一样
看来太偏东了 我们在编辑器选项中关闭实时视图 然后看看是哪里出了问题
SightingMapView 是用 mostRecentSighting 常量进行初始化的 我们来检查它的值 先不打开内联结果 我先点击边栏的眼睛图标 快速查看值
在 Xcode 15 中 我们改进了一些 MapKit 和 CoreLocation 类型的 Playground 结果 Playgrounds 现在可以显示 CLLocationCoordinate2D 的预览 我们来看看位置属性
似乎指向相同的位置 所以可能不是 SightingMapView 的问题 我们从 BirdSightings 软件包里得到错误的位置 所以可能是软件包有问题 或者我一开始就传递了错误的位置 我们来验证后者
这里好像 离 Apple Park 也不近 太偏东了 我想可能是东西方向混淆了 尝试在经度前添加负号 重新执行 Playground 看看能否解决问题
这肯定是 Apple Park 我们来使用更新后的位置执行 Playground 的剩余部分 并在 Editor Options 中 重新打开实时视图
这样就对了 现在我知道野狼山地区公园 可能是拍摄新鸟类的最佳地点 我们快速把获数据抓取代码带到 ChecklistView 中
我会把这三行复制到 sightingsToShow 函数中
这里不再使用硬编码的 appleParkLocation 而是用 从 CLLocationManager 中 获取的 lastCurrentLocation 进行替换
我还会添加带有新的 mostRecentSighting 的返回语句
现在终于可以运行 我们修改后的项目了
把重点放在了一小类鸟类上 清单看着更实际一点 来看看点击某一行会怎么样
太棒了 我的 App 现在可以显示所选鸟类的 最新观测数据 当然还有很大的改进空间 例如 在后台获取观测数据的同时 显示一个进展指示器 不过这是个好的开端 这款 App 已经给了我很大帮助 Xcode Playgrounds 让这些改进 变得更加简单 在这节中 我们使用 Xcode Playgrounds 快速创建了项目新功能的原型 使用 CustomStringConvertible 和 CustomPlaygroundDisplayConvertible 协议 对自定义类型的表示方式 进行了自定义 我们看到了调整 Playground 执行模式可以加快工作流程 使用 Value History 模式 让我们能快速查看 类对多个输入的反应 最后 我们使用了 Playground 实时视图 进一步查看复杂的 UI 元素 感谢你的观看
-
-
2:04 - birdsToShow computed property before the required changes
var birdsToShow: [Bird] { // TODO: Narrow down the list of birds to find. let birdProvider = BirdProvider(region: .northAmerica) return birdProvider.birds }
-
4:15 - birdProvider instance
let birdProvider = BirdProvider(region: .northAmerica)
-
6:31 - CustomStringConvertible
extension Bird: CustomStringConvertible { public var description: String { return "\(commonName) (\(scientificName))" } }
-
8:17 - CustomPlaygroundDisplayConvertible
extension Bird: CustomPlaygroundDisplayConvertible { public var playgroundDescription: Any { return photo as Any } }
-
9:45 - Birds without a photo
let birdsToFind = birdProvider.birds.filter { $0.photo == nil }
-
10:52 - Owls without a photo
let owlsToFind = birdsToFind.filter { $0.family == .owls }
-
11:15 - Verifying the ChecklistView
let checklist = ChecklistView() for bird in owlsToFind { checklist.add(bird) }
-
13:41 - birdsToShow computed property with the required changes
var birdsToShow: [Bird] { let birdProvider = BirdProvider(region: .northAmerica) let birdsToFind = birdProvider.birds.filter { $0.photo == nil } let owlsToFind = birdsToFind.filter { $0.family == .owls } return owlsToFind }
-
14:21 - sightingToShow function before the required changes
func sightingToShow(for bird: Bird) -> Sighting? { // TODO: Use BirdSightings package to fetch the most recent sighting. return nil }
-
15:04 - BirdSightings package example
let barnOwlCode = "BNOW" let centralParkLocation = CLLocationCoordinate2D(latitude: 40.785091, longitude: -73.968285) let sightingsProvider = SightingsProvider() sightingsProvider.fetchSightings(of: barnOwlCode, around: centralParkLocation)
-
16:08 - Parameters for sightings fetching
let bird = owlsToFind.first! let appleParkLocation = CLLocationCoordinate2D(latitude: 37.3348655, longitude: 122.0089409)
-
17:31 - Sightings fetching
let sightingsProvider = SightingsProvider() let sightings = sightingsProvider.fetchSightings(of: bird.speciesCode, around: appleParkLocation)
-
18:23 - Initializing the SightingMapView
let mostRecentSighting = sightings.first let sightingMapView = SightingMapView(sighting: mostRecentSighting)
-
18:55 - Setting up the playground's live view
import PlaygroundSupport PlaygroundPage.current.liveView = sightingMapView
-
22:20 - sightingToShow function with the required changes
func sightingToShow(for bird: Bird) -> Sighting? { let sightingsProvider = SightingsProvider() let sightings = sightingsProvider.fetchSightings(of: bird.speciesCode, around: lastCurrentLocation) let mostRecentSighting = sightings.first return mostRecentSighting }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。