Hello, I have a small lightweight macOS application that includes a medium widget but the widget does not update with new data as often as I'd like. I understand that in apple's WidgetKit documentation they mention that apple controls when the widget updates due to battery life concerns, but I'd like to know if theres any way at all to control when the widget updates or when I think it makes sense to do so if I am not able to control how often it refreshes new data.
https://github.com/Alexx1105/MacStat-v2.1
Explore 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
DESCRIPTION OF PROBLEM
When my Document-based macOS SwiftUI app starts up, it always presents a document picker. I would like it to open any documents that were open when the app last quit, and if none were open, open an "Untitled" document. I understand that there is a setting in System Settings-> Desktop & Dock ("Close windows when quitting an application") that will cause windows to reopen when it is turned off, but I'd like to give users a chance to opt-in for reopening docs even when that is turned on, since it is difficult to find, on by default, and might not be the desired behavior for all apps. I don't see any way to get a list of currently open documents to persist them at shutdown.
And even if that's considered a bad idea, I'd at least like a way to prevent the Document picker from being shown at startup and instead create an Untitled document if no documents were open.
Ideally, I'd like a way to provide this functionality in SwiftUI that doesn't require macOS 15 or later
STEPS TO REPRODUCE
Create a SwiftUI macOS document application in Xcode using the provided template, Give it any name you want
Run it, create a new ".exampletext" document, save it.
Quit the app with the document open.
Re-run the app, document picker is shown.
Cancel the document picker and quit the app with no windows shown.
Re-run the app, document picker is shown.
Hi,
Im developing a data centric App using SwiftData, I noticed that the device I use for testing doesn't sync its data with the simulator although both have same Apple Account ? What's Im missing here ? arched is my project settings.
Kind Regards
I want to create master details relationship between patient and vitals signs so which of option codes below are better performance wise ? Option one is master - details done manually ..
option 1
@Model
class TestResult {
@Attribute(.primaryKey) var id: UUID
var patientID: UUID
Option 2
@Model
final class Vital {
var patient: Patient?
In Swift 6, stricter concurrency rules can lead to challenges when making SwiftUI views conform to Equatable. Specifically, the == operator required for Equatable must be nonisolated, which means it cannot access @MainActor-isolated properties. This creates an error when trying to compare views with such properties:
Error Example:
struct MyView: View, Equatable {
let title: String
let count: Int
static func ==(lhs: MyView, rhs: MyView) -> Bool {
// Accessing `title` here would trigger an error due to actor isolation.
return lhs.count == rhs.count
}
var body: some View {
Text(title)
}
}
Error Message:
Main actor-isolated operator function '==' cannot be used to satisfy nonisolated protocol requirement; this is an error in the Swift 6 language mode.
Any suggestions?
Thanks
FB: FB15753655 (SwiftUI View cannot conform custom Equatable protocol in Swift 6.)
Hi,
I'm trying to create a UICollectionView where the cell high is automatic. Cells contains a UILabel with all anchors to the contentView of the cell.
It seems to work but I have a strange behavior with longer text, on reload data and on device rotation: Cells do not display the whole text or they change row, both randomly.
To create my collection view I first create the collection view with a custom flow layout setting the automatic size on viewWillAppear:
let collectionViewFlowLayout = CustomFlowLayout()
collectionViewFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewFlowLayout)
and I have also overridden:
override func willAnimateRotation(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
collectionView.setNeedsLayout()
self.collectionView.layoutIfNeeded()
self.collectionView.collectionViewLayout.invalidateLayout()
//self.collectionView.reloadData()
}
Then, I created the custom layout as follow:
import UIKit
final class CustomFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let layoutAttributesObjects = super.layoutAttributesForElements(in: rect)?.map{ $0.copy() } as? [UICollectionViewLayoutAttributes]
layoutAttributesObjects?.forEach({ layoutAttributes in
if layoutAttributes.representedElementCategory == .cell {
if let newFrame = layoutAttributesForItem(at: layoutAttributes.indexPath)?.frame {
layoutAttributes.frame = newFrame
}
}
})
return layoutAttributesObjects
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
guard let collectionView = collectionView else {
fatalError()
}
guard let layoutAttributes = super.layoutAttributesForItem(at: indexPath)?.copy() as? UICollectionViewLayoutAttributes else {
return nil
}
layoutAttributes.frame.origin.x = sectionInset.left
if(indexPath.section == 0){
layoutAttributes.frame.size.width = collectionView.safeAreaLayoutGuide.layoutFrame.width - sectionInset.left - sectionInset.right
} else if (indexPath.section == collectionView.numberOfSections - 1){
let width = ScreenUtility.getCollectionCellWidthForElement(in: collectionView, sectionLeft: sectionInset.left, sectionRight: sectionInset.right, minimumInteritemSpacing: minimumInteritemSpacing, multiplier: 3)
layoutAttributes.frame.origin.x = ScreenUtility.getCollectionCellOriginForElement(in: collectionView, at: indexPath, forElementHavingWidth: width, sectionLeft: sectionInset.left, sectionRight: sectionInset.right, minimumInteritemSpacing: minimumInteritemSpacing, multiplier: 3)
layoutAttributes.frame.size.width = width
} else if (indexPath.section == collectionView.numberOfSections - 3) || (indexPath.section == collectionView.numberOfSections - 4){
let width = ScreenUtility.getCollectionCellWidthForElement(in: collectionView, sectionLeft: sectionInset.left, sectionRight: sectionInset.right, minimumInteritemSpacing: minimumInteritemSpacing)
layoutAttributes.frame.origin.x = ScreenUtility.getCollectionCellOriginForElement(in: collectionView, at: indexPath, forElementHavingWidth: width, sectionLeft: sectionInset.left, sectionRight: sectionInset.right, minimumInteritemSpacing: minimumInteritemSpacing)
layoutAttributes.frame.size.width = width
} else {
let width = ScreenUtility.getCollectionCellSizeForElementFullRow(in: collectionView, sectionLeft: sectionInset.left, sectionRight: sectionInset.right)
layoutAttributes.frame.origin.x = ScreenUtility.getCollectionCellOriginForElementFullRow(in: collectionView, sectionLeft: sectionInset.left, sectionRight: sectionInset.right)
layoutAttributes.frame.size.width = width
}
return layoutAttributes
}
}
And finally on collection view cells:
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
setNeedsLayout()
layoutIfNeeded()
let targetSize = CGSize(width: layoutAttributes.frame.width, height: 0)
layoutAttributes.frame.size = contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
return layoutAttributes
}
override func prepareForReuse() {
self.nameLabel.text = ""
self.idLabel.text = ""
self.contentView.setNeedsLayout()
self.contentView.layoutIfNeeded()
}
Let me show you an example on the iPad that is the worst.
First Time I open the collection view I have cells on wrong rows and not sized properly
Then I rotate the device portrait and the cells are fine
On landscape again it changes behavior:
This is just an example, things happens apparently randomly, and also sometimes cells disappear (I think the height is set to 0).
I really do not understand why, cells width seems to be computed correctly, and cell label is set via setter:
open var step: String = "" {
didSet {
nameLabel.text = step
nameLabel.sizeToFit()
self.contentView.setNeedsLayout()
self.contentView.layoutIfNeeded()
}
}
I'm working on integrating the new format panel shown in the WWDC24 session "What's New in UIKit" under the Text Improvements section. So far, I've implemented long-press functionality on a text passage, allowing the editing options to appear. From there, you can go to Format > More..., which successfully opens the new format panel.
However, I would also like to add a button to programmatically display this format panel—similar to how the Apple Notes app has a button in the keyboard toolbar to open it.
Does anyone know how to achieve this?
Here's my current code for the text editor (I've enabled text formatting by setting allowsEditingTextAttributes to true):
struct TextEditorView: UIViewRepresentable {
@Binding var text: String
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> UITextView {
let textEditorView = UITextView()
textEditorView.delegate = context.coordinator
textEditorView.allowsEditingTextAttributes = true
return textEditorView
}
func updateUIView(_ uiView: UITextView, context: Context) {
uiView.text = text
}
class Coordinator: NSObject, UITextViewDelegate {
var parent: TextEditorView
init(_ uiTextView: TextEditorView) {
self.parent = uiTextView
}
func textViewDidChange(_ textView: UITextView) {
self.parent.text = textView.text
}
}
}
Thanks in advance for any guidance!
I receive the following compiler error:
Cannot infer key path from context; consider explicitly specifying a root type
when I attempt an @Environment(\.foodRepository) lookup in a descendant View.
Here's the setup in my App class:
import SwiftUI
import SwiftData
@main
struct BulkCutApp: App {
private var foodRepository: FoodRepository = /* some code*/
var body: some Scene {
WindowGroup {
ContentView()
.foodRepository(foodRepository)
}
}
}
extension View {
func foodRepository(_ customValue: FoodRepository) -> some View {
environment(\.foodRepository, customValue)
}
}
extension EnvironmentValues {
var foodRepository: FoodRepository {
get { self[FoodRepositoryKey.self] }
set { self[FoodRepositoryKey.self] = newValue }
}
}
struct FoodRepositoryKey: EnvironmentKey {
static var defaultValue: FoodRepository = FoodRepository(food:[])
}
Nothing special in FoodRepository:
@Observable
class FoodRepository {
var food: [Food]
// more code
}
I have an app that needs to store a SwiftUI Color within SwiftData and I was wondering if anyone had found a way to do so easily and accurately.
I'd prefer not to have to store the Color components (e.g. RGB values) and would ideally like to have a single variable in the @Model that stores the Color.
I had considered using an extension to the Color type to create a HEX encoded String of the Color and an initializer that creates a Color from the HEX encoded String. Unfortunately, doing so proved not to be accurate due data loss when converting component values to integers.
When testing this in Photoshop, the original color #FBAA1D became #FFAB00.
Is there a way to accurately store the Color in SwiftData, possibly using a binary conversion to Data or somehow storing the Color.Resolved, which itself does not appear to be compatible with SwiftData.
Any thoughts on how to best store the Color accurately within SwiftData would be greatly appreciated.
I have an old Objective-C app that has been running for several years. The last compilation was in February 2024. I just upgraded to Sequoia 15.1.
The app has four subviews on its main view. When I run the app only the subview that was the last one instantiated is visible. I know the other subviews are there, because a random mouse click in one invisible view causes the expected change in the visible view.
What changed in 15.1 to cause this?
I have an app with the following model:
@Model class TaskList {
@Attribute(.unique)
var name: String
// Relationships
var parentList: TaskList?
@Relationship(deleteRule: .cascade, inverse: \TaskList.parentList)
var taskLists: [TaskList]?
init(name: String, parentTaskList: TaskList? = nil) {
self.name = name
self.parentList = parentTaskList
self.taskLists = []
}
}
If I run the following test, I get the expected results - Parent has it's taskLists array updated to include the Child list created. I don't explicitly add the child to the parent array - the parentList relationship property on the child causes SwiftData to automatically perform the append into the parent array:
@Test("TaskList with children with independent saves are in the database")
func test_savingRootTaskIndependentOfChildren_SavesAllTaskLists() async throws {
let modelContext = TestHelperUtility.createModelContext(useInMemory: false)
let parentList = TaskList(name: "Parent")
modelContext.insert(parentList)
try modelContext.save()
let childList = TaskList(name: "Child")
childList.parentList = parentList
modelContext.insert(childList)
try modelContext.save()
let fetchedResults = try modelContext.fetch(FetchDescriptor<TaskList>())
let fetchedParent = fetchedResults.first(where: { $0.name == "Parent"})
let fetchedChild = fetchedResults.first(where: { $0.name == "Child" })
#expect(fetchedResults.count == 2)
#expect(fetchedParent?.taskLists.count == 1)
#expect(fetchedChild?.parentList?.name == "Parent")
#expect(fetchedChild?.parentList?.taskLists.count == 1)
}
I have a subsequent test that deletes the child and shows the parent array being updated accordingly.
With this context in mind, I'm not seeing these relationship updates being observed within SwiftUI. This is an app that reproduces the issue. In this example, I am trying to move "Finance" from under the "Work" parent and into the "Home" list.
I have a List that loops through a @Query var taskList: [TaskList] array. It creates a series of children views and passes the current TaskList element down into the view as a binding.
When I perform the operation below the "Finance" element is removed from the "Work" item's taskLists array automatically and the view updates to show the removal within the List. In addition to that, the "Home" item also shows "Finance" within it's taskLists array - showing me that SwiftData is acting how it is supposed to - removed the record from one array and added it to the other.
The View does not reflect this however. While the view does update and show "Finance" being removed from the "Work" list, it does not show the item being added to the "Home" list. If I kill the app and relaunch I can then see the "Finance" list within the "Home" list. From looking at the data in the debugger and in the database, I've confirmed that SwiftData is working as intended. SwiftUI however does not seem to observe the change.
ToolbarItem {
Button("Save") {
list.name = viewModel.name
list.parentList = viewModel.parentTaskList
try! modelContext.save()
dismiss()
}
}
To troubleshoot this, I modified the above code so that I explicitly add the "Finance" list to the "Home" items taskLists array.
ToolbarItem {
Button("Save") {
list.name = viewModel.name
list.parentList = viewModel.parentTaskList
if let newParent = viewModel.parentTaskList {
// MARK: Bug - This resolves relationship not being reflected in the View
newParent.taskLists?.append(list)
}
try! modelContext.save()
dismiss()
}
}
Why does my explicit append call solve for this? My original approach (not manually updating the arrays) works fine in every unit/integration test I run but I can't get SwiftUI to observe the array changes.
Even more strange is that when I look at viewModel.parentTaskList.taskLists in this context, I can see that the list item already exists in it. So my code effectively tries to add it a second time, which SwiftData is smart enough to prevent from happening. When I do this though, SwiftUI observes a change in the array and the UI reflects the desired state.
In addition to this, if I replace my custom list rows with an OutlineGroup this issue doesn't manifest itself. SwiftUI stays updated to match SwiftData when I remove my explicit array addition.
I don't understand why my views, which is passing the TaskList all the way down the stack via Bindable is not updating while an OutlineGroup does.
I have a complete reproducible ContentView file that demonstrates this as a Gist. I tried to provide the source here but it was to much for the post.
One other anecdote. When I navigate to the TaskListEditorScreen and open the TaskListPickerScreen I get the following series of errors:
error: the replacement path doesn't exist: "/var/folders/07/3px_03md30v9n105yh3rqzvw0000gn/T/swift-generated-sources/@_swiftmacro_09SwiftDataA22UIChangeDetectionIssue20TaskListPickerScreenV9taskLists33_A40669FFFCF66BB4EEA5302BB5ED59CELL5QueryfMa.swift"
I saw another post regarding these and I'm wondering if my issue is related to this.
So my question is, do I need to handle observation of SwiftData models containing arrays differently in my custom views? Why do bindings not observe changes made by SwiftData but they observe changes made explicitly by me?
I’m looking for the easiest and most efficient way to convert a SwiftUI Image to Data so that I can store it in SwiftData.
let image: Image
let data: Data = GetImageData(image: image)
How would I implement the GetImageData function above?
I have found examples of how to do so with UIImage but not Image.
I'm doing statistical formulas and need the keyboard shortcut of the symbol used to represent standard deviation (sigma), which should look like (σ).
Everything online suggests using the keyboard shortcut for option + w, but when I use that shortcut I get, ∑ instead. I've tried searching OS settings and there doesn't seem to be a place to change or determine what is the proper keyboard shortcut.
The keyboard shortcut for statistical mean (mu) is working, µ
And greater than or equal to, ≥
And less than or equal to, ≤
are also working.
Im building an recipe app for the social media of my mother. i already have the functionality for the users, when a user gets created an empty array gets initiated at the database named favoriteRecipes, which stores the id of his favorite recipes to show in a view.
This is my AuthViewModel which is relevant for the user stuff:
import Firebase
import FirebaseAuth
import FirebaseFirestore
protocol AuthenticationFormProtocol {
var formIsValid: Bool { get }
}
@MainActor
class AuthViewModel : ObservableObject {
@Published var userSession: FirebaseAuth.User?
@Published var currentUser: User?
@Published var currentUserId: String?
init() {
self.userSession = Auth.auth().currentUser
Task {
await fetchUser()
}
}
func signIn(withEmail email: String, password: String) async throws {
do {
let result = try await Auth.auth().signIn(withEmail: email, password: password)
self.userSession = result.user
await fetchUser() // fetch user sonst profileview blank
} catch {
print("DEBUG: Failed to log in with error \(error.localizedDescription)")
}
}
func createUser(withEmail email: String, password: String, fullName: String) async throws {
do {
let result = try await Auth.auth().createUser(withEmail: email, password: password)
self.userSession = result.user
let user = User(id: result.user.uid, fullName: fullName, email: email)
let encodedUser = try Firestore.Encoder().encode(user)
try await Firestore.firestore().collection("users").document(result.user.uid).setData(encodedUser)
await fetchUser()
} catch {
print("Debug: Failed to create user with error \(error.localizedDescription)")
}
}
func signOut() {
do {
try Auth.auth().signOut() // sign out user on backend
self.userSession = nil // wipe out user session and take back to login screen
self.currentUser = nil // wipe out current user data model
} catch {
print("DEBUG: Failed to sign out with error \(error.localizedDescription)")
}
}
func deleteAcocount() {
let user = Auth.auth().currentUser
user?.delete { error in
if let error = error {
print("DEBUG: Error deleting user: \(error.localizedDescription)")
} else {
self.userSession = nil
self.currentUser = nil
}
}
}
func fetchUser() async {
guard let uid = Auth.auth().currentUser?.uid else { return }
currentUserId = uid
let userRef = Firestore.firestore().collection("users").document(uid)
do {
let snapshot = try await userRef.getDocument()
if snapshot.exists {
self.currentUser = try? snapshot.data(as: User.self)
print("DEBUG: current user is \(String(describing: self.currentUser))")
} else {
// Benutzer existiert nicht mehr in Firebase, daher setzen wir die userSession auf nil
self.userSession = nil
self.currentUser = nil
}
} catch {
print("DEBUG: Fehler beim Laden des Benutzers: \(error.localizedDescription)")
}
}
}
This is the code to fetch the favorite recipes, i use the id of the user to access the collection and get the favoriteRecipes out of the array:
import SwiftUI
@MainActor
class FavoriteRecipeViewModel: ObservableObject {
@Published var favoriteRecipes: [Recipe] = []
@EnvironmentObject var viewModel: AuthViewModel
private var db = Firestore.firestore()
init() {
Task {
await fetchFavoriteRecipes()
}
}
func fetchFavoriteRecipes() async{
let userRef = db.collection("users").document(viewModel.userSession?.uid ?? "")
do {
let snapshot = try await userRef.collection("favoriteRecipes").getDocuments()
let favoriteIDs = snapshot.documents.map { $0.documentID }
let favoriteRecipes = try await fetchRecipes(recipeIDs: favoriteIDs)
} catch {
print("DEBUG: Failed to load favorite recipes for user: \(error.localizedDescription)")
}
}
func fetchRecipes(recipeIDs: [String]) async throws -> [Recipe] {
var recipes: [Recipe] = []
for id in recipeIDs {
let snapshot = try await db.collection("recipes").document(id).getDocument()
if let recipe = try? snapshot.data(as: Recipe.self) {
recipes.append(recipe)
}
}
return recipes
}
}
Now the Problem occurs at the build of the project, i get the error
SwiftUICore/EnvironmentObject.swift:92: Fatal error: No ObservableObject of type AuthViewModel found. A View.environmentObject(_:) for AuthViewModel may be missing as an ancestor of this view.
I already passed the ViewModel instances as EnvironmentObject in the App Struct.
import SwiftUI
import FirebaseCore
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
return true
}
}
@main
struct NimetAndSonApp: App {
@StateObject var viewModel = AuthViewModel()
@StateObject var recipeViewModel = RecipeViewModel()
@StateObject var favoriteRecipeViewModel = FavoriteRecipeViewModel()
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(viewModel)
.environmentObject(recipeViewModel)
.environmentObject(favoriteRecipeViewModel)
}
}
}
need to use CocoaPods, Ruby, and Xcode to set up a development environment.
trying to install CocoaPods and running into compatibility issues with Ruby versions (>= 2.7.0) and can't use rbenv.
I try to convert view to vector graphic (PDF) using ImageRenderer. But output image has ALL coordinates rounded to integer values, which is not OK - some very small circles become elipses, squares are no longer squares, very detailed or small shapes are distorted by rounding coordinates. Is a some option to switch off rounding?
I am creating an iOS app to install on legacy iPads running iOS 9 and up, using XCode 13.4.1 which is the latest version that will support iOS below 11. The app is working fine but I just added a QuickLook Preview extension, and on iOS 10.3.1 it will not install due to the following error:
This app contains an app extension that specifies an extension point identifier that is not supported on this version of iOS for the value of the NSExtensionPointIdentifier key in its Info.plist. Domain: com.apple.dt.MobileDeviceErrorDomain Code: -402653007
The NSExtensionPointIdentifier key in Info.plist is set by XCode automatically to "com.apple.quicklook.preview".
I want to set the iOS Deployment Target to the lowest iOS version that will support this configuration. The documentation does not provide any guide as to which specific NSExtensionPointIdentifier keys are compatible with which iOS version. It just says 8+ for the whole list. Trial-and-error is limited by availability of legacy Simulators.
If anyone can point to documentation that indicates which iOS is supported by which NSExtensionPointIdentifier key com.apple.quicklook.preview, I would be very grateful. Thanks
(I understand about lack of App Store support etc, this is an app for my use on old iPads)
I'm using SwiftData to store data in my app and I recently had to store both image data and colors.
I have therefore added two variables to my model, one of type Data? and the other of type Color.Resolved?
If both are set to nil then I can call context.save() without any error but when providing a value of type Color.Resolved, the following error message occurs: Thread 1: Fatal error: Composite Coder only supports Keyed Container.
Any guidance on how to solve this and what needs to be done to store image data and colors with SwiftData?
I am trying to work on a new app but everytime I try to run it and test it I get this error
error reading dependency file '/Users/jacobwright/Library/Developer/Xcode/DerivedData/LevelingUp:_Life-baixnmqgtntqiacdkadvwrgeanox/Build/Intermediates.noindex/LevelingUp: Life.build/Debug-iphonesimulator/LevelingUp: Life iOS.build/Objects-normal/arm64/LevelingUp: Life iOS-master-emit-module.d': unexpected character in prerequisites
and I do not know what to do. I even tryed makeing a new project and running it with out doing anything and I get the same thing I have tried Deleting Xcode and Reinstalling, and updating my computer and Xcode. I am not sure what else to try and I was wondering if anyone else is getting this and how do i fix it?
Is there really no way to hide a TabItem using the built-in TabView?
I have 6 pages, but the 6th one I want hidden from the bottom tab bar, because I have a button that programmatically navigates to it on the navigation bar.
I did not want to have to code a custom tab bar due to losing some useful features like pop to root in Navigation Stack.