Why Swift versions are so tightly coupled with XCode versions? - swift

Sorry if this has already been asked, but would like to understand in detail as it has created huge dependencies on all my Swift apps.
With most of the Swift version updates there is Xcode version which is tightly coupled with it.
For example if I create a framework with Swift 4.0.3 using Xcode 9.2, it does not compile with Xcode 9.3/9.4 or upcoming Xcode 10, which I understand as Swift itself is evolving and no complier can provide forward compatibility.
But would like to know any one of you treat this problem in different way is there some short route that I am completely missing.

This situation will get a lot better after we reach Swift 5. Until then, all you can do is live with it, updating every time you update to a new version of Swift. Two suggestions:
Your life is made a little easier in recent Swift versions because you can use conditional compilation with #if and a condition like swift(>=4.2) or (starting in Swift 4.2) compiler(>=4.2), so that you can talk different ways depend what version of Swift we're compiling under.
Language version is a build setting, per target. So the coupling is not completely tight; if you're using older code, you can continue (to some extent) to use an older version of the language. This screen shot is from Xcode 10, showing that you can compile Swift 4.0 code:

This is not necessarily an answer of sorts, but you do have the ability to work with varying Swift versions in newer (see this answer for more detail). My approach has always been to keep up to date with the latest Xcode releases, be them via the Mac App Store or Developer Downloads, and run a copy of my project to determine what changes are coming.
The changes between earlier versions of Swift (1.0 -> 2.0) were incredibly jarring. The changes from Swift 4.0.3 -> 4.1 (or even 4.2) are not as drastic, and while still critical to the development of the language, Xcode now does a far better job of suggesting how to update your code.
In short, there is not necessarily a short route (that I'm aware of), but you may find yourself in a better position by constantly evaluating your current code in newer Xcode versions and deciding how and when to make the move to the newer language.

Related

Conversion from Swift 3.0 to 4

How can I convert my code that I wrote a few years ago to Swift 4.0 without using XCode 10.1? Is there any way to run it on some online simulator or on the newest version of XCode? This is some code that I wrote for messing around and seeing what features the touchbar has. I get this message when I'm trying to open the project.
I'm not aware of any automatic migration tools other than the one that Apple provides in some versions of Xcode. I think your choices here are:
Use migration assistant in Xcode 10.1 (which is still available from developer.apple.com) to update the code more or less automatically.
Rewrite the code yourself.
If it's just code for experimenting with the Touch Bar, why not migrate it yourself? There are lots of resources to help you figure out what you need to change. While you're at it, you might as well move all the way to Swift 5.7 -- you'll be modernizing both your code and your knowledge of Swift.

Is there a way to avoid converting Swift from 2.2 to 2.3 or 3 using Xcode 8?

I upgraded the Xcode's version to Xcode 8.0, the error that I'm getting is that it forces me to convert the syntax, before the update I'd 2.2 version, but now it requires the version 2.3 or 3.0, and when I try to convert with the Xcode converter, it breaks the entire project (too many errors, and the converter commits lot of mistakes).
Is there a way to use the 2.2 version without upgrading ?
With Xcode 8 you can upgrade to Swift 2.3 and very nearly everything will stay the same. Your project shouldn't break and your changes should be minimal.
You'll need to go into the target's build settings and set the option Use Legacy Swift Language Version to Yes, which will allow you to keep compiling Swift 2.3
Note that the Xcode parser will complain while indexing about various things that aren't Swift 3 compatible, but the warnings will go away once you build successfully.
If you already have upgraded to Xcode 8, it will always ask you to convert to either Swift2.3 or Swift3
#par above shown you how you can just keep compiling on Swift 2.3 using the Use Legacy Swift Language and likes what others comments on your question, you will have to upgrade it sooner than later.
However, to answer your question of avoid the converting to Swift2.3 or Swift3. The way I am using is download the old Xcode7.3.1. You can look for the download here. Do not replace your Xcode8 but just maybe drag it onto desktop or another directory.
I have some project that depend on other libraries and also previous client project. Therefore I found that using Xcode7.3.1 to open such project is easier and more convenient.

How does iOS 7 implement the UI elements so that they look different depending on what SDK you compile with?

There are elements like UITableViews, UINavigationBars that have a different style on iOS 7.
This style is determined at run time, since those classes are implemented on UIKit, and UIKit is linked with your application dynamically at runtime, not statically at compile time.
So one would think that any app run on iOS 7 would have those elements look the way they look on iOS 7. However, they keep the same style they used to have on iOS 6, until you compile with the iOS 7 SDK. Except for some of them (like UIAlertView or UIMenuController)
My only explanation for this is that they do something kind of like this:
#define SDKApplicationWasLinkedAgainst ...
if (SDKApplicationWasLinkedAgainst < 7.0)
...
else
...
This is obviously really cumbersome, cause they need to keep maintaining a lot of old code. So I'm curious, is this really what is going on under the hood? What am I missing?
Without going too much into NDA'd territory, I'd just like to state that yes, they are conditionalizing appearance and behavior based off of the result from the following call:
_UIApplicationUsesLegacyUI()
This function, in turn, makes a call to GSApplicationUsesLegacyUI(), which I presume returns a result based off of the version of the linked UIKit.
This means that yes, they are conditionalizing parts of UIKit for legacy. Not sure that's a good thing, but that's what they decided to do.
My bet is that they use framework compatibility versions.
Each time you compile your app, your app is linked against an specific framework, with compatibility version and current version. You can see those numbers if you run otool -L YourApp.app/YourApp. For example, for an application compiled some time ago I obtained this:
/System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 751.58.0)
/System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 1500.0.0)
As you can see the full path of the UIKit framework is stored in the Mach-O binary, along a couple of versions (and specially the version I compiled against at that moment).
I suppose that iOS 7 will have both UIKit version included: the one from iOS6 marked with the corresponding version and saying compatibility from 1.0.0, and the one from iOS7 marked as compatible with something higher than 1500.0.0 (I don’t know if that’s the number for iOS 6.1.3, but you get the idea).
When your iOS6 binary is loaded, its library dependencies are read by dyld and resolved, because you were compiled saying current version 1500.0.0, and the library for iOS 7 says compatibility version 1501.0.0, you will be linked against the library for iOS 6.
Since a framework is also a bundle, all the resources are perfectly contained, and will be used only by the right version, and that’s how the different visual elements will look different if you compile against iOS 6 SDK or iOS 7 SDK.
I might be wrong, but I simply hope they are not using the code technique you propose, because that will be a crappy code base to maintain.
I can't be certain, but this is one guess about how it is done. Because they know what SDK version your app was linked against, they version out the framework on iOS 7 devices. So there is a hierarchy on the filesystem along the lines of /.../iPhoneOS6.1.sdk/.../Frameworks/UIKit. Then when it loads your app, they can just set the search path for libraries to point to whichever SDK your app was linked against.
This is how Xcode does it right now. Inside the Developer directory inside the Xcode package is an SDKs directory, which, in turn, contains all the different SDKs to link against.

