@@ -60,6 +60,7 @@ func exec(_ args: [String]) async throws -> String {
6060
6161struct Platform {
6262 let label , destination , archiveName : String
63+ var extraSettings : [ String ] = [ ]
6364}
6465
6566let platforms : [ Platform ] = [
@@ -68,9 +69,9 @@ let platforms: [Platform] = [
6869 . init( label: " macOS " , destination: " generic/platform=macOS " , archiveName: " macos-arm64_x86_64 " ) ,
6970 . init( label: " Mac Catalyst " , destination: " generic/platform=macOS,variant=Mac Catalyst " , archiveName: " ios-arm64_x86_64-maccatalyst " ) ,
7071 . init( label: " tvOS Device " , destination: " generic/platform=tvOS " , archiveName: " tvos-arm64 " ) ,
71- . init( label: " tvOS Simulator " , destination: " generic/platform=tvOS Simulator " , archiveName: " tvos-arm64_x86_64-simulator " ) ,
72+ . init( label: " tvOS Simulator " , destination: " generic/platform=tvOS Simulator " , archiveName: " tvos-arm64_x86_64-simulator " , extraSettings : [ " ARCHS=arm64 " ] ) ,
7273 . init( label: " visionOS Device " , destination: " generic/platform=visionOS " , archiveName: " xros-arm64 " ) ,
73- . init( label: " visionOS Simulator " , destination: " generic/platform=visionOS Simulator " , archiveName: " xros-arm64-simulator " ) ,
74+ . init( label: " visionOS Simulator " , destination: " generic/platform=visionOS Simulator " , archiveName: " xros-arm64-simulator " , extraSettings : [ " ARCHS=arm64 " ] ) ,
7475]
7576
7677// MARK: - Binary dependency info
@@ -113,6 +114,13 @@ func parseBinaryDep(repoRoot: Path, spmDir: Path, pattern: String, xcfw: String)
113114 return BinaryDep ( url: zipURL, checksum: String ( csMatch. hash) )
114115}
115116
117+ func protobufDependencyLine( repoRoot: Path ) throws -> String {
118+ let contents : String = try ( repoRoot + " Package.swift " ) . read ( )
119+ guard let line = contents. components ( separatedBy: . newlines) . first ( where: { $0. contains ( " swift-protobuf " ) && $0. contains ( " .package " ) } )
120+ else { throw ValidationError ( " swift-protobuf dependency not found in Package.swift " ) }
121+ return line
122+ }
123+
116124// MARK: - Xcode project generation
117125
118126/// Recursively add source files from a directory to an Xcode group and build phase.
@@ -213,17 +221,17 @@ func generateFrameworkProject(at projectPath: Path, repoRoot: Path) throws {
213221
214222 // Parse versions from Package.swift
215223 let pkgContents : String = try ( repoRoot + " Package.swift " ) . read ( )
216- func extractVersion( pattern: String ) -> String {
224+ func extractVersion( pattern: String ) throws -> String {
217225 guard let line = pkgContents. components ( separatedBy: . newlines)
218226 . first ( where: { $0. contains ( pattern) } ) ,
219227 let match = line. firstMatch ( of: #/exact:\s*"(?<ver>[^"]+)"/#) ?? line.firstMatch(of: #/from:\s*"(?<ver>[^"]+)"/# )
220- else { return " 1.0.0 " }
228+ else { throw ValidationError ( " \( pattern ) version not found in Package.swift " ) }
221229 return String ( match. ver)
222230 }
223231
224- let webrtcPkg = addRemotePackage ( url: " https://github.com/livekit/webrtc-xcframework.git " , version: extractVersion ( pattern: " webrtc-xcframework " ) )
225- let uniffiPkg = addRemotePackage ( url: " https://github.com/livekit/livekit-uniffi-xcframework.git " , version: extractVersion ( pattern: " uniffi-xcframework " ) )
226- let protobufPkg = addRemotePackage ( url: " https://github.com/apple/swift-protobuf.git " , version: extractVersion ( pattern: " swift-protobuf " ) )
232+ let webrtcPkg = try addRemotePackage ( url: " https://github.com/livekit/webrtc-xcframework.git " , version: extractVersion ( pattern: " webrtc-xcframework " ) )
233+ let uniffiPkg = try addRemotePackage ( url: " https://github.com/livekit/livekit-uniffi-xcframework.git " , version: extractVersion ( pattern: " uniffi-xcframework " ) )
234+ let protobufPkg = try addRemotePackage ( url: " https://github.com/apple/swift-protobuf.git " , version: extractVersion ( pattern: " swift-protobuf " ) )
227235
228236 let webrtcDep = addProductDep ( name: " LiveKitWebRTC " , package : webrtcPkg)
229237 let uniffiDep = addProductDep ( name: " LiveKitUniFFI " , package : uniffiPkg)
@@ -249,6 +257,7 @@ func generateFrameworkProject(at projectPath: Path, repoRoot: Path) throws {
249257 " SKIP_INSTALL " : " NO " ,
250258 " BUILD_LIBRARY_FOR_DISTRIBUTION " : " YES " ,
251259 " INSTALL_PATH " : " $(LOCAL_LIBRARY_DIR)/Frameworks " ,
260+ " DYLIB_INSTALL_NAME_BASE " : " @rpath " ,
252261 " DEFINES_MODULE " : " YES " ,
253262 " MACH_O_TYPE " : " mh_dylib " ,
254263 " CLANG_ENABLE_MODULES " : " YES " ,
@@ -352,7 +361,7 @@ struct BuildXCFramework: AsyncParsableCommand {
352361 repoRoot = repoRoot. parent ( )
353362 }
354363
355- let outputDir = Path ( output ?? ( repoRoot + " build " + " xcframework " ) . string)
364+ let outputDir = Path ( output ?? ( repoRoot + " . build" + " xcframework " ) . string)
356365 let buildDir = Path ( NSTemporaryDirectory ( ) ) + " livekit-xcfw- \( UUID ( ) . uuidString) "
357366 let spmDir = buildDir + " spm "
358367
@@ -402,7 +411,7 @@ struct BuildXCFramework: AsyncParsableCommand {
402411 " -archivePath " , archive. string,
403412 " -derivedDataPath " , dd. string,
404413 " -clonedSourcePackagesDirPath " , spmDir. string,
405- ] )
414+ ] + p . extraSettings )
406415 let lastLine = archiveOutput. components ( separatedBy: . newlines) . last ?? " "
407416 print ( " ✓ \( p. label) : \( lastLine) " )
408417 return ( p, . success( archive) )
@@ -443,15 +452,12 @@ struct BuildXCFramework: AsyncParsableCommand {
443452 let xcfwOutput = try await exec ( xcfwArgs)
444453 if !xcfwOutput. isEmpty { print ( xcfwOutput) }
445454
446- // --- Zip & checksum (release builds only) ---
447- var liveKitChecksum = " "
448- if !local {
449- step ( " Zipping LiveKit.xcframework... " )
450- let zipPath = outputDir + " LiveKit.xcframework.zip "
451- try fm. zipItem ( at: xcfwPath. url, to: zipPath. url)
452- liveKitChecksum = try await exec ( " swift " , " package " , " compute-checksum " , zipPath. string)
453- print ( " LiveKit.xcframework.zip checksum: \( liveKitChecksum) " )
454- }
455+ // --- Zip & checksum ---
456+ step ( " Zipping LiveKit.xcframework... " )
457+ let zipPath = outputDir + " LiveKit.xcframework.zip "
458+ try fm. zipItem ( at: xcfwPath. url, to: zipPath. url)
459+ let liveKitChecksum = try await exec ( " swift " , " package " , " compute-checksum " , zipPath. string)
460+ print ( " LiveKit.xcframework.zip checksum: \( liveKitChecksum) " )
455461
456462 // --- Generate Package.swift ---
457463 step ( " Generating Package.swift... " )
@@ -494,12 +500,13 @@ struct BuildXCFramework: AsyncParsableCommand {
494500 " webrtcChecksum " : webrtcDep. checksum,
495501 " uniffiURL " : uniffiDep. url,
496502 " uniffiChecksum " : uniffiDep. checksum,
503+ " protobufDependency " : protobufDependencyLine ( repoRoot: repoRoot) ,
497504 ] )
498505 try ( outputDir + " Package.swift " ) . write ( rendered)
499506
500- // Create stub target (workaround for apple/swift-package-manager#6069)
501- let stubDir = outputDir + " Sources " + " _LiveKitStub "
502- try stubDir . mkpath ( )
503- try ( stubDir + " Stub .swift" ) . write ( " // Workaround: without a non-binary target, SPM won't show the \n // package in Xcode's \" Frameworks, Libraries, and Embedded Content \" . \n // See https://github.com/apple/swift-package-manager/issues/6069 \n class _LiveKitStub {} \n " )
507+ // LiveKitTargets needs at least one source file
508+ let targetsDir = outputDir + " Sources " + " LiveKitTargets "
509+ try targetsDir . mkpath ( )
510+ try ( targetsDir + " LiveKitTargets .swift" ) . write ( " // Re-export LiveKit so consumers only need `import LiveKit` . \n @_exported import LiveKit \n " )
504511 }
505512}
0 commit comments