paint-brush
Cổng được mã hóa: Cách chúng tôi tạo một ứng dụng Swift sử dụng Rusttừ tác giả@ockam
10,773 lượt đọc
10,773 lượt đọc

Cổng được mã hóa: Cách chúng tôi tạo một ứng dụng Swift sử dụng Rust

từ tác giả Ockam5m2024/01/01
Read on Terminal Reader

dài quá đọc không nổi

Cách ứng dụng Cổng thông tin dành cho Mac, được tích hợp trong Swift, sử dụng thư viện Ockam Rust để chia sẻ riêng tư các dịch vụ với bạn bè của bạn qua Cổng được mã hóa hai đầu.
featured image - Cổng được mã hóa: Cách chúng tôi tạo một ứng dụng Swift sử dụng Rust
Ockam HackerNoon profile picture
0-item
1-item

Cổng thông tin là một ứng dụng Mac được xây dựng trong Swift. Đó là nguồn mở và sử dụng thư viện Ockam Rust để chia sẻ riêng tư các dịch vụ TCP hoặc HTTP từ máy Mac của bạn với bạn bè qua Cổng Ockam được mã hóa hai đầu. Một dịch vụ chia sẻ xuất hiện trên localhost của họ!


Trong bài đăng này, chúng ta sẽ tìm hiểu cách ứng dụng SwiftUI macOS tương tác với mã Rust.


Nếu bạn tò mò muốn dùng thử Portals cho Mac. Bạn có thể tìm hiểu thêm về nó trong bài viết này và cài đặt bằng Homebrew như sau:


 brew install build-trust/ockam/portals


Đây là video dài 2 phút về hoạt động của ứng dụng:

Nhanh <> Rỉ sét

Chức năng Cổng thông tin đã được triển khai trong thư viện Ockam Rust. Chúng tôi đặt mục tiêu tạo ra trải nghiệm tuyệt vời dành cho macOS.


Nỗ lực đầu tiên của chúng tôi trong việc xây dựng ứng dụng là sử dụng Tauri. Điều này có ý nghĩa vì chúng tôi muốn sử dụng thư viện Ockam Rust và hầu hết mọi người trong nhóm của chúng tôi đều cảm thấy thoải mái khi xây dựng mọi thứ trong Rust. Phiên bản đầu tiên này rất dễ xây dựng và có tất cả các chức năng cơ bản mà chúng tôi mong muốn. Tuy nhiên, trải nghiệm sử dụng ứng dụng không được tốt. Tauri chỉ cung cấp cho chúng tôi quyền kiểm soát tối thiểu về cách hiển thị menu và điều gì xảy ra khi người dùng tương tác với menu. Phiên bản ứng dụng này có cảm giác như thuộc về phiên bản macOS 10 năm tuổi khi so sánh với các mục trên thanh menu siêu dễ sử dụng được tích hợp trong macOS Sonoma.


Chúng tôi nhận ra rằng để có được trải nghiệm phong phú như mong muốn, chúng tôi phải xây dựng ứng dụng bằng SwiftUI.


Thật không may, chúng tôi không thể tìm ra giải pháp sẵn có để tích hợp Swift và Rust, điều đó sẽ mang lại cho chúng tôi những điều tốt nhất của cả hai thế giới; sự an toàn của Rust và trải nghiệm phong phú dành cho macOS của SwiftUI. Sau khi tìm hiểu thêm, chúng tôi nhận ra rằng chúng tôi có thể kết nối cả hai bằng C-89. Rust tương thích với quy ước gọi C và Swift có thể tương tác với Objective-C, một siêu bộ của C-89.


Cách Swift và Rust giao tiếp



Chúng tôi đã viết các cấu trúc dữ liệu Rust cần được hiển thị cho Swift hai lần. Một phiên bản thành ngữ trong Rust và dễ sử dụng. Phiên bản còn lại tương thích với C sử dụng con trỏ và bộ nhớ được cấp phát thủ công bằng malloc. Chúng tôi cũng đã tiết lộ một số API tương thích với C sử dụng con trỏ thô trong tình trạng không an toàn để chuyển đổi cấu trúc dữ liệu thông thường sang các phiên bản tương thích với C của chúng. Cuối cùng, chúng tôi tự động tạo tiêu đề C với sự trợ giúp của thư viện cbindgen.


Về phía Swift, chúng ta có thể gọi trực tiếp các API C, nhưng cấu trúc dữ liệu C không phải là công dân hạng nhất trong Swift. Điều này khiến chúng khó sử dụng một cách thành ngữ hơn trong mã SwiftUI. Thay vào đó, chúng tôi chọn sao chép cấu trúc dữ liệu trong Swift và chuyển đổi giữa C và Swift. Điều này có vẻ nặng nề, nhưng trên thực tế, trạng thái chia sẻ không thay đổi thường xuyên. Khả năng nhanh chóng xây dựng các thành phần trong SwiftUI bằng cách sử dụng các cấu trúc như if let ... , ForEach , enum v.v. là cực kỳ hữu ích và đáng để đánh đổi.


Đây là một ví dụ về cấu trúc tương tự ở 4 dạng:


 // 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 }


Ứng dụng Swift được liên kết tĩnh với thư viện Rust của chúng tôi tại thời điểm biên dịch. Luồng dữ liệu rất đơn giản: các tương tác giao diện người dùng được gửi từ Swift đến Rust dưới dạng hành động bằng cách gọi API C, các sự kiện thay đổi chỉ được phát ra bởi Rust và Swift được thông báo bằng cách sử dụng lệnh gọi lại dẫn đến cập nhật cho giao diện người dùng.


Hầu hết mã trong chế độ xem SwiftUI trông giống như bất kỳ ứng dụng SwiftUI nào khác.


 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") } } } ...


Nếu bạn tò mò muốn tìm hiểu thêm, hãy kiểm tra mã cho thùng ockam_app_libứng dụng Cổng thông tin trong Swift . Makefile trong thư mục swift cũng là nơi tốt để khám phá cách mọi thứ được xây dựng và liên kết với nhau.


Nếu bạn quan tâm đến việc đóng góp cho mã Swift hoặc Rust của Cổng thông tin dành cho Mac , chúng tôi sẽ thêm các số phát hành mới hay đầu tiên mỗi tuần và rất mong được giúp đỡ những người đóng góp mới. Tham gia cùng chúng tôi về sự bất hòa của những người đóng góp .


Cũng xuất hiện ở đây .