Skip to content

skiptools/skip-lib

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

SkipLib

Swift standard library for Skip apps.

See what API is currently implemented here.

About

SkipLib vends the skip.lib Kotlin package. It serves two purposes:

  1. 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.
  2. 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.
  • Unit testing is not comprehensive.

See Swift Standard Library Support.

Contributing

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:

  1. Find unimplemented API. Unimplemented API should be marked with @available(*, unavailable) in the Swift symbol files.
  2. Write an appropriate Kotlin implementation. See Implementation Strategy below. For collections API, make sure your implementation is duplicated for String as well.
  3. Write unit tests.
  4. Submit a PR.

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:

  1. 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.
  2. 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
SupportAPI
🟒
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
🟒
Bool
  • static func random() -> Bool
  • static func random(using gen: inout RandomNumberGenerator) -> Bool
βœ… CaseIterable
βœ… CGAffineTransform
βœ… CGFloat
βœ… CGPoint
βœ… CGRect
βœ… CGSize
🟒
Character
  • init(_: Character)
  • init(_: String)
  • var isNewline: Bool
  • var isWhitespace: Bool
  • var isUppercase: Bool
  • var isLowercase: Bool
  • func lowercased() -> String
  • func uppercased() -> String
🟒
Codable
🟑
Collection
  • Note: This list represents the combined supported API of Swift's many collection types: Sequence, Collection, BidirectionalCollection, etc
  • func allSatisfy(_ predicate: (Element) throws -> Bool) rethrows -> Bool
  • mutating func append(_ newElement: Element)
  • mutating func append(contentsOf newElements: any Sequence<Element>)
  • func contains(_ element: Element) -> Bool
  • func contains(where predicate: (Element) throws -> Bool) rethrows -> Bool
  • func distance(from start: Int, to end: Int) -> Int
  • func drop(while predicate: (Element) throws -> Bool) rethrows -> [Element]
  • func dropFirst(_ k: Int = 1) -> [Element]
  • func dropLast(_ k: Int = 1) -> [Element]
  • func elementsEqual(_ other: any Sequence<Element>) -> Bool
  • func elementsEqual(_ other: any Sequence<Element>, by areEquivalent: (Element, Element) throws -> Bool) rethrows -> Bool
  • func enumerated() -> any Sequence<(offset: Int, element: Element)>
  • var endIndex: Int
  • func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> [Element]
  • func first(where predicate: (Element) throws -> Bool) rethrows -> Element?
  • func firstIndex(of element: Element) -> Int?
  • func firstIndex(where predicate: (Element) throws -> Bool) rethrows -> Int?
  • var first: Element?
  • func flatMap<RE>(_ transform: (Element) throws -> any Sequence<RE>) rethrows -> [RE]
  • func formIndex(_ i: inout Int, offsetBy distance: Int)
  • func formIndex(after i: inout Int)
  • func index(_ i: Int, offsetBy distance: Int) -> Int
  • func index(after i: Int) -> Int
  • var indices: any Sequence<Int>
  • var isEmpty: Bool
  • func joined<RE>() -> [RE] where Element: Sequence<RE>
  • func joined<RE>(separator: any Sequence<RE>) -> [RE] where Element: Sequence<RE>
  • func joined(separator: String) -> String
  • func makeIterator() -> any IteratorProtocol<Element>
  • func map<RE>(_ transform: (Element) throws -> RE) rethrows -> [RE]
  • func max() -> Element?
  • func max(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Element?
  • func min() -> Element?
  • func min(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Element?
  • var underestimatedCount: Int
  • func prefix(_ maxLength: Int) -> [Element]
  • func prefix(through end: Int) -> [Element]
  • func prefix(upTo end: Int) -> [Element]
  • mutating func popFirst() -> Element?
  • mutating func popLast() -> Element?
  • func randomElement() -> Element?
  • func randomElement(using generator: inout any RandomNumberGenerator) -> Element?
  • func reduce<R>(_ initialResult: R, _ nextPartialResult: (_ partialResult: R, Element) throws -> R) rethrows -> R
  • func reduce<R>(into initialResult: R, _ updateAccumulatingResult: (_ partialResult: inout R, Element) throws -> Void) rethrows -> R
  • func remove(at i: Int) -> Element
  • mutating func removeAll(keepingCapacity keepCapacity: Bool = false)
  • mutating func removeAll(where shouldBeRemoved: (Element) throws -> Bool) rethrows
  • mutating func removeFirst() -> Element
  • mutating func removeFirst(_ k: Int)
  • mutating func removeLast() -> Element
  • mutating func removeLast(_ k: Int)
  • mutating func reverse()
  • func reversed() -> [Element]
  • mutating func shuffle()
  • mutating func shuffle<T: RandomNumberGenerator>(using generator: inout T)
  • mutating func sort()
  • mutating func sort(by areIncreasingOrder: (Element, Element) throws -> Bool) rethrows
  • mutating func swapAt(_ i: Int, _ j: Int)
  • subscript(bounds: Range<Int>) -> any Collection<Element>
  • subscript(position: Int) -> Element
  • func starts(with possiblePrefix: Any) -> Bool
  • func starts(with possiblePrefix: Any, by areEquivalent: (Element, Element) throws -> Bool) rethrows -> Bool
  • func suffix(from start: Int) -> [Element]
  • func suffix(_ maxLength: Int) -> [Element]
  • func sorted() -> [Element]
  • func sorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element]
  • var startIndex: Int
  • var count: Int
  • func count<E>(where: (E) throws -> Bool) rethrows -> Int
  • func withContiguousStorageIfAvailable<R>(_ body: (Any) throws -> R) rethrows -> R?
  • func forEach(_ body: (Element) throws -> Void) rethrows
  • func drop(while predicate: (Element) throws -> Bool) rethrows -> [Element]
  • func dropFirst(_ k: Int = 1) -> [Element]
  • func dropLast(_ k: Int = 1) -> [Element]
  • func enumerated() -> any Sequence<(offset: Int, element: Element)>
  • func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> [Element]
  • func first(where predicate: (Element) throws -> Bool) rethrows -> Element?
  • func map<RE>(_ transform: (Element) throws -> RE) rethrows -> [RE]
  • func max(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Element?
  • func min(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Element?
  • func reduce<R>(_ initialResult: R, _ nextPartialResult: (_ partialResult: R, Element) throws -> R) rethrows -> R
  • func reduce<R>(into initialResult: R, _ updateAccumulatingResult: (_ partialResult: inout R, Element) throws -> Void) rethrows -> R
  • func reversed() -> [Element]
  • func shuffled() -> [Element]
  • func shuffled<T: RandomNumberGenerator>(using generator: inout T) -> [Element]
  • func flatMap<RE>(_ transform: (Element) throws -> any Sequence<RE>) rethrows -> [RE]
  • func compactMap<RE>(_ transform: (Element) throws -> RE?) rethrows -> [RE]
  • func sorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element]
  • func joined<RE>() -> [RE] where Element: Sequence<RE>
  • func joined<RE>(separator: any Sequence<RE>) -> [RE] where Element: Sequence<RE>
  • func joined(separator: String) -> String
  • func starts(with possiblePrefix: Any) -> Bool
  • func contains(_ element: Element) -> Bool
  • func min() -> Element?
  • func max() -> Element?
  • func sorted() -> [Element]
  • var startIndex: Int
  • var endIndex: Int
  • var indices: any Sequence<Int>
  • func index(_ i: Int, offsetBy distance: Int) -> Int
  • func distance(from start: Int, to end: Int) -> Int
  • func index(after i: Int) -> Int
  • func formIndex(after i: inout Int)
  • func formIndex(_ i: inout Int, offsetBy distance: Int)
  • func randomElement() -> Element?
  • func randomElement(using generator: inout any RandomNumberGenerator) -> Element?
  • mutating func popFirst() -> Element?
  • var first: Element?
  • func prefix(upTo end: Int) -> [Element]
  • func suffix(from start: Int) -> [Element]
  • func prefix(through end: Int) -> [Element]
  • mutating func removeFirst() -> Element
  • mutating func removeFirst(_ k: Int)
  • func firstIndex(of element: Element) -> Int?
  • func firstIndex(where predicate: (Element) throws -> Bool) rethrows -> Int?
  • mutating func shuffle()
  • mutating func shuffle<T: RandomNumberGenerator>(using generator: inout T)
  • mutating func sort()
  • mutating func sort(by areIncreasingOrder: (Element, Element) throws -> Bool) rethrows
  • mutating func reverse()
  • mutating func swapAt(_ i: Int, _ j: Int)
  • mutating func append(_ newElement: Element)
  • mutating func append(contentsOf newElements: any Sequence<Element>)
  • mutating func insert(_ newElement: Element, at i: Int)
  • mutating func insert(contentsOf newElements: any Sequence<Element>, at i: Int)
  • mutating func remove(at i: Int) -> Element
  • mutating func removeAll(keepingCapacity keepCapacity: Bool = false)
  • mutating func removeAll(where shouldBeRemoved: (Element) throws -> Bool) rethrows
  • mutating func popLast() -> Element?
  • mutating func removeLast() -> Element
  • mutating func removeLast(_ k: Int)
  • subscript(bounds: Range<Int>) -> any Collection<Element>
  • subscript(position: Int) -> Element
βœ… Comparable
βœ… CustomDebugStringConvertible
βœ… CustomStringConvertible
🟒
Decodable
🟒
Dictionary
  • init()
  • init(minimumCapacity: Int)
  • init(uniqueKeysWithValues keysAndValues: any Sequence<(Key, Value)>)
  • func filter(_ isIncluded: ((Key, Value)) throws -> Bool) rethrows -> Dictionary<Key, Value>
  • subscript(key: Key) -> Value?
  • subscript(key: Key, default defaultValue: Value) -> Value
  • func mapValues<T>(_ transform: (Value) throws -> T) rethrows -> Dictionary<Key, T>
  • func compactMapValues<T>(_ transform: (Value) throws -> T?) rethrows -> Dictionary<Key, T>
  • mutating func updateValue(_ value: Value, forKey key: Key) -> Value?
  • mutating func removeValue(forKey key: Key) -> Value?
  • var keys: any Collection<Key>)
  • var values: any Collection<Value>
  • mutating func removeAll(keepingCapacity keepCapacity: Bool = false)
  • See Collection for collection API support
βœ… DiscardingTaskGroup
🟒
Double
  • static var nan: Double
  • static var infinity: Double
  • static var pi: Double
  • var isNan: Bool
  • var isFinite: Bool
  • var isInfinite: Bool
  • static func random(in range: Range<Double>) -> Double
  • func rounded() -> Double
  • func rounded(_ rule: FloatingPointRoundingRule) -> Double
🟒
Encodable
  • See [Codable](#codable)
βœ… Equatable
βœ… Error
βœ… fatalError
🟒
Float
  • static var nan: Float
  • static var infinity: Float
  • static var pi: Float
  • var isNan: Bool
  • var isFinite: Bool
  • var isInfinite: Bool
  • static func random(in range: Range<Float>) -> Float
  • func rounded() -> Float
  • func rounded(_ rule: FloatingPointRoundingRule) -> Float
βœ… Hashable
βœ… Hasher
βœ… Identifiable
🟒
Int8
  • static var min: Int8
  • static var max: Int8
  • static func random(in range: Range<Int8>) -> Int8
  • static func random(in range: Range<Int8>, using gen: inout RandomNumberGenerator) -> Int8
🟒
Int16
  • static var min: Int16
  • static var max: Int16
  • static func random(in range: Range<Int16>) -> Int16
  • static func random(in range: Range<Int16>, using gen: inout RandomNumberGenerator) -> Int16
🟒
Int32
  • static var min: Int32
  • static var max: Int32
  • static func random(in range: Range<Int32>) -> Int32
  • static func random(in range: Range<Int32>, using gen: inout RandomNumberGenerator) -> Int32
🟒
Int
  • Kotlin Ints are 32 bit
  • static var min: Int
  • static var max: Int
  • static func random(in range: Range<Int>) -> Int
  • static func random(in range: Range<Int>, using gen: inout RandomNumberGenerator) -> Int
🟒
Int64
  • static var min: Int64
  • static var max: Int64
  • static func random(in range: Range<Int64>) -> Int64
  • static func random(in range: Range<Int64>, using gen: inout RandomNumberGenerator) -> Int64
🟒 @MainActor
🟒
MainActor
  • static func run<T>(body: () throws -> T) async -> T
🟒
math.h
  • var M_E: Double
  • var M_LOG2E: Double
  • var M_LOG10E: Double
  • var M_LN2: Double
  • var M_LN10: Double
  • var M_PI: Double
  • func acosf(_ x: Float) -> Float
  • func acos(_ x: Double) -> Double
  • func acosl(_ x: Double) -> Double
  • func asinf(_ x: Float) -> Float
  • func asin(_ x: Double) -> Double
  • func asinl(_ x: Double) -> Double
  • func atanf(_ x: Float) -> Float
  • func atan(_ x: Double) -> Double
  • func atanl(_ x: Double) -> Double
  • func atan2f(_ x: Float, _ y: Float) -> Float
  • func atan2(_ x: Double, _ y: Double) -> Double
  • func atan2l(_ x: Double, _ y: Double) -> Double
  • func cosf(_ x: Float) -> Float
  • func cos(_ x: Double) -> Double
  • func cosl(_ x: Double) -> Double
  • func sinf(_ x: Float) -> Float
  • func sin(_ x: Double) -> Double
  • func sinl(_ x: Double) -> Double
  • func tanf(_ x: Float) -> Float
  • func tan(_ x: Double) -> Double
  • func tanl(_ x: Double) -> Double
  • func acoshf(_ x: Float) -> Float
  • func acosh(_ x: Double) -> Double
  • func acoshl(_ x: Double) -> Double
  • func asinhf(_ x: Float) -> Float
  • func asinh(_ x: Double) -> Double
  • func asinhl(_ x: Double) -> Double
  • func atanhf(_ x: Float) -> Float
  • func atanh(_ x: Double) -> Double
  • func atanhl(_ x: Double) -> Double
  • func coshf(_ x: Float) -> Float
  • func cosh(_ x: Double) -> Double
  • func coshl(_ x: Double) -> Double
  • func sinhf(_ x: Float) -> Float
  • func sinh(_ x: Double) -> Double
  • func sinhl(_ x: Double) -> Double
  • func tanhf(_ x: Float) -> Float
  • func tanh(_ x: Double) -> Double
  • func tanhl(_ x: Double) -> Double
  • func expf(_ x: Float) -> Float
  • func exp(_ x: Double) -> Double
  • func expl(_ x: Double) -> Double
  • func exp2f(_ x: Float) -> Float
  • func exp2(_ x: Double) -> Double
  • func exp2l(_ x: Double) -> Double
  • func expm1f(_ x: Float) -> Float
  • func expm1(_ x: Double) -> Double
  • func expm1l(_ x: Double) -> Double
  • func logf(_ x: Float) -> Float
  • func log(_ x: Double) -> Double
  • func logl(_ x: Double) -> Double
  • func log10f(_ x: Float) -> Float
  • func log10(_ x: Double) -> Double
  • func log10l(_ x: Double) -> Double
  • func log2f(_ x: Float) -> Float
  • func log2(_ x: Double) -> Double
  • func log2l(_ x: Double) -> Double
  • func log1pf(_ x: Float) -> Float
  • func log1p(_ x: Double) -> Double
  • func log1pl(_ x: Double) -> Double
  • func logbf(_ x: Float) -> Float
  • func logb(_ x: Double) -> Double
  • func logbl(_ x: Double) -> Double
  • func abs(_ x: Double) -> Double
  • func abs(_ x: Int) -> Int
  • func abs(_ x: Int64) -> Int64
  • func fabsf(_ x: Float) -> Float
  • func fabs(_ x: Double) -> Double
  • func fabsl(_ x: Double) -> Double
  • func cbrtf(_ x: Float) -> Float
  • func cbrt(_ x: Double) -> Double
  • func cbrtl(_ x: Double) -> Double
  • func hypotf(_ x: Float, _ y: Float) -> Float
  • func hypot(_ x: Double, _ y: Double) -> Double
  • func hypotl(_ x: Double, _ y: Double) -> Double
  • func powf(_ x: Float, _ y: Float) -> Float
  • func pow(_ x: Double, _ y: Double) -> Double
  • func powl(_ x: Double, _ y: Double) -> Double
  • func sqrtf(_ x: Float) -> Float
  • func sqrt(_ x: Double) -> Double
  • func sqrtl(_ x: Double) -> Double
  • func ceilf(_ x: Float) -> Float
  • func ceil(_ x: Double) -> Double
  • func ceill(_ x: Double) -> Double
  • func floorf(_ x: Float) -> Float
  • func floor(_ x: Double) -> Double
  • func floorl(_ x: Double) -> Double
  • func roundf(_ x: Float) -> Float
  • func round(_ x: Double) -> Double
  • func roundl(_ x: Double) -> Double
  • func fmodf(_ x: Float, _ y: Float) -> Float
  • func fmod(_ x: Double, _ y: Double) -> Double
  • func fmodl(_ x: Double, _ y: Double) -> Double
  • func remainderf(_ x: Float, _ y: Float) -> Float
  • func remainder(_ x: Double, _ y: Double) -> Double
  • func remainderl(_ x: Double, _ y: Double) -> Double
  • func fmaxf(_ x: Float, _ y: Float) -> Float
  • func fmax(_ x: Double, _ y: Double) -> Double
  • func fmaxl(_ x: Double, _ y: Double) -> Double
  • func fminf(_ x: Float, _ y: Float) -> Float
  • func fmin(_ x: Double, _ y: Double) -> Double
  • func fminl(_ x: Double, _ y: Double) -> Double
βœ… max(_:_:)
βœ… min(_:_:)
βœ… ObjectIdentifier
βœ… OptionSet
βœ… precondition
βœ… preconditionFailure
βœ… RandomNumberGenerator
🟠
Range
  • Only Range<Int> is generally supported
  • var lowerBound: Bound
  • var upperBound: Bound
  • func contains(_ element: Bound) -> Bool
  • var isEmpty: Bool
  • func map<RE>(_ transform: (Bound) throws -> RE) rethrows -> [RE]
βœ… RawRepresentable
🟠
Regex
  • init(_ string: String)
  • func matches(_ string: String) -> [Match]
  • func replace(_ string: String, with replacement: String) -> String
🟠
Regex.Match
  • var count: Int
  • subscript(index: Int) -> MatchGroup
🟠
Regex.MatchGroup
  • var substring: Substring?
βœ… Result
🟑
swap(_:_:)
  • Does not support swapping values in arrays and other data structures
🟒
Set
  • init()
  • init(_ sequence: any Sequence<Element>)
  • See Collection
  • See SetAlgebra
🟒
SetAlgebra
  • func contains(_ element: Element) -> Bool
  • func union(_ other: Self) -> Self
  • func intersection(_ other: Self) -> Self
  • func symmetricDifference(_ other: Self) -> Self
  • mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element)
  • mutating func remove(_ member: Element) -> Element?
  • mutating func update(with newMember: Element) -> Element?
  • mutating func formUnion(_ other: Self)
  • mutating func formIntersection(_ other: Self)
  • mutating func formSymmetricDifference(_ other: Self)
  • func subtracting(_ other: Self) -> Self
  • func isSubset(of other: Self) -> Bool
  • func isDisjoint(with other: Self) -> Bool
  • func isSuperset(of other: Self) -> Bool
  • var isEmpty: Bool
  • mutating func subtract(_ other: Self)
  • func isStrictSubset(of other: Self) -> Bool
  • func isStrictSuperset(of other: Self) -> Bool
🟒
String
  • Kotlin strings are **not** mutable
  • init(data: Data, encoding: StringEncoding)
  • init(bytes: [UInt8], encoding: StringEncoding)
  • init(contentsOf: URL)
  • var capitalized: String
  • var deletingLastPathComponent: String
  • func replacingOccurrences(of search: String, with replacement: String) -> String
  • func components(separatedBy separator: String) -> [String]
  • func trimmingCharacters(in set: CharacterSet) -> String
  • var utf8Data: Data
  • func data(using: StringEncoding, allowLossyConversion: Bool = true) -> Data?
  • var utf8: [UInt8]
  • var utf16: [UInt8]
  • var unicodeScalars: [UInt8]
  • See Collection
  • See SkipFoundation for additional string API from Foundation
βœ… strlen
βœ… strncmp
🟒
Substring
  • See String
βœ… SystemRandomNumberGenerator
🟑
Task
  • init(priority: TaskPriority? = nil, operation: @escaping () async throws -> Success)
  • static func detached(priority: TaskPriority? = nil, operation: @escaping () async -> Success) -> Task<Success, Failure>
  • var value: Success
  • func cancel()
  • static func yield() async
  • var isCancelled: Bool
  • static var isCancelled: Bool
  • static func checkCancellation() throws
  • static func sleep(nanoseconds duration: UInt64) async throws
  • static var min: UInt8
βœ… TaskGroup
βœ… ThrowingDiscardingTaskGroup
βœ… ThrowingTaskGroup
βœ… type(of:)
🟒
UInt8
  • static var min: UInt8
  • static var max: UInt8
  • static func random(in range: Range<UInt8>) -> UInt8
  • static func random(in range: Range<UInt8>, using gen: inout RandomNumberGenerator) -> UInt8
🟒
UInt16
  • static var min: UInt16
  • static var max: UInt16
  • static func random(in range: Range<UInt16>) -> UInt16
  • static func random(in range: Range<UInt16>, using gen: inout RandomNumberGenerator) -> UInt16
🟒
UInt32
  • static var min: UInt32
  • static var max: UInt32
  • static func random(in range: Range<UInt32>) -> UInt32
  • static func random(in range: Range<UInt32>, using gen: inout RandomNumberGenerator) -> UInt32
🟒
UInt
  • Kotlin UInts are 32 bit
  • static var min: UInt
  • static var max: UInt
  • static func random(in range: Range<UInt>) -> UInt
  • static func random(in range: Range<UInt>, using gen: inout RandomNumberGenerator) -> UInt
🟒
UInt64
  • static var min: UInt64
  • static var max: UInt64
  • static func random(in range: Range<UInt64>) -> UInt64
  • static func random(in range: Range<UInt64>, using gen: inout RandomNumberGenerator) -> UInt64
βœ… withDiscardingTaskGroup
βœ… withTaskCancellationHandler
βœ… withThrowingTaskGroup
βœ… withThrowingDiscardingTaskGroup
βœ… withThrowingTaskGroup

Topics

Collections

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 is 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. Skip also supports your custom CodingKeys as well as your custom encode(to:) and init(from:) functions for encoding and decoding.

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 calling decode, you must supply a concrete type literal to decode. This applies to both top-level Decoders like JSONDecoder as well as containers like KeyedDecodingContainer. The following will work:

    let object = try decoder.decode(MyType.self, from: jsonData) 

    But these examples will not work:

    let type = MyType.self
    let object = try decoder.decode(type, from: jsonData)
    
    // T is a generic type
    let object = try decoder.decode(T.self, from: jsonData)

It is common for developers to take advantage of Decodable-typed generic functions to be able to decode arbitrary types, so this last limitation is the most onerous. You must consider it when writing your decoding code, and it often requires refactoring existing decoding code being ported to Skip.

One mechanism to ease this restriction and allow you to decode unknown generic types is to write inline decoding functions that take advantage of Kotlin's reified types. Inline functions, however, come with their own limitations and tradeoffs. You can read more about this topic in the Kotlin language documentation. Skip automatically converts any Swift function with the @inline(__always) attribute into a Kotlin inline function with reified generics.

For example, a function like the following will work with Skip, so long as you call it with a concrete Response type or with a generic Response type from another inline function:

@inline(__always) public func send<R: Request, Response: Decodable>(request: R) async throws -> Response {
    let data = try await download(request: request)
    return try jsonDecoder.decode(Response.self, from: data)
}

It transpiles to code like:

inline suspend fun <reified R, reified Response> send(request: R): Response where R: Request, Response: Decodable {
    val data = download(request = request)
    return jsonDecoder.decode(Response::class, from = data) 
}