Here's another one, hopefully with a little more grounding than the debug test. 
I welcome feedback both positive and critical, -- E

Adding an Build Configuration Import Test

Proposal: SE-00XX
Author(s): Erica Sadun <http://github.com/erica>
Status: TBD
Review manager: TBD
 <https://gist.github.com/erica/b7f4226b8201945602f2#introduction>Introduction

Expanding the build configuration suite to test for the ability to import 
certain modules was first introduced 
<http://article.gmane.org/gmane.comp.lang.swift.evolution/7516/match=darwin> on 
the Swift-Evolution list by Kevin Ballard. Although his initial idea (checking 
for Darwin to differentiate Apple targets from non-Apple targets) proved 
problematic, developers warmly greeted the notion of an import-based 
configuration test. Dmitri Gribenko wrote, "There's a direction that we want to 
move to a unified name for the libc module for all platform, so 'can import 
Darwin' might not be a viable long-term strategy." Testing for imports offers 
advantages that stand apart from this one use-case: to test for API 
availability before use.

 <https://gist.github.com/erica/b7f4226b8201945602f2#motivation>Motivation

Swift's existing set of build configurations specify platform differences, not 
module commonalities. For example, UIKit enables you to write view code 
supported on both iOS and tvOS. SpriteKit allows common code to render on OS X, 
iOS, and tvOS that would require an alternate UI on Linux. Testing for Metal 
support or Media Player would guard code that will not function on the 
simulator. If the simulator adopted these modules at some future time, the code 
would naturally expand to provide compatible execution without source 
modification.

#if canImport(UIKit)
   // UIKit-based code
   #elseif canImport(Cocoa)
   // OSX code
   #elseif
   // Workaround/text, whatever
#endif
Guarding code with operating system tests can be less future-proofed than 
testing for module support. Excluding OS X to use UIColor creates code that 
might eventually find its way to a Linux plaform. Targeting Apple platforms by 
inverting a test for Linux essentially broke after the introduction of Windows 
and FreeBSD build configurations:

// Exclusive os tests are brittle
#if !os(Linux)
   // Matches OSX, iOS, watchOS, tvOS, Windows, FreeBSD
#endif
Inclusive OS tests (if os1 || os2 || os3...) must be audited each time the set 
of possible platforms expands. In addition, compound build statements are 
harder to write, to validate, and are more confusing to read. They are more 
prone to errors than a single test that's tied to the API capabilities used by 
the code it guards.

Evan Maloney writes, "Being able to test for the importability of a given 
module/framework at runtime would be extremely helpful. We use several 
frameworks that are only available in a subset of the platforms we support, and 
on only certain OS versions. To work around this problem now, we dynamically 
load frameworks from Obj-C only when we're running on an OS version we know is 
supported by the framework(s) in question. We can't dynamically load them from 
Swift because if they're included in an import, the runtime tries to load it 
right away, leading to a crash on any unsupported platform. The only way to 
selectively load dynamic frameworks at runtime is to do it via Obj-C. Some sort 
of check like the ones you propose should let us avoid this."

 <https://gist.github.com/erica/b7f4226b8201945602f2#detail-design>Detail Design

#if canImport(module-name) tests for module support by name. My proposed name 
uses lower camelCase, which is not currently used in the current build 
configuration vocabulary but is (in my opinion) clearer in intention than the 
other two terms brought up on the evolution list, #if imports() and #if 
supports(). 

This build configuration does not import the module it names
This build configuration is intended to differentiate API access
This build configuration should not be used to differentiate platforms
The supplied module token is an arbitrary string. It does not belong to an 
enumerated set of known members as this configuration test is intended for use 
with both first and third party modules for the greatest flexibility. At 
compile time, Swift determines whether the module can or cannot be linked and 
builds accordingly.
#if canImport(module)
    import module
    // use module APIs safely
#endif

#if canImport(module)
    // provide solution with module APIs
    #else
    // provide alternative solution that does not depend on that module
#endif
 <https://gist.github.com/erica/b7f4226b8201945602f2#current-art>Current Art

Swift currently supports the following configuration tests:

The literals true and false
The os() function that tests for OSX, iOS, watchOS, tvOS, Linux, Windows, and 
FreeBSD
The arch() function that tests for x86_64, arm, arm64, i386, powerpc64, and 
powerpc64le
The swift() function that tests for specific Swift language releases, e.g. 
swift(>=2.2)
 
<https://gist.github.com/erica/b7f4226b8201945602f2#alternatives-considered>Alternatives
 Considered

There are no alternatives considered.
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to