-
Create Swift Package plugins
Tailor your development workflow and learn how to write your own package plugins in Swift. We'll show you how you can extend Xcode's functionality by using the PackagePlugin API to generate source code or automate release tasks and share best practices for creating great plugins.
Recursos
Videos relacionados
WWDC22
WWDC21
WWDC20
WWDC19
-
Buscar este video…
-
-
3:40 - GenerateContributors plugin target
// MARK: Plugins .plugin( name: "GenerateContributors", capability: .command( intent: .custom(verb: "regenerate-contributors-list", description: "Generates the CONTRIBUTORS.txt file based on Git logs"), permissions: [ .writeToPackageDirectory(reason: "This command write the new CONTRIBUTORS.txt to the source root.") ] )), -
5:06 - GenerateContributors plugin implementation
import PackagePlugin import Foundation @main struct GenerateContributors: CommandPlugin { func performCommand( context: PluginContext, arguments: [String] ) async throws { let process = Process() process.executableURL = URL(fileURLWithPath: "/usr/bin/git") process.arguments = ["log", "--pretty=format:- %an <%ae>%n"] let outputPipe = Pipe() process.standardOutput = outputPipe try process.run() process.waitUntilExit() let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile() let output = String(decoding: outputData, as: UTF8.self) let contributors = Set(output.components(separatedBy: CharacterSet.newlines)).sorted().filter { !$0.isEmpty } try contributors.joined(separator: "\n").write(toFile: "CONTRIBUTORS.txt", atomically: true, encoding: .utf8) } } -
10:28 - Minimum Deployment Target
platforms: [ .macOS("10.15"), .iOS("12.0"), .tvOS("12.0"), .watchOS("6.0"), ], -
10:35 - Basic SwiftUI view and preview
import SwiftUI struct ContentView: View { var body: some View { Image("Xcode", bundle: .module) .resizable() .frame(width: 200.0, height: 200.0) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } -
14:56 - AssetConstantsExec executable target
.executableTarget(name: "AssetConstantsExec"), -
15:03 - AssetConstantsExec implementation
import Foundation let arguments = ProcessInfo().arguments if arguments.count < 3 { print("missing arguments") } let (input, output) = (arguments[1], arguments[2]) struct Contents: Decodable { let images: [Image] } struct Image: Decodable { let filename: String? } var generatedCode = """ import Foundation import SwiftUI """ try FileManager.default.contentsOfDirectory(atPath: input).forEach { dirent in guard dirent.hasSuffix("imageset") else { return } let contentsJsonURL = URL(fileURLWithPath: "\(input)/\(dirent)/Contents.json") let jsonData = try Data(contentsOf: contentsJsonURL) let asset🐱alogContents = try JSONDecoder().decode(Contents.self, from: jsonData) let hasImage = asset🐱alogContents.images.filter { $0.filename != nil }.isEmpty == false if hasImage { let basename = contentsJsonURL.deletingLastPathComponent().deletingPathExtension().lastPathComponent generatedCode.append("public let \(basename) = Image(\"\(basename)\", bundle: .module)\n") } } try generatedCode.write(to: URL(fileURLWithPath: output), atomically: true, encoding: .utf8) -
15:48 - AssetConstantsExec plugin target
.plugin(name: "AssetConstants", capability: .buildTool(), dependencies: ["AssetConstantsExec"]), -
16:12 - AssetConstantsExec plugin implementation
guard let target = target as? SourceModuleTarget else { return [] } return try target.sourceFiles(withSuffix: "xcassets").map { asset🐱alog in let base = asset🐱alog.path.stem let input = asset🐱alog.path let output = context.pluginWorkDirectory.appending(["\(base).swift"]) return .buildCommand(displayName: "Generating constants for \(base)", executable: try context.tool(named: "AssetConstantsExec").path, arguments: [input.string, output.string], inputFiles: [input], outputFiles: [output]) } -
20:19 - GenstringsPlugin target
.plugin(name: "GenstringsPlugin", capability: .buildTool()), -
20:26 - GenstringsPlugin product
.plugin(name: "GenstringsPlugin", targets: ["GenstringsPlugin"]), -
20:44 - GenstringsPlugin implementation
guard let target = target as? SourceModuleTarget else { return [] } let resourcesDirectoryPath = context.pluginWorkDirectory .appending(subpath: target.name) .appending(subpath: "Resources") let localizationDirectoryPath = resourcesDirectoryPath .appending(subpath: "Base.lproj") try FileManager.default.createDirectory(atPath: localizationDirectoryPath.string, withIntermediateDirectories: true) let swiftSourceFiles = target.sourceFiles(withSuffix: ".swift") let inputFiles = swiftSourceFiles.map(\.path) return [ .prebuildCommand( displayName: "Generating localized strings from source files", executable: .init("/usr/bin/xcrun"), arguments: [ "genstrings", "-SwiftUI", "-o", localizationDirectoryPath ] + inputFiles, outputFilesDirectory: localizationDirectoryPath ) ] -
21:10 - Localized string API
import Foundation public func GetLocalizedString() -> String { return NSLocalizedString("World", comment: "A comment about the localizable string") } -
21:44 - Path-based dependency on GenstringsPlugin
.package(path: "../GenstringsPlugin"), -
21:52 - Use of GenstringsPlugin in library target
plugins: [ .plugin(name: "GenstringsPlugin", package: "GenstringsPlugin"), ]
-