2014s407 Swift Interoperability In Depth

 [ Silence ]  [ Applause ]  >> Welcome.

  So I'm Doug Gregor.

  I'm an engineer on the Swift Compiler Team, and we're here  to talk about Swift Interoperabiity.

  We're going to talk about a couple of different things here.

  So, of course, Swift is a new language for Cocoa  and Cocoa Touch development.

  Now, Cocoa's not written in Swift.

  It's written in Objective-C, a language you've been using  for years and that all of your apps are written in.

  So, interoperability between these two very different  programming languages is absolutely critical.

  So we're going to talk about how that interoperating works  at the language level.

  We're going to hit a number of different topics today.

  We're going to talk about working with Cocoa,  seeing how the Cocoa APIs or Objective-C APIs look and feel  in Swift and how to work with them, as well as working  with some more Swift concepts like dealing with AnyObject  and doing dynamic checks on your types.

  Then we're going to talk about bridging  of the core Cocoa datatypes and NSArray, NSDictionary,  NSString into their Swift-native equivalents.

  Then we'll move on to subclassing,  so writing Swift classes that subclass from Objective-C  and how they're mapped back into Objective-C  so that you can use these two languages together.

  And, finally, we're going to talk about Core Foundation  and Core Graphics, and this general notion  of CF Interoperability within the Swift programming language.

  Let's get started talking about working with Cocoa.

  So Swift provides seamless access to Objective-C APIs  through the Objective-C Module System we introduced last year.

  So you can pull your Objective-C APIs whether they be from Cocoa  or your own into Swift and use them.

  And then Swift maps those Objective-C APIs  into the Swift syntax.

  This covers both the objective parts of Objective-C -  the classes, protocols, methods, and so on,  as well as the lower level C things like functions,  enumerations, structs, pointers.

  So you have access to all of your Objective-C APIs.

  Now when you look at one of these Objective-C APIs in Swift,  it's going to be different from Objective-C.

  There are inherent syntactic differences  between these two language, of course.

  Swift also has some modern features that we use  when expressing those Objective-C APIs in Swift  that will make it look a little bit different,  as well as the bridging of core Cocoa types  that I mentioned earlier.

  Now, despite all of these differences that you see  when looking at the APIs, it's still Cocoa,  it's still Cocoa Touch.

  And the same conventions and idioms still apply,  so what you know of Cocoa works in Swift.

  It's just a different programming language  for the same great platform, the same great frameworks.

  So we're going to walk through part of something we know  and love, and that's the UIDocument class.

  Here's a tiny slice of it in Objective-C.

  We're going to walk through how and why that maps into Swift.

  First thing, something simple, a property: fileModificationDate.

  It is an NSDate!.

  This comes into Swift as a property.

  It's the var keyword.

  The NSDate class, of course just comes into Swift.

  Nothing interesting there except  for this little exclamation point that you may have noticed.

  Now that exclamation point is an Implicitly Unwrapped Optional.

  What does that mean?  Well, let's look at Swift in Objective-C.

  They're different languages with some different ideas in them.

  So in Swift, when you have a value  of class type, so I have an NSDate!  That can never be nil.

  So a very strong constraint.

  And it makes life a bit simpler when you know  that thing is not nil.

  Now, when you want to deal with nil, you have an NSDate!  that could be nil, you use an optional type,  and optional types are covered extensively in the  "Intermediate Swift" talk.

  We're going to cover them a little bit more now.

  That's the Swift side of things.

  What about Objective-C?  Well, it does not have the notion of a never  "never-nil" pointer like we have in Swift.

  And so we have a little impedance mismatch here.

  The Objective-C APIs don't have the notion of this is not nil,  but we need to bring them into Swift.

  And so, we have the implicitly unwrapped optional  with the exclamation point here.

  And this gives us a nice balance.

  It means that we can express the notion of nil  and you can test it against nil to do those checks.

  However, you can also just directly access properties  or directly call a method on it, or you can convert it  down to NSNil, and we'll unwrap  that optional object automatically  for you doing the checking.

  So it's a fairly syntactically lightweight way of dealing  with nil in a language where nil is a much more explicit entity  like in Swift.

  Let's look at another property.

  So here we have the fileType property that's in NSString.

  This is going to come into Swift as a native Swift string.

  Now, again, we have the implicitly unwrapped optional  here so that nil can be passed  through since NSString doesn't have a notion of nil inside it.

  And there's a number of Objective-C types  that get mapped slightly differently into Swift.

  So there's some very, very fundamental types like BOOL  and NSInteger that map into the Bool and Int types within Swift.

  So we're working with all the Swift types.

  There's id and Class,  which we're very familiar with in Objective-C.

  These map over to AnyObject!  and AnyClass!, something we're going to talk  about in a couple of minutes.

  And we also have the core Cocoa types that are bridged,  like NSString and NSArray,  mapping to their Swift-native equivalents.

  Again, we'll talk about that later in this talk.

  Let's take a look at methods.

  There's an Objective-C method fileNameExtensionForType,  saveOperation.

  It comes into Swift here as, again, a method.

  Now, one important thing to know here is that all  of the selector pieces from the Objective-C method are here  in the method's signature in Swift.

  The first selector piece has become the so-called base name  of the method.

  The second selector piece, SaveOperation,  has become a label on the second argument.

  A really important thing here is that these labels  and their order are enforced at the call site.

  So, you must call it as fileNameExtensionForType,  saveOperation, just like you do in Cocoa  with the exact same ordering, so to preserve  that nice readability from Cocoa that we all know and love.

  Now the other thing to note here is the consistency here  on the Swift side.

  All of the names and the colons and the parentheses  and the commas are in exactly the same places  in the declaration of the method in the middle  and in the call site of the method at the bottom.

  So the kind of consistency we like out  of building a new language.

  Let's look at a little bit more complicated method here  where we have some blocks going on,  some more interesting things, and map that into Swift.

  And here there are two different things I want to talk about.

  The first thing I want to talk about is the naming  of these argument labels and the internal parameter names.

  So here in Objective-C you always have a selector piece,  goes before the colon.

  And then you have the name of the - at the internal parameter  that you use when you're defining the method  in your .

