I think that if you want to run Xcode 8.3. you'll need to also run an older macOS such as 10.12/Sierra.
My opinion on your example, if you expect things to go smoothly, tho, is that by the time you try to climb the ladder from swift 2.x to 5.1, you may find it less effort to just work with current tools and start over.
Yes I had sometime.
So, as KMT says, I had to climb the ladder.
For such situation, I keep an old MacBook AIr with older OS and various beta of XCode from 8, to be able to upgrade step by step.
But some transition (I think it was when going to Swift 4) may need a lot of manual corrections…
So it may be a good decision (I do agree with KMT advice) to rebuild from scratch, just using some parts of older code to copy and paste instead of retyping…
I would also recommend a rewrite. And will have to disagree to just copying and pasting some of the older code. When Swift 3 came out, there were a large quantity of function name changes all throughout the SDK. Apple also advised that developers write Swift code with preferred naming schemes (e.g. the 'ed' and 'ing' rules).
While I have steadily moved my app through all versions of Swift (it's really best to stay up-to-date), I can definitely say that the most challenging was transitioning to Swift 3. Swift 4's changes were not too bad and Xcode's fix actions could tend to most of them. Migrating to Swift 5 was then very easy.
Over the years I've also re-looked at basically everything I wrote and performed refactors as-needed. For example, my Swift 1 apps were organized just like their prior Objective-C versions. I ended up using classes for almost everything. And still had lots of loops in the app. These days, my apps are completely re-organized and make heavy use of protocols and value types. I also leverage 'map', 'filter', etc. as much as possible.