Portals é um aplicativo para Mac desenvolvido em Swift. É de código aberto e usa a biblioteca Ockam Rust para compartilhar de forma privada serviços TCP ou HTTP do seu Mac com seus amigos por meio de portais Ockam criptografados de ponta a ponta. Um serviço compartilhado aparece em seu host local !
Nesta postagem, veremos como o aplicativo SwiftUI macOS interage com o código Rust.
Se você está curioso para experimentar Portals for Mac. Você pode aprender mais sobre isso neste artigo e instalar usando o Homebrew da seguinte maneira:
brew install build-trust/ockam/portals
Aqui está um vídeo de 2 minutos do aplicativo em ação:
A funcionalidade Portals já foi implementada na biblioteca Ockam Rust. Decidimos criar uma ótima experiência nativa do macOS.
Nossa primeira tentativa de construir o aplicativo foi usando Tauri. Isso fazia sentido, pois queríamos usar a biblioteca Ockam Rust e a maioria das pessoas em nossa equipe se sente confortável construindo coisas em Rust. Esta primeira versão foi fácil de construir e tinha todas as funções básicas que queríamos. No entanto, a experiência de usar o aplicativo não foi ótima. Tauri nos deu apenas um controle mínimo sobre como o menu era renderizado e o que acontecia quando um usuário interage com o menu. Esta versão do aplicativo parecia pertencer a uma versão de 10 anos do macOS quando comparada aos itens da barra de menu super fáceis de usar integrados ao macOS Sonoma.
Percebemos que para ter a experiência rica que desejamos, devemos construir o aplicativo usando SwiftUI.
Infelizmente, não conseguimos encontrar uma solução pronta para uso, para integrar Swift e Rust, que nos desse o melhor dos dois mundos; a segurança do Rust e a rica experiência nativa do macOS do SwiftUI. Depois de mais algumas pesquisas, percebemos que podemos conectar os dois usando o C-89. Rust é compatível com a convenção de chamada C e Swift é interoperável com Objective-C, que é um superconjunto de C-89.
Escrevemos as estruturas de dados Rust que precisavam estar visíveis para o Swift duas vezes. Uma versão é idiomática em Rust e fácil de usar. A outra versão é compatível com C usando ponteiros e memória alocada manualmente com malloc. Também expusemos algumas APIs compatíveis com C que usam ponteiros brutos em ferrugem insegura para converter as estruturas de dados idiomáticas em suas versões compatíveis com C. Finalmente geramos automaticamente um cabeçalho C com a ajuda da biblioteca cbindgen.
No lado do Swift, poderíamos ter chamado as APIs C diretamente, mas as estruturas de dados C não são cidadãos de primeira classe no Swift. Isso os torna mais difíceis de usar idiomaticamente no código SwiftUI. Em vez disso, optamos por duplicar as estruturas de dados em Swift e converter entre C e Swift. Isto pode parecer pesado, mas na prática o estado compartilhado não muda com muita frequência. A capacidade de construir componentes rapidamente no SwiftUI usando construções como if let ...
, ForEach
, enum
etc.
Aqui está um exemplo da mesma estrutura em suas 4 formas:
// 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 }
O aplicativo Swift está estaticamente vinculado à nossa biblioteca Rust em tempo de compilação. O fluxo de dados é simples: as interações da UI são enviadas do Swift para o Rust como ações chamando APIs C, os eventos de mudança são emitidos apenas pelo Rust e o Swift é notificado usando retornos de chamada que levam a atualizações na UI.
A maior parte do código nas visualizações SwiftUI se parece com qualquer outro aplicativo 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") } } } ...
Se você estiver curioso para saber mais, verifique o código da caixa ockam_app_lib e do aplicativo Portals em Swift . O Makefile na pasta swift também é um bom lugar para explorar como tudo é construído e interligado.
Se você estiver interessado em contribuir com o código Swift ou Rust do Portals for Mac , adicionamos novas e boas primeiras edições todas as semanas e adoramos ajudar novos contribuidores. Junte-se a nós no discord de contribuidores .
Também aparece aqui .