2014s406 Integrating Swift with Objective-C

姬安志
2023-12-01

 [ Silence ]  [ Applause ]  >> Good morning.

  Welcome to Session 406:  Integrating Swift with Objective-C.

  Now many of the Swift demos here at the conference start  by creating either a brand-new project from one  of expo's project templates or a playground and that's great.

  Playgrounds are a great way to explore the Swift language.

  But many of you already have Mac or iOS apps and some  of you have spent years writing Objective-C code.

  You have written it carefully.

  You have debugged it, polished it, optimized it.

  It's just perfect, hmm?  And we certainly don't want you to have to throw any  of that away or rewrite anything that you don't want to.

  Also some of you have really a lot of Objective-C code  that you maybe spent a decade writing and maybe it's...

  not-so-perfect.

  It's not as clean and shiny.

  So you are going to be using  that Objective-C code for a while.

  You might want to rewrite it, but it's not going  to happen this time around, because you've got an app  to ship, etc.

 So, Objective-C is not going away.

  Both Swift and Objective-C are first class citizens  for doing Cocoa and Cocoa Touch development.

  And, in fact, we don't want you to spend any time  that you don't have to.

  We don't want you to stop refining the Objective-C code.

  We want you to be able to integrate Swift as you want to.

  And we do think that as you implement new features,  as you finally get around to replacing crufty subsystems  that you want to use Swift for that  because of its better type safety,  its expressiveness, its performance.

  Now, many of you have also gained a lot of knowledge  and experience in Cocoa and Cocoa Touch through your years  of Objective-C development and that applies to Swift as well.

  You're using the same frameworks, the same classes,  the same APIs with some syntactic differences, in fact,  the same design patterns, such as delegation.

  So all that knowledge applies and so we expect that a lot  of you are going to end up with projects that contain both Swift  and Objective-C and we worked hard to make sure  that Xcode lets you freely mix Swift and Objective-C.

  So this session over the next hour we're going  to be talking a little bit more detail  than what you've probably seen so far about exactly how  that works in practice.

  We're going to start simple by just introducing some Swift code  into an existing Objective-C app.

  Then we'll get a little bit more interesting.

  We're going to start to add some Swift code  to a model-level framework, which is then embedded  in an Objective-C app and call that from Objective-C.

  We'll talk about how to start accessing your pure Swift  framework which will start showing  up from an Objective-C app.

  And then we'll talk as we go along about how to adopt some  of the common, well-established Cocoa patterns in Swift.

  And again Swift is designed from the ground  up to be a great Cocoa/Cocoa Touch development language.

  And so it has language support for some of the Cocoa idioms  that we've gotten used to.

  So, I could go on here for, you know, two dozen slides and talk  in detail about how this works, but I'd rather just give a demo  because that's usually a bit more clear.

  Alright, so I've got a project here.

  It's, it's an iOS app and it has an embedded framework  so it's decently designed here.

  It's got a top layer which is the actual app with the view  and the controller layer in our MVC design.

  There is a framework called ListerKit down here,  which contains our model objects.

  And all of this is currently written in Objective-C.

  Just to give a sense of what this is, let's go ahead  and run it in the simulator and we're gonna go ahead here  and bring up the simulator.

  We see that, yeah, it's a typical list-taking application.

  I can go in and add colored lists here.

  I can check off items, etcetera, edit them.

  What I can't do right now is to add a new list.

  This happens to be an iCloud app so I could do that in my Mac app  and it would show up here...

  But, for this example we're going to use Swift  to add the ability to add a new list in iOS.

  Alright, so I'm going to go back here  and the first thing I'm going to do is look down here  to the group ViewControllers.

  And here we have some ViewControllers already written  in objectives [ahem] in Objective-C.

  I go to File, Create New File, and in this case I'm just going  to create an empty Swift file because I want to show you how  to get started from scratch.

  Xcode has great file templates and can fill  in a lot of code for you.

  And we want to do it in a basic way here,  to show how it's going to work.

  I'm going to call it NewListController and I'm going  to make sure that it is going to go  into the application target here.

  We also see that I have ListerKit, which is my framework  and one of the new extensions, here,  for showing in the status view.

  So, NewListController.

  Now as I create this, my first Swift file,  in a pure Objective-C project...

  Xcode will offer to create  for me what's called a bridging header.

  So a bridging header, we'll talk about that in more detail later,  but basically what it is, is a way for you to expose some  of your Objective-C APIs to Swift.

  I'm going to go ahead and create that.

  So I get that by default.

  Now, import foundation...

  actually I'm going to be needing some UIKit API here.

  So I go ahead and change that to UIKit.

  And I know that I will need to import my own model framework  because we want to obviously create our model objects  from within the ViewController.

  And I'm just going to go ahead here and say NewListController.

  And now, when I complete, we see that because I said  "import UIKit" there,  Swift pulls in the Objective-C framework UIKit  and gives me a Swift view of those symbols.

  So I'm going to subclass from UIController  and just leave it like that for now.

  So we have just a basic empty class --  a Swift class, subclassing an Objective-C class --  that we get from UI- ...

from Cocoa Touch.

  Now we could sit here and drag out rectangles  and create an API- ...

