We are living in the age of extensions. Apple is opening up so much API-s that I can not even count them. This is great, but how should you organise your codebase? If you are struggling with your project structure, listen to me I have the right solution for you.
We have four operating systems (iOS,
OS X macOS, tvOS, watchOS) and in Xcode7 we can create 10 extension just for iOS, one for tvOS and 8 for macOS. Of course the watchOS application is just a special extension for the iOS target. Ah, don't forget about your tests, those are targets as well.
As far as I can see, if you are trying to support multiple platforms you are going to have a lot of targets in your project. Of course if you create a new target that will contain some kind of source code or assets. Did I mention the schemes already?
Just take a look at Apple's Lister sample code. It's a very good example what's the problem with multi-target applications. It has 14 targets, 11 schemes, but it contains only 71 Swift source files. That's not much, but you can see the issue here, right?
You have to learn how to organise your
So my basic idea is to have a reasonable naming concept and folder structure inside the project. This involves targets, schemes, bundle identifiers and the source files on disk. Let's start refactoring the lister example to have a better understanding.
Name the targets like: [platform] [template]
- Always use the following platform names:
- Don't include project name in the targets (that's just a duplicate)
- Use extension template names from the new target window
- Use "Application" template name for the main application target
- Use "Framework" as template name for framework targets
- Order your targets ffs!!!
Examples: (WatchKit stands for watchOS1 targets here)
- iOS Framework
- iOS Application
- iOS Today Extension
- iOS WatchKit Application
- iOS WatchKit Extension
- iOS UI Tests
- iOS Unit Tests
Next change your schemes to this pattern: [project] [platform] [template]
Always write your project name first, because these schemes could be shared in a workspace. For the frameworks extend your project name with the "Kit" postfix. People usually prefer to use the Project + Kit naming for libraries so that's the correct way to go. I also like to separate the frameworks visually from the application targets, that's why I moved them to the top.
This one is hard. Code signing FTW! Let me give you an example:
- com.apple.lister (iOS Application)
- com.apple.lister.todayextension (iOS Today Extension)
- com.apple.lister.watchkitapp (iOS WatchKit Application)
- com.apple.lister.watchkitapp.watchkitextension (iOS WatchKit Extension)
- com.apple.lister.watchos (watchOS Application)
- com.apple.lister.watchos.extension (watchOS Extension)
- com.apple.lister.tvos (tvOS Application)
- com.apple.lister.macos (macOS Application)
- com.apple.lister.macos.todayextension (macOS Today Extension)
So here are the rules:
- start with the suggested com.company.project bundle for iOS
- use postfix for extensions like .extensionname
- use postfix for every other platform (macOS, tvOS, watchOS)
- watchOS1 target is an iOS extension not a platform!
- don't use more than 4 dots! (.today.extension is not a valid name!!!)
Try to sign your app and submit to iTunesConnect. Good luck.
The thing here is that I always create physical folders on the disk. If you just create a group in Xcode that's not going to be an actual folder and all your files will be under the main directory. You can go with that approach if you are not going to have the same file name for two different targets. It's a preference but I don't like to have a giant "wasteland" named as a project.
Anyway, let's move on to the scheme.
The general rule is pretty simple here:
- Create folders for the platforms
- Under the platforms subfolder the target templates
- Create a Shared container for cross-platform files
- Separate the items into Assets and Source directories
I hope that you can see that this Xcode tutorial is not only good for huge apps, but you can start working with it on small projects. Firstly it might look like an overkill, but trust me it worth it. Eventually you will face the issue of multiple targets or platforms and I really think that it's good to be prepared for that from your first commit.