I am writing a SwiftUI-based app and have the following requirements:
Use a file browser (such as UIDocumentPickerViewController) to find an arbitrary file (not one that the application knows how to open) which is external to the app bundle but local to the device the app is running on - either in local storage or on an iCloud drive.
Save this location.
At a later time, open this file. The file should open in an app that knows how to open it or in a browser.
Do all of the above in a way that works with multiple devices (synced via CloudKit/SwiftData). For example, select a file on my iCloud drive on my Mac, then save it (using CloudKit/SwiftData) and open it on an iPad that has an app that can open it.
I am addressing requirement #1 using UIDocumentPickerViewController wrapped with a UIViewControllerRepresentable. It returns a security-scoped URL. (Note: this worries me because of requirement #4).
I use the Bookmark API to implement requirement #2.
For requirement #3, I load the bookmark data, convert it back to a security-scoped URL and either
Link("Open", destination: url)
or
@Environment(\.openURL) private var openURL
if url.startAccessingSecurityScopedResource() {
defer { url.stopAccessingSecurityScopedResource() }
openURL(url) { accepted in
// do something here
}
}
Both of these implementations fail. The Link call responds with "invalid input parameters" (Error Domain=NSOSStatusErrorDomain, Code=-50), the openURL() call just returns false.
So, my questions are:
Since it appears the Link and openURL work for internet URLs, but not for security-scoped file URLs, how to I cause a document to be opened (using an application which knows how to open it or a browser).
Since UIDocumentPickerViewController is returning a security-scoped URL, how can I make this work on a different device than the one on which the user selected the document? (Assuming, of course, that we are talking about a document that is on an iCloud drive that both devices have access to).
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Xcode 16.2 (16C5032a)
FB16300857
Consider the following SwiftData model objects (only the relevant portions are shown) (note that all relationships are optional because eventually this app will use CloudKit):
@Model
final public class Team {
public var animal: Animal?
public var handlers: [Handler]?
...
}
@Model
final public class Animal {
public var callName: String
public var familyName: String
@Relationship(inverse: \Team.animal) public var teams: [Team]?
...
}
@Model
final public class Handler {
public var givenName: String
@Relationship(inverse: \Team.handlers) public var teams: [Team]?
}
Now I want to display Team records in a list view, sorted by animal.familyName, animal.callName, and handlers.first.givenName.
The following code crashes:
struct TeamListView: View {
@Query<Team>(sort: [SortDescriptor(\Team.animal?.familyName),
SortDescriptor(\Team.animal?.callName),
SortDescriptor(\Team.handlers?.first?.givenName)]) var teams : [Team]
var body: some View {
List {
ForEach(teams) { team in
...
}
}
}
}
However, if I remove the sort clause from the @Query and do the sort explicitly, the code appears to work (at least in preliminary testing):
struct TeamListView: View {
@Query<Team> var teams: [Team]
var body: some View {
let sortedTeams = sortResults()
List {
ForEach(sortedTeams) { team in
...
}
}
}
private func sortResults() -> [Team] {
let results: [Team] = teams.sorted { team1, team2 in
let fam1 = team1.animal?.familyName ?? ""
let fam2 = team2.animal?.familyName ?? ""
let comp1 = fam1.localizedCaseInsensitiveCompare(fam2)
if comp1 == .orderedAscending { return true }
if comp1 == .orderedDescending { return false }
... <proceed to callName and (if necessary) handler givenName comparisons> ...
}
}
}
While I obviously have a workaround, this is (in my mind) a serious weakness in the implementation of the Query macro.
I have a Date field that holds the scheduled start date for an activity.. However, activities can be unscheduled (i.e., waiting to be scheduled at some other time). I want to use Date.distantFuture to indicate that the activity is unscheduled. Therefore I am looking to implement logic in my UI that looks something like
@State private var showCalendar: Bool = false
if date == .distantFuture {
Button("unscheduled") {
showCalendar.toggle()
}.buttonStyle(.bordered)
} else {
DatePicker(section: $date)
}
.popover(isPresented: $showCalendar) {
<use DatePicker Calendar view>
}
But this approach requires that I access the DataPicker's Calendar view and I don't know how to do that (and I don't ever what my users to see "Dec 31, 4000"). Any ideas?
(BTW, I have a UIKit Calendar control I could use, but I'd prefer to use the standard control if possible.)
For a number of complex views in my app, I need to embed scrollable objects (like List, Form or TextEditor) in a ScrollView. To do that, I need to apply a limit to the height of the embedded object.
What I would like to do is set that limit to the actual height of the content being displayed in the List, Form, TextEditor, etc. (Note that this height calculation should be dynamic, so that content changes are properly displayed.
I attempted the following:
@State listHeight: CGFloat = .zero
List {
... content here
}
.onScrollGeometryChange(for: CGFloat.self, of: { geometry in
return geometry.contentSize.height
}, action: { oldValue, newValue in
if oldValue != newValue {
listHeight = newValue
}
})
.frame(height: listHeight)
.scrollDisabled(true)
This does not work because geometry.contentSize.height is always 0. So it is apparent that .onScrollGeometryChangedoes not interact with the internal scrolling mechanism of List. (Note, however, that.scrollDisabled` does work.)
Does anyone have a suggestion on how I might get this to work?
I am trying to update an old UIKit/CoreData app and am (among other changes) adding CloudKit synchronization support. I enabled CloudKit (ie, set up a new CloudKit domain, enabled CloudKit, Background Mode Remote Notifications, and Push Notifications.
When I did this, automatic code signing failed. (see attached for the messages). I tried creating and downloading a new provisioning profile, but that did not help.
FB19742743
Can someone help me? I have absolutely no idea of what to do next to get this app signed (and hopefully back into the AppStore soon).
Topic:
Developer Tools & Services
SubTopic:
Xcode
Tags:
Developer Tools
Xcode
Provisioning Profiles
CloudKit
The 'unique' attribute is a really nice feature, BUT. In some of my apps, the unique identifier for an object is a combination of multiple attributes. (Example: a book title is not unique, but a combination of book title and author list is.)
How do I model this with SwiftData? I cannot use @Attribute(.unique) on either the title OR the author list, but I want SwiftData to provide the same "insert or update" logic.
Is this possible?
I am currently struggling with resolving what appear to be competing design issues, and (while I may be just demonstrating my own ignorance) I would like to share my thoughts in the hope that you may have useful insights.
For purposes of discussion, consider a large and complex data entry screen with multiple sections for input. For all of the usual reasons (such as reuse, performance management, etc) each of these sections is implemented as its own, separately-compiled View. The screen is, then, composed of a sequence of reusable components.
However, each of these components has internal structure and may contain multiple focusable elements (and internal use of .onKeyPress(.tab) {...} to navigate internally). And the logic of each component is such that it has an internal @FocusState variable defined with its own unique type.
So, obviously what I want is
on the one hand, to provide a tab-based navigation scheme for the screen as a whole, where focus moves smoothly from one component's internals to the next component, and
on the other hand ,to build components that don't know anything about each other and have no cross-component dependencies, so that they can be freely reused in different situations.
And that's where I'm stuck. Since focus state variables for different components can have different types, a single over-arching FocusState passed (as a binding) to each component doesn't seem possible or workable. But I don't know how else to approach this issue.
(Note: in UIKit, I've done things like this by direct manipulation of the Responder Chain, but I don't see how to apply this type of thinking to SwiftUI.)
Thoughts?
I am trying to do a bit of fancy navigation in SwiftUI using NavigationPath and am having a problem.
I have a root view with includes a button:
struct ClassListScreen: View {
@Bindable private var router = AppRouter.shared
@State private var addCourse: Bool = false
...
var body: some View {
...
Button("Add Class") {
router.currentPath.append(addCourse)
}.buttonStyle(.borderedProminent)
...
.navigationDestination(for: Bool.self){ _ in
ClassAddDialog { course in
sortCourses()
}
}
}
}
router.currentPath is the NavigationPath associated with the operative NavigationStack. (This app has a TabView and each Tab has its own NavigationStack and NavigationPath).
Tapping the button correctly opens the ClassAddDialog.
In ClassAddDialog is another button:
struct ClassAddDialog: View {
@Bindable private var router = AppRouter.shared
@State private var idString: String = ""
...
var body: some View {
...
Button("Save") {
let course = ...
... (save logic)
idString = course.id.uuidString
var path = router.currentPath
path.removeLast()
path.append(idString)
router.currentPath = path
}.buttonStyle(.borderedProminent)
...
.navigationDestination(for: String.self) { str in
if let id = UUID(uuidString: str),
let course = Course.findByID(id, with: context) {
ClassDetailScreen(course: course)
}
}
}
}
My intent here is that tapping the Save button in ClassAddDialog would pop that view and move directly to the ClassDetailScreen (without returning to the root ClassListScreen).
The problem is that the code inside the navigationDestination is NEVER hit. (I.e., a breakpoint on the if let ... statement) never fires. I just end up on a (nearly) blank view with a warning triangle icon in its center. (And yes, the back button takes me to the root, so the ClassAddDialog WAS removed as expected.)
And I don't understand why.
Can anyone share any insight here?