Skip to content

Commit 2f00c4f

Browse files
committed
swift: add iOS simulator support
updates tailscale/tailscale#15802 This adds the required targets to build for the iOS simulator. We support both ios, ios-sim, and ios-fat framework targets. The sample app is updated to link in the ios-fat version so it can be run on any target device or sim.
1 parent 78294ac commit 2f00c4f

File tree

13 files changed

+449
-42
lines changed

13 files changed

+449
-42
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ libtailscale.so
22
libtailscale.a
33
libtailscale.h
44
libtailscale.tar*
5-
libtailscale_ios.a
6-
libtailscale_ios.h
5+
libtailscale_*.a
6+
libtailscale_*.h
77

88
/tstestcontrol/libtstestcontrol.a
99
/tstestcontrol/libtstestcontrol.h

Makefile

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,21 @@ libtailscale.a:
66
go build -buildmode=c-archive
77

88
libtailscale_ios.a:
9-
GOOS=ios GOARCH=arm64 CGO_ENABLED=1 CC=$(PWD)/swift/script/clangwrap.sh go build -v -ldflags -w -tags ios -o libtailscale_ios.a -buildmode=c-archive
9+
GOOS=ios GOARCH=arm64 CGO_ENABLED=1 CC=$(PWD)/swift/script/clangwrap-ios.sh go build -v -ldflags -w -tags ios -o libtailscale_ios.a -buildmode=c-archive
10+
11+
libtailscale_ios_sim_arm64.a:
12+
GOOS=ios GOARCH=arm64 CGO_ENABLED=1 CC=$(PWD)/swift/script/clangwrap-ios-sim-arm.sh go build -v -ldflags -w -tags ios -o libtailscale_ios_sim_arm64.a -buildmode=c-archive
13+
14+
libtailscale_ios_sim_x86_64.a:
15+
GOOS=ios GOARCH=amd64 CGO_ENABLED=1 CC=$(PWD)/swift/script/clangwrap-ios-sim-x86.sh go build -v -ldflags -w -tags ios -o libtailscale_ios_sim_x86_64.a -buildmode=c-archive
1016

1117
.PHONY: c-archive-ios
1218
c-archive-ios: libtailscale_ios.a ## Builds libtailscale_ios.a for iOS (iOS SDK required)
1319

20+
.PHONY: c-archive-ios-sim
21+
c-archive-ios-sim: libtailscale_ios_sim_arm64.a libtailscale_ios_sim_x86_64.a ## Builds a fat binary for iOS (iOS SDK required)
22+
lipo -create -output libtailscale_ios_sim.a libtailscale_ios_sim_x86_64.a libtailscale_ios_sim_arm64.a
23+
1424
.PHONY: c-archive
1525
c-archive: libtailscale.a ## Builds libtailscale.a for the target platform
1626

@@ -20,10 +30,8 @@ shared: ## Builds libtailscale.so for the target platform
2030

2131
.PHONY: clean
2232
clean: ## Clean up build artifacts
23-
rm -f libtailscale.a
24-
rm -f libtailscale_ios.a
25-
rm -f libtailscale.h
26-
rm -f libtailscale_ios.h
33+
rm -f libtailscale*.h
34+
rm -f libtailscale*.a
2735

2836
.PHONY: help
2937
help: ## Show this help

swift/Examples/TailscaleKitHello/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22

33
## Instructions
44

5-
First build TailscaleKit:
5+
First build TailscaleKit for the platform you wish to target:
66

77
From /swift:
88
```
99
$ make macos
10+
$ make ios-fat
1011
```
1112

13+
The ios target expects the universal xcframework produced by make ios-fat and
14+
can be run on either a device or the simulator.
15+
1216
In TailnetSettings, configure an auth key and a server/service to query.
1317