create a UI for this,  but fortunately I happen to already have one.

  How lucky.

  It isn't wire up though,  so we're going to do that right now.

  The first thing I'm going to do here is,  just like with Objective-C, I need to set what the class  of my UIController is.

  So I can go up here and now we see that the NewListController  that I just created is shown here among all the ones  implemented in Objective-C.

  So when you access your classes  from Swift-...

from Interface Builder, the Swift  and Objective-C classes show up next to each other  and you can work with them as equals.

  Alright, so now the next thing I want to do after setting that,  I'm going to go ahead and create a bar button item here.

  I'm going to drag that up into the toolbar, up here,  and I'm going to change the identifier to be Add,  and that also changes the visual appearance  to give it a standard Cocoa Touch "Add button" look.

  I'll create a segue, it's going to be a mobile segue.

  This is, after all, the storyboard  and I'll change the name of that segue to be New List.

  Now that's going to be important a little bit later on so  that we can access it from within our code.

  Okay.

 So now, you may have seen in one  of the other demos how we can drag from Swift code and connect  to objects inside of IB.

  We can do the same thing in the other direction as well.

  If I option-click here on my Swift class --  my ViewController class implemented in Swift --  now I can drag the other direction.

  First thing I'm going to do is I'm going to go ahead and drag  from an outlet and insert a new outlet here.

  And I'm going to call this "nameField".

  And we see that it's a UIText field and the storage is weak  because we have a controller class,  the super view- ...

the view hierarchy owns the button,  and the fields and the buttons in here,  and so we just want a weak reference to it.

  And, in fact, when we insert this,  Interface Builder knows it's a Swift file.

  So it uses the Swift syntax just as it would for Objective-C.

  And it uses the IBOutlet property  for the stored property- ...

I'm sorry, the IBOutlet attribute  for the stored property here.

  And this is implicitly weak and also optional  because not all the outlets may be connected.

  Give myself some space there; I'm going to go ahead  and do the same thing here for the buttons.

  I'm just going to control-drag these and I'm just going  to fill these in in the same way.

  Now this is a sample app.

  Let me go ahead and adjust the spacing there.

  This is a sample app and so it's not rescalable  to just have six buttons and six outlets.

  But I know that you all design better,  more maintainable code than this.

  This is for illustrative purposes only.

  You might want something where you can load colors  from a TableView- ...

uh, from a Plist and show them  in a TableView or something like that.

  I'm going to go ahead and finish connecting these.

  Alright, so now we have our outlets to the UI components.

  We can also create Actions in this way.

  If I choose the Cancel button down here and I drag in the,  I can create an Action up at the top here just for-  ...

just as for Objective-C.

  I change the connection type to Action.

  I will type here "cancel" and we can just leave the type  of the sender as any object here.

  We don't actually care because we're not going  to be messaging it.

  What we will do, however, is to invoke some Cocoa Touch API.

  I give myself some more room and I can say "self.

dismiss"  and then the code completion gives me access  to the Cocoa Touch API that we already know.

  I can complete here.

  I'm not going to need a- ...

I want it  to be animated, so I put "true".

  I don't need a completion block, so I give it "nil".

  Now, in Objective-C we always have to message "self"  if we want to talk to self.

  Swift knows from context when you are messaging yourself.

  So we can actually leave this out  and make this a little bit more readable.

  [ Applause ]  Excellent.

  Alright, so now we will connect the buttons here as well.

  And I'm going to be calling this "pickColor".

  Now in this case I choose UIButton.

  (I'm sorry, I need to make it an Action.

)  I choose UIButton down here  because in this case we will actually want to talk  to the button, or compare the button,  so we want to have that type.

  And as you can see, Interface Builder generates the code  and makes the sender be a UIButton.

  Before I forget, I'm also going to wire  up the other actions here as well.

  Let me see here.

  To connect to this one,  give myself a little bit of space there.

  And now they're all wired up to the same Action.

  So they're all wired up to the same Action.

  The sender will be different depending  on which button we touch.

  Now, I want to show that some of the power  of the Swift select statement here, the switch statement.

  Unlike Objective-C or other C-based languages,  I can actually switch on a wide variety  of different kinds of patterns.

  In this case I'm just switching on what the sender is.

  But this is a very rich construct in the Swift language  and I encourage you to catch one of the later talks  about advanced Swift to really get into the depth  of what you can use this for.

  For right now, to keep it simple, I'm just going  to use it to match on a button.

  And what I will say is "okay,  now I need to assign something here.

"  So I'm going to need to have a stored property.

  I create a stored property called selectedLabel  because we're actually choosing labels here represented  as colors on the screen.

  And then what I can do here is I can say this is  an AAPLListLabel.

  So this again is an Objective-C type that has been imported  through my import of ListerKit up there.

  I'm going to start out by saying  that that is an APLListLabel.

Gray.

  That's the default value of it.

  Because I don't put a question mark, I don't make it optional,  that means that it has to have a value.

  Remember the IB outlets are implicitly optional.

  Now it's kind of ugly to have to repeat the same thing here.

  And, in fact, Swift has great support for type inference.

  And when it knows exactly what type you mean,  then you can go ahead and leave out the type.

  So from this, Swift can tell exactly that, "okay this has  to be an AAPLListLabel," because that's how I'm initializing it,  so I can just leave out the type.

  Now I can complete my statement here.

  I can say "selectedLabel = .

