Why Embedded Swift
Showcase
HomeKit 기반 컬러 LED등
준비물
개발 환경
시작 템플릿 코드로 빌드가 되는지 확인
@_cdecl("app_main")
func app_main() {
print("🏎️ Hello, Embedded Swift!")
}
확인했으면 SDK코드를 사용해서 프로그래밍
@_cdecl("app_main")
func app_main() {
print("🏎️ Hello, Embedded Swift!")
var config = led_driver_get_config()
let handle = led_driver_init(&config)
led_driver_set_hue(handle, 240) // blue
led_driver_set_saturation(handle, 100) // 100%
led_driver_set_brightness(handle, 80) // 80%
led_driver_set_power(handle, true)
}
C API를 직접 호출하지 않도록 래핑
final class LED {
var enabled: Bool = true {
didSet {
led_driver_set_power(handle, enabled)
}
}
var brightness: Int = 100 {
didSet {
brightness = max(0, min(100, brightness))
led_driver_set_brightness(handle, UInt8(brightness))
}
}
// ...
}
let led = LED()
@_cdecl("app_main")
func app_main() {
print("🏎️ Hello, Embedded Swift!")
led.color = .red
led.brightness = 80
while true {
sleep(1)
led.enabled = !led.enabled
if led.enabled {
led.color = .hueSaturation(Int.random(in: 0 ..< 360), 100)
}
}
}
HomeKit 액세서리를 구현하기 위해서 Matter 프로토콜을 사용할 것이다.
Matter 프로토콜 구현
let led = LED()
@_cdecl("app_main")
func app_main() {
print("🏎️ Hello, Embedded Swift!")
// (1) Matter 루트 노드 만들기
// 루트 노드는 악세사리 자체를 의미한다.
let rootNode = Matter.Node()
rootNode.identifyHandler = {
print("identify")
}
// (2) 엔드포인트 구성
// 여기서는 컬러 LED가 된다.
// 상태를 가지며(색상, 밝기 등) 명령을 받을 수 있다(불 키고 끄기 등)
let lightEndpoint = Matter.ExtendedColorLight(node: rootNode)
lightEndpoint.configuration = .default
lightEndpoint.eventHandler = { event in
print("lightEndpoint.eventHandler:")
print(event.attribute)
print(event.value)
switch event.attribute {
case .onOff:
led.enabled = (event.value == 1)
case .levelControl:
led.brightness = Int(Float(event.value) / 255.0 * 100.0)
case .colorControl(.currentHue):
let newHue = Int(Float(event.value) / 255.0 * 360.0)
led.color = .hueSaturation(newHue, led.color.saturation)
case .colorControl(.currentSaturation):
let newSaturation = Int(Float(event.value) / 255.0 * 100.0)
led.color = .hueSaturation(led.color.hue, newSaturation)
case .colorControl(.colorTemperatureMireds):
let kelvins = 1_000_000 / event.value
led.color = .temperature(kelvins)
default:
break
}
}
// (3) 엔드포인트를 노드에 추가
rootNode.addEndpoint(lightEndpoint)
// (4) 노드를 애플리케이션에 제공하고 앱을 시작
let app = Matter.Application()
app.eventHandler = { event in
print(event.type)
}
app.rootNode = rootNode
app.start()
}
전체 샘플은 깃헙에서 확인 가능
How Embedded Swift differs
임베디드 환경의 제한
이 제한에 맞추기 위해서 특정 기능들은 허용하지 않는다.
ex. 리플렉션
타입의 자식을 보기 위해서는 타입의 메타데이터 레코드를 봐야한다.
여기에는 필드 이름, 오프셋, 타입 정보 등이 있다. 타입 정보는 다시 메타데이터 레코드를 가진다.
이 레코드들이 다 합쳐지면 코드사이즈가 크게 늘어난다.
let mirror = Mirror(reflecting: s)
mirror.children.forEach { … }
struct MyStruct {
var count: Int
var name: String
}
위와 같은 이유로 Metatype과 any 타입도 사용이 불가능하다.
하지만 대부분은 사용이 가능하다.
Embedded Swift는 엄격한 서브셋이지, 변종이나 방언이 아니다.
사용할 수 없는 기능을 사용하면 에러가 난다.
protocol Countable {
var count: Int { get }
}
func count(countable: any Countable) {
print(countable.count) // Cannot use a value of protocol type in embedded Swift
}
any 보다는 some을 쓸 것
protocol Countable {
var count: Int { get }
}
func count(countable: some Countable) {
print(countable.count)
}
Explore more