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 Kotlin Tuple classes. When the transpiler translates Swift code that references tuples, it uses these Tuple classes in the Kotlin it generates.
Dependencies
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.
Status
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.
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.
Other forms of contributions such as test cases, comments, and documentation are also welcome!
Implementation Strategy
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 the Array 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 actual Array 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.
Swift Standard Library Support
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
π’
Actor
Non-private mutable properties are not supported
β
Any
β
AnyActor
β
AnyHashable
β
AnyObject
π’
Array
init()
init(repeating: Element, count: Int)
init(_ sequence: any Sequence<Element>)
See Collection for collection API support
β
assert
β
assertionFailure
β
AsyncSequence
β
AsyncStream
When invoking the init(unfolding:) constructor, use a labeled argument rather than a trailing closure
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.
Codable
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 not RawRepresentable. You must implement the required protocol functions yourself.
If you implement your own encode function or init(from:) decoding constructor and you use CodingKeys, you must declare your own CodingKeys enum. You cannot rely on the synthesized enum.
Array, Set, and Dictionary are fully supported, but nesting of these types is limited. So for example Skip can encode and decode Array<MyCodableType> and Dictionary<String, MyCodableType>, but not Array<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, your decode calls must supply a concrete type literal to decode. The following will work:
init(from decoder:Decoder)throws{varcontainer=try decoder.container(keyedBy:CodingKeys.self)letarrayType=[Int].self
self.array =try container.decode(arrayType, forKey:.array)}init(from decoder:Decoder)throws{varcontainer=try decoder.container(keyedBy:CodingKeys.self)// T is a generic type of this classself.array =try container.decode([T].self, forKey:.array)}