Gray" and, in fact,  I can add to the rest of them here as well.

  I'm a very fast typer.

  Now we see here that Objective-C- ...

I mean, sorry,  Xcode, Swift has a message for us here.

  So in Swift, one of the things to notice is each  of the cases is implicitly ended  at the beginning of the next case.

  So you don't have the case of C where you implicitly fall  through and that's the source of a lot of bugs.

  In fact, Swift was explicitly designed to avoid many  of the common pitfalls that are common in C-based languages.

  Now in this case, another thing about the switch statement is  that it has to be exhaustive.

  So you have to say that you cover all the cases.

  In this particular case I'm just going to add a default  and what I'm going to do in this case right here is  to use a Swift standard library function  to just trap into the debugger.

  Because I want to make sure that if I just add new buttons  and hook them up that I actually have the code for that as well.

  So now Xcode is happy.

  That's excellent.

  We've just set our property.

  That's all we've done so far.

  But now we can use some more of the Cocoa Touch API  to actually make the button show a reflected state-  ...

show it's selected state, I should say.

  So, we say, in this case we can say "sender.

"  and then we can use our code completion to access the layer.

  And then we'll set it's borderWidth to be five.

  And then, of course, we have to initialize  that to be a borderColor as well.

  Now in this case, because we're accessing the layer --  that's a core graphics API --  we get the CGColor from the UILabel- ...

the UIColor.

  And all of these are standard Cocoa Touch APIs.

  So you see that when we access our own Objective-C APIs  or the Cocoa Touch APIs it looks all the same.

  We still use the same, nice Swift syntax.

  Another thing we can do here is to just say  that the view's tint color is going to be the color  that corresponds to the label.

  And for this we can call another one of our functions  that we brought in from Objective-C.

  And we pass it the selectedLabel.

  And, in fact, if we command-click on this,  we see here that we can get to a Swift representation  of our own Objective-C API.

  In Navigate we could actually go back to the header itself  that we declared it from.

  Alright, so now those of you  who are following closely here may have seen a little bit  of a problem: we set the borderWidth to 5;  we never actually deselect the previous button.

  So what we can do is to keep track  of the currently selectedButton and in this case,  I'm going to explicitly say that it's a weak stored property.

  So this is not going to cause, ah, the- ...

extend the lifetime  of that button which is, after all,  owned by the view hierarchy.

  And I'm going to declare  that this is a UIButton but it's optional.

  Because when we first start out it has no value.

  And so, we're going to be able  to check whether it has a value or not.

  We add some code to set the selectedButton to the sender  at the end of having assigned it.

  And then here we can say "if selectedButton" then we're going  to go ahead and clear out the border on that one.

  Now this happens to be a object reference so I could do the same  in Objective-C with a nil value.

  But one of the nice things about optionals in Swift is  that they apply to any type  of value even a Boolean or a scalar.

  So in this particular case, I'm just going to go ahead  and select the selectedButton's layer borderWidth to 0.

  Now you'll notice the exclamation mark there.

  This is the unwrapping operator and that says  that if I have an optional value,  then when I've determined it has a value,  I get at the actual value.

  And if I forgot to check whether it has a value,  that will cause a trap if it doesn't.

  So this is one of the ways that Swift makes it safer  to write your apps, because you can catch these errors earlier  without having to add a lot of assertions to your code.

  So I think we're probably good to go here.

  Let's go ahead and take a look  and I'm sure I forgot something somewhere...

  "selectED button"!  And that's because I mistyped it.

  So that shows that this is live right?  I'm not playing a QuickTime movie up here.

  [Laughter] Okay, so now we're here.

  We have our add button.

  We bring up the modal sheet.

  As we select the various buttons we can see  that we have the border and then we do the, the tint,  we tint the buttons down here and, of course,  the Save button doesn't yet work.

  Now I'm just a UI guy.

  So I just make it look pretty.

  But to actually do the model level work we're going to have  to ask one of my colleagues to come up here in a moment.

  First let's take a look at what we did in this part.

  Okay.

 So what did we just do?  So first of all, we added some Swift code to an Objective-C app  and Xcode offered to create us a bridging header,  which the next action we'll go onto some more detail about.

  We subclassed the Cocoa class- ...

