Developer Tools • iOS, macOS, watchOS • 38:45
See how you can incrementally add Swift into your existing Objective-C codebase and app development workflow. Learn how to leverage Swift's powerful language features to develop robust applications that are faster to write and easier to maintain.
Speaker: Woody L.
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
[Woody L.]
Hi, everyone. I made a comment to Stefan earlier today, saying, I hope some people come out to this. And it's incredible. You are just packing the place. So, thank you for coming out. My name is Woody, and I would like to introduce you to Improving Your Existing Apps with Swift.
Can I get a show of hands, how many of you are actually using Swift in your code right now? Okay. Excellent. Then you are all at the right session, whether you've done it or not. Hopefully by the end of it you will all be using Swift with your existing Objective-C projects.
You know, last year I was also in this room, I was sitting in the back corner as an attendee, using my laptop, trying to figure out how to use some of the technologies that I learned about in an earlier session, replying to work email even though I told them I wouldn't, and half paying attention to the presenter that was up here, as I am sure many of us are right now. Well, since then, I've switched sides from the attendee to the stage. I have moved sides of the continent from Halifax, Nova Scotia, to California, and I am out today to help you learn some techniques to work with Swift and Objective-C.
I don't have to go over and move it. I've got a remote control now. So what I want to do is take you through some of the techniques you can use to add Swift to an existing project to make that project better. And I thought the approach I would use would be to do exactly that in this session.
So I went to Infinite Loop, I went down to the parking garage, and down to a floor below that where we have our catacombs, and I went past the three-headed dog that guards the entrance to industrial design, to the end where we keep our software vault. And I opened the software vault and I took out an app called The Elements that we haven't seen at WWDC for maybe two or three years. I'd like to show you how it is right now.
Alright, this is The Elements. Do you remember seeing this before? Yeah? Some of you do, some of you don't. Somebody really like it. I like it too. So it's a standard UIKit-based table view app. We have got a bunch of cells for each of the elements in the atomic table. We have this small, little detail view with a little bit of information. And if you tap on one of the cells, we have this navigation controllers push presentation to show some details about that.
Great. I figure it's something that probably a lot of you have to do. You move around between different applications, and you come back and you revisit an app that you haven't used in a while or you haven't programmed in a while and you are asked to add new features to it or support a new operating system, a new SDK. So that's exactly what we are going to do. We are going to modernize this Objective-C app and we're going to do it using Swift.
The pitch to you is this: if you have an existing application and you have got to write new code for it and that existing application is an Objective-C app, consider taking advantage of Swift's features by writing that new code in Swift, while, at the same time, leave your original Objective-C code in Objective-C. That's perfectly fine too.
In this session, we are going to start by doing a makeover on the application to bring its user interface to a more contemporary appearance. We are going to do that using Swift. Then we are also going to take a look at, for example, Swift's structs and its functions like mapping and reducing to find ways that we can add new features to the application.
This application has small tiles and large tiles. The small tile is the table view, the large tile is the detail view. And the tiles consist of a background, plus the text that's rendered on top of that background. Because this app is an older app, the backgrounds were originally rendered in Photoshop, and then they are just embedded inside the application.
But these backgrounds which exist in different colors depending on the atomic states, solid, liquid, synthetics, gaseous, are only rendered for the original iPhone. There is no Retina artwork included. Which means when we take this older app and we run it on newer hardware, we have to scale up, and when we scale up this older, this older artwork, we end up getting these artifacts, called aliasing, on the rounded corners because we don't have enough pixel data to represent those corners smoothly. So that's something that we definitely are going to fix up in this presentation.
The other thing is, do you ever look at your middle school yearbook, maybe in the '80s or '90s [applause]? Yeah, right? You see a picture of yourself, and you are there wearing a vest with, like, this embroidered kitten on it, and you are holding a keyboard because it was cool at the time, and you've got this laser background because that was also cool. And now you are looking at it and you are just thinking, what was I thinking?
[ Laughter ]
Sometimes we look back at our apps and we think, what were we thinking? With the shine and the gloss and the reflection. So we are going to fix that. Fashions change. Styles change. And one of the ways that we can do that with this app is simply removing the gloss on the background.
We get a very contemporary, rendered rectangle outline. It looks good in the small tile as well as in the large tile for this app. Now, we could rerender the new backgrounds in Photoshop, and embed them inside the application, but clearly this is an app that we don't, we don't get into very often.
You know, it might not be until WWDC 2020 before we come back to The Elements, and I'd like to make sure that it looks good not just on past hardware and current hardware, but also on potential future hardware. So I am going to choose to put in some custom drawing code just to draw the rounded rectangle background because it's just a rounded rectangle.
I am going to do that in Swift, but I am going to call it from Objective-C. To do that, the technique I am going to use is called Mix and Match, and we have numerous sessions both last year and this year on interoperability between Swift and Objective-C, the mechanics of which can be covered better in those sessions. What I would like to do at this point is just give you an overview of how the technology works and then we'll actually dive into a demo and you can see it.
Typically when we think about classes in Objective-C, we have a header file and an implementation file, and those two things together form our class definition. But it's possible that we can have some of our methods implemented in a category, and that's perfectly fine, and then our class definition is our base class plus our category. And there's nothing to say that you can't have multiple categories. In fact, this is something that we do with UI table view. If you look at this header file in Objective-C, you will see that there's many categories on UI table view.
Now, there's also nothing to say that one of those categories couldn't be implemented in Swift, in which case the terminology changes but the concept is the same. We just call it a Swift extension on the Objective-C, in this case, base class. And there's also nothing to say you couldn't have multiple Objective-C categories mixed in with multiple Swift extensions and the content of all of them forms your class.
This lets us have some of our functions written in Objective-C, and it lets you have newer functions be written in Swift. For this interoperability technique to work, we use a set of bridging headers -- not a set of bridging headers -- we use a bridging header and a generated header.
Now, the bridging header is made in Xcode. The first time that you introduce Swift into an existing Objective-C project. And then you maintain it. You just basically go into it, and put some import statements so you can selectively choose which data types and header files from Objective-C you are going to expose to Swift.
Then on the reciprocal, the Swift compiler makes a generated header which you can import into your Objective-C implementation files to expose Swift, in this case, extensions and other data types, to Objective-C. So we have these two header files. You are going to see them in the demonstration, which is coming up right now.
Okay. So in my project, I have this class called atomic element tile view. That's the one that actually draws the background. There is a method in it. It's actually not just drawing the background, it's drawing the background and all the text that goes on top of it for both the small tile and the large tile.
So I am going to introduce the new drawing code, but I am going to do it in Swift, so I will go ahead and make a new Swift file. It's command-N in Xcode. We will choose a Swift file, create it, and because it's the first time that I've added Swift to this project, I have the option to create the bridging header now. I want to create it, so press Return.
And now I have my two files that consist of the base class in Objective-C plus the Swift file. I also have the bridging header down here. Let's just make this a little bit wider. There we go. You might notice that I chose to use the exact same file name for my Swift file as I did for the Objective-C. That's not a requirement, but it's a convenience because it means that in Xcode, I can use the shortcut of control-command-up arrow and just cycle through the Swift and the Objective-C header and implementation, just quickly move back and forth between all three.
Now, the bridging header is where I choose to expose data types that I've declared in Objective-C to Swift, and I do that by importing their header files. Since I want to extend atomic element tile view into Swift or using Swift, I have to import it into the bridging header.
The only one that I really need at the moment is the one that I've highlighted, atomic element tile view, but later on in the presentation I will need the other ones, so I am just going to go ahead and get them all imported now. Now we can go over to the Swift file, and I will write an extension on atomic element tile view.
So now the class atomic element tile view has this new function called draw raw background. And you can see I am also using the new Xcode 7 markup to give a document comment, a documentation comment, in this case, draw an atomic elements background tile. I will go ahead and put the code in that actually does the drawing.
And that's it for the Swift part. But now I want to call this Swift function from Objective-C so that instead of having the pre-rendered images being loaded, I am going to draw using this method. So I'll switch over to atomic element tile view.m, and I want to make sure that it sees the code that I just added in Swift, so I will go ahead and import the generated header. Generated header uses the same name as the product, so the elements, then you append hyphen Swift.h. And now, in this method, where I typically loaded the pre-rendered backgrounds, I'll comment that out.
And instead call 'self draw,' and you can see that the method from Swift is showing up as a native method with everything else that's there. You can even see the comment is showing up 'draw an atomic elements background tile.' So I will pass over the element. Pass it to bounding rectangle in which it will draw the rounded rectangle. Run the app. There we go. So we are having some rounded rectangles being drawn using Swift code with Objective-C [applause]. Thank you. It's the benefit of planting your friends in the audience.
[ Laughter ]
Now for the most part, it looks kind of like I expected. It's a rounded rectangle. But the rounded rectangles themselves, they don't look the way I intended. In fact, if we zoom in on it, you can see that it's rounded on the inside but it's not rounded on the outside, and that wasn't the intent. I wanted pure rounded, pure roundedness on both the inside and the outside.
So let's explore in more detail why that's happening as we take a look at Swift structures. And how Swift makes it easier and more natural to work with structs. So we have a lot of primitive structs, for example, in the Core Graphics framework. We have things like CGrects, CGpoints, CGsize, and so forth.
And what we did when we drew is I have a bounding rectangle, which is a CGrect, and I drew this bezier path. Now, the bezier path is the grey outline on the screen, drew the bezier path. Next it was put inside of the bounding rectangle, and you can see the bounding rectangle here.
Bezier paths themselves are not something that you see. They don't render until you apply a line to them, like a line stroke, and then that's what you see, that's what's rendered against the bezier path. So we draw the bezier path. We then put a stroke on it. The stroke is, for example, ten units wide, ten points wide, but it's going outside the boundaries of the bounding rectangle.
And that results in clipping. So the rounded rectangle is actually there on the outside, but this clipping that's happening with the bounding rectangle is preventing it from being there. I know some of you in the audience might think, well, if you have an issue with clipping, just turn clipping off, and then that issue is fixed and you go on to the next thing. Which is, I suppose, true. It's kind of like solving issues with Swift, though, in Xcode by just shuffling around Swift's exclamation marks until it compiles [laughter].
You can do it, but I would never call that a best practice. And when it comes to clipping, there's a performance issue with this. With every API that you call, with every task that you initiate, with every move that you make and every breath that you take - [laughter] -- you need to consider the impact on power and performance.
And it's not very performant to be constantly calculating the intersection of two rectangles and performing clipping on it. It's more performant just to set the rectangle to be the right size in the first place. That's what we are going to do. We are just going to inset the bezier path so that it doesn't clip itself. Now, to do that, we are going to use some methods -- and I do mean methods, available on CGrect, when it's being used in Swift.
Think about how these primitive types are used normally. We have things like CGrect and CGpoint, and so forth, as I said, and they might exist over here. And then separately you have got the set of global utility functions that work on them, like CGrectZero, CGrectMake, or GetWidth, or some actual functions like get the union or the intersection. There's this cognitive separation between the two. We know that this is the type, and then we have to know that these are the methods -- or sorry -- the functions that act upon it.
Well, when we work in Swift, we actually change how things like CGrect and CGpoint and CGsize come into Swift. We basically use encapsulation and take all those, those global, formerly global functions that can act on that structure and build it into the structure, which makes it much easier to have code completion, makes it more awesome to predict what the API is, because you can just do my struct dot and then you get code completion for all the methods and properties that it has.
But if we left the names like this, it wouldn't really feel native. So the names are actually remapped to have a feel to make them first-class methods on these data types. The benefit of this is the way that you work with structures in Swift, calling functions or calling methods really or accessing their properties, exactly the same way that you work with classes. It's exactly the same way that you work with enums, or enums.
It's all the same consistent style. We also get to use initializers that are the same across all the different data types. We get to have better code completion. So all in all, working with these types is much more natural in Swift because they behave as first-class data types with methods.
Okay. The other thing that we are going to do in the following demonstration is, I don't know about you, but when I work with graphics code, especially some Core Graphics stuff, one of the approaches that I would use is, I would render the code, and then I would run it, and then it builds and it copies into the simulator, then I navigate to that part in the simulator where the code is actually activated, where it's used.
Then I inspect it, and if I don't like it, I go back and I tweak the code and I run the whole thing. This is a -- this loop, this round-trip can be really expensive time-wise. Just to see what it looks like when you change a line width from three to four or you turn off rasterization or you do some sort of setting change as you try to get things to work.
So there's a better approach that doesn't have this round-tripping, and it doesn't have you often commenting out portions of your code just to experiment with how it looks. If you really want to experiment, and play around with some code, and see how it, how it works, that's what we have playgrounds for.
With playgrounds, this loop gets switched to just something like this. You just tweak the code, and you see the change right away, and if you don't like it, just change it right away. There's none of that copying to the simulator, navigate and see what it looks like. So we are going to fix up the problem with our rounded rectangles, using a playground and using the methods that are part of CGrect to get a better CGrect out of it. Let me show you.
The drawing code is over in atomic element tile view dot Swift. That's here. And this is the function that I just copied in the previous demonstration. It's the one I want to play with, so I am just going to copy it into a playground. So I've copied it, I'll hit command-N and make a new iOS playground.
Paste it in. And now I have the drawing function. No developer is an island. No drawing functions live in isolation either. Drawing functions have to draw in the context of -- well, a drawing context or graphics context. And the easiest way to get a graphics context is just to make a subclass of UIView. So that's exactly what I am going to do. I am just going to declare a subclass of UIView here. All it does is call that drawing function, and then I'll instantiate that class here.
And then I can take the image that's drawn as a result of my drawing function and just add it directly to the storyboard here. Make it larger so we can see it. And then you can experiment with it. You can kind of figure out what you need to do to make it draw the way you want it to draw. So, for example, I want to see if it looks like if it has only 120 points across. There's the result right away.
Maybe I want to see what it looks like if the line width isn't 6 but is actually 60, with a corner radius of 356. That's what we get. So you can keep experimenting with it. And then once you have the code working the way that you like, you just copy and paste it back into the actual file from which it came.
So in this case, I want to have my line width scale so that if it's the small tile view in the table view, it's got thin edges, and if it's the larger one, it has proportionately thicker edges. So I am going to make it related to the width of the background rectangle. So background rectangle.width divided by, in this case, 36. And I will do the same thing for the corner radius. Backgroundrectangle.width divided by something smaller, like 16.
So now it's starting to look like I want it to look, but I still have the problem where I am clipping, so I am not able to see the full extent of the stroke nor the rounded rectangles. So, to do that, on background rectangle, I will use the method called rect by insetting, and I will inset it to be half the line width. So that's line width divided by 2 and same thing, line width divided by 2.
And there, now I have a perfectly rounded rectangle exactly like I expected to have in the first place. I will copy this code from here back to the extension, replacing the file -- sorry -- replacing the method that's there. Rerun the app with command-R. And that's exactly what I wanted to have, rounded rectangles. Yeah, please. Yeah.
[ Applause ]
Yeah, pretty much at WWDC, if ever you feel like applauding, just applaud. Nobody is going to say stop that. It's all good [applause]. See, thank you. So the app is looking okay. I've got these rounded rectangles. That's great, that's what I want. But the next thing I want to do is make this app feel more like a current app.
Now, I don't know about you, but have you ever gone to your client after especially WWDC and said, hey, there's this new version of iOS coming out, for example, iOS 9 is coming out. I think we should just not support iOS 8 anymore, and we are only going to support the new upcoming operating system [applause]. Yeah. Have you done that [applause]? Yeah, and then you are looking for another client [laughter].
Because they all want you to support these old versions, iOS 7, 8, and now 9 is probably going to be on slate for this fall for many of you. So to do that, we have this new availability feature introduced in Swift 2.0. So as long as you are writing some code in Swift 2.0, we have a great way to check to see what SDK you are on and if you can actually use this feature or not.
Now, last year at WWDC, we introduced some new view controller presentation APIs that allow for popover presentations on iPhone. So what I want to do in my elements app is when I am on any device that supports it -- for example, an iOS 8 device or newer -- I want to use a popover presentation.
But when I am on iOS 7, I want to continue using the navigation push presentation style. All right. So how do we do this? Well, this is the classic way of checking to see if you support an API. We do whatever the type is, we check and see if it responds to selector. And if so, then we just use the selector. If not, then we do something else.
This is the way that we do it in Swift, as of Swift 2.0. We have this hashtag available, and you specify the SDK that you want, so iOS 8.3 in this case, and then if so, we use a popover, and if not, we use the other approach. And the benefit of this style is that you are not waiting to runtime to see if it actually works.
At compilation time, the compiler can tell you, yes, this will work, or it won't work depending on your deployment target. So if I am deploying to iOS 7, it's able to tell me, popover isn't available. But because I've properly guarded it by giving it another path, it's able to compile.
In this case, if I hadn't put that guard in, if I hadn't said, here's the check and I tried to compile this and it was trying to deploy back to iOS 7, it would say, popover presentation controller is not available. It's only available on 8 or newer. And then it gives you some fixes.
But instead of just showing you some more slides, let me actually show it to you in code. Alright. So first of all, the code that presents the second view controller is kept over in elementsviewcontroller.m. It's just an implementation of table view accessory but in tapped for row with index path.
And because I want to use availability checking, I have to have this implemented in Swift, not Objective-C. So I am just going to comment it out. And then I am going to create a class extension much like the original demo from 20 minutes or so ago, so that I can extend elements view controller and have some of its functionality implemented in Swift. So I will hit command-N to make a new file, it's a Swift file. It's elements view controller dot Swift. Then I will write a class extension on it here.
Extension, elements view controller, and inside of there I will just take the equivalent Swift function. So this is essentially the same code that I already had there in Swift. There's nothing new at this point. It's just the same thing now implemented in Swift, but this lets me bring up the availability checking. This particular application is targeting iOS 7.1, and I want to use this new popover presentation controller technique in it, so I am going to comment out this line and put in code that tries to directly call the popover presentation.
So I am getting some errors. One of the errors it's complaining about is that I don't actually conform to the delegate protocol that I need to for popovers. Well, just on the aside, it's okay to take an extension in Swift and use it to add conformance to additional protocols. So I'll just add UI popover presentation controller delegate there.
That clears up that error, but I still have the problem about trying to use an API that doesn't exist on iOS 7.1, which is exactly what I wanted -- well, in the demonstration anyway. So I have a couple of ways to fix it. I have it pre-baked for you here.
To say if I am running on -- in this case, iOS 8.3 -- go ahead and display as a popover. Otherwise, use navigation controller. Now, if I run it and I tap on the i -- let's see here. We get a popover. It's been 15 years and we are still getting Carbon at WWDC [applause]. Alright.
For more information about availability checking, please check out Thursday's session called Swift in Practice. There's a whole big talk about it. Next up, the previous demonstrations were about improving and modernizing the look of the application. You know, we changed the drawing to get rid of the gloss. We now have a popover and so forth. But let's actually add some functionality to it. So we are going to implement live searching. And to do that, we are going to take a look at Swift's filter method, which is now available on all collections, including sets and arrays.
Filtering can be used to drive a live search function where only atomic elements that match the search string are displayed in the table view. A standard setup for this would be, well, something like this. We've got a TableView. The TableView is paired with a view controller that acts as its data source. That view controller has an array of content.
And when I type something into the search field, I have a delegate method that's called, on the view controller called searchbar:textDidChange. I figure out the string that's been passed. It's just an argument. I filter the array of content. And then I tell the TableView to update with this now filtered array of content. Search bar:textDidChange looks something like this.
In actuality, it looks exactly like this because that's the code that I am using. Now I want to draw your attention to the highlighted section, which is a closure. So I am running a filter, and in the filter, it's going to be applied to every one of the items in the array, and that item in the array, I have a placeholder, which the dollar sign zero, I am getting its name, and then I'm asking if it has the prefix, and whatever the search string is.
It kind of works like this. Here I have my original array up at the top. And I have a closure for filter. And it just cycles through, passing in the element each time, and if it has the name that begins with the letter, in this case N, it's passed through to the returning filtered array.
And if not, it's not. Let me show you this in code. But just to speed things along, I've already gone and put a search bar up at the top. But I haven't rigged it up yet, so if I try to do anything inside of it, it just doesn't work because I haven't put the delegate method in yet. The delegate method is this. We are saying, if the search text is empty, show all the atomic elements. That's here. If it's not empty, I want to do a filter, and that part is left to be done. Let's do it right now.
So I am going to bring up a filter. And if it's the first time that you've seen closures, great, welcome to closures. Let me show you a little bit about it. And if not, maybe it's a bit of a review. When Xcode shows you these blue kind of tokenized backgrounds that are all singular items, you can double-click them, and they expand out, and you just fill in the blanks.
So I'll take this closure, double-click it, it expands out. And I know that I have an array of atomic elements. The data type is called atomic element. So the parameter that's being passed in is one atomic element. So I am just going to specify that in this closure, the parameters that are being passed in is one atomic element -- I am going to give it a local name, so atomic element is its local name, and its data type is atomic element.
Inside the closure, I need to check to see if I want to include it in the filtered results or not because I promised to give a return value that's a Boolean. So I'll say my return value is whether the atomic element, name, has prefix, and then the search text that's passed in from the search bar.
Like that. Now, when I run it, we'll do a search for everything that begins with letter N. There we go. We get it filtered using Swift's filter. But there's two things I want to show you. One, there's really two different ways to take a look at closures. There's this expanded syntax.
There's also a condensed syntax. Let me show you the condensed syntax. Swift has a very strong type inference system. We can infer a lot of things based on context and the kind of data types that you are using. So for example, if you have a single line in a closure, it's assumed that it's going to return a value, so I don't have to put the return in there.
And in fact, has prefix gives a return type of Boolean so Swift can infer that the return type for this closure is Boolean, so I don't really need to have this here, which means I don't have to have that there either, and since atomic elements is an array of atomic elements, I don't have to specify that an atomic element is being passed in, so I can get rid of that.
And since I don't have any parameters, I don't have to separate the parameters from the code, so I don't need the in keyword, which means I can get rid of that and just tidy up the spacing a bit, and I end up getting something that looks like this.
And because the last argument to the filter method is a closure in itself, we can make it into a trailing closure and just get rid of the parentheses, so we get that looking more like this. Now the only trouble is that there's no symbol declared called atomic element because I removed it. But I'm passing in one item into this filter closure every time, and I can reference that argument like that. That's exactly the same code. If you were missing Perl, well, now we have this.
[ Applause ]
So now we can do a check. Let's just put in a letter N, and great. Now I have the elements, but they are not actually sorting in the right direction. Actually, they are not sorting at all. So, let's just quickly go ahead and add a sort. I am just going to chain it to the end of this existing closure.
Dot sort. Now, in this case, I could double-click this blue token, have it expand out, and there's many ways we can compare strings, like case and sensitive compare, localized compare, so forth and so on. But because I know that in a sort I am given one atomic element, and then I'm given the other atomic element, and I only have to specify which one comes before the other, or if the first one comes before the other, I can just write out a closure myself, first of all recognizing that I am going to have two atomic elements passed in.
So I have the first one, I am going to check its name, and I have the second one. I am going to check its name. That in itself is not a comparison. However, in Swift, we've overloaded many of the standard operators, like greater than and less than, so they actually work on types that you might not expect them to work on, like strings. That's a string comparison. I'll run it again.
Search on letter N, and now it's being properly sorted. It's filtered and sorted all in just one line. I can check again. Let's search on S. We have some really important elements, like Swiftonium [laughter]. Very important. Okay. Let's come back. So in this section, we implemented filtering of our table view by using Swift's filter method.
And then we sorted it using sort and got to see an overloaded operator, that being the less than/greater than. The next feature and final feature that I want to add to this app is simply one that lets me select multiple rows and then it adds them up to give me the sum of the atomic weights for the selected elements. It looks kinds of like this. We start off with the table view. The nav bar at the top is saying select two or more items. You select two or more items, and then the nav bar at the top specifies the sum of their weights.
To do this, I start off with my content array. That's all the atomic elements that I have, those are all the ones that are displayed in the table view. But what I want is just the selected atomic elements. So to get that, I can't go to the table view and ask the table view for the array of selected items. I can only ask it for the index paths for selected items.
So the table view will give me an array of the index paths for the selected items. I can query those index paths to get the row. I can correlate that with the backing array, the content. And from that, produce the selected elements. Basically, it's this. We make a new array. We loop through the index paths. And then we go back to content and we pull out the corresponding atomic element for the current index paths' row. If you've been accustomed to writing code like this, you can do the same thing in Swift with this.
It's the map function. The important part is still there, it's the part in orange. Just all the the extra infrastructure that's around it has been removed. Next, once I have an array of all the selected items, I want to add up their atomic weights. So to do that, I might traditionally use a foreign loop where I first declare a variable that's set to zero, then I just iterate through the selected objects and just add to that, in this case, D, their weight.
If you are accustomed to doing this kind of thing with a for-in loop, you can do the same thing in Swift, with a reduce, where we set the initial value as zero, and then we just patch in a closure that takes the initial value, that's zero, and then appends to it the next item being passed in, adds them all up.
Let me just show you the code, though I will give you a preview that we can do the whole thing with just a single line of code like this. One of the graphic designers was asking me when he saw the presentation, can you make that fit on one line? It's like, not really, no, I don't think you could see it then. Let's switch over to the demo computer.
Alright. So here's the function that does all the work. I start off by ensuring that I have more than two items selected. So you can see I am using a where clause on an if to say that as long as the selected items count as greater or equal to two, continue executing this code.
Then I use map so I can actually get the objects that are selected by the table view, not the index paths. And then use reduce to add it all up. And then in the end just passed it through a number formatter and stick it up in the title. If you wanted to see the same thing on a single line, it would look like that. When I run the program, it adds them all up and puts them up top. That's showing map and reduce to make it easier to work with items that back a table view.
Alright. In summary, I'm hoping you will see that there's a lot of advantages to incorporating Swift, even with your existing Objective-C projects. It's not hard to do, and there's a lot of benefits to it. You don't have to throw away any existing code, and you get to use these modern and powerful techniques, like reducing and maps and these powerful structs, so forth.
For more information, come see us in the labs, check out our documentation, check out the Developer Forums, or send Stefan an email. He loves to get mail. You can just tell him the conference is going well, that would be fine. And with that, I'd like to thank you, and have a great conference.
[ Applause ]