Swift standard library for Skip apps.
See what API is currently implemented here.
SkipLib vends the skip.lib
Kotlin package. It serves two purposes:
- SkipLib is a reimplementation of the Swift standard library for Kotlin on Android. Its goal is to mirror as much of the Swift standard library as possible, allowing Skip developers to use Swift standard library API with confidence.
- SkipLib contains custom Kotlin API that the Skip transpiler takes advantage of when translating your Swift source to the equivalent Kotlin code. For example, the Kotlin language does not have tuples. Instead, SkipLib's
Tuple.kt
defines bespoke KotlinTuple
classes. When the transpiler translates Swift code that references tuples, it uses theseTuple
classes in the Kotlin it generates.
SkipLib depends on the skip transpiler plugin and has no additional library dependencies.
It is part of the core SkipStack and is not intended to be imported directly.
The module is transparently adopted through the automatic addition of import skip.lib.*
to transpiled files by the Skip transpiler.
- SkipLib's Swift symbol files (see Implementation Strategy) are nominally complete. They should declare all Swift standard library API. This is difficult to validate, however, so if you find anything missing, please report it to us.
- Unimplemented API is appropriately marked with
@available(*, unavailable)
annotations. Skip will generate an error when you attempt to use an unimplemented API. - In particular, a significant portion of the collections API is not yet implemented.
- Unit testing is not comprehensive.
See Swift Standard Library Support.
We welcome contributions to SkipLib. The Skip product documentation includes helpful instructions and tips on local Skip library development.
The most pressing need is to reduce the amount of unimplemented API. To help fill in unimplemented API in SkipLib:
- Find unimplemented API. Unimplemented API should be marked with
@available(*, unavailable)
in the Swift symbol files. - Write an appropriate Kotlin implementation. See Implementation Strategy below. For collections API, make sure your implementation is duplicated for
String
as well. - Write unit tests.
- Submit a PR.
Other forms of contributions such as test cases, comments, and documentation are also welcome!
Apart from the Skip transpiler itself, SkipLib implements the lowest levels of the Swift language. Its implementation strategy, therefore, differs from other Skip libraries.
Most Skip libraries call Kotlin API, but are written in Swift, relying on the Skip transpiler for translation to Kotlin. Most of SkipLib, however, is written in pure Kotlin. Consider SkipLib's implementation of Swift's Array
. SkipLib divides its Array
support into two files:
Sources/SkipLib/Array.swift
acts as a Swift header file, declaring theArray
type's Swift API but stubbing out the implementation. The// SKIP SYMBOLFILE
comment at the top of the file marks it as such. Read more about special Skip comments in the Skip product documentation.Sources/SkipLib/Skip/Array.kt
contains the actualArray
implementation in Kotlin.
This pattern is used for most Swift types throughout SkipLib. Meanwhile, SwiftLib implementations of constructs built directly into the Swift language - e.g. tuples or inout
parameters - only have a Kotlin file, with no corresponding Swift symbol file.
The following table summarizes SkipLib's Swift Standard Library API support on Android. Anything not listed here is likely not supported. Note that in your iOS-only code - i.e. code within #if !SKIP
blocks - you can use any API you want.
Support levels:
- β β Full
- π’ β High
- π‘ - Medium
- π β Low
Support | API |
---|---|
π’ |
|
β | Any |
β | AnyActor |
β | AnyHashable |
β | AnyObject |
π’ |
|
β | assert |
β | assertionFailure |
β | AsyncSequence |
β |
|
π’ |
|
β | CaseIterable |
β | CGAffineTransform |
β | CGFloat |
β | CGPoint |
β | CGRect |
β | CGSize |
π’ |
|
π’ |
|
π‘ |
|
β | Comparable |
β | CustomDebugStringConvertible |
β | CustomStringConvertible |
π’ |
|
π’ |
|
β | DiscardingTaskGroup |
π’ |
|
π’ |
|
β | Equatable |
β | Error |
β | fatalError |
π’ |
|
β | Hashable |
β | Hasher |
β | Identifiable |
π’ |
|
π’ |
|
π’ |
|
π’ |
|
π’ |
|
π’ | @MainActor |
π’ |
|
π’ |
|
β | max(_:_:) |
β | min(_:_:) |
β | ObjectIdentifier |
β | OptionSet |
β | precondition |
β | preconditionFailure |
β | RandomNumberGenerator |
π |
|
β | RawRepresentable |
π |
|
π |
|
π |
|
β | Result |
π‘ |
|
π’ |
|
π’ |
|
π’ |
|
β | strlen |
β | strncmp |
π’ |
|
β | SystemRandomNumberGenerator |
π‘ |
|
β | TaskGroup |
β | ThrowingDiscardingTaskGroup |
β | ThrowingTaskGroup |
β | type(of:) |
π’ |
|
π’ |
|
π’ |
|
π’ |
|
π’ |
|
β | withDiscardingTaskGroup |
β | withTaskCancellationHandler |
β | withThrowingTaskGroup |
β | withThrowingDiscardingTaskGroup |
β | withThrowingTaskGroup |
Collections are perhaps the most complex part of the Swift standard library, and of SkipLib. Swift's comprehensive collection protocols allow Array
, Set
, Dictionary
, String
, and other types to all share a common set of API, including iteration, map
, reduce
, and much more.
Corresponding Kotlin types - List
, Set
, Map
, String
, etc - do not share a similarly rich API set. As a result, SkipLib must duplicate collection protocol implementations in both Collections.kt
and String.kt
, and must duplicate SetAlgebra
implementations in both Set.kt
and OptionSet.kt
.
See the explanatory comments in Collections.kt
for more information on the design of SkipLib's internal collections support.
Skip supports your custom CodingKeys
as well as your custom encode(to:)
and init(from:)
functions for encoding and decoding. Skip is also able to synthesize default Codable
conformance for the Android versions of your Swift types. The Android versions will encode and decode exactly like their Swift source types.
There are, however, a few restrictions:
-
Skip cannot synthesize
Codable
conformance for enums that are notRawRepresentable
. You must implement the required protocol functions yourself. -
If you implement your own
encode
function orinit(from:)
decoding constructor and you useCodingKeys
, you must declare your ownCodingKeys
enum. You cannot rely on the synthesized enum. -
Array
,Set
, andDictionary
are fully supported, but nesting of these types is limited. So for example Skip can encode and decodeArray<MyCodableType>
andDictionary<String, MyCodableType>
, but notArray<Dictionary<String, MyCodableType>>
. Two forms of container nesting are currently supported: arrays-of-arrays - e.g.Array<Array<MyCodableType>>
- and dictionaries-of-array-values - e.g.Dictionary<String, Array<MyCodableType>>
. In practice, other nesting patters are rare. -
When implementing your own
init(from: Decoder)
decoding, yourdecode
calls must supply a concrete type literal to decode. The following will work:init(from decoder: Decoder) throws { var container = try decoder.container(keyedBy: CodingKeys.self) self.array = try container.decode([Int].self, forKey: .array) }
But these examples will not work:
init(from decoder: Decoder) throws { var container = try decoder.container(keyedBy: CodingKeys.self) let arrayType = [Int].self self.array = try container.decode(arrayType, forKey: .array) } init(from decoder: Decoder) throws { var container = try decoder.container(keyedBy: CodingKeys.self) // T is a generic type of this class self.array = try container.decode([T].self, forKey: .array) }