From 5c9f318f5abb3503bd8b95ae630dcad8d0d25524 Mon Sep 17 00:00:00 2001 From: Mathias Beaulieu-Duncan Date: Sat, 14 Mar 2026 06:53:58 -0400 Subject: [PATCH] Fix xcframework linking for pub.dev installs, bump to 0.1.1 Use DERIVED_FILE_DIR instead of symlinks for force_load path. Symlinks in PODS_TARGET_SRCROOT fail when installed from pub cache because Xcode validates build inputs before script phases run. Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 4 ++++ ios/tsnet_flutter.podspec | 28 ++++++++++++++++++++-------- pubspec.yaml | 2 +- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bdd89e..74da0e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.1 + +- Fix: xcframework linking when installed from pub.dev (use DERIVED_FILE_DIR instead of symlinks) + ## 0.1.0 - Initial release diff --git a/ios/tsnet_flutter.podspec b/ios/tsnet_flutter.podspec index 11c4764..9296fb8 100644 --- a/ios/tsnet_flutter.podspec +++ b/ios/tsnet_flutter.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'tsnet_flutter' - s.version = '0.1.0' + s.version = '0.1.1' s.summary = 'Embedded Tailscale tsnet for Flutter' s.description = <<-DESC Embed Tailscale's tsnet in Flutter apps. Provides a userspace WireGuard @@ -23,27 +23,39 @@ No VPN entitlement needed — uses userspace netstack. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386 x86_64', - 'OTHER_LDFLAGS' => '$(inherited) -force_load "$(PODS_TARGET_SRCROOT)/_current_slice/TailscaleKit.framework/TailscaleKit"', + # Link the Go static library — the script phase copies the correct slice + # to DERIVED_FILE_DIR which is always writable and available at link time. + 'OTHER_LDFLAGS' => '$(inherited) -force_load "$(DERIVED_FILE_DIR)/TailscaleKit"', } s.user_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386 x86_64', } + # Copy the correct xcframework slice to DERIVED_FILE_DIR before compile. + # DERIVED_FILE_DIR is writable and exists before the link phase. s.script_phase = { :name => 'Select TailscaleKit Slice', :script => <<-'SCRIPT', - PLUGIN_DIR="${PODS_TARGET_SRCROOT}" - LINK="${PLUGIN_DIR}/_current_slice" - rm -f "$LINK" + XCFW="${PODS_TARGET_SRCROOT}/TailscaleKit.xcframework" if [ "$PLATFORM_NAME" = "iphonesimulator" ]; then - ln -sf "TailscaleKit.xcframework/ios-arm64-simulator" "$LINK" + SLICE="ios-arm64-simulator" else - ln -sf "TailscaleKit.xcframework/ios-arm64" "$LINK" + SLICE="ios-arm64" + fi + + SRC="$XCFW/$SLICE/TailscaleKit.framework/TailscaleKit" + DEST="${DERIVED_FILE_DIR}/TailscaleKit" + + if [ ! -f "$DEST" ] || [ "$SRC" -nt "$DEST" ]; then + cp "$SRC" "$DEST" + echo "Copied TailscaleKit ($SLICE) to $DEST" + else + echo "TailscaleKit ($SLICE) up to date" fi - echo "TailscaleKit: selected $PLATFORM_NAME slice" SCRIPT :execution_position => :before_compile, + :output_files => ['$(DERIVED_FILE_DIR)/TailscaleKit'], } end diff --git a/pubspec.yaml b/pubspec.yaml index 2898f04..ff2b75b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: tsnet_flutter description: Embed Tailscale's tsnet in Flutter apps. Provides a userspace WireGuard tunnel with a localhost TCP proxy — no VPN entitlement needed on iOS. -version: 0.1.0 +version: 0.1.1 homepage: https://github.com/svrnty/tsnet_flutter repository: https://github.com/svrnty/tsnet_flutter issue_tracker: https://github.com/svrnty/tsnet_flutter/issues