Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
Binary file added .github/.DS_Store
Binary file not shown.
Binary file added .github/workflows/.DS_Store
Binary file not shown.
55 changes: 55 additions & 0 deletions .github/workflows/release-spm-tag-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Release Validation

on:
push:
tags:
- 'v*' # Only run when pushing semantic style tags (ex: v1.0.0)

jobs:
validate-spm-tag:
name: Validate SPM Release Tag
runs-on: macos-14
defaults:
run:
working-directory: ${{ github.workspace }}

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required to check tags

- name: Validate tag format
run: |
TAG=${GITHUB_REF#refs/tags/}
if [[ ! $TAG =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "❌ Tag '$TAG' is not SemVer formatted (vX.Y.Z)"
exit 1
fi
echo "✅ Valid tag format: $TAG"

- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable

- name: Resolve dependencies
run: swift package resolve --package-path ${{ github.workspace }}

- name: Build (iOS)
run: |
xcodebuild \
-scheme concordium-id-swift-sdk \
-destination 'generic/platform=iOS Simulator' \
-configuration Debug \
clean build

- name: Lint (SwiftLint via Xcode)
run: |
xcodebuild \
-scheme concordium-id-swift-sdk \
-destination 'generic/platform=iOS Simulator' \
-configuration Debug \
clean build \
-skipPackagePluginValidation=NO

Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?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>SchemeUserState</key>
<dict>
<key>concordium-id-swift-sdk.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
</dict>
</plist>
18 changes: 18 additions & 0 deletions Sources/UI/ConcordiumIDAppPopup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import SwiftUI
import CoreImage.CIFilterBuiltins
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif

/// Popup view for interacting with the Concordium ID App from a host application.
Expand Down Expand Up @@ -70,9 +72,25 @@ public struct ConcordiumIDAppPopup: View {
This method is used to redirect the user to the ID App on mobile devices.
*/
public static func openIdapp(walletConnectMobileUrl: String, walletConnectDesktopUrl: String? = nil) {
#if canImport(UIKit)
if let url = URL(string: walletConnectMobileUrl) {
UIApplication.shared.open(url)
}
#elseif canImport(AppKit)
guard let urlString = ((walletConnectDesktopUrl?.isEmpty) != nil)
? walletConnectMobileUrl
: walletConnectDesktopUrl else { return }

if let url = URL(string: urlString) {
NSWorkspace.shared.open(url)
}
#else

// Fallback for other platforms
if let url = URL(string: walletConnectMobileUrl) {
// Use SwiftUI's openURL environment value if available
}
#endif
}

/**
Expand Down
52 changes: 49 additions & 3 deletions Sources/UI/QRCodeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import SwiftUI
import CoreImage.CIFilterBuiltins
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif

/// Displays a QR code generated from the provided text.
Expand All @@ -16,12 +18,36 @@ public struct QRCodeView: View {

public var body: some View {
if let image = generateQRCode(from: text) {
#if canImport(UIKit)
Image(uiImage: image)
.resizable()
.scaledToFit()
.clipShape(RoundedRectangle(cornerRadius: 4))
.overlay(
RoundedRectangle(cornerRadius: 4)
.stroke(Color.black.opacity(0.2), lineWidth: 1)
)

#elseif canImport(AppKit)
Image(nsImage: image)
.interpolation(.none)
.resizable()
.scaledToFit()
.clipShape(RoundedRectangle(cornerRadius: 4))
.overlay(RoundedRectangle(cornerRadius: 4).stroke(Color.black.opacity(0.2), lineWidth: 1))
.overlay(
RoundedRectangle(cornerRadius: 4)
.stroke(Color.black.opacity(0.2), lineWidth: 1)
)
#else
Image(systemName: "qrcode")
.resizable()
.scaledToFit()
.clipShape(RoundedRectangle(cornerRadius: 4))
.overlay(
RoundedRectangle(cornerRadius: 4)
.stroke(Color.black.opacity(0.2), lineWidth: 1)
)
#endif
} else {
ZStack {
Color.gray.opacity(0.1)
Expand All @@ -32,10 +58,10 @@ public struct QRCodeView: View {
.clipShape(RoundedRectangle(cornerRadius: 4))
}
}

/// Create a QR `UIImage` from a string.
/// Create a QR code image from a string.
/// - Parameter string: Input to encode.
/// - Returns: Rasterized QR code or `nil` if generation fails.
#if canImport(UIKit)
private func generateQRCode(from string: String) -> UIImage? {
let data = Data(string.utf8)
filter.setValue(data, forKey: "inputMessage")
Expand All @@ -50,6 +76,26 @@ public struct QRCodeView: View {
}
return nil
}
#elseif canImport(AppKit)
private func generateQRCode(from string: String) -> NSImage? {
let data = Data(string.utf8)
filter.setValue(data, forKey: "inputMessage")

guard let outputImage = filter.outputImage else { return nil }

let transform = CGAffineTransform(scaleX: 10, y: 10)
let scaledImage = outputImage.transformed(by: transform)

if let cgImage = context.createCGImage(scaledImage, from: scaledImage.extent) {
return NSImage(cgImage: cgImage, size: NSSize(width: cgImage.width, height: cgImage.height))
}
return nil
}
#else
private func generateQRCode(from string: String) -> Never? {
return nil
}
#endif
}


3 changes: 0 additions & 3 deletions Sources/UI/StepsViews.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import SwiftUI
#if canImport(UIKit)
import UIKit
#endif

/// A single step indicator with title and active state.
struct StepView: View {
Expand Down