Skip to content

Commit 698a6e6

Browse files
Merge pull request #358 from RunanywhereAI/smonga/macos_fix
Update iOS and macOS support in RunAnywhereAI with adaptive layouts a…
2 parents 7b9205b + 11915fa commit 698a6e6

26 files changed

+1111
-165
lines changed

Package.swift

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,8 @@ import Foundation
1818
//
1919
// =============================================================================
2020

21-
// Get the package directory for relative path resolution
22-
let packageDir = URL(fileURLWithPath: #file).deletingLastPathComponent().path
23-
24-
// Path to bundled ONNX Runtime dylib with CoreML support (for macOS)
25-
let onnxRuntimeMacOSPath = "\(packageDir)/sdk/runanywhere-swift/Binaries/onnxruntime-macos"
21+
// Combined ONNX Runtime xcframework (local dev) is created by:
22+
// cd sdk/runanywhere-swift && ./scripts/create-onnxruntime-xcframework.sh
2623

2724
// =============================================================================
2825
// BINARY TARGET CONFIGURATION
@@ -44,15 +41,13 @@ let useLocalBinaries = false // Toggle: true for local dev, false for release
4441

4542
// Version for remote XCFrameworks (used when testLocal = false)
4643
// Updated automatically by CI/CD during releases
47-
let sdkVersion = "0.18.0"
44+
let sdkVersion = "0.19.1"
4845

4946
let package = Package(
5047
name: "runanywhere-sdks",
5148
platforms: [
5249
.iOS(.v17),
5350
.macOS(.v14),
54-
.tvOS(.v17),
55-
.watchOS(.v10)
5651
],
5752
products: [
5853
// =================================================================
@@ -199,8 +194,11 @@ func binaryTargets() -> [Target] {
199194
// LOCAL DEVELOPMENT MODE
200195
// Use XCFrameworks from sdk/runanywhere-swift/Binaries/
201196
// Run: cd sdk/runanywhere-swift && ./scripts/build-swift.sh --setup
197+
//
198+
// For macOS support, build with --include-macos:
199+
// ./scripts/build-swift.sh --setup --include-macos
202200
// =====================================================================
203-
return [
201+
var targets: [Target] = [
204202
.binaryTarget(
205203
name: "RACommonsBinary",
206204
path: "sdk/runanywhere-swift/Binaries/RACommons.xcframework"
@@ -213,37 +211,44 @@ func binaryTargets() -> [Target] {
213211
name: "RABackendONNXBinary",
214212
path: "sdk/runanywhere-swift/Binaries/RABackendONNX.xcframework"
215213
),
214+
]
215+
216+
// Local combined ONNX Runtime xcframework (iOS + macOS)
217+
// Created by: cd sdk/runanywhere-swift && ./scripts/create-onnxruntime-xcframework.sh
218+
targets.append(
216219
.binaryTarget(
217220
name: "ONNXRuntimeBinary",
218-
url: "https://download.onnxruntime.ai/pod-archive-onnxruntime-c-1.17.1.zip",
219-
checksum: "9a2d54d4f503fbb82d2f86361a1d22d4fe015e2b5e9fb419767209cc9ab6372c"
220-
),
221-
]
221+
path: "sdk/runanywhere-swift/Binaries/onnxruntime.xcframework"
222+
)
223+
)
224+
225+
return targets
222226
} else {
223227
// =====================================================================
224228
// PRODUCTION MODE (for external SPM consumers)
225229
// Download XCFrameworks from GitHub releases
230+
// All xcframeworks include iOS + macOS slices (v0.19.0+)
226231
// =====================================================================
227232
return [
228233
.binaryTarget(
229234
name: "RACommonsBinary",
230-
url: "https://github.com/RunanywhereAI/runanywhere-sdks/releases/download/v\(sdkVersion)/RACommons-ios-v\(sdkVersion).zip",
231-
checksum: "cfe8fa73fd70b344d737bdfc6f66bf13fdd1777247ac7612dc8ada6356f90631"
235+
url: "https://github.com/RunanywhereAI/runanywhere-sdks/releases/download/v\(sdkVersion)/RACommons-v\(sdkVersion).zip",
236+
checksum: "f6bc152b1689d7549d6a7b5e692f6babb0efc44fe334c0e60acfc0c12d848c44"
232237
),
233238
.binaryTarget(
234239
name: "RABackendLlamaCPPBinary",
235-
url: "https://github.com/RunanywhereAI/runanywhere-sdks/releases/download/v\(sdkVersion)/RABackendLLAMACPP-ios-v\(sdkVersion).zip",
236-
checksum: "c08e6eddaf6c068f6458190b1689c32f08f153a88646591e0a0b09fdacf7f40d"
240+
url: "https://github.com/RunanywhereAI/runanywhere-sdks/releases/download/v\(sdkVersion)/RABackendLLAMACPP-v\(sdkVersion).zip",
241+
checksum: "ba150fd924f71c2137d6cad0a294c3f9c2da5bc748b547cced87bc0910a9b327"
237242
),
238243
.binaryTarget(
239244
name: "RABackendONNXBinary",
240-
url: "https://github.com/RunanywhereAI/runanywhere-sdks/releases/download/v\(sdkVersion)/RABackendONNX-ios-v\(sdkVersion).zip",
241-
checksum: "2755bea9d6414a1f776a9f57da8c8de86f970cd1416fca8f44c0246428c96d5f"
245+
url: "https://github.com/RunanywhereAI/runanywhere-sdks/releases/download/v\(sdkVersion)/RABackendONNX-v\(sdkVersion).zip",
246+
checksum: "00b28c0542ab25585c534b4e33ddacd4a1d24447aa8c2178949aad89eb56cb1f"
242247
),
243248
.binaryTarget(
244249
name: "ONNXRuntimeBinary",
245-
url: "https://download.onnxruntime.ai/pod-archive-onnxruntime-c-1.17.1.zip",
246-
checksum: "9a2d54d4f503fbb82d2f86361a1d22d4fe015e2b5e9fb419767209cc9ab6372c"
250+
url: "https://github.com/RunanywhereAI/runanywhere-sdks/releases/download/v\(sdkVersion)/onnxruntime-v\(sdkVersion).zip",
251+
checksum: "e0180262bd1b10fcda95aaf9aac595af5e6819bd454312b6fc8ffc3828db239f"
247252
),
248253
]
249254
}

examples/ios/RunAnywhereAI/RunAnywhereAI/App/ContentView.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ struct VisionHubView: View {
104104
}
105105
.navigationTitle("Vision")
106106
}
107+
#if os(iOS)
108+
.navigationViewStyle(.stack)
109+
#endif
107110
}
108111
}
109112

