Tools • iOS, OS X • 45:55
Frameworks are a great way to share code between your apps and their extensions. Learn how you can convert your static libraries into frameworks. Go in-depth on how to best organize and share your code between your projects so you can increase code reuse.
Speaker: Chris Parker
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
Good afternoon. Thank you. Welcome to what they tell me is Session 416, "Modern Frameworks". Today I'm going to be talking a lot about mostly the mechanics of setting up modern frameworks on iOS, some of the API design principles that we use at Apple and that we recommend that you guys use. If you were here for Andy and Collin's talk just before this, they really delved into a lot of the deep architectural components of how to build your application.
I'm going to cover some of the higher level stuff and then we'll go through a quick tour of Xcode and talk about how some of these things get build directly into your app and just some of the mechanics of getting all of that set up. So, how many of you have actually used static libraries in your application that you get from a third party? That's everyone. Okay. If you're doing it without one of those things you're probably using the target management mechanisms in Xcode.
Right? You download a bunch of source code files, you put them someplace on your drive, you hope you remember where you left them, and you're messing around with this, and you're shifting things in and out of targets, you may be even taking them wholesale and dumping them directly into your application so now you've got six or seven copies across all of the applications you're working on. Things like that. The next step up from this is the Static Library, right? The Cocoa Touch Static Library.
It's a .a, you still have to put it someplace. You're probably checking it into your sources. The .a is separate from the header, so you need to put the header someplace. You can't carry assets along with it so you don't get to do things like have a bunch of nice pictures or customized controls and things like that in your application.
So, this is good but not great. In iOS 8-so actually let me back up for a second, OS X developers, you guys are here? All six of you? No? Okay. How many of you have actually shipped a framework? Wow, okay, a good number. Great. iOS users you can do it now, too. There are some restrictions, though.
[ Applause ]
So we'll talk about that. One of the reasons that you're going to use frameworks is to be able to package absolutely everything up that you need as a single unit. Right? So this is going to include headers, it's going to include images, asset catalogs. You name it. Throw it in the framework it goes along as a single unit.
You'll be able to put those frameworks someplace on your drive, keep track of them. Xcode will take care of moving those things into the right place on your application builds. Right? There are a lot of really neat features that have been built in for iOS 8, especially in the face of the some of the new features, to be able to make the workflow for frameworks really effective for your iOS 8 apps.
Why do you want to use frameworks? Well, all of the packaging stuff is really nice, but all of the features that are in iOS 8 take advantage of frameworks. So the first thing you probably want to try using is live views, they're the live rendering of views in Interface Builder. So if you're putting your things into a framework all of that custom-rendering, all of that custom-drawing becomes available to Interface Builder for use directly in Xcode as you're editing your NIBs and your storyboards.
I guess that shows my age, they're all storyboards now, right? It's not NIBs anymore. If you're using extensions, right, you're going to want to use frameworks because you're going to be able to share a code between the extension and the application without having to duplicate stuff in each place. Right? So you won't be putting things in the target for the extension and the target for the app, you'll put it in the framework and link the two against the framework itself.
If you write multiple applications and you're using either a third party framework you're getting from someplace else or you have a bunch of your own tried and true tested code that you like, all of your categories on NSString-because, you know, who does that. But, okay, categories on a NSString, and things like that. If you're sharing code between multiple applications you're going to want to use frameworks.
And obviously also, if you're going to be publishing a framework, so if you're going to be making all of your code available as a framework, you're going to want to be able to package that up in a single distributable thing that everybody can download and use and you can version those. So, let's talk a little bit about getting set up.
Xcode makes it really easy to create a new framework. There's a new template right inside of the new file dialogue or the new project dialogue that says Cocoa Touch Framework. Right? So you can just go click on that, click on the Cocoa Touch Framework, it's going to walk you through the same form, give it a bundle ID, things like that. Save it someplace, you're off to the races. This also works if you have an existing project and you create a new target and one of the new target templates is a Cocoa Touch Framework, right.
So what goes in it? Once you decide you're going to use Frameworks, what are you going to put in there? If you're using live views, if you're interested in frameworks for live views, you're going to want to put the views in there, and the controls, and anything else that goes along with giving your app that custom appearance.
Right, so these are going to be all the assets, in the views it'll be all the drawing code, the controls, again, it'll be assets, things like that. They all go in the framework. That way, now, when you link it for using with your application or giving it out to all of your friends and neighbors and trying to use it within Xcode for IB, all that stuff just starts to work.
What goes in if you're using extensions? Well you're going to put all of the same stuff that you would've put in for live views, the views and controls and the custom appearance because your extension probably has all of that. You're also going to put in things like view controller classes, right? The custom view controllers that you're writing and you want to make available as API to everyone else or to your extension and to your application, those are going to go in there, as well. You're probably also going to put the service API that you're using down there. So if you're doing networking code, things like that, all of that stuff's going to go down there. Resources, again, assets, configuration files that might be particular to your service go down there.
Anything that goes in common to your extension in your application. So, that's also true if you're going to be using framework contents between multiple applications. Right? So you've two or three applications in the store, your users are going to go ahead and download those applications from the store.
You're going to want to put all of the stuff in common. Right? The idea here is you want to be able to change the source code once, build a new version of your framework, make it available to all your applications on your machines so that when you ship them off to the store, all those changes get picked up.
And when you're publishing frameworks, again, it's the same thing. So, really what you're going to wind up doing is refactoring your application using a lot of the architectural things that Andy and Collin were talking about in the previous hour, pulling those into an organizational unit, like the framework, in order make your development a lot easier.
The app right now, if you're using extensions, for instance, you've got an application, it's going to have two parts. It's going to have the app and the extension. That nifty little building brick there is the extension. Right, so they sit next to each other inside the application bundle, you can think of the extension as basically a little tiny application.
All right? You've got a bunch of source code in there and it probably shares some source code and Swift files, maybe some Objective-C files. Those things are all going to be part of the target, right? Rather than have the object code generated by those things getting duplicated into each of those targets, what you'll do is just pull the common stuff down into the framework and link the application and the extension against the framework.
How do you decide what goes down there as far as code goes? If it looks like it's showing up more than once in a target or more than once in your application, it probably goes in a framework, right. We do this at Apple. We look across all the applications that we're developing. We start seeing patterns.
We start seeing things that are repeating themselves. So we take those patterns, we pull them down in the framework. They either go from the apps in the UIKit or they go in the apps in the AppKit on OS X, or they go down in the Foundation, or they wind up going down into Core OS.
There are lots of different places where we start pulling common code up out of apps and make sure that they wind up at the right level of abstraction for use everywhere else. Right? So, a really good technique in terms of separating out everything that you're doing in your applications is doing the same thing. Figure out what's in common, put it in the framework, use the framework as the unit of distribution for pulling all that stuff together.
Frameworks are API. You heard Andy and Collin talk a little bit about it yes-yesterday-earlier today. Identifying the responsibilities, figuring out who should be doing what within your classes. Right? Some guy on the internet wrote a thing about being ruthless with your objects and making sure that there are these clean, bright lines between where those responsibilities go. It is a great technique and it will make a lot of your development easier to take the same kinds of framework approach to developing your application because now you get really separable code. You get easily testable code; you get very, very good abstraction layers.
Once you've decided you're going to be offering frameworks to your application or to anyone else, you're going to publish a framework as API for your service, you know, providing all those UI view controllers that go along with it. The keyword there is it's API. The key thing is to make sure that you get the API right.
We spend weeks arguing about things on mailing lists at Apple about how to develop the API, what the architecture should look like, what things should be named, all of that stuff. There is some Cocoa guidelines that we use, I'm going to run through some of them here. One of them is that it's much better to be clear than it is to be brief. Right? So, a method like this in Objective-C, insert:at: is very brief, it's very terse.
What are you inserting? What is the "at" that you're inserting it to? Don't do this. It's not clear enough, it's not specific enough, it doesn't help you, because, you know, we all write this code, what, once, twice. We're going to read it thousands of times. I'm going to hand a whole bunch of code off to some kid who's younger and smarter than I am and he's going to have to figure out what I was doing.
Right? So, be clear, express your intent. insertObject:atIndex:, right? What are you inserting? Some object. Where is it going? At an index. Right? Now you've got a very specific guarantee. You've got a readable chunk of code. This is the kind of thing that we will literally spend a week in email arguing about to get right.
API design is like a contact sport at our company. So, if you're not really throwing some elbows in the mailing list with your friends and neighbors in doing API you're probably just not having enough fun with it. So, do this. Be really specific. Don't abbreviate, right? Don't make your selectors look like line noise. All right-setBkgndColor: We have code completion, it fills it out for you, you can get a couple of keystrokes into it and it'll tell you, oh, look, you know, "setBackgroundColor:".
Right? It's long, it tells you exactly what's going on. Do that. Don't be ambiguous. Right? There are a lot of words that can be used as a verb or a noun. Here's one of the ones that I actually proposes and managed to get by and now regret, "displayName:".
Right? You see this in a couple of places, I think it's on NSFileManager these days. We try not to do this anymore. Is display a noun or a verb? Am I telling this thing to show the name to somebody or is it a name intended for display? Right? What we do these days in the Kit mostly is anything that's intended for the display to the user is a localized name. Even if you're getting as input from the user. Because if you're getting it as input from the user, it's pretty much as localized as it's going to get. Right? It's coming from that.
If you're showing it to the user and it's in your strings files and things like that, it's the localized name, it's coming out of the bundle system to have the localization. Right, so our intent was to be able to say, we want to show the users something in their language. It's for display. And then everybody went, well, why-I'm calling displayName, why isn't this drawing? Oops, yeah, let's not do that. So, be clear.
Also, don't take advantage of some of the things that Objective-C in this case lets you do. For instance, you could declare sendAction like this. And you don't necessarily have to give names to the parameters. So this produces a call site that looks like sendAction::, you know, sendAction:- Selector:, object:, flag. It's not specific enough, what's the object intended to be? What does that flag actually mean? Right? Fill the whole selector out.
sendAction, toObject, forAllCells, YES or NO. Right, that flag could have been anything. It could have been animated, it could have been, you know, stop transmitting the message after you find a hit or keep going. Those kinds of things have to be spelled out. Again, you're going to write it once, somebody's going to come by later and curse your name because they have to read it a thousand times after that.
Don't be that guy. Do this. Swift, though, brings us some really interesting opportunities to be clear, to be expressive and still try maintain some of the terseness and the less typing and things like that that we'd like to do. And some of the things that I'm doing to talk about as far as Swift goes are a little bit different because, for us, we're still trying to figure out what some of the really good API idioms mean and we're really not sure what it means to have a language idiom for a language that has had about 200 native speakers for less than a year.
So, trying to figure out sort of what some of this stuff is going to wind up looking like is a little bit of a challenge. This is actually a great opportunity for all of you to be able to provide us feedback with Swift in terms of what the API looks like and how your experiences in using it both as a native language and through the mix and match bits with Cocoa and Cocoa Touch, and let us know exactly what you'd like to see and what makes it easy to use Swift with all of these things, versus what makes it feel like there's a little bit of friction there.
All right, so please make sure that you're filing those radars on how you'd like it to work. Let's take a quick look at a Swift function, or I pulled this out of the examples in the book but I'm going to sort of work through those and then we'll extend it to something else. So containsCharacter string: String character: Character. So a Swift function has named parameters, in this case these names are the variable names that you'll use inside a function to describe the variables.
Right, so this produces a call site that looks like this, containsCharacter, "Buddy", "u". Right, there's a string, there's a character. If you tack a little hash mark in front of it, this is not a social media construct, you don't get to find out what's trending in your code [laughter], although that might be an interesting question. Right? How often do you use the hashtag character in your code? This allows you to be able to use a named parameter at the call site and be specific about what it is. This produces a call site that looks like this. containsCharacterstring: character:.
Java code- you've got method overloading, you can wind up with a method that's one word and it has 19 different sets of parameters. Some of them have, you know, four arguments, six arguments, 12 arguments, nine arguments. Some of them are the same, some of them are different. What arguments are they? These named parameters are a great way to be able to actually to tell at the call sites which ones you're using along the way. It's a way to be expressive. It's a way to be specific. Right? One of the really nifty things about Swift is the mix and match feature, being able to take code that already exists, you've already got a ton of Objective-C code.
In terms of practical terms and shipping applications and things like that, you'd be nuts to throw that away and try to do it all over again in Swift. It'd be fun if you had the time but you probably don't, you're trying to ship, right? Mix and match lets you do things like this. This is the popover presentation API from UIKit.
It gets converted into something that looks vaguely like this by the mix and match system for Swift. Right? So, got a function, presentPopoverFromtRect rect: view: arrowDirections: animated: That produces a call site that looks like this. Okay, again, not specific enough, not a really good description of what's happening, right? What's .Any there? I have no idea. What does true mean? I mean someRect and someView are at least descriptive enough, but that doesn't really help me out if those aren't my variable names.
Right? If we re-declare-oh, and then we've got presentPopoverFromBarButtonItem and that just throws the other wrench into it. Right? Which is okay. Well, now I got barButtonItem, but arrowDirections and animated are still there. We can be more specific about these things. We can take advantage of function overloading, method overloading, and say, let's call them both presentPopover, what the heck? All right, but now we've got this really neat way to be able to separate out the other API from, you know, Cocoa and Cocoa Touch, and make these two different. So what do we have here? I've got fromRect rect.
Right? So fromRect is going to wind up being the external name that you use at the call site, rect is going to be the variable inside the method, the implementation variable that you're using. It's the parameter that-it's the name that you use for the parameter that gets passed in.
inView view and then the hash marks there again, that makes it possible to use the same name as the external name, as the internal name. Those produce call sites that look like this. Right? So now I've got presentPopover fromRect, inView, ArrowDirections, Animated, and presentPopover fromBarButtonItem, arrowDirections, animated. Great, I can tell the different between them, that's awesome.
That's a lot of typing. Code completion's nice but that's still a bunch of typing. Swift has some really nice features for default arguments in your API. So let's go back and take a look at just the presentPopover method. Okay? If we re-declare it like this, what we've got is UIPopoverArrowDirection is the type, and .Any there is the default value.
Right? Swift, when it goes through the mix and match bits takes the common pre-fix off of the enums for the options and makes them the shortened version at the end, which is one really nice thing about when we went back through and started normalizing all the enums again and the common prefixes allowed us to do this.
So, .Any, as passed into a popoverArrowDirection type, is that particular entry out of the NS-ENUM argument. And Bool gets true. There is a way to say that YES. if there's no argument presented we want to be able to have this thing be true all the time. So that turns these call sites, which are nice, they're descriptive, into these call sites.
Right? One of the patterns that you see in foundation is you'll see a really long method, it'll have probably, I don't know, anywhere between six and eight to 12 arguments. And then you'll see versions of that that are shorter. Generally you'll see that the end arguments of an Objective-C sort of start to get peeled off. Those shorter versions are calling into the longer version with default values.
Right? Those are all generally one-line implementations that call back out to the big guy with all the switches on it. Right? This is a way that you can provide all of those switches without having to provide all those methods all the time. Put in the same default values.
Right? This provides you with some really good opportunities to be able to clean up your code, be really clear in Swift, and doing this in Swift, talking to Objective-C APIs, you can start really thinning down the API surface so you don't have tons and tons and tons of methods to keep track of. You've got one place to do these implementations and the default values just go in the right place.
There's really good documentation online, the "Coding Guidelines for Cocoa" at developer.apple.com. If you just go to developer.apple.com, log in, search for this document, it's the same coding guidelines that we have up on our internal wiki for how to get started with developing API. All right, so the rules that we're trying to put down there, they're the same things that we use at Apple.
So another good reason to go and look at these is all of the Objective-C API that you're going to write, if you're not just going to chuck it over and go for Swift entirely, all of that API will work well and fit in with the idioms that we already have for our existing Cocoa frameworks. The other document you probably want to read that I'm sure you've all downloaded already, "The Swift Programming Language." I wouldn't be surprised if all of you suddenly have more experience with it than I do by this point.
It's available on iBooks, it's also available online at developer.apple.com. It's got all of the information about Swift in it and they do make some API recommendations in there as well. Again, we're all sort of just figuring this out. So, if there's something that you see that makes a lot of sense, great, call that out, tell us because we'll make sure that we try to keep it. If there's something you see that you don't like or that you'd like to make suggestions of, send them in.
Take advantage of bugreport.apple.com, get the radars in on Swift because this is really your opportunity to have a lot of influence on how all of this stuff develops. If you're publishing a framework, whether it's for your own applications, or whether it's for other people to use in their own applications, you're going to run into a problem. This is a problem that we run into every time we release a new version of the operating system and that's framework version. Right? So your user has gone and downloaded a whole bunch of apps. Darn them.
Well, I mean, good for you guys. They should download all of your apps. All of those applications, let's say, used the latest version of this new framework that just came out and it's version one. So for a while at least you're in good shape. Everybody's got the same code running, if an application sends information to another one via an extension or via the open URL call, things like that, hey, all of the objects work together. No problem, right? Awesome.
And then, you come out with a new version of the framework and some applications update to it and some applications don't. Or some of the applications just got developed and then abandoned and they'll never update. Right? So some applications on the user's device are going to be Version 2, the blue ones. Some versions are going to be Version 1.
And now suddenly I'm trying to send an object to that other application from a Version 2 framework to a Version 1 framework or from a Version 1 framework to a Version 2 framework. Not everything's the same. Those objects have grown new capabilities, the data that you're passing back and forth may have new keys in it.
Right? One of the things that you're going to have to do if you are going to be publishing a framework is be very careful about the versioning. So, a number of things that you can do about dealing with framework versions. First, you're going to have to put a framework version in there anyway.
The framework mechanism when you create a new target or when you create a new project for a framework, that goes in the info .plist. There's a short version string, keep track of that, use that. That's information that you can use in order to figure out what's happening, what version of the framework were you linked against, what version is the data coming from? You can send that data to something else to say, hey, I'm a Version 1 thing, I'm a Version 2 thing.
The other thing you should do probably is take advantage of keyed archiving or keyed archiving-like behavior. Right? So if you are doing key-value stuff as part of your archiving or part of your document format make sure that your key-value pair meanings are consistent. Right? Don't suddenly for a Version 1 key that might be background color, don't suddenly make zero mean white instead of black in Version 2.
Because then suddenly the user's going to come along and you're going to get sent some data and they're going to say, "Wow, this is completely rendered wrong. What happened?" Right? So once you've committed to what a key-value pair means or what a relationship between two key-value pairs mean inside those archives that you're sending back and forth of different versions of yourself, you really kind of have to stick with it.
Right? We do this at Apple also. There's this whole set of keys that have sort of come along to make sure that when we open up archives from older versions of the system that we can continue to read that data. You do get lots of interesting opportunities at read and write time to be able to deal with that. So, one is missing keys should return 'nil' or the equivalent. In the face of getting back 'nil', you probably want to do something sane.
Right? If you're looking for a key and it's not there, maybe that's a great opportunity for some default value to get injected in at the time that you're on archive. Right? And that's really the opportunity to be able to do that. Because the encoding and the decoding are your chances to get at that interface.
When you're writing that data out, you're going to be writing that data out for consumption by somebody else. When you're consuming that data you kind of have to look at it and go, well, I'm not really sure where this comes from so I should at least be wary of it.
Right? If you're encoding things and you know that there's a bug back there that you need to fix back in Version 1 and you can do it by encoding something different, you might do that. If you're looking at situations where you're pulling new keys in, the old code isn't going to go looking for those new keys, so it's going to ignore those things, you're probably pretty safe.
The new stuff you want to make sure you're not stomping on old keys, you're not interfering with the relationships between keys. When you encode and decode that's your interface. I know I'm writing out four something that's going to pick us up and I don't know what version it's going to be. Or, I'm getting data back in I have no idea where it's coming from.
Right? Make sure your keys are unique, right? If somebody's sub-classing your stuff and they're sending stuff back and forth but they're depending on your framework, you don't want to actually stomp on their name space. Right? So the keys, even though we're getting some name spacing behavior in Swift, the keys in these kinds of things aren't name spaced at all. So make sure they're unique. Use the prefixes. If you want to be really defensive, use your bundle ID, prefix it there.
One of the ways you can also mention this, I didn't put it on the slide, one of the ways you can also work with this is by conforming to NS secure coding. Right? Secure coding will make sure that you're only getting the objects out of the archive that you're expecting to be there.
Also, start looking for feature availability. Right? Classes respond to selectors for a reason. If you're in some code and you're using somebody else's framework you want to make sure that your code is going to keep functioning if something gets updated. Check to see if it respondsToSelector. Check NSClassFromString.
Do those things in order to make sure that when you're working with all of this stuff you don't wind up accidently pantsing the user and killing their app because of sending a message to a string or a variable that doesn't respond to that message except in Version 2. Okay, this is all awesome, great. How does all this work? Pretty well, actually.
If you follow me on the social networks of the day you probably know that I had a dog. This is my dog. This is Buddy. He's got a 60-pound bark and a 30-pound body. He's about this tall. And he's cute, and I post a lot of pictures of my dog because I can't really post anything about work [laughter].
So, a lot of people like to say, "Hey, Chris, your dog is awesome." You know, "Can I borrow your dog?" "I'd love to meet your dog." "Your dog would be a great part of my life." I'm not giving away my dog, sorry. You can't have him. But I am pleased to talk a little bit about a prototype that I've been working on. It's called Budstagram and it's a way that you can take your pictures and put my dog into them [laughter]. So --
[ Applause ]
What we'll do is, I'm going to take a quick tour through Xcode both on-for what I've done so far with this and just to walk through the initial setup of an extension and an application and a framework. But you're going to be able to do this kind of thing. The idea is-but this is a terrible picture isn't it? This is a beach in Corsica. I went on a biking trip a while back.
Our ride wound up here. It was absolutely gorgeous. We were going to try and stay a couple of days but they weren't going to let us do it. But this is a great picture but I really missed Buddy. I'd been gone about 10 days by this point and it's a great picture but now, now it's an even better picture having been photo bombed by my dog. So,
[ Applause ]
We're going to take a look at three things. Budstagram, the application, is sort of the icon that the user sees, it's the place that they're going to start. Right? It's going to allow posting and editing of photos, and as long as there's a picture of Buddy in there it'll post. We'd like to allow some comments on existing photos, sort of a feed kind of mechanism. You may have seen apps like this before somewhere, I'm not sure.
And we're going to have it share a posting view controller with the extension. Right? We want to share that code so it winds up in both places. The extension is going to allow posting and editing of photos within other apps, but it's not going to allow comments on existing photos. We're not going to put the feed in the extension. That's the business of the application. And that's actually one thing about this architecture in the frameworks and stuff like that that you want to be really careful about.
Right? You don't want to wind up with your entire application suddenly living in the framework because not all of your applications can be appropriate for the extension or not all of your applications can be appropriate for publishing as a third party framework. There's the things that are common to your service and there are things that make your app unique.
Right? So you want to try and keep those two things separate. And it's going to share the posting view controller with the application obviously. The framework is going to contain the shared posting view controller and the shared networking code for posting and things like that. And the model code. Right? All the stuff that makes up the common things that the extension needs to know to be able to deal with the objects that the applications also deal with. So we have the Budstagram application here.
And if I tap on it, there's Bud in Corsica. This is the feed here. And if we were going to post something here, this is a picture of my parents' backyard in Connecticut looking very New England in the fall and it's very pretty and I can't bring Bud there because he doesn't like to fly.
We've got this app, we've got the extension, they work together. You're going to be able to take data from the extension, throw it into the application and the code that you're going to share between the two, you're only going to use in the framework. So you don't have to be jocking targets, you don't have to be shuffling stuff around, things like that. So, let's take a look at Xcode.
So let's take a look at a new Xcode project. I'm going to start from scratch here mostly to give you sort of an orientation before I start jumping around in Budstagram to show you what I did. So, what we've got here is the start of, say, a tabbed application or a page-based application.
Let's go with the single view application here just for the basics. We'll call this "framework example" because I'm not particularly creative. And we've got a language choice here, I'm going to stick with Objective-C for a minute and we'll just-oh, I am not an organization unto myself, so we'll call that Apple, Incorporated. Desktop will create this.
So we've got here now is just an application. Right? And there's some different things here, there's some build settings, and build phases, and all this stuff that you're familiar with. You'll see the link binary with libraries bit here. And if you were just going to have an application this would be fine, you'd work with this and you'd put all of your code in and your tabs and you'd work with your targets and things like that, but we're actually going to go ahead and create a new target here. And we'll create a new framework and library target, a Cocoa Touch Framework. We'll call this-let's call it Budstakit.
And you'll see two new things here. It says here the project is the framework example here, and "embed in application": framework example. So, we're creating this new framework and Xcode's also going to set it up so that when a framework builds and then when you go and archive it all that stuff winds up in the right place. So we can do Finish here. And now we've got the link binary with libraries there. Budstakit framework is required.
And let's go ahead and actually create the extension here too while we're at it. So here's an application extension. We'll use a share extension. Okay, and this'll be the BudstaPosting extension. Okay. So now we've got BudstaKit, BudstaPosting. The BudstaPosting bits have the share view controller in here, and a storyboard for that. Let's see.
We pause for station identification. So there's nothing in BudstaKit right now. There's nothing in the-there's this sort of empty share view controller in here. And we've got this ViewController for the framework example for the application. One of the things that I mentioned before was, I wanted to be able to have the share view controller be part of the framework. Right? Because I want to use that both in Budstagram itself and in the extension. BudstaPosting is the extension.
All of this stuff, if we go look, let's take a quick look at the Finder here. Actually, if we go look in the framework example folder, right, Xcodes are organizing all the code in directories here, but the directory-obviously directory hierarchy is not necessarily reflected in how Xcode shows that in its files and groups thing here. But we're going to just move these guys up into here.
So I'm just making the share view controller part of the BudstaKit group, it's not actually moved out of the directory that it's in behind the scenes. But, you know, if you feel like you want to do that go ahead and move it with your version controller system over into the right spot. Here in the BudstaKit header I'm going to go ahead and, it says here, in this header, you should import all the public headers of your framework using statements like import BudstaKit PublicHeader. So let's do import BudstaKit/ShareViewController.
Hello code completion. And then we need to make sure that we change these to be part of the right targets. Over here on the right-hand side, ShareViewController.m is part of the BudstaPosting target, we actually want to make it part of the BudstaKit target. And the thing that you're going to do with the frameworks is different than what you've probably been doing before, is within a framework there are some designations for what a header means.
So, you're actually going to wind up adding the header to be part of the target of the framework, which isn't necessarily something you've really done before. Right? Usually you use the headers, the .m's, or the .s's are part of the framework-or the target, the headers are just loose, they never wind up getting distributed. So since this is a framework and you're going to hand it out to people, you're going to be handing it out to other developers on your team, you're going to want to tell us whether this thing is a project header, a private header, or a public header.
Right? So the project header means that only things within the project can see it. The private header goes-anything that's marked as a private header goes into a private header's directory inside the framework when that framework gets built. Right? The public portion of it is obviously the public headers, it's the headers that are just in the headers directory there. I'm going to make this a public header.
You might use private headers because just like us you've got SPI that you don't want people to use but it's really handy to be able to use it internally or across two projects, and maybe that SPI is something you want to publish later but it's not quite bulletproof enough to be able to be API now. You'll put that in a private header. If you publish your framework, don't forget to remove the private headers directory from the framework, because then everybody can grope all over it.
So, and then down here in the BudstaPosting thing, well, we're going to have to edit the target. So, let's go back up to this framework example here, and if I look at BudstaKit, BudstaKit is linking with no libraries. The framework example is linking with BudstaKit. I need to make BudstaPosting link with BudstaKit, so what I can do is just drag this guy here-no, okay.
We'll pick a plus, we'll choose BudstaKit from the project, and now all of those things will link together. So now ShareViewController is now part of the framework. Since BudstaPosting, the extension links the framework, and the app links the framework, they can both use ShareViewController. Let's go ahead and build that for the simulator.
And then let's take a quick look here at what actually got produced. So, if we go look at-I moved my build directory up here on the desktop so it would be easier to find. And if we go look in products here, we've got a couple of different products based on our target configuration and our target.
Here's sort of everything spread out. Right? So here's BudstaKit.framework. Here's the app. Here's my Budstagram application that I was building earlier. Let's look at framework example. Inside that is now a couple of different directories. This frameworks directory has the BudstaKit framework inside of it and let's get all old school and go look in the Terminal here.
The old tricks are the best. And the BudstaKit framework has inside it this headers directory and a module. Modules are generated for use with Swift and for the module at import directive for Objective-C. There's some nifty things that go along with that. But those are things that you should read about in the Xcode documentation.
So, the framework is built into the application, that's actually one thing to be aware of is you're going to have multiple copies of the frameworks on disk at a time. We're not sharing frameworks across sandbox boundaries. Right? Things inside the same sandbox could access the same framework, but we're not letting you use the framework out of some other application installed even if it's one of your own. All right, so all of these things get embedded directly in the application. So your apps are still completely standalone items.
So, let's go back to this and take a quick look here. Because the extension is its own little application some things you need to be aware of are things like NSBundleMainBundle isn't going to work exactly the way you think. So you're going to have to go look for assets by bundle identifier using the bundle API.
No, that isn't a bug I ran into myself that I introduced, why do you ask? So, and the ShareViewController has the standard extension stuff here for things like isContentValid, didSelect. These are peculiar to the ShareViewController but there are other API on all the other extensions that are specific to those as well. So let's actually take a look and see how I sort of divided up BudstaKit and Budstagram. So, there are a lot of really similar things here to the way everything got broken down.
Not really tall enough to point up there but you'll see that we've got the feed view controller and a posting view controller, which are the two things that you saw in the tab view in the demo earlier, and then the main storyboard contains those two things. And then there's a bunch of model code that's down in BudstaKit, and BudstaKit has all of the stuff like the public umbrella header. Right? So just like, you just type importUIKit.h, you should set your umbrella header in your framework to do the same thing. Put all your public API in there so you'll import BudstaKit.h. Or at least I hope you will someday.
And then also, all of the model codes down there, so the thing that manages a post is there. And the post view that displays a post with the dog resizable there and the ShareViewController and things like that. And then down here in the Budstagram poster, again, it's the same thing.
It's just the main interface storyboard, the empty storyboard, which says when you call up one of these things this is the storyboard to launch into. Right now in the extension that's it, right? There's other logic that lives in the framework that does all the other work, the sending of the data and the manipulation of the photo and things like that.
So making sure that you're pulling all of these things down into the right place is very, very, important. Most of those 13 years I've spent doing frameworks, I've worked for a lot of really talented frameworks developers, a lot of this stuff is tremendously hard-won lessons from shipping software.
Right? Being clear, being explicit, having the right amount of API, having expressive API, distributing things in the right way, being careful of your versions, making sure that you're working with data safely, getting the right abstractions. Right? All of those things make our lives much, much easier. Taking that frameworks approach even to the applications. If you look at most of our applications, on the books, you know, Maps has MapKit. Right? On iOS 8, Health has HealthKit.
Right? All of the things that make the app function and useful to the user live in the app. All of the heavy lifting, all of the shared code, all of the interfaces that allow the things to work with your app go in those frameworks. Right? It's a great separation for us, it's what helps us ship software, it can be a great separation for you as well. There are some related sessions that happened earlier in the week and there's one tomorrow. "Creating Extensions for iOS and OS X", Parts 1 and 2 were earlier. Go watch those for breaking up things into the extensions and into the mechanics of setting extensions up.
Right? And then apply some of the stuff that you got here or some of the stuff that you got from Andy and Collin's talk previously, in the previous hour, to all of that. And then sharing code between iOS and OS X. A lot of you work on both platforms and a lot of the model code and things like that is actually shareable. And much of the extensions API was developed specifically with an eye to being able to write as much cross-platform code as possible with as little of the #ifs for target OS as we could manage.
Right? So you will be able to do a fair amount of code sharing even for extensions in iOS and OS X. That's right here tomorrow morning at 9:00 am. Bright and early. Okay? So, please have an absolutely fantastic remainder of the week. Enjoy the bash. Thank you, very much.
[ Applause ]