Cocoa Touch class in Swift.

  In this case it happened to be a Cocoa Touch class that's part  of the standard libraries of Cocoa Touch itself.

  But it could also be one of your Objective-C classes  that you've declared.

  We used Swift class in the Interface Builder  and as I hope you saw, it's exactly like working  with an Objective-C class.

  And that's actually a theme under here that we wanted  to feel like exactly the same thing; it's interchangable.

  And so now to make this a bit more real in terms of the model,  I'm going to ask my colleague Dave to come up  and complete the project here or work on it further.

  [ Applause ]  >> Alright.

  Thanks Anders.

  What we saw is that you have access to everything  in Cocoa Touch and the power that you've got in Xcode  from Swift without really doing anything.

  But I'm sure what you're thinking now is  "what about MY code?  What about the Objective-C code that I have  in MY target in MY app?"  And that's what I want to show you.

  As Anders said, we could spend a lot of time in slides  and everything else and I'm actually going  to cut it a little shorter than he did  and just go straight into a demo.

  And so we come back right where we were before  and for our purposes the first thing we want do here is a  little bit of tidy-up, and that is  to make our UI text field behave the way  that we want for our user.

  It offers a great deal  of configurability using the delegate pattern.

  And to do that we're going to make NewListController conform  to UITextFieldDelegate, which is as easy as...

  if I could type...

  finding our UITextField...

  Man, I'm having some time here.

  [laughter] It's a comedy show, I promise.

  [laughter]  There we go.

  Alright, something to call out there is-...

  [ Applause ]  Success! And that's the hardest thing I'm going  to do the whole time I'm up here.

  [laughter] So UITextFieldDelegate,  all we did here was add the protocol conformance to the end  of a common separated list after our class.

  In our case it's important because we're actually,  we actually have a superclass.

  The superclass is the first thing in that list  and then our protocols follow.

  Now that I have managed  to declare textual delegate I'm actually going to take it easy  for the next part and just bring in...

  the implementation of TextField should return.

  And the only thing we're doing here is resigning first  responder on our TextField and returning  to make sure the keyboard dismisses  when the user hits return.

  Not particularly interesting, but it was just as easy  as it would be in Objective-C to go ahead  and implement this protocol.

  Next, as Anders promised, we are going to look a little bit more  at the interaction with the model side of things  and actually implement Save.

  The first thing I'm going to do in-  ...

who caught the error here?  Somebody knows what I forgot to do, and that is:  manage to declare it as a UITextFieldDelegate  but we never actually connected it and that would have made  for an interesting end.

  So now that we've got that set up I will control-drag over,  manage to hit the button, set it up as an action, call it "Save".

  Now, inside here I actually want to interact  with the representation of our documents that we see  in the document list on the main screen.

  And that's a part of our app target, not a part of ListerKit.

  And because of that we have to take a different approach.

  We can't just simply import our framework in.

  And this is where we make use of the Objective-C bridging header  that Xcode offered to create for us  when we created the first Swift file in this project.

  So we'll navigate over to that and it gets created  in the same directory -- or in the same group, I should say --  as the Swift file that we created.

  It's not overly interesting at first.

  It simply has a comment that's very instructive.

  This is where we put the imports for the classes in our target  that we want to expose to Swift.

  In our case, it's a single class and that is our ListInfo class.

  This is the representation that backs the TableView that you see  when the app initially comes up.

  We save that and head back to...

  really don't need the storyboard anymore, so, I'm gonna go ahead  and collapse things down and let us actually focus on the code,  because the wrapping really kind of gets on my nerves.

  That's one of the things that I like about Swift,  is it's a little more succinct.

  So just heading back down to our Save...

  Now that we've pulled in the bridging header - or,  now that we've added our class to the bridging header --  we can actually create one.

  I'm going to create it as a "let"  because it's going to be constant.

  I'm only going to assign it once.

  And so my ListInfo is equal ListInfo.....

and, of course, I keep saying "ListInfo"  like we instruct you guys to do  when we're writing Objective-C code we actually use a prefix.

  And I've been writing a lot of Swift code lately,  so it's not on the forefront of my mind.

  So, our ListInfo...

  and it offers us two constructors.

  One of them takes URL.

  The other takes an NSMetadataItem  for that iCloud support we were talking about.

  We're going to go with the URL one.

  I could construct the URL right here.

  A little bit of forethought, thinking forward,  I know that I'm actually going to want  to revise this a little bit later and iterate on it.

  So I'm going to set  up a computed property as a convenience.

  Now to declare a computed property is pretty similar  to the way that we define a stored property.

  And we'll go with var.

  I'm going to call it "fileURL".

  Now with a computed property we do have  to be explicit about our type.

  I'm going to make it optional and that's  because if the user actually hasn't provided us a value  in the TextField yet, I can't actually construct a valid URL.

  I've got an implementation ready that just happens to make use  of the list coordinator from down in the ListerKit.

  And after defining the computed property I can come down  and make use of it right away.

  And you'll notice as Anders had mentioned earlier we don't  actually have to explicitly refer to self  when accessing our property.

  We set up a few pieces of information that we've captured  for the user so we'll get the name from our nameFields.

text  and the label is going to be equal  to our selectedLabel and that looks good.

  So now that we've set it up, we actually want to save this out  and ListInfo provides us a convenience for that.

  And we will createAndSaveWith CompletionHandler.

  And this CompletionHandler is actually going to call-  ...

is going to come back to us  with a Bool letting us know whether  or not the save was successful.

  And I could use what code completion's suggesting here  and fill  out my completionHandler closure inside of the parentheses.

  But because it's the last argument in the call  and because it's a closure and it's going to be a little long,  I'm actually going to make use  of Swift's trailing closure syntax.

  And in this case I don't even have to put the parens  after the method call because our createAndSaveWith  CompletionHandler actually doesn't take any  other arguments.

  So inside of here I will note the variable name that's being  passed in, which is success....

