A few months ago, Apple threw out my app Interval Runcalc for being too old. While it wasn’t the most innovative, complex or profitable, it was an app I used frequently myself. It was a calculator for time, pace and distance for running not only for straight runs but for the run/walk/run community who use multiple-speed intervals to do the same computations. I decided to update it, and get it back in the App Store, but to journal that process as I did it. Over this summer, I’ve gone from a few sketches to a new data model to upgrading Xcode 6 code to Xcode 8, a working app with picker views. I finished debugging and layout for the app in the previous installment of this series. Having taken all summer to do this in installments meant I ran into a new issue: Xcode 9 launched. So instead of submitting the app for iOS 10.3.3, I’m going to submit it for iOS 11.
Some might debate this. There is a very valid argument to write for legacy users and keep the app at 10.3. 3 or even 10.0 for all to use. I have three reasons why I’m going to set up a iOS 11 app instead. The first is the high adaptability rate for iOS. If I was working with Android, this would be a different story, which has a huge legacy base. On the other hand, iOS usually has less than 10% legacy base, and with many of the new features of iOS 11, I expect that to be the same. Secondly, this is an app with almost no sales. I’m dealing with an app that has sold 67 units since December 2013. An update does not affect a huge user base. Finally, I’ve made the process of updating into something instructional. With a GM seed release, nothing is more instructional than updating between versions.
Getting Xcode 9GM seed
The first step was the painfully long load of Xcode 9, competing for server bandwidth with everyone else, and from a hotel room.
Once downloaded I unzipped the application which took a bit more time. Once up, I made a copy of the project, which I’ll change to version 2.0.1, then open the app.
Opening the app.
Once open I let it sit and index. Supposedly in Xcode 9, I don’t have to do that, but I’m paranoid. I get two expected warnings:
Xcode wants me to update. I’ll go to the general tab of the project, and first change my version and name to RunCalc
I’ll click the warning for iOS 11.
And I get a list of changes to make:
I’ll do all of them, so I’ll hit Perform Changes
One Warning disappears. However, I still have a iOS Deployment target of 8.0
With a certain scene from This is Spinal Tap playing in my mind, I turn that up to 11.
Next I’ll convert to Swift 4, just to be completely up to date.
I’m asked for the targets to convert:
I have only one, so I’ll hit Next. I get another question:
Xcode 8 and Swift 3 did a lot of inference of what Objective-C could and could not use. That was a lot of extra overhead. In Swift 4, this become more explicit to make faster more compact compilation. I’ll use the recommended settings here. I get a notice.
I’ll get the migration guide up and handy for use. So I’ll click the View Migration Guide if I need it.
Xcode tells me after I hit OK,
So I hit Update. I hve a new Warning:
I’ll shut down Xcode for the moment. I clean out the DerivedData folder to make sure nothing is pointing wrong there. And that solves the warning.
I have no warning now, So I’ll try running the project on the simulator. I’ll try it on an iPhone 7 first. It works here, using the very beautiful new simulator
We do get a warning about the @objc inference.
The full warning reads:
The use of Swift 3 @objc inference in Swift 4 mode is deprecated. Please address deprecated @objc inference warnings, test your code with “Use of deprecated Swift 3 @objc inference” logging enabled, and then disable inference by changing the "Swift 3 @objc Inference" build setting to "Default" for the "RunnersCalc" target.
Time to look at that migration guide. It does not give me a lot to go on. It tells me almost the same thing as the error, except that bit about logging enabled. In my case, I had Objective-C code in my app, but there is none now, except for the bridging header and some old starting code. The @objc is for Objective C to recognize my swift code if there are odd characters in it. In Swift 4, you have to be more explicit about it.
Since I have no such code in my app, I can set the build setting to Off or default. I go to my build settings for the target.
Search for the @objc settings
You’ll find the setting on most likely
Set it to Default
I run again and the app runs fine for Iphone7 without warnings. I check the landscape too.
Tht’s working too. Now for a little fun. I’ll try a iPhone X. The portrait looks great:
And the Landscape works, but with a small issue:
iPhone X will use the full screen. The sensor area on the trailing edge obscures some of the table. This isn’t so bad, but rotate so the sensor/camera area is on the leading edge.
It’s near impossible to lock the pace or distance. The buttons are under the sensor. There are three ways to handle this. The first would be to create code in didLayout Subviews to move the stack view with the Time Distance and Pace over enough to not be in the way of the buttons. That’s the most complicated.
There’s also a layout solution: use the right side for the lock buttons. There are two changes I’ll have to make: change the lock button from leading to trailing and change where the text get’s its leading constraint. I’ll directly select the constraint in the document outline for the Lock.leading = leading.
then change the attributes from Leading to Trailing
The Label for the entry is constrained 6 points to the left of the lock button, and the unit is constrained to the entry. I’ll have to use the super view’s, which was a blank view I used for a background and container, Leading here, then set the leading for the label to 48 points from the leading of the containing view so it looks like this:.
After doing this for all the buttons, That gives us buttons on the right instead of the left.
Looking at this, I’m okay with that. The locks are not used frequently, so being toward the center out of reach is okay. I check the storyboard against an iPhone SE in portrait and landscape, since if any will have problems with this, it will be that device.
However, there’s the third, simpler solution. It’s the one Apple wants you to use in the iPhone X Human Interface Guidelines. I should have read them first, but I didn’t. I’ve been very guilty of not doing something Apple has recommending for years: using margins for constraints, also known as the safe area. For the flush-left layout, I often use the superview’s leading attribute with a constant of zero, not the superview’s margin’s and a constant of -16. For example, the stack view holding the time distance and speed buttons has a constraint of this:
Apple wants you to use the margin, which changes depending on the phone and orientation. For the attribute inspector, It’s a simple change: Make this relative to margin, which is a single click in the selection box.
The simplest answer to layout problems is to keep everything relative to the margins. Unless it’s a background that can be obscured, don’t use the absolute leading or training constraints. I run this, and the result is much better:
With no issues there, I solved the one problem I’ve encountered with iPhone X. I’ll test more with the simulator, and I’ll change the trailing constraint to the margin, but it looks like the code and layout now work with the newest iPhone.
While there were a few bumps in the process. I found converting up to iOS11 not too bad. It did need a shutdown and cleaning out of the derived data to solve one warning, and flipping a switch for a second. The biggest problem, one I could have avoided was the layout issues has I used the safe area correctly. I’m ready to submit, and in the next installment, I’ll go through that process.
Steven Lipton is the mind behind makeapppie.com for learning iOS app development. He is the author of several books on iOS programming and development. He is also a LinkedIn Learning and Lynda.com author. His latest courses are Learning Swift Playgrounds Application Development and Advanced iOS App Development: MapKit & Core Location