Unified way for target platform condition

Target platform condition


This proposal intends to indroduce a unified way to check building and evironment
conditions and allow to expand them in future. Another intention is to remove redundant os checkes. e.g. all Apple platforms can be checked by #if target(kernel: Darwin) rather than checking for 4 different OSes.


A developer usually must check for environment to expose different behavior in different building situations. In Swift, this usually is done with #if precompiling directive.

A flow with current approach is we should check every OS separately, this can lead to confusion and increase maintanence cost for currnet codes as Swift is expanding to new environments and a code should be checked against 12 OSes and architectures.

Proposed Solution

This proposal deprecates existing #if arch() & #if os() directives and introduces a new way:

#if (type: condition)

The type can be arch, os, kernel, config and env. This design allows to replace:

#if os(macOS) || os(ios) || os(tvOS) || os(watchOS)

simply with a more meaningful:

#if target(kernel: Darwin)

If a new operating system would be introduced by Apple based on Darwin, new code will be compiled correctly without any modification.

Detailed Design

This will conditions available:


For arch type, condition can be x86_64, arm, arm64, i386, powerpc64, powerpc64le and s390x.

Operating system

For os type, condition can be macOS, iOS, watchOS, tvOS, GNU, Windows, FreeBSD, Android, PS4, Cygwin, Haiku, Fuchsia.

Please note Linux is a kernel. GNU fits better as operating system.


for kernel type, condition can be Darwin, Linux, Posix, Zircon.

Darwin will cover macOS, iOS, watchOS, tvOS.

Linux will cover GNU, Android, Cygwin.

BSD will cover FreeBSD, PS4.

Posix will cover all Darwin, BSD and Linux OSes.

  • Windows has it’s own proprietary kernel and defining a separate condition is redundant.

An example of using:

// statvfs(2) doesn't support 64bit inode on Darwin (apfs), fallback to statfs(2)
#if target(kernel: Darwin)
    var s = statfs()
    guard statfs(path, &s) == 0 else {
        throw _NSErrorWithErrno(errno, reading: true, path: path)
#elseif (target(kernel: Linux) || target(kernel: BSD)) && !target(os: Android)
// equal with `#elseif target(kernel: Posix) && !target(os: Android)` as we checked Darwin already
    var s = statvfs()
    guard statvfs(path, &s) == 0 else {
        throw _NSErrorWithErrno(errno, reading: true, path: path)


For env type, condition can be simulator, embedded, desktop.

Also we can define mobile, tablet, watch as environment, but these conditions should be handled runtime rather than compile time.

This section covers proposal 190.


For config type, condition can be debug, release. This will be affected by Xcode build configuration.


For endian type, condition can be little, big. This will be mapped to corresponding arch types.

Other considerations

We may add support for Metal version by this approach. e.g. #if target(metal: 2).

Technically, macOS is platform, Darwin is operating system and XNU is kernel. In proposed system macOS a condition for os and Darwin as kernel.

Cygwin is considered as operating system. We can define it as env with Windows as operating system. But this may add complexity.

Source compatibility

This is an additive proposal, existing code will continue to work but we can deprecate existing approach.

A warning and fixit may be provided for migrating recognizable cases in existing code, but this will not be necessarily.

Effect on ABI stability


Effect on API resilience


Alternatives Considered

Some possible alternatives can be considered:

  1. Keep current approach for architecture and os determination.
  2. Add #if config() and #if kernel() to existing directives.
  3. Using cpu as type instead of arch.

EDIT: I’ve changed sample to be more clear.

1 Like

I’m glad to see someone giving these attention!

Related to this is conditionally-compiling based on whether or not there is an Objective-C runtime available. It seems a little awkward to base this on “kernel” and “os”, where kernel is a categorization of os, and as you note the technical difference is not commonly understood.

What about naming this based on the property you’re trying to ask. Example:

#if has(Darwin)
  import Darwin
#elseif has(Glibc)
  import Glibc
#if has(ObjC)

I’m glad to see you address endianness. A frequent condition I want to ask is pointer size, which is currently particularly onerous and opaque (“what is s390x again?”). Have you thought about that?

Windows used to have a POSIX subsystem (not cygwin) but I don’t know if that still holds true.

This already has been implemented as canImport() in Swift 4.1 (SE-0075).

1 Like

There have been several previous ideas pitched along these lines.

It’s important that discussions that reprise previous conversations not start from scratch, ignoring the contributions that others have already painstakingly made. Please kindly review those discussions and summarize the points discussed before proposing a similar idea again without context.

I will say that pitching the renaming of something that has just been approved is exceedingly unlikely to be successful. To do so requires proving that the existing design is causing active harm. One of the criteria for proposals is that they fit with the direction of Swift, and proposing to change something just approved a few months ago is a clear sign that, no, the new proposed idea does not fit with the direction of Swift.

Members of the community are always welcome to pitch their ideas for improving Swift. There is no requirement to first scan the complete archives looking for earlier proposals along the same lines. All we ask is that people look for existing active discussions and check that their idea isn’t on the Commonly Rejected Proposals list.

If you feel that a pitch would benefit from reading an earlier thread, why not find that thread and link it? I’ve found that doing this can be quite constructive. Among other things, often I discover that I’ve misremembered what was actually said in that thread, meaning that that the new pitch isn’t quite as redundant as I thought.

I agree that it’s good not to have the same discussions over and over, but we also don’t want to discourage people from trying to contribute. If a particular idea keeps coming so often that it’s crowding out other conversations, we can add it to the list.


My response came off too strong: it wasn’t meant to be a statement of iron-clad requirements but an exhortation to do certain things; it ended up taking on a discouraging tone rather than an encouraging one, for which I should apologize.

I do want to encourage participants to follow the established steps to take before making a pitch, which is outlined in the document titled “Swift Evolution Process”:

How to propose a change

  • Check prior proposals: many ideas come up frequently, and may either be in active discussion on the forums, or may have been discussed already and have joined the Commonly Rejected Proposals list. Please search the forums for context before proposing something new. [Emphasis added.]

  • Consider the goals of the upcoming Swift release […]

  • Socialize the idea: propose a rough sketch of the idea in the “pitches” section of the Swift forums, the problems it solves, what the solution looks like, etc., to gauge interest from the community.

While nothing can be said to be a requirement, I think the plain meaning of the text is that users should scan the archives looking for any earlier proposals along the same lines. In earlier times on the mailing list, I would post links to older threads because they were difficult to find if not already subscribed.

One of the stated benefits of moving to a forum was to enable much easier searching; say what you will about other drawbacks, Discourse has delivered on that promise. I no longer find previous threads and link to them for others’ pitches because:

  1. I can do so no faster than anyone else.
  2. It is now no more difficult to find an inactive discussion than an active one.
  3. If the act of finding old threads is potentially edifying for me, how much more so it would be for the originator of the pitch!

I’m sensitive to the notion that certain expectations can discourage wider participation, but at the same time the aim should be for more thoughtful participation. I feel very strongly, as someone who spends far more time reading others’ contributions than writing, that it is no more unreasonable to have a general expectation that authors put in the (now vastly simplified) legwork of researching context on the front end than an expectation for them to “be responsive to questions and feedback” during proposal review on the back end.

Indeed I’ve studied all proposals in github repository and I did a fast search in forum. Maybe I did use wrong keywords for searching.

I didn’t find any related topic.

I certainly agree that pitch-makers can often make their pitches better by looking for previous discussions on the same topic. But I’m not sure “you could have done a better job making this pitch” is really something that needs to be said; it doesn’t move the discussion forward in any way, unless the proposer is conducting some sort of post-mortem on their proposal.

I think it’s okay to point out that something’s been discussed before, but it’s important to do that in a constructive way. The most obvious way to be constructive is to point out exactly where it’s been discussed, either with a link or with a search phrase that you know will find results. (Often, as may have been the case here, people use slightly different words for the same concepts, so their searches will fail. The term that I use for this particular feature set is “build configurations”, but I wouldn’t want to count on that appearing in any previous proposal, just as it doesn’t appear in this one.)

With that said, I think it’s time to return to talking about the proposal.