Which version of the iPhone SDK should I use for an OS4 app?

I'm wondering if I will have compatibility issues with OS4.0 if my app is built with the 4.1 SDK, or is this a non-issue?
The short answer to your question is: Use a Base SDK Version of at least 4.0 for your application and a Deployment Target Version of exactly 4.0.
I've downvoted both answers because they are wrong or at least incomplete.
When building applications there are two settings that you should be aware of:
There is the Base SDK Version which defines against which version of the SDK your application will be compiled.
There is also the Deployment Target Version which defines the lowest version of the iOS that your application requires. You can set this to as low as 2.2.1 in Xcode but the App Store will not accept versions lower then 3.1.3 at the moment.
The Base SDK Version can be higher then the Deployment Target Version. This simply means that your application is backward compatible with older version of the iOS. This also means that your app needs to make decisions at runtime to be sure not to use newer functionality when running on an older version.
For example, the MFMessageComposeViewController was introduced in 4.0 so if your app has been configured to also run on 3.1.3 then you should use NSClassFromString() to find if that specific class is actually available before you use it.
There are many questions here on Stack Overflow on how to discover available functionality so I will not repeat those techniques here.
And a somewhat important note:
Behaviour does change across OS releases. There were a pile of changes in 3.0 (we had issues with UITableView/UITableViewCell):
Some changes happened on old apps (e.g. compiled for 2.2.1). I forget what these were.
Some of them were in the toolchain (I think they changed how nibs were compiled with ibtool; it was something like setting both an image and a custom view for a UITableViewCell). This happened when we upgraded the SDK on the build server, even if we didn't touch "Base SDK"
Some of them happen only when you compiled with a Base SDK of 3.0 (UIKit automagically detects which version you've linked against and has backwards-compatability modes for some things)
Additionally, there were some runtime/C++ changes in GCC 4.2, which meant a GCC 4.2 app running on OS 2.2.1 crashed when casting unsigned int/long to float/double or using std::ostringstream. I've since added a check for GCC version.
So no, compiling with a newer SDK can result in issues — you might not want to risk it if there are time constraints (you don't have time to implement multitasking support, or you don't have time to do a complete re-test and fix all the bugs, or so). Or maybe you still want to support 2.2.1 for some strange reason (4.0 dropped 2.x "device support", which effectively made it impossible to debug apps on 2.x devices).
Usually nothing breaks across minor OS releases (discounting 3.1/3.2). I'd recommend upgrading SDKs as soon as you have the time; don't shy away from new features just because old OSes don't have them.
Depending on the APIs you will be using. Let's say you want to implemented in-app texting (MFMessageComposeViewController) and want ALL of your end-users (will be defined as EUs) to access that feature then you'll have to compile against iOS 4.0. But let's say you want the in-app texting to be optional and a "plus" for your EUs, you'll just compile your app against iOS 3.0 (let's say).
Hope I answered your question :-)

iPhone - validating code for a specific SDK version

Suppose I have my project using Base SDK = 4 but set the Target OS to 3.
Now suppose that I may have used some functionality that belongs to 4, on a framework that already exists on 3.0. So, it is not a matter of using a framework that doesn't exist on 3, it is a matter of using a method that exists on 4 but not on the same framework on 3.
Is there a way to check which lines of my code are illegal on the target OS?
I ask this because when I set Base SDK to 4, Xcode will allow me to use instructions that belong to 4.0, even if the target OS is 3. This will simply make iPhone 3GS crash and will not be detected during compilation.
thanks
As far as I know there's no way of doing this from the newest Xcode.
You can install a previous version with the iPhone 3.0 SDK, build the project and check the compiler warnings. This method is not foolproof as it doesn't cover all possible cases.
And I highly recommend that you test your app in a iPhone 3.X device anyway. Some APIs behave differently, even if their signature hasn't changed.
Cocoa with Love has a good roundup of working with older OSes. I think this might answer your question:
http://cocoawithlove.com/2010/07/tips-tricks-for-conditional-ios3-ios32.html