having more typing problems  so that you guys can get a good laugh.

  And I'll just check the value and for now I'm just going  to print a little debug message  to let me know that it was saved.

  And just like Cancel, we want to get out of the user's way now,  so we'll go ahead and dismiss.

  So let's build and run, see  that everything is doing what we want it to do,  make sure that everything is good to go.

  Bring it up...

  Let's see, I need to go  to the hardware store a little bit later.

  And so we'll name our list hardware, hit return.

  Keyboard goes away; that's a good sign.

  The hardware store I frequent actually uses orange  in the logo rather heavily, so that's going  to help me remember what's going on there.

  And I'll go ahead and hit the Save button.

  That's not really what we wanted.

  I really wanted to see my hardware list show up here.

  Did it actually save?  I'll go over and check the debug console.

  Indeed it did save and just for fun I'll actually stop  and run the app again.

  And you'll see that it actually did save and get created.

  But we never passed anything back to our document list  to let it know that we created anything new and we kind  of wanted this to not be sitting there  and polling the file system over and over and over again,  just looking for new files that might show up.

  So for our purposes, we're going to take a page  out of UITextField's book and we're actually going  to use Delegation to pass information back  to the ViewController that presented us.

  The first step there is usually to define a protocol  that we expect our delegate to conform to.

  And in Swift we can do that just as easily as declaring a class  or a struct or anything else.

  We'll just start with the  "protocol" keyword (that I actually spelled correctly)  and NewListControllerDelegate.

  And I already have a method that I want my delegate to conform  to that I've prepared that just takes a new ListController  and a ListInfo letting it know what's been created  and done over here.

  Now at this point this protocol is visible to my Swift code  but I want to make sure that my Objective-C code can see it  as well.

  To do that it's as simple as adding an attribute at Obj-C  and this is going to go ahead and say,  "even though this is all Swift here,  we do want to make this available  to Objective-C a little bit later on.

"  So now that I have a delegate protocol I'll actually define a  delegate and we'll make it weak  because we don't really want to own it.

  We don't want to extend its lifetime.

  And I can declare it as being just  of the NewListControllerDelegate type.

  I don't have to do something like ID and angle brackets  around my protocol to indicate what's going on here.

  I can simply have a property that is of this delegate type.

  It's going to be optional  because I may not have a delegate.

  So now that we've defined a delegate property I think maybe  we ought to do something a little more useful  than printing a debug statement down here.

  And for that we're going to reach out to our delegate  and I'm going to make use of another Swift feature,  which is optional chaining.

  So the delegate is an optional.

  It may or may not have a value.

  By adding a question mark here before actually calling  out to the method that we want to invoke, I'm going to be able  to say, "IF I have a delegate --  if there is a value there -- make this call.

  If there's not, don't do anything.

"  [ Applause ]  And now I'm trying to remember what the name of it was.

  Ah, there we go.

  Pass my self and the ListInfo that we just created.

  (REALLY not much of a typist, in case you can't tell.

)  And at this point we've wrapped up the Swift implementation  of this, but we've defined this delegate.

  Now we actually need our Objective-C code to be able  to see the Swift code we've just put together  and interact with it.

  To do that I'm going to head back over to the document-  ...

the, the controller for the document list,  and inside of here we're brought to- ...

I mean,  what comes to your mind is the fact  that the way I make one object in Objective-C available  and visible to another object is by importing its header.

  Well, with Swift we don't have headers.

  We think that's a good thing within Swift, but we've got  to find a way to bridge the gap back to Objective-C.

  And so in that case Xcode's got our back and is going  to provide us with a generated header  that contains the entirety of our Swift interface.

  Being generated, obviously it has  to have a well-constructed naming scheme, and that's going  to be our product name --  which in this case is "Lister-Swift.

h".

  And so this is a generated header, so I'm going to go ahead  and take the time to build, to make sure  that we've got the most up-to-date items in it.

  And after importing that we will come over here and we're going  to go through basically the same process  that you would ordinarily to implement any of the Cocoa  or Cocoa Touch delegates on your own class.

  We'll add our protocol conformance and we can pull  in our NewListControllerDelegate.

  I'm going to choose to use the jump bar to head  down to an appropriate place I feel  to go ahead and implement this.

  And as I code complete here  with the NewListController didCreateListInfo, you'll notice  that even though we declared this completely in within Swift,  what we get is a syntax that matches what I'd expect  from Objective-C so that everything still feels natural;  everything feels like a first class citizen in its place.

  Happen to have the implementation...

  Wellllll...

  The implementation of the last thing ready.

  In this case what we're really doing is just updating our data  source and then refreshing the TableView  with a little bit of new information.

  And the very last thing here, or the final step,  is that we actually need this ViewController  to make itself the delegate for our new ListController.

  We're using a modal segue,  so why not do it in prepareForSegue?  Down here we'll just check to make sure  that it's the right segue.

  We'll grab the newListController and we'll set ourselves --  in this case our ListDocumentsViewController --  as the delegate.

  At this point I fully expect things to do what I want.

  And...

 Coming up with another list, "WWDC Must Haves".

  Not really sure what those are yet,  but I'm pretty sure I'm going to think something up.

  And this time it actually comes through,  shows up in our DocumentViewController  and just for, you know, proof's sake I can come into it;  I can change it around.

  I really like this salmon color that they're using  for the Apple badges this year.

  So I'm going to actually pick that  and that's integrating our code.

  [ Applause ]  And just to head back to the slides for a second,  what we've just seen here is that it's very easy  to expose our Objective-C code to Swift.

  Simply import whatever headers you want  into the bridging header and they'll be made available  to the Swift in your target.

  We defined a protocol in Swift which was as easy  as defining a protocol in Objective-C.

  And finally, we've exposed that protocol and the class  that we just created in Swift back  to the Objective-C code in our target.

  And we actually didn't have to do anything there.

  Xcode did the generation of the header  that provided that interface.

  So just to reiterate a little bit more when we want to go  from our Objective-C headers  and make our Objective-C objects available to Swift we're going  through the bridging header.

  This is going to be created- ...

