Apple's Files.app is a great way to interact with files on iOS.
Today we'll begin taking a look at how our app can better integrate with it. First up, we're going to learn how to allow users to open files in our app, from the Files.app. Let's get started.
We'll begin by creating a new project with the Single View App template in Xcode. In the future, we'll look at how Document Picker apps work, but for simplicity, we'll use just a plain single view app this time.
Then, we'll head into our AppDelegate.swift file, and add a new function:
This application(_:open:options:)function will be called when the user selects a file inside the Files app and presses the Share ( ) button on it, and selects our app in the UIActivityViewController that appears.
Pro Tip: We can also access this by long-pressing a file in the Files app and selecting Share from the menu:
Next we'll need to tell Xcode that our app supports the type of files we want to our app to be able to open. For this, we'll choose Text Files.
We'll click on our project at the top of the project navigator and click Info at the top. We'll find this screen:
We want that area labeled Document Types (0). We'll click the + button and enter in Text for the Name and public.text for Types.
Then, we'll add a new key to our Info.plist to make our app work more smoothly. We'll right-click on the keys at the top and select Add Row, then add the key LSSupportsOpeningDocumentsInPlace. We'll make it a Boolean and set its value to YES:
This allows our app to work with the files users open, without needing to first copy them into its own sandbox directory.
Finally, we'll head back to our AppDelegate.swift file and fill out that function we added earlier:
First we have to tell the system when we begin using a URL, saving the Bool value that url.startAccessingSecurityScopedResource() returns. If that Bool is true, then we'll also need to call stopAccessingSecurityScopedResource on our URL once we're finished with it.
After that we simply read the Data at the URL, then put it into a String so we can print it out.
Now we can Build & Run the app on our device, open the Files.app, choose an .txt file, share it, and our app appears in the Share sheet:
Tapping our app's icon opens it, and runs our code to print out the contents of the file, neat!
Asset Catalogs have been around for a few Xcode releases. They're a great way for us to organize and configure graphical assets (among many other things) for our app. Today we'll check out the improvements to Asset Catalogs in Xcode 9. Let's dive in.
First up, colors.
Yep, we can now define and organize named colors inside of an Asset Catalog!
We can select "New Color Set", and then use all of the normal features of Asset Catalogs, including new Wide Gamut support.
Then, in our code we can reference these colors like this:
The second big addition we're going to look at is "real" vector-based asset support.
In past Xcode releases, we were able to add image assets to our catalogs with a format of PDF. This worked great, but under the hood, Xcode would simply render our asset at the @1x, @2x, and @3x sizes and save non-vector (i.e. png images) into our app's bundle.
In Xcode 9 however, we're given a beautiful new checkbox called "Preserve Vector Data".
This means that if our image is loaded in our code, and we ask it to display at a a larger size than it is by default, it will be scaled up at runtime by the system.
This means we can now write code like this without any quality loss at render time:
The clipsToBounds ensures the "clipping" takes place, and the next rounds all four corners with a radius of 8:
This approach works great for rounding all four corners of a view. But what if we wanted to round only some of the four corners? Previously we'd need to drop down and create our own mask layer, giving it a path we created manually. Bummer.
Continuing our look at the new tidbits and goodies from WWDC 2017, today we'll learn about the changes to screen edge behavior in iOS 11. Let's dive in.
What we're really talking about here is the behavior when a user drags from offscreen. This could be the user trying to pull down Notification Center from the top, or bring up Control Center from the bottom of the screen.
In past iOS releases, the system has looked at the visibility of the status bar to determine how to behave in these cases.
If we configured one of our view controllers to hide the status bar, the system would show a little "tab" UI with an arrow that the user would need to drag a second time before Notification Center or Control Center would be shown:
Keying off of the visibility of the status bar probably isn't the best way for us to "tell" the system what to do here.
In iOS 11, we've been given a wonderful new way to describe how our app should behave when the user performs these gestures.
Now, all we need to do is override this function in our view controlller:
There's always one more fun trick tucked away in UIFontMetrics, and that is scaling arbitrary values. This is great for helping us size our UI elements (for example buttons or headers) to accomodate dynamically sized fonts that live inside:
UIReferenceLibraryViewController supports many different languages. New languages can be installed using the "Manage" option in the bottom-left:
Finally, it's worth noting that we get definition functionality for "free" in UITextFields via the "Look Up" item in the UIMenu that's shown when a user selects some text:
... and it's also worth noting that the view controller that appears when a user taps one of these "Look Up"menu items in a UITextField appears to be far more advanced than the what we get when presenting a plain ol' UIReferenceLibraryViewController:
The Dictionary results are still present, but we also get results across Music, Wikipedia, Movies, Websites, and even the App Store. Very cool.
(And nope, UIKit does not seem to provide a way to trigger this fancier view controller directly. Anyone interested in this should probably file a Radar.)
That's all for today. Those who want more UIKit B-Sides can check out these bites here.
We first covered Today Extensions way back in Bite #36. They're a great way to offer quick, glanceable information or entry-points to our app. Today we'll take a look at some lesser known features of Today Extensions, and how we can use them in our code. Let's begin.
3D Touch Shortcut Widgets
First up, is one of the newest additions to the Today Extension world: 3D Touch Homescreen Widgets.
The coolest bit is, we don't really need to do anything to "get" this. If our app has a Today Extension, the widget will automatically appear when a user 3D Touches our app's icon.
The B-Side comes into play when we have multipleToday Extensions in our app. We'll need a way to tell the system which one to display above our icon. We can do this by adding a new key to our app's Info.plist:
(We can also choose "Home Screen Widget" from the keys dropdown).
Today Extensions Widgets don't have to always be visible in the Today view. We can actually tell the system whether or not our widget should be displayed with this one function:
Calling this with false will hide the widget in the Today View until our app calls the function again with a true value.
Opening Our App
This one is a bit of a stretch to truly call a B-side, but it can easily be done incorrectly, so here we are.
It's quite common for a Today Extension Widget to need to open its containing app.
Apple has tightened the reigns in recent OS releases to "validate" when and how apps (and specifically Today Extensions) can open apps. Not to worry, we can use this special function on an NSExtensionContext to open a deep link into our app.
Pro Tip: Opening our own app (i.e. our app that contains the Today Extension) is just fine, but beware if we start trying to open other apps using this function, Apple may scrutinize our Today Extension further during App Review.