@@ -154,6 +157,9 @@ struct MoreHubView: View {
154157
}
155158
.navigationTitle("More")
156159
}
160+
#if os(iOS)
161+
.navigationViewStyle(.stack)
162+
#endif
157163
}
158164
}
159165

examples/ios/RunAnywhereAI/RunAnywhereAI/Core/Services/ConversationStore.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,11 @@ class ConversationStore: ObservableObject {
134134
guard let conversation = conversations.first(where: { $0.id == conversationId }) else {
135135
return
136136
}
137-
137+
138138
// Get the fallback title to compare
139139
let fallbackTitle = conversation.messages.first(where: { $0.role == .user })
140140
.map { generateTitle(from: $0.content) } ?? "New Chat"
141-
141+
142142
// Only generate if title is still the default or fallback
143143
let currentTitle = conversation.title
144144
guard currentTitle == "New Chat" || currentTitle == fallbackTitle else {
@@ -449,6 +449,9 @@ struct ConversationListView: View {
449449
Text("This action cannot be undone.")
450450
}
451451
}
452+
#if os(iOS)
453+
.navigationViewStyle(.stack)
454+
#endif
452455
}
453456
}
454457

@@ -457,7 +460,7 @@ struct ConversationListView: View {
457460
struct ConversationRow: View {
458461
let conversation: Conversation
459462
let searchQuery: String
460-
463+
461464
init(conversation: Conversation, searchQuery: String = "") {
462465
self.conversation = conversation
463466
self.searchQuery = searchQuery

examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Views/ChatDetailsView.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,19 @@ struct ChatDetailsView: View {
3737
PerformanceTab(messages: messages)
3838
.tag(2)
3939
}
40+
#if os(iOS)
4041
.tabViewStyle(.page(indexDisplayMode: .never))
42+
#endif
4143
}
44+
#if os(iOS)
4245
.background(Color(.systemGroupedBackground))
46+
#else
47+
.background(Color(nsColor: .controlBackgroundColor))
48+
#endif
4349
.navigationTitle("Analytics")
50+
#if os(iOS)
4451
.navigationBarTitleDisplayMode(.inline)
52+
#endif
4553
.toolbar {
4654
ToolbarItem(placement: .confirmationAction) {
4755
Button("Done") { dismiss() }

examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Views/ChatInterfaceView.swift

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ struct ChatInterfaceView: View {
4444
iOSView
4545
#endif
4646
}
47-
.sheet(isPresented: $showingConversationList) {
47+
.adaptiveSheet(isPresented: $showingConversationList) {
4848
ConversationListView()
4949
}
50-
.sheet(isPresented: $showingModelSelection) {
50+
.adaptiveSheet(isPresented: $showingModelSelection) {
5151
ModelSelectionSheet(context: .llm) { model in
5252
await handleModelSelected(model)
5353
}
5454
}
55-
.sheet(isPresented: $showingChatDetails) {
55+
.adaptiveSheet(isPresented: $showingChatDetails) {
5656
ChatDetailsView(
5757
messages: viewModel.messages,
5858
conversation: viewModel.currentConversation
@@ -111,10 +111,13 @@ extension ChatInterfaceView {
111111
modelRequiredOverlayIfNeeded
112112
}
113113
.navigationTitle(hasModelSelected ? "Chat" : "")
114+
#if os(iOS)
114115
.navigationBarTitleDisplayMode(.inline)
115116
.navigationBarHidden(!hasModelSelected)
117+
#endif
116118
.toolbar {
117119
if hasModelSelected {
120+
#if os(iOS)
118121
ToolbarItem(placement: .navigationBarLeading) {
119122
Button {
120123
showingConversationList = true
@@ -136,10 +139,35 @@ extension ChatInterfaceView {
136139
ToolbarItem(placement: .navigationBarTrailing) {
137140
modelButton
138141
}
142+
#else
143+
ToolbarItem(placement: .automatic) {
144+
Button {
145+
showingConversationList = true
146+
} label: {
147+
Image(systemName: "clock.arrow.trianglehead.counterclockwise.rotate.90")
148+
}
149+
}
150+
151+
ToolbarItem(placement: .automatic) {
152+
Button {
153+
showingChatDetails = true
154+
} label: {
155+
Image(systemName: "info.circle")
156+
.foregroundColor(viewModel.messages.isEmpty ? .gray : AppColors.primaryAccent)
157+
}
158+
.disabled(viewModel.messages.isEmpty)
159+
}
160+
161+
ToolbarItem(placement: .automatic) {
162+
modelButton
163+
}
164+
#endif
139165
}
140166
}
141167
}
168+
#if os(iOS)
142169
.navigationViewStyle(.stack)
170+
#endif
143171
}
144172
}
145173

@@ -391,7 +419,7 @@ extension ChatInterfaceView {
391419
}
392420
.disabled(!viewModel.canSend)
393421
.background {
394-
if #available(iOS 26.0, *) {
422+
if #available(iOS 26.0, macOS 26.0, *) {
395423
Circle()
396424
.fill(.clear)
397425
.glassEffect(.regular.interactive())

examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Views/ChatMessageComponents.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ struct MessageBubbleView: View {
108108
Spacer(minLength: AppSpacing.padding60)
109109
}
110110
}
111-
.sheet(isPresented: $showToolCallSheet) {
111+
.adaptiveSheet(isPresented: $showToolCallSheet) {
112112
if let toolCallInfo = message.toolCallInfo {
113113
ToolCallDetailSheet(toolCallInfo: toolCallInfo)
114114
.adaptiveSheetFrame()

examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Chat/Views/ModelLoadedToast.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ struct ModelLoadedToast: View {
3737
.padding(.horizontal, 16)
3838
.padding(.vertical, 12)
3939
.background {
40-
if #available(iOS 26.0, *) {
40+
if #available(iOS 26.0, macOS 26.0, *) {
4141
RoundedRectangle(cornerRadius: 16, style: .continuous)
4242
.fill(.clear)
4343
.glassEffect(.regular.interactive())

examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Diffusion/DiffusionViewModel.swift

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ class DiffusionViewModel: ObservableObject {
133133
isModelLoaded = true
134134
currentModelName = model.name
135135
currentBackend = model.framework.displayName
136-
136+
137137
// Show helpful info about the model
138138
let stepsInfo = variant.defaultSteps == 1 ? "1 step (ultra-fast)" : "\(variant.defaultSteps) steps"
139139
statusMessage = "Model loaded (\(currentBackend), \(stepsInfo))"
@@ -161,20 +161,20 @@ class DiffusionViewModel: ObservableObject {
161161
do {
162162
// Use model variant defaults for optimal performance
163163
// - SDXS: 512x512, 1 step, no CFG (ultra-fast ~2-10 sec)
164-
// - LCM: 512x512, 4 steps, low CFG (fast ~15-30 sec)
164+
// - LCM: 512x512, 4 steps, low CFG (fast ~15-30 sec)
165165
// - SD 1.5/Turbo: defaults based on variant
166166
let variant = self.currentModelVariant
167167
let resolution = variant.defaultResolution
168168
let steps = variant.defaultSteps
169169
let guidanceScale = variant.defaultGuidanceScale
170-
170+
171171
// For mobile, cap resolution to avoid memory issues
172172
let maxMobileRes = 512
173173
let width = min(resolution.width, maxMobileRes)
174174
let height = min(resolution.height, maxMobileRes)
175-
175+
176176
logger.info("Generating with \(variant.rawValue): \(width)x\(height), \(steps) steps, CFG=\(guidanceScale)")
177-
177+
178178
let options = DiffusionGenerationOptions(
179179
prompt: prompt,
180180
width: width,
@@ -197,8 +197,12 @@ class DiffusionViewModel: ObservableObject {
197197
}
198198
return true // continue generation
199199
}
200-
if let uiImage = createImage(from: result.imageData, width: result.width, height: result.height) {
201-
generatedImage = Image(uiImage: uiImage)
200+
if let platformImage = createImage(from: result.imageData, width: result.width, height: result.height) {
201+
#if os(iOS)
202+
generatedImage = Image(uiImage: platformImage)
203+
#elseif os(macOS)
204+
generatedImage = Image(nsImage: platformImage)
205+
#endif
202206
statusMessage = "Done in \(result.generationTimeMs)ms"
203207
} else {
204208
errorMessage = "Failed to create image"

examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Diffusion/ImageGenerationView.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,13 @@ struct ImageGenerationView: View {
3636
.navigationBarTitleDisplayMode(.inline)
3737
#endif
3838
}
39+
#if os(iOS)
40+
.navigationViewStyle(.stack)
41+
#endif
3942
.task {
4043
await viewModel.initialize()
4144
}
42-
.sheet(isPresented: $showModelPicker) {
45+
.adaptiveSheet(isPresented: $showModelPicker) {
4346
DiffusionModelPickerView(viewModel: viewModel, isPresented: $showModelPicker)
4447
}
4548
}
@@ -302,6 +305,9 @@ struct DiffusionModelPickerView: View {
302305
}
303306
}
304307
}
308+
#if os(iOS)
309+
.navigationViewStyle(.stack)
310+
#endif
305311
}
306312
}
307313

examples/ios/RunAnywhereAI/RunAnywhereAI/Features/Models/ModelStatusComponents.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ struct ModelRequiredOverlay: View {
254254
// Bottom section with glass effect button
255255
VStack(spacing: AppSpacing.medium) {
256256
// Primary CTA with glass effect
257-
if #available(iOS 26.0, *) {
257+
if #available(iOS 26.0, macOS 26.0, *) {
258258
Button(action: onSelectModel) {
259259
HStack(spacing: 8) {
260260
Image(systemName: "sparkles")

0 commit comments

Comments
 (0)