1418
```

swift/Examples/TailscaleKitHello/TailscaleKitHello.xcodeproj/project.pbxproj

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
/* Begin PBXBuildFile section */
1010
C25260032D7A71E800BD3CCA /* TailscaleKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C2525FC52D7A69DE00BD3CCA /* TailscaleKit.framework */; };
1111
C25260052D7A71FE00BD3CCA /* TailscaleKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2525FC52D7A69DE00BD3CCA /* TailscaleKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
12-
C289804A2DBAA8DA0019B7EB /* TailscaleKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C28980492DBAA7E50019B7EB /* TailscaleKit.framework */; };
13-
C289804B2DBAA8DA0019B7EB /* TailscaleKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C28980492DBAA7E50019B7EB /* TailscaleKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
12+
C2CE237E2DD7756B0096C105 /* TailscaleKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C2CE23762DD76A5C0096C105 /* TailscaleKit.framework */; };
13+
C2ED636C2DDE3C1400297161 /* TailscaleKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C2ED636B2DDE3C1400297161 /* TailscaleKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
1414
/* End PBXBuildFile section */
1515

1616
/* Begin PBXCopyFilesBuildPhase section */
@@ -30,7 +30,7 @@
3030
dstPath = "";
3131
dstSubfolderSpec = 10;
3232
files = (
33-
C289804B2DBAA8DA0019B7EB /* TailscaleKit.framework in Embed Frameworks */,
33+
C2ED636C2DDE3C1400297161 /* TailscaleKit.xcframework in Embed Frameworks */,
3434
);
3535
name = "Embed Frameworks";
3636
runOnlyForDeploymentPostprocessing = 0;
@@ -43,16 +43,11 @@
4343
C25260082D7A7DC400BD3CCA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
4444
C289803E2DBA8A350019B7EB /* HelloFromTailscale_iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloFromTailscale_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
4545
C28980492DBAA7E50019B7EB /* TailscaleKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TailscaleKit.framework; path = "../../build/Build/Products/Release-iphoneos/TailscaleKit.framework"; sourceTree = "<group>"; };
46+
C2CE23762DD76A5C0096C105 /* TailscaleKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TailscaleKit.framework; path = "../../build/Build/Products/Release-iphonesimulator/TailscaleKit.framework"; sourceTree = "<group>"; };
47+
C2ED636B2DDE3C1400297161 /* TailscaleKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = TailscaleKit.xcframework; path = "../../build/Build/Products/Release-iphonefat/TailscaleKit.xcframework"; sourceTree = "<group>"; };
4648
/* End PBXFileReference section */
4749

4850
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
49-
C25260072D7A7BAE00BD3CCA /* Exceptions for "HelloFromTailscale" folder in "HelloFromTailscale" target */ = {
50-
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
51-
membershipExceptions = (
52-
Info.plist,
53-
);
54-
target = C2525FF02D7A70B700BD3CCA /* HelloFromTailscale */;
55-
};
5651
C289803F2DBA8A360019B7EB /* Exceptions for "HelloFromTailscale" folder in "HelloFromTailscale_iOS" target */ = {
5752
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
5853
membershipExceptions = (
@@ -62,13 +57,20 @@
6257
);
6358
target = C28980342DBA8A350019B7EB /* HelloFromTailscale_iOS */;
6459
};
60+
C2CE23752DD76A330096C105 /* Exceptions for "HelloFromTailscale" folder in "HelloFromTailscale" target */ = {
61+
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
62+
membershipExceptions = (
63+
Info_iOS.plist,
64+
);
65+
target = C2525FF02D7A70B700BD3CCA /* HelloFromTailscale */;
66+
};
6567
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
6668

6769
/* Begin PBXFileSystemSynchronizedRootGroup section */
6870
C2525FF22D7A70B700BD3CCA /* HelloFromTailscale */ = {
6971
isa = PBXFileSystemSynchronizedRootGroup;
7072
exceptions = (
71-
C25260072D7A7BAE00BD3CCA /* Exceptions for "HelloFromTailscale" folder in "HelloFromTailscale" target */,
73+
C2CE23752DD76A330096C105 /* Exceptions for "HelloFromTailscale" folder in "HelloFromTailscale" target */,
7274
C289803F2DBA8A360019B7EB /* Exceptions for "HelloFromTailscale" folder in "HelloFromTailscale_iOS" target */,
7375
);
7476
path = HelloFromTailscale;
@@ -89,7 +91,7 @@
8991
isa = PBXFrameworksBuildPhase;
9092
buildActionMask = 2147483647;
9193
files = (
92-
C289804A2DBAA8DA0019B7EB /* TailscaleKit.framework in Frameworks */,
94+
C2CE237E2DD7756B0096C105 /* TailscaleKit.framework in Frameworks */,
9395
);
9496
runOnlyForDeploymentPostprocessing = 0;
9597
};
@@ -118,6 +120,8 @@
118120
C2525FC42D7A69DE00BD3CCA /* Frameworks */ = {
119121
isa = PBXGroup;
120122
children = (
123+
C2ED636B2DDE3C1400297161 /* TailscaleKit.xcframework */,
124+
C2CE23762DD76A5C0096C105 /* TailscaleKit.framework */,
121125
C2525FC52D7A69DE00BD3CCA /* TailscaleKit.framework */,
122126
C28980492DBAA7E50019B7EB /* TailscaleKit.framework */,
123127
);
@@ -374,7 +378,7 @@
374378
DEVELOPMENT_TEAM = W5364U7YZB;
375379
ENABLE_HARDENED_RUNTIME = YES;
376380
ENABLE_PREVIEWS = YES;
377-
FRAMEWORK_SEARCH_PATHS = "../../build/Build/**";
381+
FRAMEWORK_SEARCH_PATHS = ../../build/Build;
378382
GENERATE_INFOPLIST_FILE = YES;
379383
INFOPLIST_FILE = HelloFromTailscale/Info.plist;
380384
INFOPLIST_KEY_NSHumanReadableCopyright = "";
@@ -403,7 +407,7 @@
403407
DEVELOPMENT_TEAM = W5364U7YZB;
404408
ENABLE_HARDENED_RUNTIME = YES;
405409
ENABLE_PREVIEWS = YES;
406-
FRAMEWORK_SEARCH_PATHS = "../../build/Build/**";
410+
FRAMEWORK_SEARCH_PATHS = ../../build/Build;
407411
GENERATE_INFOPLIST_FILE = YES;
408412
INFOPLIST_FILE = HelloFromTailscale/Info.plist;
409413
INFOPLIST_KEY_NSHumanReadableCopyright = "";
@@ -432,7 +436,7 @@
432436
DEVELOPMENT_TEAM = W5364U7YZB;
433437
ENABLE_HARDENED_RUNTIME = YES;
434438
ENABLE_PREVIEWS = YES;
435-
FRAMEWORK_SEARCH_PATHS = "../../build/Build/Products/Release-iphoneOS";
439+
FRAMEWORK_SEARCH_PATHS = "../../build/Build/Products/Release-iphonefat";
436440
GENERATE_INFOPLIST_FILE = YES;
437441
INFOPLIST_FILE = HelloFromTailscale/Info_iOS.plist;
438442
INFOPLIST_KEY_NSHumanReadableCopyright = "";
@@ -467,7 +471,7 @@
467471
DEVELOPMENT_TEAM = W5364U7YZB;
468472
ENABLE_HARDENED_RUNTIME = YES;
469473
ENABLE_PREVIEWS = YES;
470-
FRAMEWORK_SEARCH_PATHS = "../../build/Build/Products/Release-iphoneOS";
474+
FRAMEWORK_SEARCH_PATHS = "../../build/Build/Products/Release-iphonefat";
471475
GENERATE_INFOPLIST_FILE = YES;
472476
INFOPLIST_FILE = HelloFromTailscale/Info_iOS.plist;
473477
INFOPLIST_KEY_NSHumanReadableCopyright = "";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Scheme
3+
LastUpgradeVersion = "1620"
4+
version = "1.7">
5+
<BuildAction
6+
parallelizeBuildables = "YES"
7+
buildImplicitDependencies = "YES"
8+
buildArchitectures = "Automatic">
9+
<BuildActionEntries>
10+
<BuildActionEntry
11+
buildForTesting = "YES"
12+
buildForRunning = "YES"
13+
buildForProfiling = "YES"
14+
buildForArchiving = "YES"
15+
buildForAnalyzing = "YES">
16+
<BuildableReference
17+
BuildableIdentifier = "primary"
18+
BlueprintIdentifier = "C2CE22D62DD680A20096C105"
19+
BuildableName = "HelloFromTailscale_iOS_Simulator.app"
20+
BlueprintName = "HelloFromTailscale_iOS_Simulator"
21+
ReferencedContainer = "container:TailscaleKitHello.xcodeproj">
22+
</BuildableReference>
23+
</BuildActionEntry>
24+
</BuildActionEntries>
25+
</BuildAction>
26+
<TestAction
27+
buildConfiguration = "Debug"
28+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
29+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
30+
shouldUseLaunchSchemeArgsEnv = "YES"
31+
shouldAutocreateTestPlan = "YES">
32+
</TestAction>
33+
<LaunchAction
34+
buildConfiguration = "Debug"
35+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
36+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
37+
launchStyle = "0"
38+
useCustomWorkingDirectory = "NO"
39+
ignoresPersistentStateOnLaunch = "NO"
40+
debugDocumentVersioning = "YES"
41+
debugServiceExtension = "internal"
42+
allowLocationSimulation = "YES">
43+
<BuildableProductRunnable
44+
runnableDebuggingMode = "0">
45+
<BuildableReference
46+
BuildableIdentifier = "primary"
47+
BlueprintIdentifier = "C2CE22D62DD680A20096C105"
48+
BuildableName = "HelloFromTailscale_iOS_Simulator.app"
49+
BlueprintName = "HelloFromTailscale_iOS_Simulator"
50+
ReferencedContainer = "container:TailscaleKitHello.xcodeproj">
51+
</BuildableReference>
52+
</BuildableProductRunnable>
53+
</LaunchAction>
54+
<ProfileAction
55+
buildConfiguration = "Release"
56+
shouldUseLaunchSchemeArgsEnv = "YES"
57+
savedToolIdentifier = ""
58+
useCustomWorkingDirectory = "NO"
59+
debugDocumentVersioning = "YES">
60+
<BuildableProductRunnable
61+
runnableDebuggingMode = "0">
62+
<BuildableReference
63+
BuildableIdentifier = "primary"
64+
BlueprintIdentifier = "C2CE22D62DD680A20096C105"
65+
BuildableName = "HelloFromTailscale_iOS_Simulator.app"
66+
BlueprintName = "HelloFromTailscale_iOS_Simulator"
67+
ReferencedContainer = "container:TailscaleKitHello.xcodeproj">
68+
</BuildableReference>
69+
</BuildableProductRunnable>
70+
</ProfileAction>
71+
<AnalyzeAction
72+
buildConfiguration = "Debug">
73+
</AnalyzeAction>
74+
<ArchiveAction
75+
buildConfiguration = "Release"
76+
revealArchiveInOrganizer = "YES">
77+
</ArchiveAction>
78+
</Scheme>

swift/Makefile

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ endif
1111
# the libtailscale.a and libtailscale_ios.a dependencies.
1212

1313
.PHONY: all
14-
all: test ios macos ## Runs the tests and builds all library targets
14+
all: test ios macos ios-fat ## Runs the tests and builds all library targets
1515

1616
.PHONY: macos
1717
macos: ## Builds TailscaleKit for macos to swift/build/Build/Products/Release (unsigned)
1818
@echo
19-
@echo "::: Building TailscaleKit for macOS :::"
19+
@echo "::: Building TailscaleKit.framework for macOS :::"
2020
cd .. && make c-archive
2121
mkdir -p build
2222
xcodebuild build -scheme "TailscaleKit (macOS)" \
@@ -26,9 +26,9 @@ macos: ## Builds TailscaleKit for macos to swift/build/Build/Products/Release (
2626
CODE_SIGNING_ALLOWED=NO | $(XCPRETTIFIER)
2727

2828
.PHONY: ios
29-
ios: ## Builds TailscaleKit for iOS to swift/build/Build/Products/Release (unsigned)
29+
ios: ## Builds TailscaleKit for iOS to swift/build/Build/Products/Release-iphoneos (unsigned)
3030
@echo
31-
@echo "::: Building TailscaleKit for iOS :::"
31+
@echo "::: Building TailscaleKit.framework for iOS :::"
3232
cd .. && make c-archive-ios
3333
mkdir -p build
3434
xcodebuild build -scheme "TailscaleKit (iOS)" \
@@ -37,6 +37,27 @@ ios: ## Builds TailscaleKit for iOS to swift/build/Build/Products/Release (unsi
3737
-destination 'generic/platform=iOS' \
3838
CODE_SIGNING_ALLOWED=NO | $(XCPRETTIFIER)
3939

40+
.PHONY: ios-sim
41+
ios-sim: ## Builds TailscaleKit for iOS to swift/build/Build/Products/Release-iphonesimulator (unsigned)
42+
@echo
43+
@echo "::: Building TailscaleKit.framework for iOS Simulator :::"
44+
cd .. && make c-archive-ios-sim
45+
mkdir -p build
46+
xcodebuild build -scheme "TailscaleKit (Simulator)" \
47+
-derivedDataPath build \
48+
-configuration Release \
49+
-destination 'generic/platform=iOS Simulator' \
50+
CODE_SIGNING_ALLOWED=NO | $(XCPRETTIFIER)
51+
52+
.PHONY: ios-fat
53+
ios-fat: ios-sim ios ## Builds TailscaleKit.xcframework to swift/build/Build/Products/Release-iphonefat
54+
@echo
55+
@echo "::: Building TailscaleKit.xcframework for ios and ios-simulator :::"
56+
mkdir -p ./build/Build/Products/Release-iphonefat
57+
xcodebuild -create-xcframework \
58+
-framework ./build/Build/Products/Release-iphoneos/TailscaleKit.framework \
59+
-framework ./build/Build/Products/Release-iphonesimulator/TailscaleKit.framework \
60+
-output ./build/Build/Products/Release-iphonefat/TailscaleKit.xcframework
4061

4162
.PHONY: test
4263
test: ## Run tests (macOS)

swift/README.md

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,28 @@ Build Requirements:
1414

1515
Building Tailscale.framework:
1616

17-
First build the libtailscale dependecies:
18-
19-
20-
2117
From /swift
2218
```
2319
$ make macos
24-
# make ios
20+
$ make ios
21+
$ make ios-sim
22+
$ make ios-fat
2523
```
2624

27-
Will build TailscaleKit.framework into /swift/build/Build/Products.
25+
These recipes build different variants of TailscaleKit.framework into /swift/build/Build/Products.
2826

29-
Separate frameworks will be built for macOS and iOS. All dependencies (libtailscale.a)
27+
Separate frameworks will be built for macOS and iOS and the iOS Simulator. All dependencies (libtailscale*.a)
3028
are built automatically. Swift 6 is supported.
3129

30+
The ios and ios-sim frameworks are purposefully separated. The former is suitable is free of all
31+
simulator code and is suitable for app-store submissions. The latter is suitable for embedding when you
32+
wish to run on a simulator in dev.
33+
34+
In addition, make ios-fat will product an an xcframework bundle including both simulator and device
35+
frameworks for development.
36+
37+
The frameworks are not signed and must be signed when they are embedded.
38+
3239
Alternatively, you may build from xCode using the Tailscale scheme but the
3340
libraries must be built first (since xCode will complain about paths and
3441
permissions)
@@ -37,9 +44,14 @@ From /
3744
```
3845
$ make c-archive
3946
$ make c-archive-ios
47+
$ make c-archive-ios-sim
4048
```
4149

42-
Non-apple builds are not supported (yet). We do use URLSession and Combine though
50+
Can be used to build the static C libraries for macOS, iOS and the iOS simulator respectively. The required static
51+
libs are built automatically by the framework recipes. If you're writing pure C, or C++, link these and use
52+
the generated tailscale.h header.
53+
54+
Non-apple swift builds are not supported (yet). We do use URLSession and Combine though
4355
it is possible to purge both.
4456

4557
## Tests
@@ -94,7 +106,16 @@ func fetchURL(_ url: URL, tailscale: TailscaleNode) async throws -> Data {
94106
}
95107
```
96108

97-
See the [TailscaleKitTests](./Tests/TailscaleKitTests/TailscaleKitTests.swift) for more examples.
109+
### LocalAPI
110+
111+
TailscaleKit.framework also includes a functional (though somewhat incomplete) implementation of
112+
LocalAPI which can be used to track the state of the embedded tailscale instance in much greater
113+
detail.
114+
115+
### Examples
116+
117+
See the TailscaleKitHello example for a relatively complete implementation demonstrating proxied
118+
HTTP and usage of LocalAPI to track the tailnet state.
98119

99120
## Contributing
100121

0 commit comments

Comments
 (0)