Xcode will offer  to create this for you automatically  when you add a Swift file to an existing Objective-C target  or an Objective-C file to an existing Swift target.

  When we want to get our Swift interface exposed  to our Objective-C implementation,  there we're looking at the generated header  that Xcode provides and this is going to be named  after our product and it's going to be  "our product name dash Swift dot h".

  One last thing to remember about these,  is that the bridging header is created for you by Xcode  but you own it from that point forward.

  So it should be something you check  into your source control system.

  It should be something that you maintain, you manage  and it's there so that you only expose  to Swift what you want to expose to Swift.

  In the case of the generated header, it's just that.

  It's generated as part of the build process.

  It's going to contain your com- .

.

.

the complete interface  for the Swift files within your target.

  And those are going to be readily available  to Objective-C simply by importing that header.

  So at this point to talk with you a little bit more  about what you can do with frameworks in your product-  ...

in your projects, I'd like to invite my colleague Jordan up.

  [ Applause ]  >> Thanks Dave.

  Alright, so we've seen how easy it is to deal  with integrating your Swift code  into an existing Objective-C app.

  And Xcode pretty much takes care of all the details for you.

  It's available in Interface Builder.

  It's going to show up in the simulator.

  Everything works just fine.

  So I'd like to move a step down in the hierarchy  and look at the framework.

  So at this point we've been working pretty much the entire  time up in this Lister part of the application,  which is the actual application target.

  Now we're going to move down to ListerKit  which handles the model and the object graph for this target.

  And specifically we're going to add a new functionality:  the ability to add attachments to every list item,  something like, oh, a photo or a voice recording.

  To do this in Objective-C you would create a new class,  add it to your object graph by adding a new property  and then setting up an interface to mange it and we're going  to do the exact same thing but using Swift.

  So I'll start here by going to File, New File  and this time let's use the Cocoa Touch class template.

  I can name this APLListItemAttachment.

  Subclass of NSObject and the language, of course, is Swift.

  I make sure that it's  in the ListerKit target and click Create.

  You notice here that Xcode did not offer  to create a bridging header for us.

  This is because we're working with a framework target.

  In a framework you already have a header  that specifies the entire contents of the framework.

  That's your umbrella header.

  So when you're working with a framework target  in Swift everything that's available  in your umbrella header will automatically be visible  to the Swift code, no work required.

  So we're going to want attachment objects to persist.

  So the very first thing I'm going to do is add a conformance  to a standard Cocoa Touch protocol, NSCoding.

  Attachments are made up of two things:  their data (which will I represent using NSData)  and their type.

  For types of data Cocoa Touch likes  to use uniform type identifiers, which are a very general way  to represent file and data types  that the user just stored as Strings.

  The next thing to do here is to declare an initializer  and that just takes the data and type from outside  and will assign them into our own fields.

  But we do need to be careful because the data  that we are given might be an NS mutable data  and somebody might try to change it  after they've created our attachment.

  So in order to work around this, we need to call the copy method.

  However, copy returns ID in Objective-C  and in Swift that's going to come  through as the Any object type.

  We can't go directly from any object to NSData in Swift,  because that's not guaranteed to be safe.

  So instead, we have  to explicitly tell the compiler to do a cast here.

  And this is important, because if anybody ever implements the  copy method incorrectly  and doesn't return an NSData we would much rather find  out about it right here in the initializer than crash somewhere  down the line when we try to use it.

  On the other hand, we can just assign the type directly  and this is because strings in Swift are values.

  Unlike NSString, every Swift string has an  independent existence.

  And when you assign across using the equal operator you get  independent strings.

  So, whatever somebody does over here  to their string won't affect your string over here.

  [ Applause ]  So next I need to implement the NSCoding protocol.

  (Let's close this for a bit more room.

)  And here we can co- (ah!)  ...

