Playing audio is an important part of many apps. One common trick is to fade in the volume of audio playback so we don't surprise or startle the user. This year, Apple has made this much simpler to implement using AVAudioPlayer. Let's take a look.
First we'll set up a standard AVAudioPlayer, and begin playing it at a volume of 0:
Xcode Project folders can be a messy place. Today we'll check out a tool from the folks at Venmo that can help us tidy up called Synx. Let's take a look.
At its core, Synx's main purpose is to reorganize the files and folders in our Xcode project folder to match the groups and structure we've setup inside Xcode's Navigator pane.
We can start by organizing a project full of content:
Then we can simply head into our project's directory and run the main command:
synx ./OCMock.xcodeproj
Synx will work its magic and re-organize our files on disk, creating directories and moving files as needed to make things match the groups in our project.
Additionally, we can also use Synx to remove files no longer referenced in our project, like this:
synx --prune ./OCMock.xcodeproj
We're also provided a couple arguments to help us control how Synx behaves.
We can exclude files:
synx --exclusion /OCMockTests ./OCMock.xcodeproj
Last but not least, for those of us who like to manually sort our files by concept rather than name, we can disable sorting:
synx --no-sort-by-name ./OCMock.xcodeproj
Choosing how to organize projects can be a very subjective and personal choice, this is just one approach. Always use whatever works best.
The .lightContent style lives up to its name, as it makes the status bar'scontent white.
This work great until we put our view controller inside a navigation controller. Overriding the property then has no effect, as the system is actually asking the navigation controller.
We've all been here:
Yuck. Let's fix that up.
Since our content here is in a navigation controller we can use a "tried and true" approach that involves simply subclassing UINavigationController:
To make this work, we can set this custom subclass in Interface Builder, or use it when creating our UI in code. Let's have a look now:
Nice.
This approach works in our case, but might not be the best way to do this. If the navigation bar ever gets hidden, UIKit will actually start asking the containedview controller for the status bar's style, which may or may not be what we want.
We could have accomplished the same thing using a plain old UINavigationController by simply setting its barStyle property when we create it (or by setting this property in Interface Builder) to the .blackOpaque style:
This week we're welcoming a brand new sponsor, it's Zendesk!
None of us have ever built a flawless app.
Chances are, no matter how good we think our app's user experience is, there's probably something users will need help with while they're in our app.
Yet in many apps, getting help is reduced to a "contact us" button that launches an compose email screen, or a just drops the user on a web page.
By forcing folks out of the app to get help, small problems can turn into big annoyances, and those will almost certainly turn into negative App Store reviews and poor ratings.
With Zendesk's Mobile SDKs, we can bring native, in-app support functionality to our apps quickly and easily.
Our users can view help and support content, and even submit tickets to support without ever leaving our app. Neat!
Tickets go into Zendesk and can include technical details about the user and their device, history with our apps, and more.
Best of all, it's included with Zendesk at no extra charge.
We can use Zendesk's "out-of-the-box" iOS UI components to get up and running quickly, or we can build your own UI with SDK API Providers.
A huge thanks to Zendesk for sponsoring this week's Bites!
We've been looking at the various improvements in Notifications in iOS 10, and today we're trying out one of the coolest new features: Notification Content Extensions. These allow us to display completely custom content when the user drags down or 3D Touches on one of our app's notifications. Let's dive in.
We'll start by adding a new target to our app and choosing Notification Content Extension.
We'll give it a name, and check out the files we get.
Like many of the other extension types we've covered, the whole thing is basically a view controller, storyboard, and Info.plist.
We'll head to the Info.plist first and configure a few things.
Since we want our custom content to be all that's visible once the users opens up our notification, we've added the UNNotificationExtensionDefaultContentHidden key hide the title and body text labels that are visible before open
We've also set our notification category to match the one we'll send in our UNNotificationContent.
Before we continue, let's add some actions to our notification so the user can actually do something about it. We've covered Notification Actions in the past (Bite #122), but here's how things work in the User Notifications Framework world:
// Somewhere before we request our first notification:letevade=UNNotificationAction(identifier:"evade",title:"Evade with Autopilot 🤖")letdestroy=UNNotificationAction(identifier:"destroy",title:"Destroy (Weapons to Maximum!) ",options:[.destructive])
Then we'll send off a request to the notification center to show the notification in 2 seconds. (Enough time for us to press the home button and head out to the home screen).
Nice, so far so good. Now, let's see what happens when we open it up (either by 3D Touching on a supported device, or simply dragging down the banner):
Success! Our custom content is shown inside the notification, along with our custom actions.
Today we'll continue our look at Notifications in iOS 10 by looking at the new "rich" attributes we can use to give our notifications titles, subtitles, and even media! Let's check it out.
Most of this code will center around our old friend UNNotificationContent:
We're already using two new fields here: title and body. This allows us to properly highlight the important and extra information in our notification's UI:
Nice.
Now, it'd be really great if we could show the user an image of the giant space rock they're hurdling towards. Let's attach some media to do this:
Heads up: That will need to be a local file URL, not a remote one.
Now, when our notification is shown, we see a nice little preview of the asteroid:
One small note here: The thumbnail image shown here is the result of adding padding manually to the image to account for the the system's aggressive cropping.
There is a way to pass a dictionary of options to UNNotificationAttachement. There's even a key called UNNotificationAttachmentOptionsThumbnailClippingRectKey.
This key requires a "normalized" CGRect describing how the image should be clipped.
Setting this key to CGRect(x: 0, y: 0, width: 1, height: 1)should allow the whole thing to be visible, but it seems the system ignores this value entirely as of Beta 6.
Today we'll continue our look at Notifications in iOS 10 by checking out some new capabilities for interacting with the user's notification preferences, and also the notifications we've requested and setup with the notification center. Let's begin.
First up, we finally have a way to "ask" the system for the user's current notification preferences state:
UNUserNotificationCenter.current().getNotificationSettings{settingsin// settings is a beautifully descriptive// UNNotificationSettings object.}
Inside this fancy UNNotificationSettings object is a wealth of detail about exactly how the user has configured our app's notifications to work. Everything from authorizationStatus to alertStyle. Neat!
Next up, since we've been requesting all these notifications to be triggered, we can now ask the system to give us the list of pending notifications. (Notifications that are scheduled, but haven't been displayed yet).
UNUserNotificationCenter.current().getPendingNotificationRequests{requestsin// requests is an array// of UNNotificationRequest objects.}
From here, we can take the identifier of any of these requests, and use it to (for example) "unschedule" it:
Continuing our look at Notifications in iOS 10, today we'll check out the different notification triggers we can use to display notifications in our app. Let's get started.
We'll use the same standard authorization and testbed setup we did in Bite #258, only this time, we'll create some triggers.
We'll compose a quick UNNotificationContent:
letnotification=UNMutableNotificationContent()notification.title="Entered Mothership!"notification.body="Welcome to Apple."
Now, we'll start trying out triggers. First, let's look at a location trigger:
When it comes to time-based triggers, we're given a couple of options.
We'll compose another quick UNNotificationContent:
letnotification=UNMutableNotificationContent()notification.title="WAKE UP!"notification.body="You're late for the Mothership!"
There's UNCalendarNotificationTrigger. This allows us to specify only the components we want to trigger on. For example, here we're creating a trigger that will repeat every morning at 7:15 AM:
Notifications are a huge part of making our apps useful. They allow us to notify the user both actively and passively of important information, providing the immediacy users have come to expect.
In iOS 10, Apple has overhauled how notifications work. Today we'll begin looking at these changes with the new User Notifications framework. Let's dive in. 🏊
It all starts with UNUserNotificationCenter, and more specifically:
UNUserNotificationCenter.current()
We'll use the current notification center to authorize, schedule, retrieve, and generally manage the notifications our app displays.
Before we can do anything, we'll need to ask for permission. We can pass in some options for the kinds of notification behaviors we'd like permission to use:
For the next step, we'll return to our imaginary Spaceships application. Let's say we have a feature that alerts the user when an asteroid is detected in our path.
To show a notification to the user about the space rock, we'll need to create a new UNNotificationRequest and add it to the notification center.
We'll start by creating a UNMutableNotificationContent and configuring it for our needs:
The categoryIdentifier here is simply a unique string we create. Later, we'll use this to identify "Asteroid Detected"notifications and differentiate them amongst from other kinds of notifications our app might display.
Next, we'll actually create the notification request:
We haven't specified a trigger here, which tells the system we'd like this notification to be shown right away.
Finally, we'll add the request to the notification center:
UNUserNotificationCenter.current().add(request)
We'll wrap all this in a quick function and call it with a 2 second delay so we have time to press the home button and get to the home screen to see the notification appear.
Today we're welcoming a brand new sponsor to LBOC, it's Bugsee! Allowing our users to report bugs is a crucial part of building our app. We can release builds through TestFlight, but what about allowing our users to report issues they encounter? Today we'll check out Bugsee's SDK and how it can help us super-charge this process. Let's take a look.
What is Bugsee?
Bugsee is a free SDK. It gives us truly effective debugging. It changes how we receive bug and crash reports from our users. We're going to be using it on iOS, but it works just as well on Android and on the Web!
How Does it Work?
After we integrate Bugsee into our app, our users can simply snap a screenshot to report a bug. (We can also configure different methods of reporting like shaking the device, etc.)
Bug reports include video of the last minute, full events log, console logs and network traffic logs along with all environment details.
For crash reports, Bugsee will include the full video leading up to the crash, including the reason, method, as well as the file and number that caused the crash.
Bugsee's viewer will allow us to review the video synchronized with all system events and logs. We can essentially "play back" a complete recreation of what was going on right before a crash or bug report occurred. Super neat!
Installation
Bugsee is straightforward to install, it's just one line of code (and it passes all App Store checks):
Bugsee.launchWithToken("app_token_goes_here")
Bugsee also integrates with our bug tracking systems, dramtically speeding up QA and debugging round trips, saving hours for both engineering and QA teams alike.
Bugsee doesn't impact the performance of our UI, and doesn't consume network bandwidth (no streaming). The SDK also automatically obscures secure fields in our app. Nice!
Mobile developers have needed a complete solution like Bugsee for quite some time. Signup right now at bugsee.com/jake for a free lifetime account.
A huge thanks to Bugsee for sponsoring this week's Bites!