m file.

  In Objective-C you always have to write both of these,  of course, and many times they're the same.

  So you have some redundancy.

  We do a little bit of syntax optimization here in Swift,  so you just write the name once.

  It serves both as the label and the internal name.

  If you want those names to be different, fine,  we can handle that, too.

  You just write the two names next to each other.

  The first one is the label because that's what's important  for the caller to use.

  And then the second one is the internal name that you're going  to use within the implementation of your method.

  The next thing I want to point out here is the Block.

  So here we have a method that takes a Block,  and Blocks in Objective-C get mapped into Closures in Swift.

  You see, again, this is an implicitly unwrapped optional  so that you can pass a nil Block in here.

  Now the really great thing  about getting Objective-C Blocks mapped into Swift Closures is  that we get all of the great closure syntax that is provided  by Swift, including trailing closures  when your block is the last parameter.

  So all of your block-based APIs that you've written,  all the ones from Cocoa, when they're following the convention  of putting the block last get this nice trailing closure  syntax in Swift.

  Let's talk a little bit about Initializers.

  So in Objective-C we have init methods  and init methods have a lot of conventions built around them.

  They start with the word "init.

"  They should be returning instance type,  although that's a fairly new invention.

  So sometimes they're still returning ID.

  And when you're implementing these things,  you have a lot of requirements.

  You need to call "super init.

"  You need to reassign "self.

"  You need to check "self," you need to return "self.

"  So all of this screams, we need something formalized  in the language.

  And so Swift has this notion of Initializers.

  And we import Objective-C init methods as Swift Initializers.

  How do we get from the top Objective-C code  to the Swift code in the bottom?  Well, we find the init, so we match the init name  and the camel-case string here.

  We actually look forward a little bit to see  if it's really initWith because that's extremely common.

  And then we take the rest of that Selector,  and lowercase the first character in it,  and turn that into an argument label for the Swift Initializer.

  Now, why do we do this?  Well, let's look at how we build objects  in Objective-C versus in Swift.

  So in Objective-C you do an alloc on your class  and then you immediately send it an init message  to initialize the Object.

  These two steps are almost never separated.

  Now in Swift we have our Initializers  and we use this Unified Object Construction syntax  where we write the name of the Class  and then we pass arguments directly to the Initializer.

  And notice here, we're using the argument label of fileURL to say  which Initializer we're actually using  and then give it the argument.

  And, of course, we folded the alloc and the init together  in this one nice syntax that also happens to work  for all other types of in Swift whether they be structs  or enums.

  Okay.

 So let's talk about factory methods  because this is the other way  that we build Objects in Objective-C.

  So here we have something from UIColor.

  I've stepped away from UIDocument.

  And they have colorWithRed blue green alpha.

  And, of course, we can go and construct  that by calling UIColor colorWithRed green blue alpha.

  All of this can be directly imported in Swift.

  It would just be a class method, colorwithRed,  and green blue alpha as argument labels,  and we could just call it on the class.

  This would be fine.

  However, we don't really love  that they're two completely different kinds  of initialization.

  So we recognize the common patterns  in how factory methods are described in Objective-C,  and bring them in as Swift Initializers.

  And the really great thing here is we get  that common Initialization syntax for all  of these Objective-C APIs.

  You don't have to think, "Is there an init method for this?  Or is there a class method for this?"  It's there as an Initializer.

  Do you like that?  [ Applause ]  Let's go a little bit down the stack  and let's talk about Enums.

  So here's the UIDocumentSaveOperation enum  as defined in Objective-C.

  And if we look at this we see a whole lot of redundancy.

  This UIDocumentSave prefix is used for the enum  and for both of its enum values.

  Why is this?  Well, this is C.

  The enum values in C are in a global namespace.

  We can't call these enum values just ForCreating  and ForOverwriting because that's going to stomp  on some other completely different enumeration somewhere  else in the system and cause havoc.

  So we do this common prefix by convention.

  It helps code completion find the right thing.

  But when we're talking about Swift, it's also a great cue  for us that we can do better.

  And so we can import this NS-ENUM  as a Swift enum chopping off all of those common prefixes  to get us nice short names for the cases.

  Now the reason we can do this is because the enum cases  in Swift are scoped within the enum type itself.

  How does this play out in actual code?  Well, okay, if we call fileNameExtensionForType  saveOperation, we can refer to, say ForCreating,  with its fully dotted name - class name.

enum,  the same dotted syntax we use  for a number of anything in Swift.

  But Swift has type inference.

  We know that this method takes the UIDocumentSaveOperation,  so there's absolutely no reason to write that.

  You can just pass .

ForCreating  and we will infer the enum type from that.

  I'd also like to talk about NSError.

  So this is our pattern in Cocoa for dealing with errors.

  And so there are many methods throughout Cocoa  and throughout your own apps that take an NSError ,  and that's a C pointer to an NSError object.

  We bring this in with a special type in Swift.

  In fact, if you type alias  for a much longer type name call NSErrorPointer.

  We're going  to see NSErrorPointer twice in this talk.

  For now we're going to talk about how to use it  when we're calling into the API.

  And it's not actually all that much different from Objective-C.

  So, if we bring this up, we declare a local variable.

  It's called error.

  It's a type NSError optional.

  And we pass its address in when we're calling  contentsForType error.

  And so in this code we check whether we're getting back some  contents from this.

  Then we can deal with those contents.

  If we fail to find any contents well, we probably have an error.

  So we can go unwrap that optional error  and present it to the user.

  And if we fall through the else here, now we're in trouble  because something failed and we have nothing we can do about it.

  We hope that doesn't happen.

  But this is the pattern you'll be using when dealing  with NSError in Swift.

  If you truly don't care about the error,  you can also pass nil.

  So we've walked through a lot of little pieces  of the Objective-C mapping into Swift.

  And there are a lot of rules that we've talked  about that you're certainly not going to remember.

  That's perfectly fine.

  Xcode has your back here.

  Use the tools to help you.

  So if you're in Xcode, you're in some Swift Code,  you can Command+Click on a class name.

  And we'll show you the Swift projection  of the underlying Objective-C class.

  So take your favorite Cocoa class and look  at how it maps into Swift.

  Get a feel for the language, an intuitive feel  for how these languages work together and you'll get  into Swift really fast.

  And the great thing is, all of these tools,  all the rules I've talked about, apply equally  to any Objective-C API when it comes into Swift.

  It doesn't matter if it's Cocoa.

  It doesn't matter if it's your own API.

  The same rules apply, and you can view your own Objective-C  APIs in Swift to get to know them better.

  Now this works best when you're following  "modern" Objective-C practices.

  So these are using features like Properties, instancetype,  marking your enumerations with NS-ENUM or NS-OPTIONS  to describe more semantic information  about what these enums actually mean in C.

  We've also introduced NS-DESIGNATED-INITIALIZER this  year to mark your designated Initializers  and formalize a Designated Initializer pattern,  both in Objective-C through additional warnings,  and as the initialization model for Swift.

  So, of course, we want you to follow all of these  "modern" Objective-C practices but we don't want you  to have to go it alone.

  And so this year we introduced the Objective-C Modernizer  that helps find these cases in your code  where we could possibly modernize them to work  with all these "modern" Objective-C features  and give you a better projection into Swift.

  And that Modernizer is discussed in the "What's New  in LLVM" talk earlier today.

  Highly recommend you catch it on video if you missed it.

  With that, let's talk about id.

  What is id in Objective-C?  It's kind of a placeholder in some sense.

  It means, I have a value.

  I know it's an object but I don't know  or I don't care what the static type of that object is.

  It's going to vary at runtime most likely.

  And there's a couple of core operations  that you can perform on id.

  You can do upcasting on it.

  So, if I have this variable object of type id,  I can put an NSURL into it.

  Later I can go reassign it and I can put a UIView on it.

  That's perfectly fine.

  They can both be upcasted essentially to id.

  They're both objects.

  I can do Message sends to id just directly  by doing a Message Send.

  I can subscript an id if I really feel like.

  In Swift, any object is the equivalent to id.

  And it provides these same core operations - Upcasting,  Message Sends, Subscripting -  that you can do on id in Objective-C.

  Now one of the things we know from using id in Objective-C is  that you sometimes have to be a little bit careful  because if you send a message to an object  that doesn't have a corresponding method,  you're going to get a runtime failure  that this is an unrecognized selector.

  Now in Objective-C we have an answer for this.

  Using this respondsToSelector idiom.

  Do an if.

 Check whether it respondsToSelector, then do it.

  In Swift we like to do a little bit better.

  So let's take the same call  and let's do a removeFromSuperview() call  on this object.

  And the thing to note here is that removeFromSuperview()  on an object of unknown type,  it may be there, it may not be there.

  Well, how do we deal with this notion in Swift?  We use an "optional" that says there may be a value there;  there may not be.

  And so the reference to removeFromSuperview()  on object is in effect, optional.

  That means we can use the optional Chaining Operator here  to -  [ Applause ]  What we're doing, of course,  is we're folding the respondsToSelector check in,  so we do the reference, go look for RemoveFromSuperview.

  If it's there, go on, call it.

  If it's not there, stop evaluating this expression.

  We're done.

  Now, something that id does  that AnyObject does not do is implicitly downcast.

  So I have Object, which is a type AnyObject, and I'm trying  to assign it into a UIView.

  This is going to produce a compiler error  because this is a unsafe downcast.

  How do we deal with this?  Well, there's really two cases here that need  that you need to think about.

  One case is, I know it's a UIView but for some reason  that strong type information got lost when going  through some API somewhere.

  If I know for sure it's a UIView,  I can use the cast operator, "as", to say,  "Treat this object as a UIView.

"  We're going to do these kind of class check at runtime  to make sure that's absolutely true.

  But the type system will believe you at this point.

  Now if you don't know whether this object is a UIView  or not, you can use the "as?"  to perform a conditional downcast.

  [ Applause ]  Think you guys have figured it out?  But just to be sure, this is doing "is kind of" class check.

  And it's wrapping the result in an optional UIView.

  It's nil if the check failed.

  It has the UIView if the check succeeded.

  We can do an if-let here  to completely do this entire thing safely,  and view in here is the UIView we were looking for.

  So let's take a little bit of detour and talk  about tiny bit of protocols.

  Here's an Objective-C Protocol for a UITableViewDataSource  and its Swift equivalent.

  Not a whole lot new here.

  But there are two things that I do I want to point out.

  The first thing I want to point out is optional and required.

  So in Objective-C optional  and required are essentially modes in the protocol.

  You say @optional, and everything  that follows is optional up until the point  where you hit an @required and then everything is required.

  And we're not totally thrilled with this decision now.

  And the basic reason is that you can't just look  at one single declaration in the protocol  and know whether it's optional or required.

  You have to go scan up your protocol to find it.

  And so we did something a little bit different in Swift  in that requirements in protocol are required  by default in Swift.

  If you want to make them optional, then tag them  with the optional attribute to make them optional.

  The other thing I want to point out here.

  We're doing a little bit of Protocol Inheritance  and we're inheriting from NSObjectProtocol.

  So in Objective-C we will have NSObject the class  and NSObject the protocol.

  And they have the same name so we have to add the "the class"  or "the protocol" at the end when we talk about it.

  The language keeps these in syntactically distinct points  so the language isn't confused.

  But in Swift we wanted to bring all these things  into the same namespace because that's far more convenient  for the general case.

  And so we needed to rename something.

  And essentially when there's a conflict between a class name  and a protocol name, we'll append protocol  to the name of the protocol.

  Why did we do this?  Well, let's take a look at another use of id that we see  in Objective-C, and that's protocol-qualified id.

  So this dataSource here is an object of some unknown type.

  But we know that the type conforms  to the UITableViewDataSource protocol.

  We describe that a little bit more directly  in the Swift language by just saying the dataSource is  a UITableViewDataSource!.

  That's it.

  Now some of you here noticed with protocol-qualified id,  you can have many different protocols if you want.

  We can use the protocol keyword with angle brackets  to describe more than one protocol.

  Now, one of the things we do  with Protocol Conformance is we have an object of unknown type  and we want to determine, does it conform to the protocol?  This is the "conforms to protocol check"  in the Objective-C runtime.

  In Swift, we do this same thing  with the conditional downcast operator.

  So we can just ask, is my object the UITableViewDataSource"  conforms to protocol check, happens in the runtime,  captures the results in an optional.

  Here we can go easily do that, call one of the methods  and compute the number of rows in the first section  of this TableViewDataSource.

  Let's make our example a little bit more interesting.

  Let's compute the number of rows in the last section.

  So here we need to compute the number of sections that exist  in the TableView, subtract one off of it, and then we can ask  for the number of rows in that section.

  Now there's a problem with this code.

  And the problem is the number of sections in TableView,  as you might remember, is an optional method.

  It might not be there at runtime.

  So we're going to need to compile error out of this  because we need to deal with the optionality  of this protocol method.

  And we deal with this the same way we deal  with optionality everywhere else in the Swift language,  using the mechanisms we have.

  So here we're going to use the chaining "?"  operator.

 We're checking, does my DataSource have a number  of sections in TableView method?  If so, call it, given the TableView,  and then we capture the result in numSections  so we can compute the last section number  and get the number of rows  in the last section of our TableView.

  That's about all we're going to talk about with Protocols here.

  If you're interested in Protocols and some  of the amazing things they can do,  there's an "Advanced Swift" talk tomorrow morning.

  It goes into more depth on those and their interaction  with the generic system.

  So, wrapping up here, AnyObject is Swift's equivalent to id.

  The functionality is similar.

  The ideas are similar and the uses are similar.

  However, it's more safe by default.

  Now we didn't talk about it, but there's also AnyClass,  which is Swift's equivalent to class and has most  of the same behaviors.

  Now the other thing that we've seen is how Optionals are used  throughout the language to represent dynamic checks.

  We've taken "is kind of" class checks,  conforms to protocol checks, responds to selector checks,  and folded them all into the notion of Optionals  within the language with their optimized syntax  to make them easy to use and easy to do the right thing.

  With that, let's switch gears a little bit and talk  about Bridging of Cocoa data types.

  Now first, let's talk a little bit about the native Strings,  Arrays, and the Dictionaries within Swift.

  The goal of Swift is to have one set  of general-purpose native value types that you use  for nearly everything.

  These need to be safe by default.

  This means bounds restricting for arrays,  automatic memory management, and so on.

  They need to have predictable performance so that you can look  at code and have a sense of how it's going to behave  with no surprises, how it's going to perform.

  And, of course, we want arrays and dictionaries  to be collections and they need to be strongly typed collections  that work with any type.

  We can't limit them just to objects  because sometimes you need an array of strings or ints.

  And we don't have a seed to fall back to for the cases  where the other tools don't work.

  This is it.

  Now, to support having this one notion of one set  of general purpose native value types, we're going to bridge  from Cocoa's NSString, NSArray, NSDictionary,  into the Swift-native equivalents.

  So let's first talk a little bit  about the native string type itself.

  So, String is an efficient, Unicode-compliant string type.

  Core string type of Swift has Unicode built in through  and through so it makes it easy to work with.

  We provide flexible  and efficient high-level APIs to work with strings.

  You can easily do concatenation, searches,  prefix matches, sub-strings.

  And the strings provide value semantics,  which makes them easier to work with.

  And value semantics is generally a fairly simple notion of,  you know, if I have two variables of string type,  modifying one of them doesn't affect the other one, all right.

  This is very nice for a fundamental data type.

  Now, of course, you can also think of strings as a unit,  but you can also think of them as being composed  of characters which, in fact, they are.

  And so we can go walk over a string and using the for loop,  and get all of the characters out of the string.

  And you get the answer that you would expect.

  There are five characters here even though there's no emoji  at the end.

  So I want to talk a little bit about characters and code points  because the character that you're getting  out of here is a full Unicode character.

  It's not a UTF-8 code point or UTF-16 code point  that you have to deal with.

  It is a Unicode character.

  And now, one of the challenges with the Unicode characters is,  you really can't encode them efficiently in a way  that treats a string as just an array of characters.

  It would be too large.

  And so what you generally see is  that a string is encoded as, say, UTF-8 or UTF-16.

  But working with those UTF-8 or UTF-16 code points,  that requires Unicode expertise to get right all the time.

  And so we made a really interesting decision here.

  We decided not to provide the super low-level operations  like length and characterAtIndex to let you poke directly  at the UTF-16 or UTF-8 code points, or whatever is stored  in the string because doing so causes big problems.

  Instead we want you to use the high-level APIs  and let the library do the hard work of dealing  with all the intricacies of Unicode.

  Of course, there's still common operations you want to use.

  You may want to count the number of characters in a string,  so there's this countElements algorithm.

  It works on any sequence and allows you to, well,  just count the number of characters in a string,  and this produces the right answer,  which is there are five characters in this string.

  Some of you will want to work with code points, right.

  You may be Unicode experts and that's wonderful.

  You can get access to the code points.

  There's a property UTF-16 that gives you a lazily computed view  on the string producing the UTF-16 code points  in that string.

  And we can go walk over the 16-bit unsigned integer  code points.

  We can print out the number of code points here and, of course,  you'll get the answer "6"  because there are six UTF-code points in this string.

  The last thing I want to talk  about with strings is the relationship  between string and NSSring.

  So NSString has a wealth  of really great text processing APIs  that you've probably been using for years.

  So we've made all those Foundation APIs directly  available on the string type, so the APIs you know  and love are there, and you can use them.

  Now in doing so, we've made them a little bit more Swift.

  We've tightened up the type signatures so that  if you're going to split a string into its components,  well, you're getting it back an array of strings rather  than just an array of somethings.

  Now you may have developed your own categories on NSString  with additional functionality that you use  within your own applications.

  You can get to those with a simple Cast.

  So you can take a Swift string, turn it into an NSString,  so this is just a conversion,  and then call your NSStringMethod.

  If you find yourselves doing this a lot,  feel free to just go ahead  and extend the underlying string type.

  Add your StringMethod, make it a little more Swift  with strong type signatures, closures if you'd like.

  But this should help you feel at home in Swift fairly quickly  and use the String type.

  Now let's move from String to a container.

  Let's talk about Arrays.

  So here we have toolbar items that is in an NSArray.

  That's going to come into Swift as an array of AnyObject.

  Now these two types are fairly similar.

  You can iterate over them and what you're going to get  out of it are values of type AnyObject.

  They're objects but you don't know what kind of object it is.

  You can subscript into them and, of course,  you will get an AnyObject.

  Now, in Swift, you tend to deal in typed arrays more often.

  And so there are some other operations  that the core language needs to provide  for you to make this clean.

  So maybe I'm composing my toolbar items into a Swift array  and that Swift array is going to contain UIBarButtonItems.

  That's what actually goes into the toolbar items property.

  I can work with that Swift Array and then I can assign it  into the AnyObject array.

  So this is essentially doing a safe upcast of any array  of ToolbarItems to any array of AnyObjects.

  It also happens to be calling into Objective-C,  which we'll get to in a minute.

  Now we also see the flip side of this where we want to, say,  iterate over all the ToolbarItems  in this particular view controller and here we're going  to get AnyObject values and, we talked about cast earlier,  so we can downcast each of them.

  This is fine but it's a little bit on the tedious side.

  And so we have specialized syntax here  to downcast an entire array at a time doing the  "is kind of" class checks necessary  to make this safe lazily behind the scenes.

  And then you can walk over all with them.

  [ Applause ]  Now, we've seen NSArrays in the Objective-C side,  Swift arrays on the Swift side.

  Let's take a little bit of a peek under the hood  at how this actually works because you're going  to be writing a lot of Swift code that interacts with a lot  of Objective-C code and we want this to perform well.

  So there's a Swift array.

  And the Swift array actually has two internal representations.

  Its first representation is probably what you'd expect  out of Swift.

  It's a native representation.

  It has a length, which is the number of elements in the array.

  It has a capacity that's used  so we can algorithmically efficiently add things  to the array.

  And then it has the buffer of elements that are in the array.

  And, of course, those buffered elements, whatever kind  of array it is, that's how much storage they take.

  If we have an array of 32-bit integers,  each element takes 32 bits of storage.

  There's not extra boxing going on here,  no extra performance loss.

  It's just a native buffer.

  Now we also have this second representation  and we do a couple of pointer tricks so that we can fit it  into just a tiny amount of memory.

  And that's the Cocoa representation.

  So any Swift array can actually be an NSArray underneath  as representation.

  And all the operations on an array handle both  of these representations.

  So if I subscript my array and it happens to be an NSArray,  we'll use object and index behind the scenes.

  So you get the result that you want.

  If we do append to such an array, well,  we can flip the representation quickly and give you  that efficient append operation.

  So given these two representations,  we can talk about the notion of bridging, of converting  between an NSArray, as Objective-C would see,  and a Swift array that you use within Swift.

  There's two directions here:  going to Objective-C and coming back.

  So first, let's talk about coming back.

  So we have an Objective-C method.

  In this case, it's the getter for toolbar items.

  In Objective-C, that returns NSArray.

  In Swift, that's going to come back as an array of AnyObject.

  How do we do this?  Well, given our two representations,  it's extremely efficient to do it  because we have our representation  that can just take that NSArray directly.

  All we need to do is one copy operation  to make sure the contents don't change underneath us  if it's a mutable array.

  But the common case here is that it's not a mutable array.

  It's an immutable NSArray and so this copy operation is trivial.

  It's a message send, it's a retain, and that's it.

  So its conversion is extremely fast.

  Let's talk about the other direction,  going from a Swift array to an NSArray.

  So this would happen when we take, say, our ToolbarItems.

  It's a Swift array.

  And we call the Objective-C setter,  which expects an NSArray.

  Well, now we have an interesting question  because there's two possible representations here.

  There's the really easy answer.

  It's already in the Cocoa representation  and we can just hand off the NSArray, no work at all.

  But the native one, that a different question entirely.

  We could copy the whole buffer but that would be awful.

  We could possibly go allocate a little shim object.

  That's also possible.

  Instead, we decided to make our native representation  into a little bit of an Objective-C object [laughter].

  It's already an NSArray.

  And we've optimized the allocation here  so we can build these objects super fast  and just pass off our native representation  as if it were an NSArray and it works beautifully  on the Objective-C side.

  [ Applause ]  I think that's enough of Bridging.

  Let's talk about subclassing.

  Okay.

 So, Swift objects are all Objective-C objects.

  Now what this means is that, if you define a class in Swift,  it has basic Objective-C interoperability built in.

  We use the same layout as an Objective-C class so there's an  "isa" pointer in there.

  The "isa" pointer points out to Objective-C metadata.

  There's the same underlying infrastructure,  the thing that makes "arch" work and the basic frameworks work  with retain and release.

  You can expect a class.

  That sort of thing.

  They're all available.

  Now, if you really want to make use of your classes  from within your Objective-C code, well,  then you should inherit from an Objective-C class,  whether it's NSObject or some other class.

  And this is going to expose your class to the Objective-C world  and make all the things you write in Swift available also  to your Objective-C code.

  So we're going to continue with our UIDocument example  from earlier and we're going to create a little MyDocument class  that inherits from UIDocument.

  And we're going to talk about a couple  of the things that subclasses do.

  They override methods.

  So here we're going to override the handleError,  userInteractionPermitted method.

  And you do this exactly the same way as you'd do it  if you were overriding a Swift method.

  It doesn't matter.

  The syntax is the same.

  The fact that the super class is written in Objective-C?  Completely irrelevant to the syntax of the language  and how you work with it.

  Now one thing to note in Swift is that the  "override" keyword is mandatory.

  Why do we do that?  There's a couple of reasons for doing that.

  One of the reasons is because when you look at a method,  you probably want to know if the intention here is  to override your super class's behavior  because that's a really important part of your API.

  It's a really part  of understanding what this method is supposed to do.

  Now the other thing it does is it helps overriding accidents.

  For example, I meant to override something from my super class,  but I typed part of the selector wrong  and the method name no longer overrides.

  My code isn't running and I have no idea why.

  Well, with mandatory override, we catch that.

  If you didn't override something and you thought you did,  compiler will complain.

  There's also the real surprise which is  when you override something from your super class  that you didn't even know existed.

  And this is the case where you just wrote a method and maybe  in this release that method happens to exist  or maybe it doesn't exist in this release,  but some joker added it to the next release in the frameworks  that you use and now you're overriding something  that you didn't know existed at the time.

  We can catch that by requiring override throughout  the language.

  We could also talk a little bit about overriding properties.

  So, in Swift, you don't override the getter  or the setter separately.

  Instead you override the property itself  and then you provide a getter or a setter as appropriate.

  So here we're doing something very simple.

  We're overriding the description property  and providing a new getter for it.

  Now what this means to the Objective-C runtime,  to your Objective-C code,  is that you've overridden the getter.

  But the semantic model in Swift is different.

  It's based on overriding the actual thing that was declared.

  In this case, the property.

  As I mentioned before, NSError is going to come back.

  So we have contentsForType error we're going  to override in our subclass.

  And remember that the Objective-C method took NSError,  a C pointer to an NSError which could be nil.

  The way we work with these in Swift is  that the NSErrorPointer class provides a couple of operations  that you would expect out of a pointer.

  You can test it for nil, as we do in the if check here,  to see whether we were actually given a valid point error  where it's being given nil by our caller.

  Now, if it's not nil, we can point  at the memory location associated with that pointer  by referring to it as error.

