Today we're going to begin looking at the lazy keyword in Swift. It describes some functionality in the language for what Apple calls " just-in-time calculation" of work.
This is great for times when we have expensive work to perform, or maybe the work is just some ugly complex code we'd like to tuck away. Perhaps we simply want to better compartmentalize the bits of our code that initialize UI elements, the use cases are plenty.
Today we'll start with Lazy Variables. Let's dive in!
Say we have a View Controller with a titleLabel. Pretty old school standard stuff.
We need to create the titleLabel, save it to a property, configure some stuff about it, then add it into the view heirarchy.
We want this work to be done after the view is loaded, and not when the view controller is first created, so we are forced to put it in viewDidLoad.
Additionally, we're forced to use a UILabel! force-unwrapped type on our titleLabel property to make the compiler happy.
It ends up looking something like this:
classViewController:UIViewController{vartitleLabel:UILabel!overridefuncviewDidLoad(){super.viewDidLoad()titleLabel=UILabel()titleLabel.font=UIFont(name:"Source Sans Pro",size:18.0)titleLabel.textColor=UIColor.brownview.addSubview(titleLabel)}
Let's make our titleLabellazy and clean this junk up:
classViewController:UIViewController{lazyvartitleLabel:UILabel={letlabel=UILabel()label.font=UIFont(name:"Source Sans Pro",size:18.0)label.textColor=UIColor.brownreturnlabel}()overridefuncviewDidLoad(){super.viewDidLoad()view.addSubview(titleLabel)}}
Neat!
We've now dramatically improved our viewDidLoad implementation. Cool.
We've also neatly compartmentalized the bit of code that sets up that label. Cool cool.
Perhaps most importantly though, we've now better expressed our intentions using language keywords (and their resulting behaviors), Cool cool cool.
Note: We have to use var instead of let when defining lazy properties like this. Our property might not (likely won't) have an initial value until after the instance of our view controller is created and its view is loaded. This completely breaks the rules of lets (constants).
That's all for now, in future Bites we'll look at other lazy behaviors in Swift!
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.
Recognizing and understanding new terms can be a big challenge when we're trying to grow as creators.
Often an unknown term or phrase can make us feel inexperienced, or ask "should I already know this?"
(Pro Tip: No, you shouldn't. We all learn new things constantly. That's the whole point. Keep going!)
Today we'll begin trying to identify and shed light on some of the more opaque terms or phrases we might come across while learning to build our apps.
We'll approach them from the point of view of iOS/macOS developers, but the terms themselves can be used to describe things on just about any platform.
Let's begin!
Dependency Injection
We're starting with a classic. This is one of the fanciest names for one of the simplest concepts.
This is just a complicated way of describing the idea of creating some type or object, while passing in some other type or object that the first typedepends on to do its work.
Note how we're passing in the manager rather than accessing some PlacesManager.sharedinstance.
This technique has numerous implications, the most common one being around testing.
By passing in the "dependency" we give ourselves the chance to pass in a "mock" version that can behave in expected ways.
Additionally, by not using shared instances, we reduce global/shared state in our apps.
Singleton
This one is easy, and we actually just mentioned it.
A "singleton" is just a fancy word for a shared instance...
...which is just a fancy phrase for a thing that is created once and only once. They are usually long lived, and available globally via some kind of Thing.sharedstatic property.
We covered more about singletons all the way back in Bite #4.
Modules
This is a fun one. A Swift "Module" describes the unit of code that makes up an app or framework.
Sometimes we use a module's name as a prefix for colliding type names. Like this:
// in SailingKit.frameworkstructShip{init(){}}
// in SpaceshipsKit.frameworkstructShip{init(){}}
// in an App:importSailingKitimportSpaceshipsKitfuncsomeFunction(){letship1=SailingKit.Ship()letship2=SpaceshipsKit.Ship()}
Tests
Finally we'll finish up with tests. Testing itself is a fairly straightforward concept:
We write some code that doesn't go into our app. It calls the code we wrote in our app, and checks its output to make sure its working as expected.
The terms come into play when we start trying to describe types of tests.
Unit Tests
These kinds of tests usually target one small individual piece of code.
For example, we might call a function that calculates some CGRects for a layout, based on a given available container width. We'd then check that the CGRects had the expected values.
For these reasons, "Unit" testing libraries tend to provide low level assertion functions to ensure (for example) return values are what we expect.
Integration/UI Tests
These are generally a bit more broad. They tend to target code that is closer to the user's actual behavior or interactions in the app.
On iOS/macOS, these are also commonly referred to as "UI" tests, since this is the name Apple gives the feature in Xcode we use to similuate a user tapping around in our app.
Pro Tip: Learn more about "UI" Testing in these Bites
In contrast to "Unit" testing libraries, "Integration" testing libraries tend to provide tools for replicating user behavior. Tapping buttons, filling fields, and generally using the app.
That's all for today, we'll continue to explore more of these kinds of terms in future Bites!
Is there one you'd like to see more about here on LBOC? Feel free to share it using hello@littlebitesofcocoa.com!
Author's Note: 300 Bites! Never in my wildest dreams could I have imagined LBOC being such a success. My sincere thanks goes out to everyone who continues to read and subscribe each day, as well as the team at Spec and all of our wonderful sponsors who make this all possible. Here's to the next 300!
We're welcoming back one of our favorite sponsors this week, 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.
Building great forms for users to enter information on iOS is tough. There's a lot of small details which are easy to get wrong. Today we'll begin looking at ways to improve this process.
First up, is navigation. 📍
Allowing for quick and simple navigation from one form field to the next can dramatically reduce friction for our users.
Last but not least, we aren't limited to a "standard" set of buttons/items in the toolbar.
We'll use the navigationFieldToolBar optional property that the library adds to UITextField and UITextView to first customize some individual properties directly:
In Bite #297, we learned about about how to simulate a light bulb accessory using Apple's HomeKit Accessory Simulator. Today we'll continue where we left off, and learn how to change the color of our light bulb.
Let's dive in.
Everything in HomeKit is built around the HMHomeManager type. We'll use this manager to interact with homes, accessories, and more:
letmanager=HMHomeManager()manager.delegate=self
We've set ourselves as the delegate here so we can implement one function:
Like all user data, we'll need to ask for permission before we can access the user's HomeKit data. Once the user has allowed us, the homeManagerDidUpdateHomesfunction will be called. This function will also be called anytime signficant changes happen in a home.
We can use this as a cue to update our app's UI to match. Neat.
Ok, let's have some fun and see what it would look like (in code) to change all of the lights in our house to a different color.
Nice. We now have an array of all lights in our home as HMAccessory objects. To make changes, we'll need to get to the services and characteristicsinside of each accessory.
Let's flatMap our way to success and get an array of just the "Hue" characteristics.
Note: We're just going to try this out by tinting our light blue. In a complete app, we'd need to make sure to update all 3 Hue, Brightness, and Saturation characteristics of the light.
Here we grab our newHue value for our UIColor.blue color, then iterate through all the hueCharacteristics from before, ensuring we multiply our newHue value by each characteristic'smaximumValue property. This makes sure we're speaking the language of the accessory, and setting a value in a scale it understands.
Finally, we wrap up our value in an NSNumber and write it to the HMCharacteristic.
When we build and run, we're prompted for permission. After allowing, we can flip back over to the HomeKit Accessory Simulator app and see the hue of our light has changed. Success!
Try adding more simulated lights, setting them to random colors, then running the app again to see a greater effect.
That's all for now. We've got plenty more to explore in HomeKit, look out for more Bites soon.
PDF files have been around a long time. Over that time they evolved to become an incredibly versatile and functional format. They're also wide-ranging in terms of how they're used. Comic books, Apartment leases, flight manuals, etc. All powered by PDFs.
Being able to take advantage of all this great functionality in our apps would be huge for our users.
Today, we're welcoming a brand new sponsor to LBOC, it's PSPDFKit! PSPDFKit delivers an intuitive & seamless SDK for integrating PDF functionality into apps on iOS, Android, and the Web.
With it, we can beautifully display PDFs, and allow our users to intuitively annotate documents, and much, much more.
Let's get started.
We'll head over to PSPDFKit's wonderful documentation area and follow the Getting Started steps to integrate the Framework directly, or through a system like CocoaPods.
Once we've got PSPDFKit in our app, we can start playing around.
PSPDFKit provides tons of great APIs for programmtically interacting with PDF files, as well as a bunch of polished, (and incredibly customizable) user interfaces to allow our users to read and manipulate PDF files themselves.
We create a new PSPDFDocument, then a new view controller to show it. We use PSPDFKit's awesome PSPDFConfiguration type to neatly build up our settings.
From here we can simply present it like any other view controller. Neat!
PSPDFKit stands out with an awesome, well thought-out API. It's clear that they care deeply about API design, and developer experience.
Before we go, let's try out another couple features. First up, Annotations:
// Create a new PSPDFDocumentletdocument=PSPDFDocument(url:documentURL)// Create a link annotation, and set its URLletlinkAnnotation=PSPDFLinkAnnotation(url:URL(string:"https://pspdfkit.com")!)// Position the annotation in the documentletboundingBox=CGRect(x:200,y:400,width:50,height:300)linkAnnotation.boundingBox=boundingBox// Customize the link annotation's appearancePSPDFLinkAnnotationView.appearance().borderColor=.blue// Add the newly created annotation to the documentdocument.add([linkAnnotation])
Very cool, Next up, Forms. Our users can of course fill out any form fields in documents they open, but we can actually fill them out programmtically too:
"Home automation" is perhaps one of the hottest topics in technology these days.
While still an emerging market, many iOS device owners now also own at least one or two "smart home" devices.
Today we'll begin looking at HomeKit, Apple's framework for communicating with and controllling these devices from inside our apps.
Before we can dive in though, there's a bit of tooling we need to learn about first. Specifically, we need to learn how to simulateHomeKit devices.
For example, we might not own any HomeKit devices ourselves. Even if we do though, we'd rather not need to phsyically change things about our home to test our app.
Not to worry, Apple provides a great solution to this challenge in the form of a HomeKit Accessory Simulator app for macOS.
In it, we can setup and configure a "simulated" set of devices in any kind of Home setup we'd like.
Sadly though, it doesn't ship with Xcode.
We'll need to head over to Apple's "More Developer Downloads" page here and search for "Hardware IO Tools for Xcode". We'll download the latest release, then install the HomeKit Accessory Simulator app.
Now, let's open it up and simulate our first accessory.
We'll click the + button in the bottom left and select New Accessory...
We'll fill out the fields with some example information. The values aren't super important, they merely need to be unique and somewhat realistic.
Neat. We've now got a (pretend) lamp. 💡
Well, sort of. There's actually one more important step, and it's speaks to the heart of how HomeKit works.
So far, HomeKit doesn't know anything about our new lamp. "Lamp" is just the name we gave it.
For HomeKit to do something useful with our device, we'll need to add a HomeKit Service to it. HomeKit Services describe the capabilities and functionality of a device
We'll click the Add Service... button on our new device, and choose Lightbulb from the dropdown menu.
We can leave the rest of the fields alone.
Neat! Not only do we now have a fully-simluted, color changing light bulb, we're also provided with some nice sliders and controls to read from, and write to, the current state of the device.
That's all for today. We'll learn more about the HomeKit Accessory Simulator as we continue to explore HomeKit. Next time we'll learn how to change this light's color in code! 🌈💡
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:
UIApplicationShortcutWidget
(We can also choose "Home Screen Widget" from the keys dropdown).
Neat!
Conditional Display
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.
In Bite #294 we learned about annotations in Sourcery. They're a great way to make extra metadata available on our types when accessing them in Sourcery's .stencil templates.
Today we'll take a look at one of the most powerful features of annotations in Sourcery, Key/Value Annotations.
We'll explore these by building a fictional API client powered by a simple Swift Enum. Let's begin!
First, we'll need to create our enum. Then, we'll define some cases to represent each HTTP endpoint we want to be able to call.
For us, these are a classic set of "get all", "get one", "create", "change", and "delete":
Long-time readers may recognize this enum technique from Bites such as #93 or #150.
Here's where the magic begins though. 🎩
SourceryKey/Value Annotations are similar to regular Annotations. Instead of a simple tag though, they allow us to annotate types, enums, enum cases, etc with a set of names and a values.
Key/Value Annotations work everywhere that regular Annotations do. We can annotate types, enums, enum cases, and more.
Ok, back to our API client. Now that we know we can add simple metadata like this above things in our code, let's use this technique to describe each API endpoint.
Nice! Normally, we might put things like method and path into essentially a big switch statement switching on self, and returning the correct method or path.
With SourceryKey/Value Annotations though, we can let Sourcerygenerate all of these statements for us.
Let's create a SpaceshipsAPI+Properties.stencil template that extends our enum, generating the computed properties: