Porting Septourian to Windows






Initial iOS version
When I first made Septourian for iOS, it was a bit of an experiment and evaluation of how it would be to make a game mostly in Swift. The pros were apparent from the start in that progress could be made very quickly as it was non-verbose, safe and performant enough. Some functions like audio and blitting were still in C though and executed through a command buffer administered through an Objective-C bridge.
Porting to other platforms
I got interested in trying to port it to Mac, which was done fairly easy as basically most code could be reused. The headaches came later when considering the possibility to port to Windows and Linux.. For maximum future portability I initially began a rewrite of all Swift code to C, and managed to get about half way, but it was very cumbersome as so much time was invested in checking so that everything behaved the same. A lot of the ported code needed new structuring as well which took even more time.
I had to shelf it for a while due to dayjob, but then I started thinking about the possibility of instead compiling the Swift code on Windows and was a bit curious of finding out how much work it would actually be.
I got myself a cheap ThinkPad with Windows 11 and started experimenting. It turned out that Swift code with any dependency to either Objective-C, Foundation or any math function from Darwin (like floor and ceil) would make the compiler sad. I considered using the cross platform version of Foundation, which might've made porting it easier. However at the time of writing this was not entirely complete and it has been advised to leave it for now which I did, so the approach I went with was to have the shared code without the dependency.
A major part of the current architecture of the game was dependent on the command buffer Objective-C bridge previously mentioned. With the strategy I went with, this had to be rewritten as a pure ABI bridge between C and Swift. For simple data types this was easy mapping, but variable length strings had to be made separate with buffer pointer and length as parameters etc.
Once the new bridge was in place and tested, I knew that it would work so I could continue with wrapping up the rest.
Conclusion
Would I go with this approach again? Probably not:
- It's very hard to debug any ABI related issues as the Swift code is compiled into a DLL before running.
- Even though the Swift DLL and main binary can be built separately, it creates an overhead in compile times and iterations are slow.
- You will need to cross check with a Windows compilation of the code regularly to ensure that it's still isolated from Apple dependencies. A lot of the time function calls had references to Foundation behind the scenes which was not apparent from a first glance.
- Decoding JSON was not easy in Swift on Windows without Foundation and no good library existed for it currently, so the solution was to decode the objects in C and then map them through ABI to Swift.
- Swift and its tooling is under constant development, and maintaining a working dev environment will be a lot of work in the long run. It will not survive long without breaking changes (especially compared to C89 which is eternal).
- It's easy to end up in "DLL Hell" to get a runnable version as Swift requires a lot of dynamic dependencies, including some Windows specific ones which the user may or may not have on their system, and will need to be the correct version and be compatible with each other (64bit only etc).
If I really had to do it though I would:
- Consider checking if swift-foundation is production ready and add it as a dependency if it could simplify JSON decoding for example without adding too much bloat.
- Have a well thought out ABI bridge design from the start.
- Regularly compile and test the isolated Swift code on a non Apple platform (and with special flags on Apple platforms restricting dependencies if needed).
- Keep the codebase as similar as possible on all platforms to be able to catch most issues early on in Xcode tools and be able to debug the ABI bridge on Mac etc.
Next steps
So what is left? I still don't have an Android version unfortunately, but there is a good chance a compiled Swift lib could be used there through JNI with NDK. Also, a SteamDeck version wouldn't be too bad, and that would include making a Linux build. The possibilities are there if there is enough time and interest, however the financial incentive for progressing is tiny at the moment and I will probably prioritise other things until there is enough interest ;) Still, it was a fun project to complete and good look into of the current state of cross platform Swift.
Files
Get Septourian
Septourian
Status | Released |
Author | lundstroem |
Genre | Role Playing |
Tags | Procedural Generation, Roguelike, Roguelite, ZX Spectrum |
Leave a comment
Log in with itch.io to leave a comment.