跳转到内容
Skip 中文站
3.2k

SkipFoundation

为 Skip Lite 转译后的 Swift 提供 Foundation 支持。

在此处查看当前已实现的 API。

SkipFoundation 提供 skip.foundation Kotlin 包。它是在 Android 上面向 Kotlin 的 Foundation 重新实现,目标是尽可能完整地映射 Foundation,让 Skip 开发者能够放心使用 Foundation API。

SkipFoundation 还实现了部分 CryptoKit API。

SkipFoundation 依赖 skip ↗ 转译器插件和 SkipLib 包。

SkipFoundation 属于 Skip Core Frameworks,并不建议直接导入。 Skip 转译器会把 import Foundation 转换为 import skip.foundation.*,因此该模块会被透明地引入。

  • SkipFoundation 包含 UrlEncoderUtil ↗ 库的源代码,用于实现百分号转义。

SkipFoundation 已支持 Foundation 框架中的许多常用 API,但仍有大量 API 尚未移植。参见 Foundation 支持。

需要使用尚未实现的 Foundation API 时,可以选择仅使用受支持的 API 寻找替代方案、按照 Skip 文档的说明直接嵌入 Kotlin 代码,或为 SkipFoundation 添加支持。如果选择增强 SkipFoundation 本身,请考虑将代码贡献回来,以便纳入正式版本。

欢迎为 SkipFoundation 贡献代码。Skip 产品的文档包含本地开发 Skip 库时很有帮助的说明与技巧。

当前最迫切的任务是实现更多最常用的 Foundation API。 若要帮助补齐 SkipFoundation 中尚未实现的 API:

  1. 找到尚未实现的 API。
  2. 编写合适的 Kotlin 实现。参见下方的实现策略。
  3. 修改对应测试,确保它们不再被跳过并能通过。如果没有现成测试,请编写测试。参见测试。
  4. 提交 PR。 ↗

也欢迎测试用例、注释和文档等其他形式的贡献!

SkipFoundation 的目标是在 Android 上映射 Foundation 框架。在条件允许时,SkipFoundation 类型会封装对应的 Kotlin 或 Java 基础类型。当某个 SkipFoundation 类型封装了对应的 Kotlin 或 Java 类型时,请遵循 skip.lib.KotlinConverting<T> 协议,也就是添加 .kotlin() 函数:

#if SKIP
extension Calendar: KotlinConverting<java.util.Calendar> {
public override func kotlin(nocopy: Bool = false) -> java.util.Calendar {
return nocopy ? platformValue : platformValue.clone() as java.util.Calendar
}
}
#endif

你还应实现一个能够接收等价 Kotlin 或 Java 对象的构造器。

SkipFoundation 的 Tests/ 文件夹包含 Foundation 框架的整套官方测试用例。借助 SkipUnit,我们可以在 Android 上使用 Foundation 团队在 iOS 上采用的同一套测试,验证 SkipFoundation 的 API 实现。

SkipFoundation 的目标是尽可能纳入并通过更多官方测试。

