Just a quick note to say – Wasted Time for the Apple TV is now available. This is the perfect app for a conference room to show people how much it is costing to get a meeting started! Drop me your feedback!!!
Demo
on
Simulator
Just a quick note to say – Wasted Time for the Apple TV is now available. This is the perfect app for a conference room to show people how much it is costing to get a meeting started! Drop me your feedback!!!
Demo
on
Simulator
One of the real benefits for users coming with macOS 11 and iOS 14, et. al. is that developers can now activate bundles for an app. I am working to create a bundle that will enable an iOS user to use Wasted Time on iOS, iPadOS, macOS, and tvOS all with one App Store purchase. Of course, my app is free, so it’s really a way for more people to leverage the app.
To do that, I have to take down my existing macOS version from the App Store. Given I’ve had very little uptake, I don’t think it’s a big deal. Hopefully existing users won’t have it taken off their machines. If it does, I hope the delay for the release of macOS 11 won’t be too long.
As I continue to work on Wasted Time, I now have added working keyboard shortcuts, and menu items to both the iPad and macOS versions. I can’t wait for Big Sur and iPadOS 14 to come up so I can release these to the world.
First a few screenshots
The code ended up not being to difficult, once I figured it out. Of course, that is almost always the case. Moving from all the programming habits I developed in college and the few years that I made my living as developer, to modern languages and architectures, has been hard. While I fully understand the concepts, putting it in practice can be difficult. So let’s look at the code behind the Menu Builder function in Swift:
override func buildMenu(with builder: UIMenuBuilder) {
super.buildMenu(with: builder)
// Ensure that the builder is modifying the menu bar system.
guard builder.system == UIMenuSystem.main else { return }
// Meeting Menu
let advanceMeetingStatus = UIKeyCommand(title: "Advance Meeting Status", action: #selector(handleKeyAdvanceMeetingStatus(sender:)), input: UIKeyCommand.inputRightArrow, modifierFlags: .alternate, propertyList: UIKeyCommand.inputRightArrow)
let incrementParticipants = UIKeyCommand(title: "Increment Participants", action: #selector(handleKeyIncrementParticipants(sender:)), input: UIKeyCommand.inputUpArrow, modifierFlags: .alternate, propertyList: UIKeyCommand.inputUpArrow)
let decrementParticipants = UIKeyCommand(title: "Decrement Participants", action: #selector(handleKeyDecrementParticipants(sender:)), input: UIKeyCommand.inputDownArrow, modifierFlags: .alternate, propertyList: UIKeyCommand.inputDownArrow)
let cancelMeeting = UIKeyCommand(title: "End Meeting Immediately", action: #selector(handleKeyCancelMeeting(sender:)), input: UIKeyCommand.inputLeftArrow, modifierFlags: .alternate, propertyList: UIKeyCommand.inputLeftArrow)
let advanceMeetingItem = UIMenu(title: "Advance Meeting", image: nil, identifier: UIMenu.Identifier("advanceMeetingStatus"), options: .displayInline, children: [advanceMeetingStatus])
let incrementParticipantsItem = UIMenu(title: "Add Participants", image: nil, identifier: UIMenu.Identifier("incrementParticipants"), options: .displayInline, children: [incrementParticipants])
let decrementParticipantsItem = UIMenu(title: "Remove Participants", image: nil, identifier: UIMenu.Identifier("decrementParticipants"), options: .displayInline, children: [decrementParticipants])
let cancelMeetingItem = UIMenu(title: "Cancel Meeting", image: nil, identifier: UIMenu.Identifier("cancelMeeting"), options: .displayInline, children: [cancelMeeting])
let meetingMenu = UIMenu(title: "Meeting", children: [advanceMeetingItem,incrementParticipantsItem,decrementParticipantsItem,cancelMeetingItem])
// View Menu
let meetingView = UIKeyCommand(title: "Meeting View", action: #selector(handleKeyCommand1(sender:)), input: "1", modifierFlags: .command, propertyList: 1)
let setupView = UIKeyCommand(title: "Setup View", action: #selector(handleKeyCommand2(sender:)), input: "2", modifierFlags: .command, propertyList: 2)
let totalsView = UIKeyCommand(title: "Totals View", action: #selector(handleKeyCommand3(sender:)), input: "3", modifierFlags: .command, propertyList: 3)
let helpView = UIKeyCommand(title: "Help View", action: #selector(handleKeyCommand4(sender:)), input: "4", modifierFlags: .command, propertyList: 4)
let meetingViewItem = UIMenu(title: "Meeting View", image: nil, identifier: UIMenu.Identifier("meetingView"), options: .displayInline, children: [meetingView])
let setupViewItem = UIMenu(title: "Setup View", image: nil, identifier: UIMenu.Identifier("setupView"), options: .displayInline, children: [setupView])
let totalsViewItem = UIMenu(title: "Totals View", image: nil, identifier: UIMenu.Identifier("totalsView"), options: .displayInline, children: [totalsView])
let helpViewItem = UIMenu(title: "Help View", image: nil, identifier: UIMenu.Identifier("helpView"), options: .displayInline, children: [helpView])
let tabsMenu = UIMenu(title: "Tabs", children: [meetingViewItem, setupViewItem, totalsViewItem, helpViewItem])
builder.insertSibling(meetingMenu, afterMenu: .edit)
builder.insertSibling(tabsMenu, afterMenu: .edit)
builder.remove(menu: .file)
builder.remove(menu: .edit)
builder.remove(menu: .format)
builder.remove(menu: .help)
}
The net of this code is to build up the actions, then the items, and then attach the items to a new UIMenu object. I add the various items ad children to the UIMenu, and then insert the UIMenu after the file menu. Since I don’t use the file menu, I then remove it, along with edit, format and help.
Not too hard.
One of the really cool things about keeping up with Apple’s summer release schedule is that you can add new features easily to your apps. This summer I spent time restructuring Wasted Time to add support for Widgets on iOS, macOS, and iPadOS. After I got that working, I thought, what the hell, why not make a Apple TV version of the app.
So, here you go, my first build of Wasted Time for Apple TV.
I am having a few issues with getting a valid provisioning profile. I have a new Apple TV 4K, and while I can select it in the Devices & Simulators section of Xcode, it won’t show as a valid deployment target. Without that, I cannot get the UDID to finish setting up a Device, which is required to complete the provisioning profile.
More to come, as I figure it out.
Sometime I just need to post a picture… and today is one of those days:
I’ve been working on adding a set of widgets to Wasted TIme for iOS14. After playing with this program for over a decade, I realized people may not really understand the statistics on the totals page. This will may become more confusing once the widget is live.
Let’s take a look at the two sets of statistics on the new widget on iPadOS 14.
The to major items are “All Meetings” and “Lifetime”. “All Meetings” is about the meetings, while “Lifetime” is about yourself. You can reset the “All Meetings” but you can’t reset Lifetime.
So People, in “All Meetings” is the total number of people in the meetings since you last hit “reset” on the Totals tab. People in the “Lifetime” is the number of people in all your meetings, for all time.
Waste is the total waste for all the people. Again this can be Reset in “All Meetings”, but it can’t be reset in Lifetime. Cost; however, is the cost of the meetings in “All Meetings”, BUT in Lifetime, it is the cost to you for your time.
I’ve been thinking thru for a long time if I need to relabel things. What do you think?
I wanted to start this day early, since I was declined for my second Lab request. I have been working on Shortcuts support in Wasted Time and while I had a great session on Wednesday, I am still having issues with the launching of Wasted Time from the Shortcuts app. I am getting a “Sorry there was a problem with the app” error. So I started the day early and went back to a WWDC19 session on Adding Parameters for Shortcuts. While it was helpful, it didn’t solve my problem and I will have to go back to a WWDC18 sessions on Shortcuts.
My second session was one that should have directly helped me – it was called Decipher and Deal with Common Siri Errors. This two minute session gave a few tips for debugging Siri errors, and by inference Shortcut errors. The first tip is that in Xcode you can pass a Siri command to the run command. That doesn’t really help me, but there was still hope. The second thing is you can look at the Console log and monitor the simulator for error messages. This should be helpful and I will do this, to see if I can figure out which of the three common problems above are the problem. The third suggestion was about using os_log statements to capture the program flow. While I do this with print statements, it seems that this may be better. I already had plans to change my prints to os_log statements based on a session earlier in the week.
The next session (number three for those keeping count) was called “Empower Your Intents”. This was a deeper dive into the technology behind intents. The one piece that was helpful to my problem was about adding in-app intent handling. This perhaps would be helpful in dealing with my issue, as I could at least allow Siri control for adding and removing people to a meeting with out major problem. Much of the processing I want to add is more appropriate for running in the app anyway. But to handle in-app intent handling I would need to change the architecture of my app to be a multi-window app. Not sure that is really appropriate for it’s function. Will have to think thru this.
The next session was about handling background mode for your app. This seemed like it might help me, but turned into a session about optimization of your app. Not a bad session at all, and it explained how iOS manages background execution and prioritization. What was most interesting was how your user’s behavior will change your background processing over time. Additionally, did you know that all apps are set to run in the background by default. I will have to do a better job of going thru my settings on my machine and turn that off more apps. There are 7 factors that iOS uses to manage background execution priorities, and many of them are under the user’s control.
Next (Session 5) was on designing great widgets. While this was interesting, I didn’t focus on it much, as they spent tons of time explaining the design aesthetics of their own apps. I really should spend more time in the design documentation that Apple provides. Perhaps that would make my app gain more users, perhaps not.
Session 6 – Structure Your App for SwiftUI Previews! Ok, now this session was amazing. I have been enjoying SwiftUI over the last year, but as it was the first release, Xcode has been a bit buggy. This session really showed what it could be, once you restructure how you code in Xcode and with SwiftUI. I’ve under used Pinning of views, this is when you lock a specific Preview so that as you move to other source modules you can see how your changes impact it in real time. Adding multiple Preview Groups is something I have used, especially in building my new Widget for Wasted Time. The other key thing I need look at for my app, from this session, was the use of the @StateObject – this will allow key models in your to only be created when you first launch your app. The other key insight that came thru is to break up your screens into multiple smaller views. You won’t get penalized by this approach, and it allows for you to do interesting things with the new found modularity of your app.
Session 7 was about Building SwiftUI views for Widgets. The speaker did a great job of showing many trick and tips to build out a caffeine tracking app and widget. I will need to download the code for future reference. To better adapt Apple’s design principles they have added a new CornerRelativeShape feature, that will make your views conform with the high-level view they sit in. If you look above you can see it in the Caffeine widget. The other key new feature is relative date/time processing. As you can see in the code above, if you use these in a widget, it will dynamically update to show relative time. In this case, the last time you had coffee. If I were to add tracking of when your last meeting was in Wasted Time, I could show a time tracker. Should I do this?
Session 8 – we are getting down to the wire (my plan was 9 sessions a day for a total of 36 session. SF Symbols 2 – Symbols were introduced last year for iOS and WatchOS, they are now available for macOS Big Sur. These symbols are powerful in how they behave across platforms and sizes. They now have over 750 symbols to help change the iconography of your app. They also have added multi-color capabilities. I have updated WastedTime last year to use this iconography, but will need to double check how I am doing it, as there are some things that would make it much better looking this year. I’ve wondered if any of my users have even noticed yet. Oh, and by the way, they renamed some of them, so developers do need to deal with deprecated names in their code, if they support back levels of the OS. I also never thought about localization of iconography, but good thing Apple has. Icons can be flipped for right to left languages, and in some case they have different glyphs in side to reflect the differences in how script looks.
And what was my last session? Well Core Data of course! (Seriously, I am still not 100% comfortable with Core Data, and was really looking forward to a total reimagine of this with some like SwiftData (maybe one day)). This session really went deep into how to handle batch processing. As more and more data is being stored online, the core data team has been modernizing these processes and making it much more performant for cloud access. I applaud the team doing this. Perhaps one day I can take advantage of it.
After a few more days I will give a summary of what I thought overall about an online only WWDC.
Another fun filled day, and now I am starting to get back into my code.
I began the day with a session on the OS.Logging extensions added into Swift. I am thinking I should change my code to start using Logging instead of print statements. I do a lot of inline print statements so when I run the code locally I can see a few key messages. But of course, once the app is deployed, I won’t get access to these. If I am to change to logging, I will be able to find out more information about my app while it is running, and when I get crash logs I should be able to get to more data.
Session 2, was actually one I saw on Tuesday, but had not marked as watched. So was a nice refresher on Scribble.
I was looking forward to Modern Cell Configuration, and while it was a very informative section, by switching over to SwiftUI last year, this session held little value to me. It does seem that they are bringing much of the declarative methods from SwiftUI back to UIKit. I have not converted my greeting card app to SwiftUI, yet, so perhaps I can take advantage of these new features.
Session 4 was on integrating hardware keyboards on iPad apps. This one is really interesting to me, as I need to add keyboard shortcuts to Wasted Time. It is really starting to come together on how I can make the app truly cross platform with the same code base. The keyboard features will allow me to take my Catalyst app and make it much more like a full fledge Mac app. If you look at what Apple has done with Messages in the latest Big Sur beta, you will see that they are serious about making iPad and iPhone apps, at home on the Mac. So my question here is, should I make the + and – keys for adding and removing people from the meeting, or should I use the up and down arrows? If I do, I could make it so holding down the key just keeps add or removing people.
Session 5 was about the new Grid and Outline views. I am very interested in the Grid format for my greeting card app. Being able to quickly see a history of all the cards I’ve sent someone over the year will make it much less likely that I’ll accidentally send them the same card. The use of the new LazyHGrid and LazyVGrid seem to really make strong performance improvements. Overall, it’s not hard to implement, so will have to do when I get back to that app.
Session 6 is all about the new way you can write a Swift app with by just using the @main entry point and the power of the new scene types. What is not quite clear to me yet is how these apps can then be extended with the various features that need to register in the AppDelegate or enable additional entry points, like Shortcuts and Intent handling. I am sure the answer to that will show itself in some demo code somewhere soon.
Session 7 was about push notifications. While I have no need for any push notifications, so this was more informational. I enjoyed learning how this all get’s setup, but in all honesty there was nothing that didn’t just make sense.
Session 8 – Sync a Core Data Store with the CloudKit Public Database. I miss read this session, but in the end it was really cool. I had figured this would take me thru how to sync my CoreData Store via CloudKit to other devices. I guess I really should pay attention to the full title. The Public Database is just that, it is Apple’s CloudKit data store for public access to data. Think of a leaderboard in an app. Having the ability to multiple people replicate data locally, and have appropriate access to the data that is common across teams of people. A key thing is how do you handle a delete, when multiple people may have it locally replicated. The discussion on that part, was well described and informative.
Session 9 – the final session of the day for me, was on Build document-based apps in SwiftUI. This really is a great example of bringing together many of the things about new Swift App structure, and SwiftUI in general, to show how easy it is to create a Document based app. Right out of the box you can create a simple text editor with the template. Updating it to handle different filetypes, etc. is what this session shows you. All in about 15 minutes. Pretty cool!
Day three was loads of fun, the highlight of which was the Code-Along for WidgetKit. I also had my lab appointment on the problems I was having with SiriIntents and Shortcuts. While I was a able to get my Intents to work, we ran out of time for the Shortcut problem. But I do believe I have a better understanding of what I was doing wrong, AND I raised my third bug report for this cycle. So overall a win.
The sessions I did were all about getting better at the new stuff. First I watched the Design for iPad session. The key aspect of which was learning about sidebars verses the tab bar. For now, I don’t think Wasted Time needs to worry about this.
Then on to building complications in SwiftUI. They spent a lot of time explaining how the new Watch Face Tinting works. I assume this is because you want your new complications (which a user can use multiple complications from the same app to build a customized watch face and share it with others) to feel as if they belong to the watch face they are on. A really nice feature of Xcode is by using SwiftUI you can do the live preview function and see how your complication looks on multiple watch faces and with multiple tints all at the same time.
Session 3 was Visually editing SwiftUI views. This very quick tutorial was jammed packed with how to improve your productivity in building new views. I learned a lot!
Session 4 on Creating complications for Apple Watch got into the guts of the various providers you can use in complications. It also explained how the CLKDefaultComplicationHandler is necessary if you want users to be able to share your complications with others. The whole idea of which means that your users can be a great marketing arm for distributing your app to more people.
Session 5 – What’s new in WatchOS design. This session was more about explaining the new design principles of WatchOS. Apple really wants you to use their design principles so that you can be more forward compatible. They made the point multiple times to stop using gestured based menus, sounds like this will deprecated soon.
Session 6 – Secure you app: Threat modeling and anti-patterns. I have to confess, I am a security buff. I am fascinated by all things related to computer security and getting a perspective of how Apple sees you should test your app was cool. The basic premise was simple, understand your apps assets and those who which to get inappropriate access to those assets. This model then is used in a series of examples of where the coding practices many of us have developed may be inadvertently exposing those assets to bad actors. If you want to learn more about securing your apps, I highly recommend watching this one.
Session 7 – Explore the new system architecture of Apple Silicon Macs. Another must watch session. Apple is making major architectural changes with the new Macs and you should understand them. At one level you can just assume all the good and bad parts of the iPhone and iPad are coming to the Mac, but you’d be wrong. They understand the importance of developers having much deeper access to the hardware, and they will allow it… to a point. They do quickly flash a sudo command on the screen to turn off SIP. Got do a screen grab of that.
Session 8-10 were the Widgets Code-Along. I went thru these a few times to better understand Widgets. I think I will create a Wasted Time widget this summer to show the status of your meeting history.
Well today promises to be another jammed back day. Which Apple would drop the videos earlier in the day, but what can you do.
Wow! Made it thru 9 sessions today, and have 2 more that I wanted to do, but I am bushed. I will try and post a quick summary in the morning of the session on this same blog post… More to come…
As promised, here’s what I’ve learned on Day 2 (or at least the sessions that caught my fancy.
Session 1 – Meet Scribble for the iPad.
I did this one first, because it was the first one that downloaded for me. My goal was to start downloading everything I was going to watch for the day and then start going thru them. Even thought the videos “Dropped” at 1pm ET, there was a delay before they showed in Developer app, but not a surprise. Apple had to coordinate the distribution of all these videos to various content distribution centers. It was certainly with the wait.
The simple aspect of Scribble is that any “standard” field that support text will handle scribble by default. This is as expected. I immediately tested with Wasted Time on the iPad and “it just worked”. There were only a few APIs that the session covered, mainly to allow for custom controls to recognize Scribble.
If you want to allow the user to add a new element in a list (think the Reminders app), then you need to use UIIndirectScribbleInteraction. Think of this as recognizing that the user is in your list, but in an area that doesn’t “yet” have content. Overall, Scribble seems pretty clean, and simple.
Session 2 – What’s new in SwiftUI
As you can see from the list above, there a lot new things in SwiftUI. This is great to hear! I rewrote my app last year to use SwiftUI and while it certainly improved a lot of my code, there are things that still need to be worked on.
The most existing thing to me was the change to allow for @main as the entry point for your app. This means that a lot of the upfront code that was original developed in your AppDelegate, SceneDelegate and View, can now be simplified.
Lazy loading for Stacks means that you can greatly improve the performance and memory usage of your app.
Session 3 – Port your Mac App to Apple Silicon
This session was one of the longest sessions I watched. It was, however, well done, pointing out those areas that your app may have problems during the transition. I won’t go thru the details, but I have signed up to work on the transition with Wasted Time. I am expecting it to be painless, as I have kept my code up to date with Apple’s recommended changes year after year.
Session 4 – What’s New in Swift
This session really focused on how Swift5.3 is now a first class language, being available on multiple platforms, building out a thriving ecosystem of Open Source projects, etc. If you want to get all the details, I suggest that you go to Swift.org where they include a great blog on their enablement of Swift on AWS Lambda, along with updates on how you can contribute to the evolution of the Swift language.
Session 5 – What’s New in Mac Catalyst
As you can see from the list above, Mac Catalyst is now supporting more and more of the iOS frameworks. This aligns really well with the transition to Apple Silicon. A key item is that for those services that don’t work on the Mac – think AR Kit, instead of requiring #if directives and compiling differently based on target, you can now write your code, like you would on iOS, and check for feature availability on the device. Nice!
Session 6 – Meet WidgetKit
You can tell the sessions that keep me way too focused, I only get a screen capture from the title page.
Widgets are really taking advantage of the Intents that I’ve been working on in Wasted Time. If you have used the Siri face on your Watch, and how it exposed information that should be right and right on time, this is the logic on how WidgetKit will expose information in Widgets.
Keeping in the overall theme so far, these Widgets are written in SwiftUI and are exposed across all platforms! Can’t wait to see if I can get Wasted Time to automatically be exposed when meetings start. We shall see.
Session 7 – What’s New in SiriKit and Shortcuts
This picture really explains what’s going on. Siri and Shortcuts are becoming much more integrated in to the system and will be much less intrusive. Siri will now use the shortcut intent UI (as you see on the right) to expose information. And Shortcuts now can run on top of other apps, instead of taking over the whole device.
Session 8 – Lists in UICollectionsView
It was getting late in the day when I viewed this one. I am going to have go back and re-view this session, as the speaker was doing very detailed analysis of what is new. I am hoping that my greeting card app can take advantage of this session.
Session 9 – iPhone and iPad apps on Apple Silicon
Another late in the day session. The good news is – “it should just work”. But the key answer is … compile and test! Good advice.