code complete here, no code completion, awesome.

  So here I'm implementing the NSCoder initializer  and you'll notice something a little funny here.

  In Objective-C we'd have the method named initWithCoder.

  But in Swift we have an initializer that's just  init coder.

  So in order for more consistency in the Swift world,  where object creation syntax is a little different,  an init method from Objective-C that starts with  "initWith" will come into Swift with the "With" dropped.

  This ensures a consistent world with Objective-C.

  Sure, the implementation of this is pretty much the same  as what you'd expect.

  Just call decodeObjectForKey  and again explicitly downcasting to the type we want.

  If somebody hands us a bad archive we'll find  out about it now rather than crashing later on.

  Finally, the last thing  to do here is implement the encodeWithCoder method  and this one does not get any name changes  because that is limited specifically to initializers.

  And the implementation of this, again, is very simple.

  We call encodeObjectForKey with both the data and the type.

  And that's it; this is an entire Swift model object  and it fits on one slide.

  I didn't do anything crazy or confusing here beyond some  of the things you need to do to be a good Cocoa Touch citizen.

  [ Applause ]  The next step, of course, is to add this into our object graph.

  So I go back to our Objective-C header  and now we have a bit of a problem.

  Because last time that we wanted to access Swift code  from Objective-C what we did is import the generated header  into the Objective-C file.

  But we can't do that here  because the generated header depends on the Swift.

  And the Swift reads the entire public contents  of our framework (via the umbrella header)  and that includes this header right here.

  So instead, I use exactly the same mechanism  that I have in Objective-C.

  This is what we use to break cycles.

  It's a forward declaration using @class  and I can forward-declare the Swift class just  like I would any Objective-C class.

  And now I am perfectly able  to create a property here using the attachment.

  The last thing to do, of course, is to update the implementation  of the list item to actually handle the attachment and for  that I'm going to import the generated header now  that we're in a .

m file.

  And because this is a framework, and the generated header is part  of our public interface, we are going  to use framework syntax to import it.

  All that's left to do here now is to update the implementation  of this class so that we actually handle this  new property.

  So that means having an extra key for encoding here.

  Setting the attachment explicitly to nil,  making sure that we can encode and decode it.

  And while I'm doing this I'd like you all to note  that this is exactly what you'd be doing  if we had implemented the attachment class in Objective-C.

  There's nothing surprising going on here.

  It's exactly what you would have done before.

  Finally we do need to make sure that we update the copy method  so that when we copy a list item,  that we actually do get a copy of the attachment as well.

  (Square bracket.

)  And since we made our attachments immutable using that  "let" keyword to define the properties,  we can just assign this across directly.

  We don't need to do any special copying to ensure  that they are independent and not going to change on us.

  So that's it; that's the entire model.

  And now you're probably thinking, "Oh great,  here we go again, back to Interface Builder  to define a new layout, a new ViewController and add a bunch  of outlets and actions and hook everything up all over again.

  We already saw that!"  And you'd be right.

.

.

  except that, we're actually really lucky here.

  The three of us work in a department with lots  of other great people and another team is working  on a very similar app that needs to deal  with these kind of attachments.

  And because their app is new,  they've written it entirely in Swift.

  But they've also factored out their attachment ViewController  into a separate framework, which is a new feature of iOS 8.

  So we're going to use their framework, written entirely  in Swift, to implement the interface that deals  with this model object.

  To start off here I'm going to go to the bottom here  and check the plus, yet another way  to add new files to our target.

  Choose "Add Files Here" and I'm going to the media view folder  to select their project, mediaviewer.

xcodeproj.

  Once I have this in my project then I can just add this  to our target by going to our own project, selecting the list  or target and in the embedded binary section clicking  the plus.

  This lets us add the framework directly.

  Notice what we just did there.

  This is a new feature of iOS to allow embedded frameworks.

  Of course, if you're a Mac programmer you're probably used  to this by now.

  These embedded frameworks need to be made available  to Interface Builder, to other classes in your project,  linked into your binary so that you can actually run it,  and copied into the app so that  when other people download it they don't need  to download the framework separately.

  And Xcode 6 did all of that for us without asking  if the framework was written in Objective-C or Swift  or a mixture of the both.

  [ Applause ]  So in our storyboard now.

  We can zoom out, and.

.

.

  let's bring up the library here to bring  out a new ViewController.

  And in this case I'm going to set the class  of that ViewController to be the media ViewController  from the other framework.

  And you'll see the module field here populates as well;  this is that feature of Swift  that keeps different framework classes from stepping  on each other at runtime, even without a prefix.

  We don't need the view that Interface Builder provides  for us, because it already has one in its own framework.

  So I can just delete that.

  And now, to make sure that we can actually get to this,  I'll select the tables out here and drag over- ...