memory and we can read  from that memory or write to that memory  by just reading or signing to it.

  Now, and this error pointer is going to take care  of the nitty-gritty details of making this auto-releasing  to fit in with the code conventions  of Cocoa NSError handling.

  So it's actually fairly easy to deal with the C pointer  from within the Swift world.

  Now let's take a look at the Swift class  that we've been building here.

  We have MyDocument.

  It inherits from UIDocument.

  It has a property in it.

  It has some method overrides.

  It's just a Swift class through and through.

  But all of this is accessible directly  in your Objective-C code.

  So if you project this into Objective-C,  it would look like this.

  All the same elements are there.

  We have properties.

  We have methods.

  Now there's some interesting things to point out.

  Well, for one, we have this item's property  that is in an NSArray.

  Remember, we talked about bridging here.

  The original Swift code had an array of strings.

  We bridged that seamlessly over to an NSArray  that contains NSString objects  for your Objective-C code to use.

  So you can use strong typing in the Swift world and it maps  over to the natural thing within the Objective-C world.

  The other thing I want to point out is this really ugly name  up top you've noticed.

  So this is a mangled name.

  Usually you're going to see this as MyApp.

MyDocument,  unless you're poking at the internal somewhere.

  And the purpose of this mangled name is to put everything  in a namespace of some sort so you don't have  to prefix all of your class names.

  Instead, what Swift does is it puts the module name,  which is your target, your framework, or your app -  that name into the names of the classes it creates  so you can use the simple names that you want  to use throughout your application and not worry  about a conflict with something else in the system somewhere.

  [ Applause ]  When you're writing your Swift Classes,  you do need to be a little bit cognizant  of the limits of Objective-C.

  Swift has a lot of cool features; you might want  to use them -Tuples, Generics and so on.

  And if you go crazy in your classes  and you use these features, you might be a little surprised  that this generic method that returns a tuple doesn't show  up in your Objective-C code because Objective-C has no way  to express that signature.

  There's nothing we can do.

  We don't have tuples in Objective-C.

  So if this happens to you and you're surprised by it,  there's an attribute you can add to your method.

  It's the objc attribute.

  And what this does is it asks the compiler to check  and make sure that this method or property or initializer  or whatever is expressible in Objective-C so it can be used  from your Objective-C code.

  And if it's not expressible in Objective-C for some reason,  the compiler will give you a hard error  to tell you this is not something you can use  from Objective-C.

  Now the objc attribute actually has a second purpose.

  And that's controlling the names that you see  in the Objective-C side of things.

  So, here's a property enabled.

  It has a getter.

  It has a setter.

  In Objective-C this is going to come through as a property named  "enabled," a getter named "enabled," and a setter named  "setEnabled:" That's not Cocoa convention.

  You'd really rather the call, the getter, "isEnabled".

  And so to do that we just use the objc attribute,  provide it with the selector "isEnabled"  so we can control the mapping ourselves  between these two languages.

  I don't expect you to do this often,  but it's there if you need it.

  You can also do this for the name of a class.

  And what we have here in the parentheses,  in the objc attribute, is the non-named space name of a class.

  So why would you do this?  Well, perhaps you're porting part of your application  from Objective-C to Swift for some reason.

  And so you had ABCMyDocument.

  Now you just want to call it MyDocument  because you're sick of typing ABC.

  However, you have some archives that you still want  to have work, because this is going  to be a drop-in compatible implementation.

  In Swift, for your Objective-C class,  you can use the objc attribute  to give this class the runtime name of ABCMyDocument  and keep all of your archives working.

  One last thing I promised to talk about,  and that's CF Interoperability.

  So by CF we're referring to all of the C-like APIs  that work with the C objects.

  So, this is Core Foundation.

  This is Core Graphics and other frameworks,  maybe some of your own frameworks.

  And let's take a little look at using CF,  particularly Core Graphics in Objective-C.

  I'm going to do something really simple here.

  I'm going to draw a gradient in a rectangle using Core Graphics.

  Here's my start.

  I need to go build up the ColorSpace  and build up the Gradient.

  Now there's a couple of things of here that I find non-optimal.

  So the one thing is bridge casts.

  So we're in ARC.

  It's partly great.

  It's handling our NSArray for us.

  We're using the nice array literal syntax.

  But now we need to do bridge casts between CGColorRef and id  so we can put things into the NSArray.

  And we have this CFArray cast sort  of doing toll-free bridging there  between the NSArray and the CFArray.

  And there's also this semi-amusing thing  that we're using three different kinds of arrays  in four lines of code.

  And you can write this - you can try to write this better.

  I couldn't, actually, find a way to make it cleaner than this.

  And it's really unfortunate  because the NSArray gives us some useful behavior.

  ARC is managing its lifetime for us.

  That's great.

  We need the C Array because we need to put CGFloats in it,  and we can't do that within NSArray.

  And finally, we need to do the toll-free bridging  over to CFArrayRef because that's what we use  with Core Graphics APIs.

  Now, moving along, we can create some points  with CGPointMake and, of course, even though you're under ARC  where memory management is automatic,  it's not automated for CF things.

  So we have to remember to release the ColorSpace  and release the Gradient.

  We feel like we can do a little bit better  in the world of Swift.

  So let's start again, this time in Swift.

  And first, let's build our colorSpace.

  So here we're just calling CGColorSpaceCreateDeviceRGB().

  Nothing different about that.

  However, the type that we infer  for this ColorSpace variable is CGColorSpace.

  Note the lack of a ref at the end of this.

  This isn't some opaque pointer.

  This is the CGColorSpace class that we've created.

  What's the nice thing about being a class?  Well, that means we're in the ARC model and we're going  to automatically manage the memory for you.

  [ Applause ]  Let's go a little further and create  that Gradient we talked about.

  So here, remember, we need to pass a couple of arrays through.

  We can use this nice Swift array literal syntax  to form an array containing startColor and Color.

  We're doing all of the bridging automatically here for you.

  So we've created the NSArray we need.

  We've toll-free bridged it to the CFArray behind the scenes  so you don't have to deal with the fact that there are  so many array types running around.

  Did the exact same thing for the C parts.

  So here we just have an array of floating point values.

  So it's treated as a native Swift array of CGFloats  that we bridged seamlessly to the underlying C array  that this C function expects.

  Let's keep going with our example here.

  CGPoint.

 You can use CGPointMake if you want.

  It's perfectly fine.

  It works exactly the same way as it does in Objective-C.

  However, whenever we import a struct,  like CGPoint is a struct, we provide it with initializers  that have labeled arguments.

  And so a better way to build CGPoints in Swift is  to just construct a CGPoint value using  that same construction syntax we've been talking  about throughout this talk.

  And then use the argument labels x and y  to make it absolutely clear what you're doing.

  And this brings a little bit of a flavor  of that nice Cocoa readability using argument labels  into the underlying CF APIs.

  And that's it for our example in Swift.

  It's smaller.

  It's easier.

  There's far fewer concepts that you have to deal  with because we've automatically taken over the management.

  [ Applause ]  Now you may have some of your own APIs that we refer  to as explicitly bridged.

  So these are CF-like APIs  where we're not quite sure whether you're following all the  CF memory conventions because, unlike the world of Cocoa  which is fairly tame, and we've been following conventions  fairly well for many years, we haven't been following them  so well in C as a community.

  And so we may have this function GetRandomColor,  produces some random color.

  When we pull this in, the Swift compiler doesn't know whether we  can really trust that this returns plus zero or not.

  It has get in the name but we're not sure.

  And so we do the safe thing and we import it  as an Unmanaged<CGColor>  which means we can't directly manage the memory here  because we don't know what the conventions are.

  So what is this Unmanaged thing?  So Unmanaged is actually a generic struct  over an arbitrary T.

  Now the details of generic structs we don't need  to go into now.

  They're covered in the Advanced talk which I highly recommend.

  What we want to look at right now is just the simple API  of this Unmanaged type.

  We have two core operations - takeUnretainedValue,  which you use for +returns, and takeRetainedValue  which you use for +1 returns.

  Now, when we call our CGColorGetRandomColor,  we want to immediately use one of these two functions.

  So we know that GetRandomColor returns a plus zero,  so we're going to do a takeUnretainedValue of it.

  Now the reason to do this immediately  after the call is this gets us a CGColor  which takes us right back  into automatically managing memory for you.

  So the window in which you have to do something  with manual memory management is tiny.

  It's just this one little line of code  where you establish what the convention is  for CGColorGetRandomColor.

  Now, if you own CGColorGetRandomColor,  you can audit your APIs to make sure they follow the Core  Foundation naming conventions for memory management.

  And when they do, you can use these annotations here  in core foundation -  CF-IMPLICIT-BRIDGING-ENABLED and DISABLED.

  Put this over a whole header once you've audited all the  methods in that header.

  And when you do that, well, now your function when you use  from Swift, gets you right back into the managed world directly.

  So you have the great automatic memory management  that Swift can provide for CF.

  So let's wrap up here.

  We've talked about a lot of different topics.

  We've talked about interoperability  between Swift and Objective-C.

  We've talked about a whole lot of rules about how that works.

  But let the tools and documentation help you.

  They can show you Swift and Objective-C side-by-side  so you can get a feel for how your Objective-C APIs work  in Swift.

  We've talked about some of the details  of bridging Core Cocoa data types  and using Swift's native types.

  And we talked about automated CF memory management available  in Swift.

  For more information, check out the  "Swift Programming Language Book," and also the "Using Swift  with Cocoa and Objective-C" guide that goes  into more details on the interplay  between these two programming languages.

  Thank you.

  [ Applause ] 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值