The skip-model framework is available at https://github.com/skiptools/skip-model.git, which can be checked out and tested with skip test
once Skip is installed.
SkipModel
Model object observation for Skip apps.
See what API is included here.
About
SkipModel vends the skip.model
Kotlin package. This package contains Observable
and ObservableObject
interfaces, representing the two core protocols that SwiftUI uses to observe changes to model objects. It also includes limited Publisher
support.
Dependencies
SkipLib depends on the skip transpiler plugin and the SkipFoundation package.
SkipModel is part of the core SkipStack and is not intended to be imported directly. The transpiler includes import skip.model.*
in generated Kotlin for any Swift source that imports the Combine
, Observation
, or SwiftUI
frameworks.
Status
From the Observation
package, SkipModel supports the @Observable
and @ObservationIgnored
macros.
From Combine
, SkipModel supports the ObservableObject
protocol, the @Published
property wrapper, and limited Publisher
functionality. See API support below.
Much of Skipβs model support is implemented directly in the Skip transpiler. The Observable
and ObservableObject
marker protocols are are sufficient for the Skip transpiler to recognize your observable types. When generating their corresponding Kotlin classes, the transpiler then adds the necessary code so that their state can be tracked by the Compose runtime.
Contributing
We welcome contributions to SkipModel. The Skip product documentation includes helpful instructions and tips on local Skip library development. When submitting code, please include unit tests in your PR.
Model Objects
Like Skip itself, SkipModel objects are dual-platform! Not only do your @Observable
and ObservableObject
properties participate in SwiftUI state tracking, but they are tracked by Compose as well. The Skip transpiler backs your observable properties with MutableState
values in Kotlin, so Compose automatically tracks reads and writes and performs recomposition as needed.
This means that you can write shared model-layer Swift code using observable objects, and use it to power both SwiftUI (whether iOS-only or dual-platform with Skip) as well as pure Android Compose UI code. For example, the following model class:
@Observable class TapCounter {
var tapCount = 0
}
could power a Compose UI:
val tapCounter = TapCounter()
...
TapIt(counter = tapCounter)
...
@Composable fun TapIt(counter: TapCounter) {
Button(onClick = { counter.tapCount += 1 }) {
Text("Tap Count: ${counter.tapCount}")
}
}
API Support
The following table summarizes SkipModelβs 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 Swift API you want. Additionally:
- In all Combine publishes and related API, the
Failure
type must beNever
: throwing errors in Combine chains is not supported. - In Skip, Combine is not automatically imported when you
import Foundation
. Make sure toimport Combine
orimport SwiftUI
explicitly.
Support levels:
- β β Full
- π’ β High
- π‘ β Medium
- π β Low
Support | API |
---|---|
π’ |
|
π |
|
π’ |
|
π |
|
β | func NotificationCenter.publisher(for: Notification.Name, object: Any? = nil): Publisher<Notification, Never> |
π’ |
|
π’ |
|
π |
|
β | @ObservationIgnored |
π |
|
β | @Published |
π |
|
β | func Timer.publish(every: TimeInterval, tolerance: TimeInterval? = nil, on runLoop: RunLoop, in mode: RunLoop.Mode, options: RunLoop.SchedulerOptions? = nil) -> ConnectablePublisher<Date, Never> |
License
This software is licensed under the GNU Lesser General Public License v3.0, with the following linking exception to clarify that distribution to restricted environments (e.g., app stores) is permitted:
This software is licensed under the LGPL3, included below. As a special exception to the GNU Lesser General Public License version 3 (βLGPL3β), the copyright holders of this Library give you permission to convey to a third party a Combined Work that links statically or dynamically to this Library without providing any Minimal Corresponding Source or Minimal Application Code as set out in 4d or providing the installation information set out in section 4e, provided that you comply with the other provisions of LGPL3 and provided that you meet, for the Application the terms and conditions of the license(s) which apply to the Application. Except as stated in this special exception, the provisions of LGPL3 will continue to comply in full to this Library. If you modify this Library, you may apply this exception to your version of this Library, but you are not obliged to do so. If you do not wish to do so, delete this exception statement from your version. This exception does not (and cannot) modify any license terms which apply to the Application, with which you must still comply.