下表汇总了 SkipFoundation 在 Android 上对 Foundation API 的支持情况。未列出的内容很可能尚不受支持。请注意,在仅用于 iOS 的代码中(即 #if !SKIP 代码块内),你可以使用任意 Foundation API。

支持级别:

  • ✅ – 完整支持
  • 🟢 – 高度支持
  • 🟡 – 中等支持
  • 🟠 – 少量支持
支持度API
🟠
AttributedString
  • init()
  • init(stringLiteral: String)
  • init(markdown: String)
  • init(localized keyAndValue: String.LocalizationValue, table: String? = nil, bundle: Bundle? = nil, locale: Locale? = nil, comment: String? = nil)
  • init(localized key: String, table: String? = nil, bundle: Bundle? = nil, locale: Locale = Locale.current, comment: String? = nil)
🟡
Bundle
  • static var main: Bundle
  • static var module: Bundle
  • init?(path: String)
  • init?(url: URL)
  • init(for forClass: AnyClass)
  • init()
  • var bundleURL: URL
  • var resourceURL: URL?
  • var bundlePath: String
  • var resourcePath: String?
  • func url(forResource: String? = nil, withExtension: String? = nil, subdirectory: String? = nil, localization: String? = nil) -> URL?
  • func path(forResource: String? = nil, ofType: String? = nil, inDirectory: String? = nil, forLocalization: String? = nil) -> String?
  • var developmentLocalization: String
  • var localizations: [String]
  • func localizedString(forKey key: String, value: String?, table tableName: String?) -> String
  • var localizations: [String]
🟡
Calendar
  • 仅支持 .gregorian 和 .iso8601 标识符
  • init(identifier: Calendar.Identifier)
  • static var current: Calendar
  • var locale: Locale
  • var timeZone: TimeZone
  • var identifier: Calendar.Identifier
  • var eraSymbols: [String]
  • var monthSymbols: [String]
  • var shortMonthSymbols: [String]
  • var weekdaySymbols: [String]
  • var shortWeekdaySymbols: [String]
  • var amSymbol: String
  • var pmSymbol: String
  • func component(_ component: Calendar.Component, from date: Date) -> Int
  • func minimumRange(of component: Calendar.Component) -> Range<Int>?
  • func maximumRange(of component: Calendar.Component) -> Range<Int>?
  • func range(of smaller: Calendar.Component, in larger: Calendar.Component, for date: Date) -> Range<Int>?
  • func dateInterval(of component: Calendar.Component, for date: Date) -> DateInterval?
  • func dateInterval(of component: Calendar.Component, start: inout Date, interval: inout TimeInterval, for date: Date) -> Bool
  • func ordinality(of smaller: Calendar.Component, in larger: Calendar.Component, for date: Date) -> Int?
  • func date(from components: DateComponents) -> Date?
  • func date(byAdding components: DateComponents, to date: Date, wrappingComponents: Bool = false) -> Date?
  • func date(byAdding component: Calendar.Component, value: Int, to date: Date, wrappingComponents: Bool = false) -> Date?
  • func date(bySetting component: Calendar.Component, value: Int, of date: Date) -> Date?
  • func date(bySettingHour hour: Int, minute: Int, second: Int, of date: Date, matchingPolicy: Calendar.MatchingPolicy = .nextTime, repeatedTimePolicy: Calendar.RepeatedTimePolicy = .first, direction: Calendar.SearchDirection = .forward) -> Date?
  • func date(_ date: Date, matchesComponents components: DateComponents) -> Bool
  • func dateComponents(in zone: TimeZone? = nil, from date: Date) -> DateComponents
  • func dateComponents(_ components: Set<Calendar.Component>, from start: Date, to end: Date) -> DateComponents
  • func dateComponents(_ components: Set<Calendar.Component>, from date: Date) -> DateComponents
  • func startOfDay(for date: Date) -> Date
  • func compare(_ date1: Date, to date2: Date, toGranularity component: Calendar.Component) -> ComparisonResult
  • func isDate(_ date1: Date, equalTo date2: Date, toGranularity component: Calendar.Component) -> Bool
  • func isDate(_ date1: Date, inSameDayAs date2: Date) -> Bool
  • func isDateInToday(_ date: Date) -> Bool
  • func isDateInWeekend(_ date: Date) -> Bool
  • func enumerateDates(startingAfter start: Date, matching components: DateComponents, matchingPolicy: Calendar.MatchingPolicy, repeatedTimePolicy: Calendar.RepeatedTimePolicy = .first, direction: Calendar.SearchDirection = .forward, using block: (_ result: Date?, _ exactMatch: Bool, _ stop: inout Bool) -> Void)
  • func nextDate(after date: Date, matching components: DateComponents, matchingPolicy: Calendar.MatchingPolicy, repeatedTimePolicy: Calendar.RepeatedTimePolicy = .first, direction: Calendar.SearchDirection = .forward) -> Date?)
🟠
CharacterSet
  • 所提供的字符集并不完整
  • static var whitespaces: CharacterSet
  • static var whitespacesAndNewlines: CharacterSet
  • static var newlines: CharacterSet
  • static var urlHostAllowed: CharacterSet
  • static var urlFragmentAllowed: CharacterSet
  • static var urlPathAllowed: CharacterSet
  • static var urlQueryAllowed: CharacterSet
  • init()
  • init(charactersIn: String)
  • func insert(_ character: Unicode.Scalar) -> (inserted: Bool, memberAfterInsert: Unicode.Scalar)
  • mutating func insert(charactersIn: String)
  • func update(with character: Unicode.Scalar) -> Unicode.Scalar?
  • func remove(_ character: Unicode.Scalar) -> Unicode.Scalar?
  • mutating func remove(charactersIn: String)
  • func contains(_ member: Unicode.Scalar) -> Bool
  • func union(_ other: CharacterSet) -> CharacterSet
  • mutating func formUnion(_ other: CharacterSet)
  • func intersection(_ other: CharacterSet) -> CharacterSet
  • mutating func formIntersection(_ other: CharacterSet)
  • func subtracting(_ other: CharacterSet)
  • mutating func subtract(_ other: CharacterSet)
  • func symmetricDifference(_ other: CharacterSet) -> CharacterSet
  • mutating func formSymmetricDifference(_ other: CharacterSet)
  • func isSuperset(of other: CharacterSet) -> Bool
  • func isSubset(of other: CharacterSet) -> Bool
  • func isDisjoint(with other: CharacterSet) -> Bool
  • func isStrictSubset(of other: CharacterSet) -> Bool
  • func isStrictSuperset(of other: CharacterSet) -> Bool
  • var isEmpty: Bool
✅ CocoaError
✅ ComparisonResult
🟠
CryptoKit
  • 有关受支持 API 的详情,请参见 CryptoKit 专题。
✅ CustomNSError
🟡
Data
  • Data 不遵循 Collection 协议
  • init()
  • init(count: Int)
  • init(capacity: Int)
  • init(_ data: Data)
  • init(_ bytes: [UInt8], length: Int? = nil)
  • init(_ checksum: Digest)
  • init?(base64Encoded: String, options: Data.Base64DecodingOptions = [])
  • init(contentsOfFile filePath: String) throws
  • init(contentsOf url: URL, options: Data.ReadingOptions = [])
  • var count: Int
  • var isEmpty: Bool
  • var bytes: [UInt8]
  • var utf8String: String?
  • func base64EncodedString() -> String
  • func sha256() -> Data
  • func subdata(in range: Range) -> Data
  • func hex() -> String
  • mutating func reserveCapacity(_ minimumCapacity: Int)
  • mutating func append(_ other: Data)
  • mutating func append(contentsOf bytes: [UInt8])
  • mutating func append(contentsOf data: Data)
  • subscript(index: Int) -> UInt8
  • func write(to url: URL, options: Data.WritingOptions = []) throws
🟡
Date
  • 除以下函数外,其他格式化函数均不受支持:
  • func ISO8601Format(_ style: Date.ISO8601FormatStyle = .iso8601) -> String
  • func formatted(date: Date.FormatStyle.DateStyle, time: Date.FormatStyle.TimeStyle) -> String
🟢
DateComponents
  • nanosecond, yearForWeekOfYear are not supported
🟡
DateFormatter
  • var dateStyle: DateFormatter.Style
  • var timeStyle: DateFormatter.Style
  • var isLenient: Bool
  • var dateFormat: String
  • func setLocalizedDateFormatFromTemplate(dateFormatTemplate: String)
  • static func dateFormat(fromTemplate: String, options: Int, locale: Locale?) -> String?
  • static func localizedString(from date: Date, dateStyle: DateFormatter.Style, timeStyle: DateFormatter.Style) -> String
  • var timeZone: TimeZone?
  • var locale: Locale?
  • var calendar: Calendar?
  • func date(from string: String) -> Date?
  • func string(from date: Date) -> String
  • func string(for obj: Any?) -> String?
✅ DateInterval
🟠
Decimal
  • 作为 java.math.BigDecimal 的别名
🟠
DispatchQueue
  • static let main: DispatchQueue
  • func async(execute: () -> Void)
  • func asyncAfter(deadline: DispatchTime, execute: () -> Void)
  • func asyncAfter(wallDeadline: DispatchWallTime, execute: () -> Void)
🟢
FileManager
  • static let `default`: FileManager
  • let temporaryDirectory: URL
  • let currentDirectoryPath: String
  • func createSymbolicLink(at url: URL, withDestinationURL destinationURL: URL) throws
  • func createSymbolicLink(atPath path: String, withDestinationPath destinationPath: String) throws
  • func createDirectory(at url: URL, withIntermediateDirectories: Bool, attributes: [FileAttributeKey : Any]? = nil) throws
  • func createDirectory(atPath path: String, withIntermediateDirectories: Bool, attributes: [FileAttributeKey : Any]? = nil) throws
  • func destinationOfSymbolicLink(atPath path: String) throws
  • func attributesOfItem(atPath path: String) throws -> [FileAttributeKey: Any]
  • func setAttributes(_ attributes: [FileAttributeKey : Any], ofItemAtPath path: String) throws
  • func createFile(atPath path: String, contents: Data? = nil, attributes: [FileAttributeKey : Any]? = nil) -> Bool
  • func copyItem(atPath path: String, toPath: String) throws
  • func copyItem(at url: URL, to: URL) throws
  • func moveItem(atPath path: String, toPath: String) throws
  • func moveItem(at path: URL, to: URL) throws
  • func subpathsOfDirectory(atPath path: String) throws -> [String]
  • func subpaths(atPath path: String) -> [String]?
  • func removeItem(atPath path: String) throws
  • func removeItem(at url: URL) throws
  • func fileExists(atPath path: String) -> Bool
  • func isReadableFile(atPath path: String) -> Bool
  • func isExecutableFile(atPath path: String) -> Bool
  • func isDeletableFile(atPath path: String) -> Bool
  • func isWritableFile(atPath path: String) -> Bool
  • func contentsOfDirectory(at url: URL, includingPropertiesForKeys: [URLResourceKey]?) throws -> [URL]
  • func contentsOfDirectory(atPath path: String) throws -> [String]
  • func url(for directory: FileManager.SearchPathDirectory, in domain: FileManager.SearchPathDomainMask, appropriateFor url: URL?, create shouldCreate: Bool) throws -> URL
✅ HTTPURLResponse
🟢
IndexPath
  • 不能通过数组字面量赋值
  • 不能向范围下标赋值
🟡
IndexSet
  • 这是一个使用内部 Set 的低效实现
  • init(integersIn range: any RangeExpression<Int>)
  • init(integer: Int)
  • init()
  • func integerGreaterThan(_ integer: Int) -> Int?
  • func integerLessThan(_ integer: Int) -> Int?
  • func integerGreaterThanOrEqualTo(_ integer: Int) -> Int?
  • func integerLessThanOrEqualTo(_ integer: Int) -> Int?
  • func count(in range: any RangeExpression<Int>) -> Int
  • func contains(integersIn range: any RangeExpression<Int>) -> Bool
  • func contains(integersIn indexSet: IntSet) -> Bool
  • func intersects(integersIn range: any RangeExpression<Int>) -> Bool
  • mutating func insert(integersIn range: any RangeExpression<Int>)
  • mutating func remove(integersIn range: any RangeExpression<Int>)
  • func filteredIndexSet(in range: any RangeExpression<Int>, includeInteger: (Int) throws -> Bool) rethrows -> IndexSet
  • func filteredIndexSet(includeInteger: (Int) throws -> Bool) rethrows -> IndexSet
  • 完整支持 SetAlgebra 协议
🟡
ISO8601DateFormatter
  • static func string(from date: Date, timeZone: TimeZone) -> String
  • var timeZone: TimeZone?
  • func date(from string: String) -> Date?
  • func string(from date: Date) -> String
  • func string(for obj: Any?) -> String?
✅ JSONDecoder
✅ JSONEncoder
✅ JSONSerialization
✅ Locale
✅ LocalizedError
✅ LocalizedStringResource
✅
OSLog.Logger
  • Log messages on Android can be viewed with the adb logcat command, or in the Android Studio console
✅ Notification
🟡
NotificationCenter
  • static let default: NotificationCenter
  • func addObserver(forName name: Notification.Name?, object: Any?, queue: OperationQueue?, using block: (Notification) -> Void) -> Any
  • func removeObserver(_ observer: Any)
  • func post(_ notification: Notification)
  • func post(name: Notification.Name, object: Any?, userInfo: [AnyHashable: Any]? = nil)
  • func notifications(named: Notification.Name, object: AnyObject? = nil) -> Notifications
  • 另请参见 SkipModel 模块对 NotificationCenter.publisher 的支持
✅ NSError
✅ NSLock
✅ NSLocalizedString
✅ NSRecursiveLock
🟢
Number
  • init(value: Int8)
  • init(value: Int16)
  • init(value: Int32)
  • init(value: Int64)
  • init(value: UInt8)
  • init(value: UInt16)
  • init(value: UInt32)
  • init(value: UInt64)
  • init(value: Float)
  • init(value: Double)
  • init(_ value: Int8)
  • init(_ value: Int16)
  • init(_ value: Int32)
  • init(_ value: Int64)
  • init(_ value: UInt8)
  • init(_ value: UInt16)
  • init(_ value: UInt32)
  • init(_ value: UInt64)
  • init(_ value: Float)
  • init(_ value: Double)
  • var doubleValue: Double
  • var intValue: Int
  • var longValue: Int64
  • var int64Value: Int64
  • var int32Value: Int32
  • var int16Value: Int16
  • var int8Value: Int8
🟢
NumberFormatter
  • 支持以下样式:.none, .decimal, .currency, .percent
  • var numberStyle: NumberFormatter.Style
  • var locale: Locale?
  • var format: String
  • var groupingSize: Int
  • var generatesDecimalNumbers: Bool
  • var alwaysShowsDecimalSeparator: Bool
  • var usesGroupingSeparator: Bool
  • var multiplier: NSNumber?
  • var groupingSeparator: String?
  • var percentSymbol: String?
  • var currencySymbol: String?
  • var zeroSymbol: String?
  • var minusSign: String?
  • var exponentSymbol: String?
  • var negativeInfinitySymbol: String
  • var positiveInfinitySymbol: String
  • var internationalCurrencySymbol: String?
  • var decimalSeparator: String?
  • var currencyCode: String?
  • var currencyDecimalSeparator: String?
  • var notANumberSymbol: String?
  • var positiveSuffix: String?
  • var negativeSuffix: String?
  • var positivePrefix: String?
  • var negativePrefix: String?
  • var maximumFractionDigits: Int
  • var minimumFractionDigits: Int
  • var maximumIntegerDigits: Int
  • var minimumIntegerDigits: Int
  • func string(from number: NSNumber) -> String?
  • func string(from number: Int) -> String?
  • func string(for object: Any?) -> String?
  • func number(from string: String) -> NSNumber?
🟠
OperationQueue
  • static let main: OperationQueue
🟢
OSAllocatedUnfairLock
  • init()
  • init(initialState: State)
  • init(uncheckedState initialState: State)
  • func lock()
  • func unlock()
  • func lockIfAvailable() -> Bool
  • func withLockUnchecked<R>(_ body: (inout State) throws -> R) rethrows -> R
  • func func withLockUnchecked<R>(_ body: () throws -> R) rethrows -> R
  • func withLock<R>(_ body: (inout State) throws -> R) rethrows -> R
  • func withLock<R>(_ body: () throws -> R) rethrows -> R
  • func withLockIfAvailableUnchecked<R>(_ body: (inout State) throws -> R) rethrows -> R?
  • func withLockIfAvailableUnchecked<R>(_ body: () throws -> R) rethrows -> R?
  • func withLockIfAvailable<R>(_ body: @Sendable (inout State) throws -> R) rethrows -> R?
  • func withLockIfAvailable<R>(_ body: () throws -> R) rethrows -> R?
✅ POSIXError
🟡
ProcessInfo
  • static let processInfo: ProcessInfo
  • var globallyUniqueString: String
  • var systemProperties: [String: String]
  • var environment: [String : String]
  • var processIdentifier: Int32
  • var arguments: [String]
  • var hostName: String
  • var processorCount: Int
  • var operatingSystemVersionString: String
  • var isMacCatalystApp: Bool
  • var isiOSAppOnMac: Bool
🟠
PropertyListSerialization
  • static func propertyList(from: Data, options: PropertyListSerialization.ReadOptions = [], format: Any?) throws -> [String: String]?
  • 会忽略传入的所有 options 和 format
🟠
RelativeDateTimeFormatter
  • localizedString(from dateComponents: DateComponents) -> String
  • func localizedString(fromTimeInterval timeInterval: TimeInterval) -> String
  • func localizedString(for date: Date, relativeTo referenceDate: Date) -> String
  • func string(for obj: Any?) -> String?
🟠
RunLoop
  • static let main: RunLoop
✅ RecoverableError
🟢
String
  • 核心 String API 定义于 SkipLib,这些扩展则定义于 SkipFoundation
  • init(data: Data, encoding: StringEncoding)
  • init(bytes: [UInt8], encoding: StringEncoding)
  • init(contentsOf: URL)
  • static func localizedStringWithFormat(_ format: String, _ arguments: Any...) -> String
  • 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
  • func addingPercentEncoding(withAllowedCharacters: CharacterSet) -> String?
  • var removingPercentEncoding: String?
  • var utf8Data: Data
  • var utf8: [UInt8]
  • var utf16: [UInt8]
  • var utf32: [UInt8]
  • var unicodeScalars: [UInt8]
  • func data(using: StringEncoding, allowLossyConversion: Bool = true) -> Data?
  • func write(to url: URL, atomically useAuxiliaryFile: Bool, encoding enc: StringEncoding) throws
  • func write(toFile path: String, atomically useAuxiliaryFile: Bool, encoding enc: StringEncoding) throws
✅ StringLocalizationValue
✅ func strlen(_ string: String) -> Int
✅ func strncmp(_ str1: String, _ str2: String) -> Int
🟡
Thread
  • current
  • main
  • isMainThread
  • sleep(for: TimeInterval)
  • sleep(until: Date)
  • callStackSymbols
  • 尚未实现线程的启动和停止,也不支持使用代码块构造 Thread
🟡
Timer
  • init(timeInterval: TimeInterval, repeats: Bool, block: (Timer) -> Void)
  • static func scheduledTimer(withTimeInterval interval: TimeInterval, repeats: Bool, block: (Timer) -> Void) -> Timer
  • var timeInterval: TimeInterval
  • func invalidate()
  • var isValid: Bool
  • var userInfo: Any?
  • 另请参见 SkipModel 模块对 Timer.publish 的支持
🟢
TimeZone
  • static var current: TimeZone
  • static var autoupdatingCurrent: TimeZone
  • static var `default`: TimeZone
  • static var system: TimeZone
  • static var local: TimeZone
  • static var gmt: TimeZone
  • init?(identifier: String)
  • init?(abbreviation: String)
  • init?(secondsFromGMT seconds: Int)
  • var identifier: String
  • func abbreviation(for date: Date = Date()) -> String?
  • func secondsFromGMT(for date: Date = Date()) -> Int
  • func isDaylightSavingTime(for date: Date = Date()) -> Bool
  • func daylightSavingTimeOffset(for date: Date = Date()) -> TimeInterval
  • var nextDaylightSavingTimeTransition: Date?
  • func nextDaylightSavingTimeTransition(after date: Date) -> Date?
  • static var knownTimeZoneIdentifiers: [String]
  • static var knownTimeZoneNames: [String]
  • static var abbreviationDictionary: [String : String]
  • func localizedName(for style: NameStyle, locale: Locale?) -> String?
✅ UnknownNSError
🟢
URL
  • init(_ url: URL)
  • init?(string: String, relativeTo baseURL: URL? = nil)
  • init?(string: String, encodingInvalidCharacters: Bool)
  • init(fileURLWithPath path: String, isDirectory: Bool? = nil, relativeTo base: URL? = nil)
  • static func currentDirectory() -> URL
  • static var homeDirectory: URL
  • static var temporaryDirectory: URL
  • static var cachesDirectory: URL
  • static var documentsDirectory: URL
  • let baseURL: URL?
  • var scheme: String?
  • var host: String?
  • func host(percentEncoded: Bool = true) -> String?
  • var port: Int?
  • var path: String?
  • func path(percentEncoded: Bool = true) -> String?
  • var hasDirectoryPath: Bool
  • var query: String?
  • func query(percentEncoded: Bool = true) -> String?
  • var fragment: String?
  • func fragment(percentEncoded: Bool = true) -> String?
  • var standardized: URL
  • var standardizedFileURL: URL
  • mutating func standardize()
  • var absoluteURL: URL
  • var absoluteString: String
  • var relativePath: String
  • var relativeString: String
  • var pathComponents: [String]
  • var lastPathComponent: String
  • var pathExtension: String
  • var isFileURL: Bool
  • func appendingPathComponent(_ pathComponent: String) -> URL
  • mutating func appendPathComponent(_ pathComponent: String)
  • func appendingPathComponent(_ pathComponent: String, isDirectory: Bool) -> URL
  • mutating func appendPathComponent(_ pathComponent: String, isDirectory: Bool)
  • func appendingPathExtension(_ pathExtension: String) -> URL
  • mutating func appendPathExtension(_ pathExtension: String)
  • func deletingLastPathComponent() -> URL
  • mutating func deleteLastPathComponent() -> URL
  • func deletingPathExtension() -> URL
  • mutating func deletePathExtension()
  • func resolvingSymlinksInPath() -> URL
  • mutating func resolveSymlinksInPath() -> URL
  • func checkResourceIsReachable() throws -> Bool
🟢
URLComponents
  • init()
  • init?(url: URL, resolvingAgainstBaseURL resolve: Bool)
  • init?(string: String)
  • init?(string: String, encodingInvalidCharacters: Bool)
  • var url: URL?
  • func url(relativeTo base: URL?) -> URL?
  • var string: String?
  • var scheme: String?
  • var host: String?
  • var port: Int?
  • var path: String
  • var fragment: String?
  • var query: String?
  • var queryItems: [URLQueryItem]?
  • var percentEncodedHost: String?
  • var encodedHost: String?
  • var percentEncodedPath: String
  • var percentEncodedQuery: String?
  • var percentEncodedFragment: String?
  • var percentEncodedQueryItems: [URLQueryItem]?
✅ URLError
✅ URLQueryItem
🟡
URLRequest
  • URLSession 目前会忽略许多属性
  • 不支持 httpBodyStream
✅ URLResponse
🟡
URLSession
  • static let shared: URLSession
  • init(configuration: URLSessionConfiguration, delegate: URLSessionDelegate? = nil, delegateQueue: OperationQueue? = nil)
  • var configuration: URLSessionConfiguration
  • var sessionDescription: String?
  • var delegate: URLSessionDelegate?
  • var delegateQueue: OperationQueue?
  • func data(for request: URLRequest, delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse)
  • func data(from url: URL, delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse)
  • func upload(for request: URLRequest, fromFile fileURL: URL, delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse)
  • func upload(for request: URLRequest, from bodyData: Data, delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse)
  • func bytes(for request: URLRequest, delegate: URLSessionTaskDelegate? = nil) async throws -> (AsyncBytes, URLResponse)
  • func bytes(from url: URL, delegate: URLSessionTaskDelegate? = nil) async throws -> (AsyncBytes, URLResponse)
  • func dataTask(with url: URL, completionHandler: ((Data?, URLResponse?, Error?) -> Void)? = nil) -> URLSessionDataTask
  • func dataTask(with request: URLRequest, completionHandler: ((Data?, URLResponse?, Error?) -> Void)? = nil) -> URLSessionDataTask
  • func uploadTask(with: URLRequest, from: Data?, completionHandler: ((Data?, URLResponse?, Error?) -> Void)? = nil) -> URLSessionUploadTask
  • func uploadTask(with: URLRequest, fromFile: URL, completionHandler: ((Data?, URLResponse?, Error?) -> Void)?) -> URLSessionUploadTask
  • func webSocketTask(with url: URL) -> URLSessionWebSocketTask
  • func webSocketTask(with request: URLRequest) -> URLSessionWebSocketTask
  • func webSocketTask(with url: URL, protocols: [String]) -> URLSessionWebSocketTask
  • func finishTasksAndInvalidate()
  • func getTasksWithCompletionHandler(_ handler: ([URLSessionDataTask], [URLSessionUploadTask], [URLSessionDownloadTask]) -> Void)
  • var tasks: ([URLSessionDataTask], [URLSessionUploadTask], [URLSessionDownloadTask])
  • func getAllTasks(handler: ([URLSessionTask]) -> Void)
  • var allTasks: [URLSessionTask]
  • func invalidateAndCancel()
  • struct AsyncBytes: AsyncSequence
🟡
URLSessionConfiguration
  • URLSession 目前会忽略许多属性
🟢
URLSessionDataTask
  • static let defaultPriority: Float
  • static let lowPriority: Float
  • static let highPriority: Float
  • var taskIdentifier: Int
  • var taskDescription: String?
  • var originalRequest: URLRequest?
  • var delegate: URLSessionTaskDelegate?
  • var state: URLSessionTask.State
  • var error: Error?
  • var priority: Float
  • func suspend()
  • func resume()
  • func cancel()
