paint-brush
암호화된 포털: Rust를 사용하는 Swift 앱을 만든 방법~에 의해@ockam
10,773 판독값
10,773 판독값

암호화된 포털: Rust를 사용하는 Swift 앱을 만든 방법

~에 의해 Ockam5m2024/01/01
Read on Terminal Reader

너무 오래; 읽다

Swift에 내장된 Portals for Mac 앱이 Ockam Rust 라이브러리를 사용하여 종단 간 암호화된 포털을 통해 친구들과 비공개로 서비스를 공유하는 방법입니다.
featured image - 암호화된 포털: Rust를 사용하는 Swift 앱을 만든 방법
Ockam HackerNoon profile picture
0-item
1-item

Portals는 Swift로 구축된 Mac 앱입니다 . 오픈 소스이며 Ockam Rust 라이브러리를 사용하여 종단 간 암호화된 Ockam 포털을 통해 Mac의 TCP 또는 HTTP 서비스를 친구들과 비공개로 공유합니다. 로컬 호스트 에 공유 서비스가 나타납니다!


이 게시물에서는 SwiftUI macOS 앱이 Rust 코드와 상호 작용하는 방법을 자세히 살펴보겠습니다.


Mac용 Portals를 사용해 보고 싶으시다면. 이 기사에서 이에 대해 자세히 알아볼 수 있으며 다음과 같이 Homebrew를 사용하여 설치합니다.


 brew install build-trust/ockam/portals


다음은 실제 애플리케이션에 대한 2분짜리 비디오입니다.

스위프트 <> 러스트

Portals 기능은 Ockam Rust 라이브러리에 이미 구현되어 있습니다. 우리는 훌륭한 macOS 기반 경험을 만들기 시작했습니다.


앱을 구축하려는 첫 번째 시도는 Tauri를 사용하는 것이었습니다. 우리는 Ockam Rust 라이브러리를 사용하고 싶었고 우리 팀의 대부분의 사람들은 Rust로 작업하는 데 익숙했기 때문에 이것은 의미가 있었습니다. 이 첫 번째 버전은 구축하기 쉬웠고 우리가 원했던 모든 기본 기능을 갖추고 있었습니다. 하지만 앱 사용 경험은 별로 좋지 않았습니다. Tauri는 메뉴가 렌더링되는 방식과 사용자가 메뉴와 상호 작용할 때 어떤 일이 발생하는지에 대해 최소한의 제어만 제공했습니다. 이 버전의 앱은 macOS Sonoma에 내장된 사용하기 매우 쉬운 메뉴 표시줄 항목과 비교할 때 10년 전 macOS 버전에 속하는 것처럼 느껴졌습니다.


우리가 원하는 풍부한 경험을 얻으려면 SwiftUI를 사용하여 앱을 구축해야 한다는 것을 깨달았습니다.


불행하게도 우리는 Swift와 Rust를 통합하여 두 가지 장점을 모두 제공할 수 있는 기성 솔루션을 찾을 수 없었습니다. Rust의 안전성과 SwiftUI의 풍부한 macOS 네이티브 경험입니다. 좀 더 조사한 후에 우리는 C-89를 사용하여 두 개를 연결할 수 있다는 것을 깨달았습니다. Rust는 C 호출 규칙과 호환되며 Swift는 C-89의 상위 집합인 Objective-C와 상호 운용 가능합니다.


Swift와 Rust가 통신하는 방법



우리는 Swift에 두 번 표시되어야 하는 Rust 데이터 구조를 작성했습니다. 한 버전은 Rust에서 관용적이며 사용하기 쉽습니다. 다른 버전은 malloc을 사용하여 수동으로 할당된 포인터와 메모리를 사용하는 C와 호환됩니다. 우리는 또한 관용적 데이터 구조를 C 호환 버전으로 변환하기 위해 안전하지 않은 Rust에서 원시 포인터를 사용하는 일부 C 호환 API를 공개했습니다. 마지막으로 cbindgen 라이브러리의 도움으로 C 헤더를 자동으로 생성했습니다.


Swift 측면에서는 C API를 직접 호출할 수도 있었지만 C 데이터 구조는 Swift에서 일급 시민이 아닙니다. 이로 인해 SwiftUI 코드 내에서 관용적으로 사용하기가 더 어려워집니다. 대신 우리는 Swift의 데이터 구조를 복제하고 C와 Swift 간에 변환하기로 결정했습니다. 이는 부담스러워 보일 수 있지만 실제로는 공유 상태가 자주 변경되지 않습니다. if let ... , ForEach , enum 등과 같은 구문을 사용하여 SwiftUI에서 구성 요소를 빠르게 빌드하는 기능은 매우 유용하며 절충할 가치가 있습니다.


다음은 4가지 형태의 동일한 구조의 예입니다.


 // Rust idiomatic structure #[derive(Default, Clone, Debug, Eq, PartialEq)] pub struct LocalService { pub name: String, pub address: String, pub port: u16, pub shared_with: Vec<Invitee>, pub available: bool, } // Rust C-compatible structure #[repr(C)] pub struct LocalService { pub(super) name: *const c_char, pub(super) address: *const c_char, pub(super) port: u16, pub(super) shared_with: *const *const Invitee, pub(super) available: u8, } // Generated C header structure typedef struct C_LocalService { const char *name; const char *address; uint16_t port; const struct C_Invitee *const *shared_with; uint8_t available; } C_LocalService; // Swift idiomatic structure class LocalService { let name: String @Published var address: String? @Published var port: UInt16 @Published var sharedWith: [Invitee] @Published var available: Bool }


Swift 앱은 컴파일 타임에 Rust lib에 정적으로 연결됩니다. 데이터 흐름은 간단합니다. UI 상호 작용은 C API를 호출하여 작업으로 Swift에서 Rust로 전송되고, 변경 이벤트는 Rust에서만 발생하며, Swift는 UI 업데이트로 이어지는 콜백을 사용하여 알림을 받습니다.


SwiftUI 보기에 있는 대부분의 코드는 다른 SwiftUI 애플리케이션과 비슷해 보입니다.


 VStack(alignment: .leading, spacing: 0) { Text(service.sourceName).lineLimit(1) HStack(spacing: 0) { Image(systemName: "circle.fill") .font(.system(size: 7)) .foregroundColor( service.enabled ? (service.available ? .green : .red) : .orange) if !service.enabled { Text(verbatim: "Not connected") } else { if service.available { Text(verbatim: service.address.unsafelyUnwrapped + ":" + String(service.port)) } else { Text(verbatim: "Connecting") } } } ...


더 자세히 알고 싶다면 ockam_app_lib 크레이트의 코드와 Swift의 Portals 앱을 확인하세요. Swift 폴더에 있는 Makefile은 모든 것이 어떻게 구축되고 함께 연결되는지 탐색하기에 좋은 장소이기도 합니다.


Portals for Mac 의 Swift 또는 Rust 코드에 기여하는 데 관심이 있다면 매주 새로운 좋은 첫 호를 추가하고 새로운 기여자를 돕는 것을 좋아합니다. 기여자 불일치 에 참여하세요.


여기에도 나타납니다.