Today was a bit of a chores day. I’m working on a Swift package to hold all the common extension, helpers, components etc. between my apps. Cleverly named SamKit. I added SamKit, dealt with issues like making things public and whatnot. I’ve added it as a local package so that I can edit it along side my app.
I’m also getting things ready for TestFlight 😱. It will be a bit before it’s ready for public testing—want to get some webpages ready first. I was a bit frustrated because apparently the name I picked (and already bought a domain for) is taken. I had done an App Store search and didn’t find anything the night I was coming up with names. Maybe I missed it. But it’s there now. So I had to spend about 20 mins coming up with a name that would fit in the 30 character limit1. Hopefully the other app will be just go away one of these days and I can take the short name2.
I’m giving Xcode Cloud a shot again, but honestly, not very impressed at the moment. Queue times seem to be really long. Like 20mins. Was much faster when I was beta testing it. So maybe I’ll switch to something like Fastlane and do it locally instead.
What’s the name you ask? Patience. I will let you all know soon.↩︎
It hasn’t been updated in over a year and has 1 star.↩︎
Today’s feature is sorting. Initially, I hardcoded it to sort by number of credits, most to least. Thought it might be nice to let you wort the other way, and by name. The trickiest part of figuring out what UI to have for option. Right now, the best place is in my “more” menu. I went through a couple iterations though.
First
Wall sort options
This was my first take. My instinct is to lay all the sort options out so they are easily accessible. Generally, I think this is a good idea. But here, it felt too repetitive, cluttered and, in my opinion, didn’t give enough information of what the sort actually was. Maybe if I could think of better labels and icons, it would work better.
Second
Looking for inspiration, I came across the sort options in iA Writer.
iA Writer is a good app and we should emulate good apps right?
I like that it divides what you can sort by from the direct. This also lets you give really descriptive labels for the sort directions 👍1. So I went in that direction.
Look familiar?
I like it! But, it has one crucial flaw—changing what you sort by immediately closes the menu, so you have to open it up again to change the direction. Not a great experience.
Third
Okay. Too many taps. Can I make it less taps? What about more nested menus? Everybody likes that right?
Better?
You have to really drill in, but you can get exactly the sort you want without too much fuss. Which I like better.
Final?
Then it hit me, why am I putting the options under a submenu. So I put them in their own section at the top level of the menu. This is my favorite so far and what I’m sticking with. Lets me keep the descriptive sort direction labels, doesn’t take up too much space, and doesn’t require too many taps. Plus it has a cute like section header.
Just right...hopefully
I didn’t know you could put a section header in a menu. Here’s the code if it’s helpful at all.
Menu {
Section("Sort") {
Menu {
Picker("Number of Credits", selection: $creditSort) {
Text("Most to Least")
.tag(CreditSort.numCredits(.descending))
Text("Least to Most")
.tag(CreditSort.numCredits(.ascending))
}
} label: {
ifcase .numCredits(_) = creditSort {
Label("Number of Credits", systemImage: "checkmark.circle")
} else {
Text("Number of Credits")
}
}
Menu {
Picker("Number of Credits", selection: $creditSort) {
Text("A to Z")
.tag(CreditSort.name(.ascending))
Text("Z to A")
.tag(CreditSort.name(.descending))
}
} label: {
ifcase .name(_) = creditSort {
Label("Name", systemImage: "checkmark.circle")
} else {
Text("Name")
}
}
}
}
This might not be the simplest way of doing this, but it’s straightforward, at least to me. Had to do some trickery to add a checkmark to the menu item to show a child is selected. Might clean that up later.
It was fun to iterate on this and figure out all the different ways I could do this. There’s a bunch of options I didn’t explore at all. Going to let this sit for a few days and see how it feels.
In general, ascending and descending mean almost nothing to be when it comes to sorting. I like it when it’s clear—A to Z, Z to A, etc.↩︎
My family has been sick for a bit, so I’ve been mostly working on taking care of them and trying to not get sick myself. Finally starting to get back to a bit of normalcy now.
Today I added a button.
🥳
It’s a details button next to a name. It will open the details of that person in The Movie Database. I don’t want to make a full TMDb client. So my current plan is to just link to their pages. A pretty straightforward
I don't like where the button is...
Along with that, I added a setting to open the details in the app or in Safari. The in-app browser uses SFSafariViewController. This is the one that comes with the done button, the open in Safari button, etc. I first tried WKWebView but that just displays a webpage with none of the other stuff. When the setting is enabled to open details in Safari, instead of using Link, I use openUrl environment variable. Pretty nifty.
structDetailsLink: View{
@AppStorage(SettingsKeys.openDetailsInBrowser) var openDetailsInBrowser = false
@Environment(\.openURL) var openUrl
var body: someView {
Button {
if openDetailsInBrowser {
openUrl(url)
} else {
// Show
}
} label: {
Label("Details", systemImage: "info.circle")
}
}
}
I’m using @AppStorage for saving the setting. It is simple, but I’m not 100% sold on it yet. It requires a lot of duplication. If I want to use the setting in multiple places, I need to define a default in multiple places, which feels odd to me. Just found this article about an alternative that looks interesting. Going to give that a closer read.
I’ll be brief because kids are crying and we need to eat dinner soon.
I did a small revamp of history. I was storing each time a comparison ran, even if the two movies or shows were exactly the same. So first, I added a toggle to hide duplicates. Then I started on deletion. I wanted to show all items when you went to delete them. It started to get a bit unwieldy. So I removed that setting. Back to seeing all your comparisons. Then I decided, instead of adding a new entry each time, if there is an existing one, update the history item. Maybe I can add a counter for the number of times those two were compared or something.
Anyway, came full circle a couple times, but happy where I landed.
Did have a strange issue with EditMode where the environment was not being set how I would expect. I ended up having to declare my own EditMode state and then pass that as an environment to EditButton and my List. Otherwise, my History View had no idea if the list was editing or not.
Went the ZStack route. Things seem to be working well now!
Much better!
I also added the poster images behind the thick material background. I think this gives a cool colored effect when something is selected. I let the material do the magic so I don’t have to determine the dominant colors of an image.
I’m sure I will come across some edge cases I need to deal with, but I’m pretty happy with the effect.
Was kinda in a funk today. Took on a task that was maybe a bit beyond my energy level today. I’m trying to make the images shrink as you scroll up. I have a GeometryReader updating a PreferenceKey as you scroll. The problem is that the size adjustment changes the position of my measurement view in the scroll view. I think this is because my header is in a safeAreaInset. I may need to change my header to be in a ZStack or something.
Not great...
I’m not quite sure what else to try. Using safeAreaInset seems to be the right choice for this layout, but obviously is causing issues. Even if I use the global coordinate space instead of a named/local one, the offset does not seem to change “smoothly”. So probably going to need to try some different layouts.