control-drag,  to create a new segue.

  Make that a push segue and we'll give that a name: "attachment".

  Now we've set up Interface Builder.

  We've set up the interface and the model object.

  It's time to put them together.

  And once again we're going to use a delegate for that.

  So let's go to our Objective-C code.

  This is the code that displays the lists.

  At the top here we have the NotificationCenter  which is a system framework and the import for ListerKit,  which is our own model framework within this project.

  I can add a third import for MediaViewer,  which is the framework in the other project.

  I'm going to hit Build again, to make sure  that everything is compiled and up to date.

  And now I get code completion  for the MediaViewerDelegate protocol.

  Just like before I'm going to jump down to a nice,  convenient place in the application to implement this.

  And the first thing I'm going  to do is implement a little helper method here  that just returns the selectedItem.

  Again, even though this is sort of details of, like,  how the application currently works, I want you to remember  that everything we're doing here is exactly what you do  in Objective-C.

  From the perspective of this code you can't really tell  that the entire framework was written in Swift.

  We even get code completion for the delegate method here.

  MediaViewController didChooseNewMedia, type.

  And now that we have all of this set up it's really easy  to add an attachment to the currently selected item.

  So here we say that its attachment is going  to be a new attachment that we create now  by calling alloc initWithData.

  Wait a second!  We designed this attachment class in Swift,  and it had an initializer named "initData, type".

  But in Objective-C it's showing up as "initWITHData, type".

  So you see that this name translation works both ways  so that you have a consistent world  in Swift and in Objective-C.

  [ Applause ]  I'll do a bit of housekeeping here to make sure  that we update our backing store  and now we have everything set up here.

  All that's left to do, just like Dave did, is set ourselves  as the delegate for the MediaViewController.

  So this starts off the same, where we set the attachment  and we set ourselves as the delegate.

  And then we can also set the title of the NewViewController  and make sure that it's showing the attachment that's actually  on the selected item.

  Or if the selected item does not have an attachment,  make sure that the controller isn't showing anything.

  So with that, everything should be working.

  Let's build and run.

  Let's go into the tech toys section here  and you see now we have these disclosure indicators  on each of the items.

  We'll go into Thunderbolt this way.

  Then I can see it says No Attachment.

  But I can click this Edit button using the ViewController  from the media views framework.

  That brings up a standard iOS control.

  Making sure that our privacy is protected, and, ah.

.

.

  This kid looks like he'd really enjoy a Thunderbolt display.

  [Laughter] Okay, we can go back to another one of these here;  managed to get myself stuck editing.

  There we go.

  And you can see that the image actually does persist  in the Thunderbolt display section.

  So it looks like everything's working.

  So you saw how easy it was, to not just deal  with a new model object- ...

or, sorry, not just a new object  in your application target,  but also a new model object in your framework.

  Additionally, this is not related to Swift at all.

  But in iOS 8 we now have embedded frameworks.

  And Xcode 6 will handle all the details of that for you.

  Yes, clap!  [ Applause ]  Finally, once again, we updated our interface  and added a delegate- ...

adopted a delegate protocol.

  And once again I want to impress on you  that it's exactly what we would have done had the delegate  protocol and the ViewController been implemented in Objective-C.

  We didn't have to do anything unusual or different  because it was implemented in Swift.

  So you see how easy it is with Xcode 6 to use user frameworks.

  And in general we want you to be treating those the same  as we do system frameworks.

  So that means in Swift you'll be using "import MyFramework"  and in Objective-C you'll be using  "@import MyFramework; (semicolon)".

  In general we do want you  to be using @import instead of #import.

  For Swift- ...

for frameworks containing Swift code this is  absolutely required.

  But even for other frameworks, if they've been built  with Xcode 6 and have the defined module build setting  turned on -- which is on, by default --  then you have- .

.

.

then you'll have a framework ready for use  in Objective-C or Swift.

  The only reasons now to use #import are if you are working  with an older framework that has not yet been rebuilt  with Xcode 6 in a defined module setting or if you're working  with C++, which does not  yet support the modules feature we introduced last year.

  So with that, everything seems to be working  and our walkthrough is complete.

  I'd like to hand it back to Anders.

  [ Applause ]  >> Thank you Jordan.

  So, over the past hour we've seen several different ways  of mixing Swift and Objective-C.

  Swift is a better, safer way to do Cocoa development.

  It was designed to support the Cocoa frameworks,  the Cocoa Touch idioms and the common patterns  that we already know.

  It's still Cocoa and Cocoa Touch on iOS.

  So that means that the knowledge and experience  that you've gained over the years  and the code you have applies to Swift as well.

  And as we've seen some of the patterns  such as optionally calling a delegate method are much easier  in Swift and much more succinct.

  And in other cases you've seen how Swift makes us define our  intentions much more clearly, which can help save us later  on so we don't just accidentally message nil  or message an object of the wrong type.

  And another thing that I hope you've seen here is we've put a  lot of effort into making sure that Xcode takes care  of the details so that  when you're using Objective-C your Swift code looks  like Objective-C and when you're using Swift the Objective-C code  looks like Swift.

  And you saw some examples of subclassing Cocoa Touch classes,  but that equally applies to your own classes as well.

  So for more information about this there is, of course,  the Swift book in the iBooks Bookstore,  but I'd also especially like to call your attention  to the Using Swift with Cocoa and Objective-C document  that we have on developer.

apple.

com.

  It's available as a PDF file that you can download and read  on your favorite iOS device and it goes  into a lot more details about this.

  There are several related sessions.

  One of them has already been but it's on video.

  It's the Introduction to Swift, of course.

  Later on today there's going to be a session  on Swift Interoperability in Depth and that goes into more  of the language details of how to mix Objective-C with Swift.

  There's a great session  on Creating Modern Cocoa Apps coming up and another one  on Building Modern Frameworks so that you can make sure  that you make your frameworks embeddable on iOS  and useable from Swift.

  Thanks and enjoy the rest of the week.

  [ Applause] 


 类似资料: