Fix Objective-C Imports
A way to change Objective-C
#import <Framework/Framework.h> to modern
Objective-C modules were first introduced with iOS 7. Modules came to live for a reason, for lots of reasons. They are designed to overcome shortcomings of current preprocessor, specifically the way
#imports are handled. You can find more information here and here.
If your app minimum deployment target is iOS 7.0, you are free to switch to using modules for all system frameworks, such as
Foundation and the rest. With iOS 8.0 and support for custom dynamic frameworks, even your own frameworks can be imported as modules, but in the scope of this article we will look at system frameworks only. In terms of syntax your change would look like this
A totally valid question to ask is “Should I even bother with fixing this?”. And the answer is “No, not necessarily”. As soon as you enabled modules for your project all
#import statements are automatically mapped to corresponding
@import statement. This is described in some detail here and here.
So, technically, fixing imports in legacy code is the question of code base maintenance and code style. It does look a bit off when both
@import are sitting few lines apart in the same file. Weigh all pros and cons before making a final decision. Keep in mind the fact that if you already have the script at hand, the conversion will take no time at all.
Xcode is know to offer code migration features, such as “Convert to ARC” or “Convert to Modern Objective-C Syntax”. However, converting all
@imports is not part of Xcode feature set. I would add “yet”, because I believe sooner or later Apple will proclaim iOS 7.0 as minimum OS supported by Xcode and force all imports conversion. Until that happens we need to handle this migration ourselves.
So what would be the recipe to fix imports for all system frameworks? Here’s one I have in mind
- Get a list of all system frameworks
- For each framework
Let’s solve the first part and get a list of system frameworks. Naturally, I googled first and ended up here. My desire to scrape the web page luckily died off right away, reading through the first paragraph I found out that all system frameworks are located in
That path looked a bit familiar to me and I knew I could get at least some of it with help of
xcrun, so this is what I got in the end
Then just append the missing bit and list the directory. Everything that ends with
.framework is (quite logically) a system framework.
Now I would like to turn this into a regular expression. What I want to have is a regex that says something like
UIKit OR Foundation OR iAd and so on, with POSIX regex syntax it will look like this
UIKit|Foundation|iAd. So I loop through results of
ls, drop the
.framework bit and create a string of or-ed framework names. My bash skills do not shine really bright in this example, but the code does the job.
The output is like this
With this regex pattern it is now possible to fix the imports. For the live of me I couldn’t win the battle with
awk and use regex back-reference in the pattern itself. This is not to say anything bad about
awk as such, this is just me lacking the skills. However, I still managed to stay purely within a realm of Unix-based system by using
This is exactly the case where back reference needs to be used in the pattern expression itself, not in the replacement. This is an illustration to explain what I actually mean
() parenthesis are capturing a group and then captured content can be back-referenced using
\2 and so on. In this example
(group-capture)/\1 used in pattern match literally means string “group-capture” repeated 2 times and separated by
Anyway, assuming the source file name is stored in
FILE_PATH variable, the in-place replacement with Perl looks like this
Here you can see that regex accounts for missing or varying number of spaces after
#import and that
\1 back-reference works perfect with perl.
This bit is almost the same as the previous case. In fact, regex is a little bit more common and doesn’t use back-reference in match patter, instead uses 2 back-references in replacement string. It is important to note that these perl commands must be applied exactly in the order they are described, so you don’t end up with
In fact, you know of course there are other modules as well, such as runtime modules, e.g.
This case is somewhat more complicated than fixing system frameworks. I played a bit with
--show-sdk-platform-path option of
xcrun command and tried other things, but overall there’s no straightforward obvious solution to this problem. You may have to fix these imports by hand or wait until Xcode automates it.
As a traditional TL;DR; part, I will just post the link to the upgraded shell script version.
blog comments powered by Disqus