I'm looking to develop a very rich networking macOS app (like social media apps) operated by very large number of users, each user is able to create a number of windows, operate/view each of them, able to customize the app to his liking etc. The UI is expected to be very rich and dynamic.
The question is, should I choose AppKit or SwiftUI?
I have a basic understanding of SwiftUI, its declarative way of defining UI layouts and populating it with data. Not sure if SwiftUI can handle a very rich and dynamic UI customised by large number of users.
Any thoughts? What works best in this scenario? What is Apple's recommendation?
General
RSS for tagExplore the various UI frameworks available for building app interfaces. Discuss the use cases for different frameworks, share best practices, and get help with specific framework-related questions.
Post
Replies
Boosts
Views
Activity
I have a SwiftUI project which has the following hierarchy:
IOSSceneDelegate (App target) - depends on EntryPoint and Presentation static libs.
Presentation (Static library) - Depends on EntryPoint static lib. Contains UI related logic and updates the UI after querying the data layer.
EntryPoint (Static library) - Contains the entry point, AppDelegate (for its lifecycle aspects) etc.
I've only listed the relevant targets here.
SceneDelegate was initially present in EntryPoint library, because the AppDelegate references it when a scene is created.
public func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Set the SceneDelegate dynamically
let sceneConfig: UISceneConfiguration = UISceneConfiguration(name: "mainWindow", sessionRole: connectingSceneSession.role)
sceneConfig.delegateClass = SceneDelegate.self
return sceneConfig
}
The intent is to move the SceneDelegate to the Presentation library.
When moved, the EntryPoint library fails to compile because it's referencing the SceneDelegate (as shown above).
To remove this reference, I tried to set up the SceneDelegate in the old way - In the info.plist file, mention a SceneConfiguration and set the SceneDelegate in Presentation.
// In the Info.plist file
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>Presentation.SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
// In the AppDelegate
public func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Refer to a static UISceneconfiguration listed in the info.plist file
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
As shown above, the Presentation.SceneDelegate is referred in the Info.plist file and the reference is removed from the AppDelegate (in EntryPoint library).
The app target compiles, but when I run it, the SceneDelegate is not invoked. None of the methods from the SceneDelegate (scene(_:willConnectTo:options:), sceneDidDisconnect(_:), sceneDidEnterBackground(_:) etc.) are invoked. I only get the AppDelegate logs.
It seems like the Configuration is ignored because it was incorrect. Any thoughts? Is it possible to move the SceneDelegate in this situation?
In our app clip, we open/show the full app download banner. We used to have the expected behavior, but with seemingly no changes to the app download banner code we have the following issue.
Expected behavior:
App download banner shows in app clip, user presses "Get" button, app is downloaded and installs, "Get" button changes to "Open" button (note: button is blue), user presses "Open" button and the full app is opened.
Current behavior:
App download banner shows in app clip, user presses "Get" button, app is downloaded and installs, "Get" button changes to "Open" button (note: button is now grey), user presses "Open" button but nothing happens.
With the current behavior, the full app is correctly downloaded and the appclip removes itself from the phone, but the open button does nothing.
Hey, I am building some widgets and I'm quite surprised that Swipe gestures for widgets is not supported. It means the user must sacrifice home screen real estate to view multiple widgets to receive the same information. Ideally, swiping left / right inside of the widget should give a developer access to present different views.
I realize that it means that a user would need to swipe outside of the widget, (or swipe to the beginning/end of the series of views inside of the widget) for the page to swipe, but I'd argue that this is the intuitive behavior of what widget scrollview would or should look like anyway.
Given Apple's new .limited contact authorization introduced in ios18, I want to be able to present the ContactAccessPicker directly from my app, via ionic capacitor. I present the .contactAccessPicker view via a UIHostingController, and I manage the view controller's dismissal accordingly when the ContactAccessPicker completes and is no longer presented.
Bug: After a few searches or interactions with the Contact Access Picker (ex. searching, selecting contacts, clicking the "show selected" button), the contact access picker crashes and the overlay remains. Any interaction with my app is then blocked because I can't detect that the contact access picker has disappeared when it crashes so I can't dismiss the viewController.
Is there a way for me to prevent the contact access picker from crashing, and how can I detect if it does crash, so I can at least dismiss the view controller if that happens?
struct ContactAccessPickerView: View {
@Binding var isPresented: Bool
let completion: @MainActor ([String]) -> Void
var body: some View {
Group {
if #available(iOS 18.0, *) {
Color.clear
.contactAccessPicker(isPresented: $isPresented) { result in
Task { @MainActor in
completion(result)
}
}
} else {
}
}
}
}
@objc func selectMoreContacts(_ call: CAPPluginCall) {
guard isContactsPermissionGranted() else {
call.resolve(["success": false])
return
}
// Save the call to ensure it's available until we finish
self.bridge?.saveCall(call)
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
var isPresented = true
let picker = ContactAccessPickerView(isPresented: .init(get: { isPresented }, set: { isPresented = $0 })) { contacts in
call.resolve(["success": true])
self.dismissAndCleanup(call)
}
let hostingController = UIHostingController(rootView: picker)
hostingController.modalPresentationStyle = .overFullScreen
self.bridge?.viewController?.present(hostingController, animated: true)
}
}
Since iOS 18.3, icons are no longer generated correctly with QLThumbnailGenerator.
No error is returned either.
But this error message now appears in the console:
Error returned from iconservicesagent image request: <ISTypeIcon: 0x3010f91a0>,Type: com.adobe.pdf - <ISImageDescriptor: 0x302f188c0> - (36.00, 36.00)@3x v:1 l:5 a:0:0:0:0 t:() b:0 s:2 ps:0 digest: B19540FD-0449-3E89-AC50-38F92F9760FE error: Error Domain=NSOSStatusErrorDomain Code=-609 "Client is disallowed from making such an icon request" UserInfo={NSLocalizedDescription=Client is disallowed from making such an icon request}
Does anyone know this error? Is there a workaround?
Are there new permissions to consider?
Here is the code how icons are generated:
let request = QLThumbnailGenerator.Request(fileAt: url, size: size, scale: scale, representationTypes: self.thumbnailType)
request.iconMode = true
let generator = QLThumbnailGenerator.shared
generator.generateRepresentations(for: request) { [weak self] thumbnail, _, error in
}
I am trying to support dragging out a 'file' object from my app into Finder, on macOS. I have my object conform to Transferable and the files are saved on disk locally, so I just want to pass it the URL. This works fine when dragging out to other apps, like Notes or Mail, but not in Finder. I setup a ProxyRepresentation as well, as suggested by another thread, but it doesn't seem to help. Is there any other setup I need to do in the Xcode project file for it to work, or is there something else that I'm missing?
@available(iOSApplicationExtension 17.0, macOSApplicationExtension 14.0, *)
extension FileAttachments: Transferable {
public static var transferRepresentation: some TransferRepresentation {
FileRepresentation(exportedContentType: UTType.content) { content in
SentTransferredFile(content.fullFileURL(), allowAccessingOriginalFile: false)
}
.exportingCondition { file in
if let fileUTI = UTType(filenameExtension: file.fullFileURL().pathExtension), let fileURL = file.fullFileURL() {
print("FileAttachments: FileRepresentation exportingCondition fileUTI: \(fileUTI) for file: \(fileURL)")
return fileUTI.conforms(to: UTType.content)
}
return false
}
.suggestedFileName{$0.fileRenamedName}
ProxyRepresentation { file in
if let fileURL = file.fullFileURL() {
print("FileAttachments: ProxyRepresentation returning file")
return fileURL
}
return file.fullFileURL()!
}
}
}
Is it the default behavior that the standard back swipe (interactivePopGestureRecognizer) does not work when running a designed for iPhone app on an iPad?
To my knowledge, all apps behave this way.
Are there any workarounds?
I'm writing an app to help with astrophotography, and I need to perform a contrast stretch to the image, because it was taken with a specialized astrophotography camera in monochrome and most of the data is very dark.
Most astrophotography software (astropy, Pixinsight) has something called an autostretch, which is a form of contrast stretching. I would like to do the same thing in my iOS app, using the tools available to me in SwiftUI, UIImage, CIImage, and CGImage.
I am to the point that I have created a buffer .withUnsafeMutableBufferPointer that contains the image data as 16-bit unsigned integers (the format given to me by the camera). I then create a vImage_Buffer using:
var buffer = vImage_Buffer(data: outPtr.baseAddress, height: vImagePixelCount(imageHeight), width: vImagePixelCount(imageWidth), rowBytes: MemoryLayout<Float>.size * imageWidth)
... and now I would like to apply either an equalizeHistogram() or a contrastStretch() to the buffer. What do I need to do? Do I need to create a CGImageFormat, like this?
let cgiImageFormat = vImage_CGImageFormat(bitsPerComponent: 16, bitsPerPixel: 16, colorSpace: CGColorSpaceCreateDeviceGray(), bitmapInfo: bitmapInfo)!
Which function should I use to do the equalization or contrast stretch? There appears to be a vImageContrastStretch_PlanarF() function, but I'm not sure the input data will be in the proper format (is a monochrome CGImage 32-bit planar F?), and I certainly don't know how to setup the histogram_entries parameter for that function. It seems like the function could just scan the image itself, form the histogram, and then stretch it, right?
So a code example would help a lot!
Thanks in advance,
Robert
I am trying to add a custom policy to Entity Mapping and it refuses to work because the app name has a space in it. I tried replacing the space character with underscore and hyphen but it still does not work.
I tried creating an MVP where app name did not have any space and it worked in the first try. However, for another MVP where app name had a space in it, it is not working at all.
Hi everyone! I'm thrilled to share that I'm conducting a field research as part of my final university project, focused on iOS architecture.
The goal is to dive deeper into the best practices, challenges, and trends in the iOS development world. To make this research truly impactful, I need your help!
If you're an iOS developer, I’d love it if you could take a few minutes to answer a short survey. Your insights and experiences will be invaluable for my research, and I greatly appreciate your
support!
Here is the link:
https://docs.google.com/forms/d/e/1FAIpQLSdf9cacfA7my1hnlazyl7uJraa2oTsQ7dJBWvFtZ_4vbYenRA/viewform?usp=send_form
Thank you so much in advance for helping me out—feel free to share this post with others who might also be interested. Let’s build something amazing together! 💡✨
I'm trying to setup a widget to pull an image down from a webserver and I'm running into an error of Widget archival failed due to image being too large [9] - (1024, 1024), totalArea: 1048576 > max[718080.000000].
I've tried two different approaches to resolve this error and both have failed to resolve the image.
I've also confirmed that I'm getting the image in the AppIntentTimelineProvider.
private func getImageUI(urlString: String) -> UIImage? {
guard let url = URL(string: urlString) else { return nil }
guard let imageData = try? Data(contentsOf: url) else { return nil }
return UIImage(data: imageData)?.resizedForWidget()
}
Is there another approach I could take on addressing this issue so the image appears on the widget?
Simple approach
extension UIImage {
func resized(toWidth width: CGFloat, isOpaque: Bool = true) -> UIImage? {
let canvas = CGSize(width: width, height: CGFloat(ceil(width/size.width * size.height)))
let format = imageRendererFormat
format.opaque = isOpaque
return UIGraphicsImageRenderer(size: canvas, format: format).image {
_ in draw(in: CGRect(origin: .zero, size: canvas))
}
}
}
extension UIImage {
/// Resize the image to strictly fit within WidgetKit’s max allowed pixel area (718,080 pixels)
func resizedForWidget(maxArea: CGFloat = 718_080.0, isOpaque: Bool = true) -> UIImage? {
let originalWidth = size.width
let originalHeight = size.height
let originalArea = originalWidth * originalHeight
print("🔍 Original Image Size: \(originalWidth)x\(originalHeight) → Total Pixels: \(originalArea)")
// ✅ If the image is already within the limit, return as is
if originalArea <= maxArea {
print("✅ Image is already within the allowed area.")
return self
}
// 🔄 Calculate the exact scale factor to fit within maxArea
let scaleFactor = sqrt(maxArea / originalArea)
let newWidth = floor(originalWidth * scaleFactor) // Use `floor` to ensure area is always within limits
let newHeight = floor(originalHeight * scaleFactor)
let newSize = CGSize(width: newWidth, height: newHeight)
print("🛠 Resizing Image: \(originalWidth)x\(originalHeight) → \(newWidth)x\(newHeight)")
// ✅ Force bitmap rendering to ensure the resized image is properly stored
let format = UIGraphicsImageRendererFormat()
format.opaque = isOpaque
format.scale = 1 // Ensures we are not letting UIKit auto-scale it back up
let renderer = UIGraphicsImageRenderer(size: newSize, format: format)
let resizedImage = renderer.image { _ in
self.draw(in: CGRect(origin: .zero, size: newSize))
}
print("✅ Final Resized Image Size: \(resizedImage.size), Total Pixels: \(resizedImage.size.width * resizedImage.size.height)")
return resizedImage
}
}
These are logs from a failed image render if that helps
🔍 Original Image Size: 720.0x1280.0 → Total Pixels: 921600.0
🛠 Resizing Image: 720.0x1280.0 → 635.0x1129.0
✅ Final Resized Image Size: (635.0, 1129.0), Total Pixels: 716915.0
now i must use voip + livekit to developing, When incoming offline messages arrive at the device through VoIP, call ConversationManager The method of reporting NewIncomingConversation (uuid: update:) only first time can push new system UI,second or more time will crash, and acrsh stack appears to indicate that callkit has not been called
I want to be able to dynamically update the phrase dictionary in an AppShortcut. However, whenever I abstract the phrases, the shortcut fails to display. That is, I am trying to do:
static var phrases: [AppShortcutPhrase<MyIntent>] = ["\(.applicationName) hello world"]
AppShortcut(
intent: MyIntent(),
phrases: phrases,
shortTitle: "hello world",
systemImageName: ""
)
However, the following works:
AppShortcut(
intent: MyIntent(),
phrases: "\(.applicationName) hello world",
shortTitle: "hello world",
systemImageName: ""
)
So, what gives?
I am working on a React Native application where I want to modify the native text selection menu (the menu that appears when you long-press on text). Specifically, I want to add a custom option alongside the default ones like Copy, Look Up, Translate, Search Web, and Share.
Is there a way to modify the native text selection menu inside a WebView on iOS?
How can I add a custom menu option to the default text selection menu while keeping all the default options intact?
We were using below delegate methods from QuickLook to get modified PDF file URL after the sketching But we are not able see the multi line text properly laid out on PDF and part of text missing. Same time Other pencil kit tools are working as expected.
`func previewController(_ controller: QLPreviewController, didSaveEditedCopyOf previewItem: QLPreviewItem, at modifiedContentsURL: URL)
func previewController(_ controller: QLPreviewController, didUpdateContentsOf previewItem: any QLPreviewItem)`
We tested all code in iOS 18.2.
Please let us know if the text edited URL on PDF can be retrieved in any possible way without tampering text
I am implementing a new Intents UI Extension and am noticing that the viewWillDisappear, viewDidDisappear, and deinit methods are not being called on my UIViewController that implements INUIHostedViewControlling, when pressing the "Done" button and dismissing the UIViewController.
This causes the memory for the UI Extension to slowly increase each time I re-run the UI Extension until it reaches the 120MB limit and crashes.
Any ideas as to what's going on here and how to solve this issue?
Worth noting that while the memory does continuously increase on iOS versions before iOS 17, only in 17 and later does the 120MB memory limit kick in and crash the extension.
The DeviceActivityReport view does not render immediately when added to the view hierarchy. Instead, it requires repeated navigation to the screen hosting the DeviceActivityReport view for it to appear.
Furthermore, there is no programmatic way to determine whether the view is being rendered for the user, leading to an inconsistent and often poor user experience.
I've created a sample project that demonstrates the issue.
After uploading the app to App Store Connect, Apple automatically generated a Default App Clip Link. However, the App Clip card only opens successfully if the main app is already installed on the device. If the main app is not installed, the App Clip card displays an image and the message "App Clip Unavailable"
What could cause this behavior, and how do I ensure the App Clip works without requiring the main app to be installed?
With "Requires full screen" Split View and Slide Over are disabled but the line on the bottom of the screen remains.
How can that line removed as when a video is displayed full screen?