🟢
URLSessionUploadTask
  • static let defaultPriority: Float
  • static let lowPriority: Float
  • static let highPriority: Float
  • var taskIdentifier: Int
  • var taskDescription: String?
  • var originalRequest: URLRequest?
  • var delegate: URLSessionTaskDelegate?
  • var state: URLSessionTask.State
  • var error: Error?
  • var priority: Float
  • func suspend()
  • func resume()
  • func cancel()
🟢
URLSessionWebSocketTask
  • static let defaultPriority: Float
  • static let lowPriority: Float
  • static let highPriority: Float
  • var taskIdentifier: Int
  • var taskDescription: String?
  • var originalRequest: URLRequest?
  • var delegate: URLSessionTaskDelegate?
  • var state: URLSessionTask.State
  • var error: Error?
  • var priority: Float
  • func suspend()
  • func resume()
  • func cancel()
  • func cancel(with closeCode: CloseCode, reason: Data?)
  • var maximumMessageSize: Int
  • var closeCode: CloseCode
  • var closeReason: Data?
  • func send(_ message: Message) async throws -> Void
  • func receive() async throws -> Message
🟢
UserDefaults
  • static var standard: UserDefaults
  • init(suiteName: String?)
  • func register(defaults registrationDictionary: [String : Any])
  • func set(_ value: Int, forKey defaultName: String)
  • func set(_ value: Boolean, forKey defaultName: String)
  • func set(_ value: Double, forKey defaultName: String)
  • func set(_ value: String, forKey defaultName: String)
  • func set(_ value: Any?, forKey defaultName: String)
  • func removeObject(forKey defaultName: String)
  • func object(forKey defaultName: String) -> Any?
  • func string(forKey defaultName: String) -> String?
  • func double(forKey defaultName: String) -> Double?
  • func integer(forKey defaultName: String) -> Int?
  • func bool(forKey defaultName: String) -> Bool?
  • func url(forKey defaultName: String) -> URL?
  • func data(forKey defaultName: String) -> Data?
  • func dictionaryRepresentation() -> [String : Any]
  • 不支持数组和字典值
🟢
UUID
  • init()
  • init?(uuidString: String)
  • static func fromString(uuidString: String) -> UUID?
  • var uuidString: String

SkipFoundation 通过委托给 Java 内置实现,提供 CryptoKit 框架的部分功能:

  • SHA256
  • SHA256Digest
  • SHA384
  • SHA384Digest
  • SHA512
  • SHA512Digest
  • Insecure.MD5
  • Insecure.MD5Digest
  • Insecure.SHA1
  • Insecure.SHA1Digest
  • HMACMD5
  • HMACSHA1
  • HMACSHA256
  • HMACSHA384
  • HMACSHA512

每种受支持的算法都包含以下 API:

associatedtype Digest
public static func hash(data: Data) -> Digest
public func update(_ data: DataProtocol)
public func finalize() -> Digest

返回的 Digest 本身可作为 UInt8 字节序列使用。

Skip 实现了 Foundation.FileManager 的大部分功能;它应当作为 与文件系统交互的主要接口。

可以像下面这样访问应用专属文件夹:

// 在 Android 上对应 Context.getFilesDir()
let folder = URL.documentsDirectory
// 这是以下写法的简写:
let folder = try FileManager.default.url(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask, appropriateFor: nil, create: false)

要读写缓存文件夹,可以使用:

// 在 Android 上对应 Context.getCachesDir()
let caches = URL.cachesDirectory
// 这是以下写法的简写:
let caches = try FileManager.default.url(for: FileManager.SearchPathDirectory.cachesDirectory, in: FileManager.SearchPathDomainMask.userDomainMask, appropriateFor: nil, create: false)

系统临时文件夹可以通过以下方式访问:

// 在 Android 上,这与 Context.getCachesDir() 相同
let tmpdir = URL.temporaryDirectory
// 也可以使用:
let tmpdir = NSTemporaryDirectory()

Skip 尚未实现其他任何 FileManager.SearchPathDirectory 枚举值。

Data 和 String 都支持通过 URL 或路径字符串进行读写。

skiptools/skip-foundation ↗ 最近发布的 10 个版本:

完整历史记录: github.com/skiptools/skip-foundation/releases ↗ — .atom 订阅源 ↗