Skip to content

Commit

Permalink
feat(swiftsdk): swift sdk scaffolding (#38)
Browse files Browse the repository at this point in the history
jackpooleyml authored Dec 13, 2024
1 parent 68bdf0d commit dda9ebe
Showing 35 changed files with 1,736 additions and 2 deletions.
7 changes: 7 additions & 0 deletions .github/package.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ZKsyncSSO"
BuildableName = "ZKsyncSSO"
BlueprintName = "ZKsyncSSO"
ReferencedContainer = "container:../packages/sdk-platforms/swift/ZKsyncSSO">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ZKsyncSSOTests"
BuildableName = "ZKsyncSSOTests"
BlueprintName = "ZKsyncSSOTests"
ReferencedContainer = "container:../packages/sdk-platforms/swift/ZKsyncSSO">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ZKsyncSSOTests"
BuildableName = "ZKsyncSSOTests"
BlueprintName = "ZKsyncSSOTests"
ReferencedContainer = "container:../packages/sdk-platforms/swift/ZKsyncSSO">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ZKsyncSSO"
BuildableName = "ZKsyncSSO"
BlueprintName = "ZKsyncSSO"
ReferencedContainer = "container:../packages/sdk-platforms/swift/ZKsyncSSO">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
45 changes: 45 additions & 0 deletions .github/workflows/ci-swift.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Swift CI

on:
push:
paths:
- 'packages/sdk-platforms/**'

jobs:
swift-sdk:
name: Swift Package - latest
runs-on: macos-15
strategy:
matrix:
config:
- debug
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Run sccache-cache
uses: mozilla-actions/[email protected]
- run: rustup update stable && rustup default stable
- name: Select Xcode 16.1
run: sudo xcode-select -s /Applications/Xcode_16.1.app
- name: Select Simulator
run: |
UDID=$(xcrun simctl list devices | awk '/-- iOS 18.1 --/{flag=1; next} /--/{flag=0} flag' | grep "iPhone 16 Pro" | awk -F '[()]' '{print $2}' | head -1)
if [ -z "$UDID" ]; then
echo "Simulator not found!" >&2
exit 1
fi
echo "Simulator UDID: $UDID"
echo "SIMULATOR_UDID=$UDID" >> $GITHUB_ENV
- name: Install swiftformat
run: brew install swiftformat
- name: Build bindings
run: sh packages/sdk-platforms/rust/zksync-sso/crates/ffi/build-swift-framework.sh
- name: Build & test Swift SDK
run: |
xcodebuild test \
-skipMacroValidation \
-configuration debug \
-workspace .github/package.xcworkspace \
-scheme zksyncsso \
-destination "platform=iOS Simulator,id=${{ env.SIMULATOR_UDID }}" || exit 1
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@ name: CI

on:
pull_request:
paths-ignore:
- 'packages/sdk-platforms/**'
workflow_dispatch:

jobs:
@@ -166,4 +168,3 @@ jobs:
- name: Run contract test
run: pnpm test
working-directory: packages/contracts

16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -31,3 +31,19 @@ playwright/.cache/
.DS_Store

dist

# Rust
debug/
target/
Cargo.lock

# Swift
xcuserdata/
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
timeline.xctimeline
playground.xcworkspace
.build/
out/
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -28,3 +28,6 @@ deployments-zk
**/*.tsx
**/*.mjs
**/*.cjs

# Ignore Swift/Rust files
packages/sdk-platforms/*
3 changes: 2 additions & 1 deletion cspell.json
Original file line number Diff line number Diff line change
@@ -26,7 +26,8 @@
"**/test-results/**",
"**/playwright-report/**",
"**/blob-report/**",
"**/playwright/.cache/**"
"**/playwright/.cache/**",
"packages/sdk-platforms/**"
],
"caseSensitive": true,
"dictionaries": [
11 changes: 11 additions & 0 deletions packages/sdk-platforms/rust/zksync-sso/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[workspace]
members = ["crates/*"]
resolver = "2"

[workspace.package]
version = "0.1.0"
edition = "2021"
rust-version = "1.83"
license = "Apache-2.0"

[workspace.dependencies]
9 changes: 9 additions & 0 deletions packages/sdk-platforms/rust/zksync-sso/crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "cli"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true

[dependencies]
sdk = { path = "../sdk" }
3 changes: 3 additions & 0 deletions packages/sdk-platforms/rust/zksync-sso/crates/cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
println!("{}", format!("{}", sdk::add(1, 1)));
}
16 changes: 16 additions & 0 deletions packages/sdk-platforms/rust/zksync-sso/crates/ffi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "ffi"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true

[lib]
crate-type = ["staticlib"]

[dependencies]
uniffi = { version = "0.28.3", features = ["cli"] }
sdk = { path = "../sdk" }

[build-dependencies]
uniffi = { version = "0.28.3", features = ["build", "cli"] }
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#!/bin/sh

set -eo pipefail

pushd `dirname $0`
trap popd EXIT

NAME="ffi"
VERSION=${1:-"1.0"} # first arg or "1.0"
REVERSE_DOMAIN="dev.matterlabs"
BUNDLE_IDENTIFIER="$REVERSE_DOMAIN.$NAME"
LIBRARY_NAME="lib$NAME.a"
FRAMEWORK_LIBRARY_NAME=${NAME}FFI
FRAMEWORK_NAME="$FRAMEWORK_LIBRARY_NAME.framework"
XC_FRAMEWORK_NAME="$FRAMEWORK_LIBRARY_NAME.xcframework"
HEADER_NAME="${NAME}FFI.h"
OUT_PATH="out"
MIN_IOS_VERSION="18.0"
WRAPPER_PATH="../../../../swift/ZKsyncSSO/Sources/ZKsyncSSOFFI"
TARGET_PATH="../../target"
BUILD_TYPE="debug" # use debug during development

AARCH64_APPLE_IOS_PATH="$TARGET_PATH/aarch64-apple-ios/$BUILD_TYPE"
AARCH64_APPLE_IOS_SIM_PATH="$TARGET_PATH/aarch64-apple-ios-sim/$BUILD_TYPE"
X86_64_APPLE_IOS_PATH="$TARGET_PATH/x86_64-apple-ios/$BUILD_TYPE"
AARCH64_APPLE_DARWIN_PATH="$TARGET_PATH/aarch64-apple-darwin/$BUILD_TYPE"
X86_64_APPLE_DARWIN_PATH="$TARGET_PATH/x86_64-apple-darwin/$BUILD_TYPE"

targets=("aarch64-apple-ios" "aarch64-apple-ios-sim" "x86_64-apple-ios" "aarch64-apple-darwin" "x86_64-apple-darwin")

# Build for all targets
for target in "${targets[@]}"; do
echo "Building for $target..."
rustup target add $target

if [ "$BUILD_TYPE" = "debug" ]; then
echo "Building debug for $target"
cargo build --target $target
else
echo "Building release for $target"
cargo build --release --target $target
fi
done

# Generate swift wrapper
echo "Generating swift wrapper..."
mkdir -p $OUT_PATH
mkdir -p $WRAPPER_PATH
CURRENT_ARCH=$(rustc --version --verbose | grep host | cut -f2 -d' ')
cargo run --features=uniffi/cli --bin uniffi-bindgen generate --library $TARGET_PATH/$CURRENT_ARCH/$BUILD_TYPE/$LIBRARY_NAME --language swift --out-dir $OUT_PATH

# Merge libraries with lipo
echo "Merging libraries with lipo..."
lipo -create $AARCH64_APPLE_IOS_SIM_PATH/$LIBRARY_NAME \
$X86_64_APPLE_IOS_PATH/$LIBRARY_NAME \
-output $OUT_PATH/sim-$LIBRARY_NAME
lipo -create $AARCH64_APPLE_DARWIN_PATH/$LIBRARY_NAME \
$X86_64_APPLE_DARWIN_PATH/$LIBRARY_NAME \
-output $OUT_PATH/macos-$LIBRARY_NAME

# Create framework template
rm -rf $OUT_PATH/$FRAMEWORK_NAME
mkdir -p $OUT_PATH/$FRAMEWORK_NAME/Headers
mkdir -p $OUT_PATH/$FRAMEWORK_NAME/Modules
cp $OUT_PATH/$HEADER_NAME $OUT_PATH/$FRAMEWORK_NAME/Headers
cat <<EOT > $OUT_PATH/$FRAMEWORK_NAME/Modules/module.modulemap
framework module $FRAMEWORK_LIBRARY_NAME {
umbrella header "$HEADER_NAME"
export *
module * { export * }
}
EOT

cat <<EOT > $OUT_PATH/$FRAMEWORK_NAME/Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$FRAMEWORK_LIBRARY_NAME</string>
<key>CFBundleIdentifier</key>
<string>$BUNDLE_IDENTIFIER</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$FRAMEWORK_LIBRARY_NAME</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$VERSION</string>
<key>NSPrincipalClass</key>
<string></string>
<key>MinimumOSVersion</key>
<string>$MIN_IOS_VERSION</string>
</dict>
</plist>
EOT

# Prepare frameworks for each platform
rm -rf $OUT_PATH/frameworks
mkdir -p $OUT_PATH/frameworks/sim
mkdir -p $OUT_PATH/frameworks/ios
mkdir -p $OUT_PATH/frameworks/macos
cp -r $OUT_PATH/$FRAMEWORK_NAME $OUT_PATH/frameworks/sim/
cp -r $OUT_PATH/$FRAMEWORK_NAME $OUT_PATH/frameworks/ios/
cp -r $OUT_PATH/$FRAMEWORK_NAME $OUT_PATH/frameworks/macos/
mv $OUT_PATH/sim-$LIBRARY_NAME $OUT_PATH/frameworks/sim/$FRAMEWORK_NAME/$FRAMEWORK_LIBRARY_NAME
mv $OUT_PATH/macos-$LIBRARY_NAME $OUT_PATH/frameworks/macos/$FRAMEWORK_NAME/$FRAMEWORK_LIBRARY_NAME
cp $AARCH64_APPLE_IOS_PATH/$LIBRARY_NAME $OUT_PATH/frameworks/ios/$FRAMEWORK_NAME/$FRAMEWORK_LIBRARY_NAME

# Create xcframework
echo "Creating xcframework..."
rm -rf $OUT_PATH/$XC_FRAMEWORK_NAME
xcodebuild -create-xcframework \
-framework $OUT_PATH/frameworks/sim/$FRAMEWORK_NAME \
-framework $OUT_PATH/frameworks/ios/$FRAMEWORK_NAME \
-framework $OUT_PATH/frameworks/macos/$FRAMEWORK_NAME \
-output $OUT_PATH/$XC_FRAMEWORK_NAME

# Copy swift wrapper
# Need some temporary workarounds to compile swift wrapper
# https://github.com/rust-lang/cargo/issues/11953
cat <<EOT > $OUT_PATH/import.txt
#if os(macOS)
import SystemConfiguration
#endif
EOT
cat $OUT_PATH/import.txt $OUT_PATH/$NAME.swift > $WRAPPER_PATH/$NAME.swift

# Fix initializationResult compilation error
sed -i '' 's/private var initializationResult: InitializationResult = {/private let initializationResult: InitializationResult = {/' "$WRAPPER_PATH/$NAME.swift"

# Don't format ffi.swift with swift-format
echo "// swift-format-ignore-file" | cat - "$WRAPPER_PATH/$NAME.swift" > temp && mv temp "$WRAPPER_PATH/$NAME.swift"
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
uniffi::uniffi_bindgen_main()
}
30 changes: 30 additions & 0 deletions packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use uniffi;

uniffi::setup_scaffolding!();

#[uniffi::export]
fn greet(name: &str) -> String {
format!("Hello, {}", name)
}

#[uniffi::export]
fn add(left: u64, right: u64) -> u64 {
sdk::add(left, right)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn add_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}

#[test]
fn greeting_works() {
let greeting = greet("Rust");
assert_eq!("Hello, Rust", greeting);
}
}
9 changes: 9 additions & 0 deletions packages/sdk-platforms/rust/zksync-sso/crates/sdk/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "sdk"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true

[dependencies]
alloy-zksync = { version = "0.5.0" }
14 changes: 14 additions & 0 deletions packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pub fn add(left: u64, right: u64) -> u64 {
left + right
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn add_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
7 changes: 7 additions & 0 deletions packages/sdk-platforms/rust/zksync-sso/rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
max_width = 80
use_small_heuristics = "Max"
comment_width = 100
format_code_in_doc_comments = true
doc_comment_code_block_width = 100
group_imports = "One"
imports_granularity = "One"
8 changes: 8 additions & 0 deletions packages/sdk-platforms/swift/ZKsyncSSO/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
6 changes: 6 additions & 0 deletions packages/sdk-platforms/swift/ZKsyncSSO/.swift-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"indentation" : {
"spaces" : 4
},
"version" : 1
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import SwiftUI
import ZKsyncSSO

struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text(greetRust(name: "Rust"))
}
.padding()
}
}

#Preview {
ContentView()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import SwiftUI

@main
struct ExampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Testing
@testable import Example

struct ExampleTests {

@Test func example() async throws {
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import XCTest

final class ExampleUITests: XCTestCase {

override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.

// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false

// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}

override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}

@MainActor
func testExample() throws {
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launch()

// Use XCTAssert and related functions to verify your tests produce the correct results.
}

@MainActor
func testLaunchPerformance() throws {
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
// This measures how long it takes to launch your application.
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import XCTest

final class ExampleUITestsLaunchTests: XCTestCase {

override class var runsForEachTargetApplicationUIConfiguration: Bool {
true
}

override func setUpWithError() throws {
continueAfterFailure = false
}

@MainActor
func testLaunch() throws {
let app = XCUIApplication()
app.launch()

// Insert steps here to perform after app launch but before taking a screenshot,
// such as logging into a test account or navigating somewhere in the app

let attachment = XCTAttachment(screenshot: app.screenshot())
attachment.name = "Launch Screen"
attachment.lifetime = .keepAlways
add(attachment)
}
}
29 changes: 29 additions & 0 deletions packages/sdk-platforms/swift/ZKsyncSSO/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// swift-tools-version: 6.0
import PackageDescription

let package = Package(
name: "ZKsyncSSO",
platforms: [
.iOS(.v18),
.macOS(.v15),
],
products: [
.library(
name: "ZKsyncSSO",
targets: ["ZKsyncSSO"])
],
targets: [
.target(
name: "ZKsyncSSO",
dependencies: ["ZKsyncSSOFFI"]),
.target(
name: "ZKsyncSSOFFI",
dependencies: ["ffiFFI"]),
.binaryTarget(
name: "ffiFFI",
path: "../../rust/zksync-sso/crates/ffi/out/ffiFFI.xcframework"),
.testTarget(
name: "ZKsyncSSOTests",
dependencies: ["ZKsyncSSO"]),
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import ZKsyncSSOFFI

public func greetRust(name: String) -> String {
greet(name: name)
}

public func addRust(left: UInt64, right: UInt64) -> UInt64 {
add(left: left, right: right)
}
513 changes: 513 additions & 0 deletions packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSOFFI/ffi.swift

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Testing
@testable import ZKsyncSSO

@Test func testGreetRust() async throws {
#expect(greetRust(name: "Rust") == "Hello, Rust")
}

@Test func testAddRust() async throws {
#expect(addRust(left: 2, right: 2) == 4)
}

0 comments on commit dda9ebe

Please sign in to comment.