In this video, focus on the main purpose of this course. Every programmer is, at some point, taught to make their code as reusable as possible. This video dives into that topic in detail and explains why a clean interface is important, not just for the user experience, but also for the developer using your framework.
- [Instructor] Now that we got our camera working, let's talk a little bit about how to design for an interface of a framework as opposed to just an application. What we've got working so far is fairly straightforward and generally easy to use but there are some things we need to think about. For starters, think about how most iOS devices you interact with have two cameras on them, both the front camera and a back camera. Think about how the person who uses this camera framework may want control over specifying which camera works at runtime. One easy way that you could do this is pop it into the constructor.
Your constructor might look something like this if you wanted to go that way. In your SampleApplication ViewController.swift file, you might think that something like this would be good, withInitialPosition equals something like .back and you might think, "Okay, that's simple. "We're keeping it to two lines of code," but what if we add more parameters? We don't want to stop everything inside of one constructor because then we have to handle creating different versions and it doesn't give you a whole lot of flexibility as you're working through the runtime. So we're going to come up with a better way to handle the initial position and hopefully this shows you enough about how you can make a good interface experience for whatever developers using your framework.
Let's go ahead and go back into the CameraViewController.swift file in your framework. We're going to start by adding a couple of things. Scroll up to the top, right underneath where you imported AVFoundation, and we're going to add another type of enumeration called CameraPosition. We're going to type public enum CameraPosition. And we're going to add two cases: case front, case back. Now, how do we use this? Well, we go inside of our ViewController, underneath line 17, and you're going to type public var position of type CameraPosition and we'll start by specifying .back.
What this does is this gives us a public stored property that's accessible by any application using this framework. And the default value is set to the back camera here. Next, we want to do a little bit of refactoring along the way. Go underneath your discoverySession and right above VideoOutput, type in var videoInput and specify it AVCaptureDeviceInput, and make sure that's optional as well. We'll be storing this later on. Then what you want to do is underneath public.init, type in viewWillAppear and it should fill in public and override and the function for you.
Then you want to type in super.viewWillAppear and passing your animated parameter. Now what we're going to do is we're going to move the createUI and commitConfiguration lines of code into this particular part of our camera. This means that we only create the UI and commit the configuration when this controller is going to be created on our stack. So we can go up here, cut these lines of code from our init constructor and paste them into viewWillAppear. Now that we've created our position enum, we want to scroll down to our getDevice function.
At this point, it should be on line 80 of your ViewController. Because we're specifying the position, we can actually pass that in as a parameter and reuse this code throughout our framework. So, inside the parameters of your constructor, you'll type with position equals AVCaptureDevice.Position and this should still return an optional AVCaptureDevice. Scroll down to the line where you're specifying if device.position is equal to ACCaptureDevice.Position.back and instead of that AVCaptureDevice.Position.back, simply type in position.
This means that whenever we reuse this function, we're passing in a specific position so we can choose what kind of device we want to get out of this particular function. Additionally, scroll up to your getPreviewLayer function and underneath previewLayer.frame, enter another line and type previewLayer.videoGravity equals AVLayerVideoGravity.resizeAspectFill. I recommend you look into these options individually but this is just something we want to refactor and specify as we deal with other things in our camera.
Make sure that you can build your sample application and you'll notice that of course we have a failure here because we have a missing parameter because we haven't added that to where we call getDevice. So we go up to commitConfiguration where it has this error. And now we're going to type a little bit of logic to get the position that we specified on our camera. So for this, we'll type getDevice(with: self.position == .front then we do a ternary operator to choose AVCaptureDevice.Position.front or AVCaptureDevice.Position.back.
All we've typed here is we're looking at the position we've set as a publicly stored property on this ViewController and we're choosing what to get out of our device based on what that is. Above our do-catch block, we want to add logic to clean up the input and output for whatever we've set previously so that we can clean that up and reset the new input and output as we change this. So we're going to type if let currentInput = self.videoInput, then we want to do self.session.removeInput(currentInput) and self.session.removeOutput(self.videoOutput).
Consider that somebody taps a button to switch from the back camera to the front camera on your ViewController, this will give the ability to clean out the old input and output and refresh it with the new one. So we scroll down here, and inside of our if statement, to check and see if we can add the input and output, right above self.session.addInput on line 67, add a new line and type self.videoInput = input. Hit cmd + b on your framework in order to make sure that the framework builds fine. And then what you'll do here is go back to your Project explorer and if you add a line after line 16, in viewDidAppear in your sample ViewController, you should be able to specify the position.
We can do this by typing camera.position = .back. You could also choose front if you wanted to specify the front camera but in this, we'll just choose back and we should be able to confirm that if we run this, once we see that the camera's loaded, we've chosen that we can actually see the back camera. This is a great example of what you need to do when you're thinking about how to make a good interface for your developers to use so that they have the freedom to use your framework however they need to in any particular use case, example of the three different use cases from clients that we had, and you can start to see how you need customization options on your framework.
This is how you can set that.
Along the way, he explains the differences and nuances between writing code for an application and for a reusable framework, as well as some of the fundamentals of AVFoundation, one of the core camera frameworks in iOS. David also shows how to refactor your code, understand Swift access control, develop an interface, and handle memory leaks, so your framework is ready to share with other developers.
- Creating your first build
- Making the camera work
- Creating a framework delegate
- Adding media
- Capturing images
- Correcting orientation
- Versioning and tagging releases in Git