Sari la conținut
megapromotingHai să discutăm

Expertiză · Aplicații desktop

Aplicații care se instalează pe calculator. Pe macOS am construit două; pentru Windows și Linux pagina asta e ofertă, nu istoric.

Construim aplicații native pentru macOS, cu nucleul de logică separat de ecran, ca aceeași logică să meargă și pe telefon. Pentru Windows și Linux/Ubuntu nu avem încă nimic livrat și scriem asta ca atare, cu condițiile în care am accepta lucrarea. Prima întrebare rămâne dacă îți trebuie într-adevăr o aplicație instalată.

Ofertă, cu condițiiRegula e mecanică și o aplic mecanic. Pe macOS avem două implementări proprii, construite și rulabile: o aplicație de bară de meniu cu 25 de fișiere Swift, care refolosește 15 fișiere din nucleul unui proiect de iPhone, și o a doua aplicație, mai simplă, care e un înveliș WebKit peste o aplicație web. Ambele au binar compilat pe această mașină. Dar serviciul, așa cum e numit, acoperă trei familii de sisteme, iar pentru două dintre ele — Windows și Linux/Ubuntu — am căutat serios și am găsit zero: niciun proiect .NET sau Qt propriu, niciun `tauri.conf.json`, niciun Electron scris de noi, niciun `.desktop`, niciun ambalaj deb, rpm sau AppImage. În plus, niciuna dintre cele două aplicații de macOS nu a fost vreodată împachetată într-un instalator distribuibil. Cu doar una din trei platforme acoperită și zero distribuții, pagina se scrie ca ofertă cu condiții. Când vom livra prima aplicație de Windows sau de Linux, se schimbă valoarea, nu textul.

Prima întrebare pe care ți-o punem nu e pentru ce sistem, ci dacă îți trebuie într-adevăr o aplicație instalată. Merită instalată când are nevoie de ceva ce browserul nu-i dă: acces direct la senzori sau la periferice, prezență permanentă în bara de sistem, lucru fără internet, acces la fișierele de pe disc fără ca omul să le încarce de fiecare dată, sau rulare în fundal când fereastra e închisă. Dacă niciuna dintre acestea nu e cerută, un site care se deschide dintr-un link e mai ieftin de construit, de actualizat și de întreținut — și ți-o spunem, chiar dacă lucrarea iese mai mică.

Pe macOS avem două aplicații construite. Prima e o aplicație de bară de meniu: 25 de fișiere Swift proprii, plus 15 fișiere de nucleu luate direct din proiectul de iPhone al aceluiași produs — aceleași fișiere, nu o copie. Se construiește cu un script care cheamă direct compilatorul Swift, țintă `arm64-apple-macosx14`, legând unsprezece cadre de sistem, între care SwiftUI, AppKit, CoreMotion, Vision și UserNotifications. Binarul rezultat are 6.516.128 de octeți. A doua e mult mai simplă și e cinstit să spunem cât: trei fișiere Swift, un înveliș WebKit peste o aplicație web, cu sandbox și runtime întărit activate, compilat universal pentru procesoare Intel și Apple. Sunt două lucruri diferite și le numim diferit.

Ce nu avem, spus în clar. Windows: zero. Am căutat proiecte .NET, WPF, WinForms, WinUI, fișiere `.xaml`, `.appxmanifest`, `.msi`, `.wxs` — tot ce am găsit pe disc aparține unei biblioteci C terțe, vendorizate într-un alt proiect, pe care n-am scris-o noi. Linux desktop: zero — niciun fișier `.desktop`, niciun ambalaj deb sau AppImage construit aici. Electron sau Tauri scrise de noi: zero. Și, la fel de important: niciuna dintre cele două aplicații de macOS n-a fost vreodată împachetată într-un instalator. Există ca pachete `.app` construite local, semnate ad-hoc, fără identificator de echipă.

Ce transferă totuși experiența de până acum, și e partea care contează pentru un proiect nou: disciplina de a ține nucleul de logică separat de interfață. În proiectul de macOS, fișierele de calcul și de politici nu importă interfața, așa că se compilează și se rulează pe Mac ca program obișnuit, fără simulator; suita lor de logică rulează în câteva secunde. Aceeași separare face ca portarea pe alt sistem să fie o problemă de înveliș, nu de rescriere. Pe asta ne bazăm când spunem că am putea face Windows sau Linux — nu pe o experiență pe care n-o avem.

Ce cuprinde

Lucrarea, pe componente

Întâi decidem dacă îți trebuie o aplicație instalată

Cinci criterii, verificate pe rând: acces la senzori sau periferice, prezență în bara de sistem, lucru fără internet, acces direct la fișierele de pe disc, rulare în fundal cu fereastra închisă. Dacă niciunul nu se aplică, recomandarea e o aplicație web, și motivul e practic: o aplicație instalată trebuie semnată, distribuită și actualizată pe fiecare calculator, iar fiecare dintre cei trei pași costă la nesfârșit, nu o singură dată.

Nucleul se scrie o dată și nu știe pe ce rulează

În aplicația noastră de macOS, ținta de desktop listează 15 fișiere din nucleul proiectului de iPhone ca surse proprii — calcul, calibrare, istoric, politici de notificare și de date de sănătate. Nu sunt copiate, sunt aceleași fișiere. Consecința: o corectură de algoritm se face într-un loc și apare pe ambele. Consecința a doua, mai importantă pentru tine: nucleul se poate testa fără să pornești aplicația.

Învelișul de platformă rămâne subțire, și îți spunem cât de subțire

Aplicația de bară de meniu folosește `MenuBarExtra` peste un delegat de aplicație AppKit — cod nativ, dar puțin, pentru că restul e nucleul comun. Cealaltă aplicație a noastră de macOS e la extrema opusă: trei fișiere, un `WKWebView` împachetat pentru SwiftUI, adică o fereastră peste o aplicație web. Amândouă sunt aplicații de macOS legitime, dar înseamnă lucruri diferite ca efort, ca viteză și ca acces la sistem. Care dintre ele îți trebuie se decide la început, nu se descoperă la livrare.

Construim cu un script, nu cu apăsarea unui buton

Aplicația de bară de meniu se construiește dintr-un script de sub o sută de linii care cheamă direct compilatorul Swift, cu ținta și lista de cadre scrise explicit, apoi așază pachetul `.app` și îl semnează. Un build care încape într-un script rulează la fel pe altă mașină și poate fi pus într-o linie automată; un build care există doar în interfața unui program de dezvoltare nu poate.

Sandbox și runtime întărit, când aplicația iese în rețea

A doua aplicație de macOS pornește cu sandbox activat și cu o singură permisiune de rețea, cea de client — nu server, nu acces la fișierele utilizatorului, nu cameră, nu microfon. Runtime-ul întărit e pornit din configurația de proiect. Permisiunile se declară de la început, ca listă scurtă care se poate citi, nu se adaugă pe parcurs când ceva nu merge.

Semnarea e o problemă de cont, nu de cod, și o punem pe masă la început

Aplicațiile noastre de macOS sunt semnate ad-hoc: verificarea trece local, dar nu există identificator de echipă, iar în configurația de proiect câmpul echipei de dezvoltare e gol. Pentru distribuție în afara magazinului, un pachet de macOS are nevoie de semnare cu un certificat de dezvoltator identificabil și de trecerea prin notarizarea Apple, altfel sistemul de operare al celui care îl primește îl blochează. E o problemă de cont și de proces, nu de funcționalitate — dar e o problemă reală și nu o ascundem în ultima ședință.

Windows și Linux: ce cerem înainte să acceptăm

O verificare de compatibilitate plătită ca primă etapă, care se termină cu un schelet care pornește pe calculatorul tău, nu cu un document. În ea se decid trei lucruri: ce mediu de execuție folosim și de ce, cine deține certificatul de semnare și contul de distribuție, și cum ajunge o actualizare pe calculatoarele oamenilor. Dacă vreunul dintre cele trei nu are răspuns, lucrarea nu începe. Nu ne asumăm o platformă pe care n-am livrat-o pretinzând că e o formalitate.

Linux server nu e Linux desktop, și nu confundăm cele două

Sistemele noastre rulează zi de zi pe Linux: unități de sistem cu repornire la eșec, server web în față, jurnale și rotația lor. Asta e experiență reală cu Linux, dar e experiență de server. O aplicație cu ferestre pentru Ubuntu e altă disciplină — ambalare, integrare cu mediul grafic, distribuție prin depozit sau prin fișier autoconținut — și acolo nu avem nimic livrat. Diferența asta o face un inginer în trei secunde; e mai bine să o spunem noi înainte.

Ce nu facem

Nu promitem un instalator pe care nu l-am produs niciodată fără să spunem că e prima dată. Nu alegem un mediu de execuție doar pentru că e la modă, fără să verificăm întâi ce cere aplicația de la sistem. Și nu preluăm o aplicație desktop existentă înainte să o putem construi de la zero, pe mașina noastră, din sursele primite — dacă build-ul nu se reproduce, orice estimare de după e o ghicitoare.

Cum arată

Traseul, pas cu pas.

01

Decidem instalat sau web, pe cinci criterii

Senzori și periferice, bară de sistem, lucru fără internet, acces la fișiere, rulare în fundal. Livrăm decizia scrisă, cu motivul, chiar și când motivul e „nu-ți trebuie aplicație instalată”. Etapa asta e scurtă și se poate opri aici fără pierdere.

02

Construim nucleul și îl testăm fără platformă

Logica, stările, persistența, politicile — scrise ca să nu importe interfața și să se poată compila și rula ca program de linie de comandă. Livrăm suita de teste și comanda cu care o rulezi tu. Dacă ulterior apare a doua platformă, aici se vede dacă lucrarea a fost făcută corect de la început.

03

Punem învelișul de platformă

Pe macOS: ferestre sau bară de meniu, notificări, permisiuni, pachetul `.app`, scriptul de construcție. Pe Windows sau Linux: după verificarea de compatibilitate din prima etapă, cu mediul de execuție ales acolo. La finalul etapei aplicația pornește pe mașina ta, nu pe a noastră.

04

Semnare, distribuție, actualizare

Etapa pe care majoritatea proiectelor o descoperă târziu și noi o punem ultima explicit, ca să fie văzută: certificat, cont, notarizare unde platforma o cere, canal de actualizare. Aici e și blocajul nostru actual pe macOS — aplicațiile sunt semnate ad-hoc, fără identificator de echipă. Într-un proiect al tău, contul e al tău și blocajul dispare.

DocumenteConversațiiSurseSintezăAcțiuneEchipăContexteazăSurse autorizate. Acțiuni revizuite.
Un nucleu în centru — calcul, calibrare, istoric, politici — care nu importă nicio interfață și de aceea se testează singur. În jur, patru învelișuri de platformă: iPhone și macOS, legate și construite, desenate plin; Windows și Linux desktop, desenate deschis, pentru că nu există. Al cincilea nod, distribuția, e legat de macOS printr-o linie întreruptă: pachetul se construiește și se semnează ad-hoc, dar nu trece de notarizare, deci nu ajunge pe alt calculator. Desenul spune ce e făcut și ce nu, în aceeași imagine.

Datele

Ce atingem, unde stau și cât rămân

Întrebările pe care le pune oricine are un responsabil cu protecția datelor — puse aici înainte să le pună el.

O aplicație instalată vede mai mult decât una din browser
Asta e motivul pentru care o vrei și, în același timp, riscul ei: are acces la disc, la sistemul de fișiere al utilizatorului și, dacă i se dau permisiuni, la periferice. De aceea permisiunile se declară explicit și se țin la minimum. Aplicația noastră cu sandbox are exact o permisiune, cea de client de rețea.
Unde stau datele când aplicația nu are server
Pe calculatorul omului. În aplicația de bară de meniu, istoricul se scrie într-un fișier propriu, iar preferințele în magazia de setări a sistemului, cu chei prefixate pe aplicație. Nu pleacă nimic spre un server pentru un motiv simplu: nu există cod de rețea în aplicație. Când există, se scrie în pagina de permisiuni cine primește ce.
Ce nu colectăm implicit
Nicio telemetrie, niciun identificator de dispozitiv, niciun raport de utilizare, dacă nu sunt cerute explicit și declarate în interfață. O aplicație desktop e un loc în care telemetria trece neobservată; de aceea, dacă se pune, se pune vizibil, cu buton de oprire, și se scrie ce câmpuri pleacă.
Actualizările sunt o decizie, nu un detaliu tehnic
Nu avem azi un canal de actualizare automată construit. Într-un proiect nou asta e una dintre primele trei decizii, alături de semnare și de distribuție: se actualizează manual, prin magazinul platformei, sau printr-un canal propriu? Fiecare variantă are alt cost permanent și altă suprafață de securitate, și se alege înainte de prima linie de cod, nu după.
Ce rămâne la noi
Sursele, cât timp lucrăm; după predare, ce s-a convenit în contract. Certificatele de semnare rămân, ideal, pe contul tău de organizație — dacă rămân pe al nostru, ești legat de noi pentru fiecare actualizare, iar asta trebuie să fie o alegere conștientă, nu o consecință.

Un caz

O aplicație de macOS scoasă din nucleul unei aplicații de iPhone, fără a doua implementare

Situația

Aveam un produs propriu pentru iPhone, care măsoară înclinarea capului față de o poziție calibrată la începutul sesiunii. Aceleași reguli — calibrare, praguri, istoric, politici de notificare — aveau sens și pe Mac, unde omul stă oricum în fața ecranului. Întrebarea reală nu era dacă se poate, ci cât din logică ar trebui rescrisă.

Ce am construit

Zero, pentru că nucleul fusese scris de la început fără să știe de ecran. Ținta de macOS listează 15 fișiere din proiectul de iPhone ca surse proprii, la care se adaugă 25 de fișiere Swift specifice desktopului: fereastra, meniul din bara de sistem printr-un delegat AppKit, notificările, serviciul de mediu. Construcția nu trece prin interfața Xcode: un script cheamă direct compilatorul Swift cu ținta `arm64-apple-macosx14` și leagă explicit unsprezece cadre de sistem, apoi așază pachetul `.app` și îl semnează, iar la final verifică semnătura.

Ce a ieșit

Un binar de 6.516.128 de octeți pentru procesoare Apple, care pornește și stă în bara de meniu, construit dintr-o singură comandă. Aceeași corectură de algoritm ajunge pe telefon și pe Mac, pentru că e același fișier, nu o copie sincronizată manual.

Ce nu spune cazul

Aplicația nu e distribuită și nu poate fi, azi, în forma asta: semnătura e ad-hoc, fără identificator de echipă, iar câmpul echipei de dezvoltare e gol în configurația proiectului. Pe alt calculator ar fi blocată de sistem. E o problemă de cont și de notarizare, nu de funcționalitate — dar rămâne o problemă nerezolvată și o scriem ca atare, nu ca detaliu.

Întrebări

Ce ne întreabă oamenii înainte să sune

De ce scrie „ofertă” pe pagina asta, dacă spuneți că aveți două aplicații de Mac?

Pentru că serviciul se numește „aplicații desktop” și acoperă trei familii de sisteme, iar noi avem livrat pe una. Regula pe care ne-o aplicăm nouă înșine cere minimum două implementări proprii, arătabile în cod, pentru fiecare afirmație de tip „am făcut”. Pe macOS le avem. Pe Windows și pe Linux avem zero, și niciuna dintre cele două aplicații de Mac n-a fost vreodată împachetată într-un instalator distribuibil. O pagină care ar spune altceva ar cădea la a doua întrebare a unui inginer.

Aveți o aplicație de Mac pe care pot să o descarc acum?

Nu. Amândouă există ca pachete `.app` construite pe mașina noastră: una compilată pentru procesoare Apple, cealaltă universală, pentru Intel și Apple. Sunt semnate ad-hoc, ceea ce înseamnă că trec verificarea locală dar nu au identificator de echipă, deci pe alt calculator sistemul le-ar bloca. Ce putem face în fața ta e să le pornim, să deschidem codul și scriptul de construcție și să rulăm suita de logică.

Care e diferența, la voi, între cele două aplicații de Mac?

Una e nativă, cealaltă e o fereastră peste web, și diferența e mare. Cea nativă are 25 de fișiere Swift proprii plus 15 fișiere de nucleu comune cu versiunea de iPhone, se agață în bara de meniu printr-un delegat de aplicație AppKit și leagă unsprezece cadre de sistem, între care senzori de mișcare, notificări și procesare de imagine. Cea de-a doua are trei fișiere și afișează o aplicație web într-un `WKWebView`. A doua se face rapid și e o alegere corectă când aplicația ta e deja web și vrei doar o pictogramă în Dock. Nu e o alegere corectă când ai nevoie de sistem.

Faceți aplicații pentru Windows?

Nu am livrat niciuna și nu pretindem altceva. Am căutat pe discurile noastre orice urmă — proiecte .NET, WPF, WinForms, WinUI, fișiere de interfață XAML, manifeste de aplicație, ambalaje MSI sau WiX — și tot ce apare aparține unei biblioteci C terțe pe care doar o folosim într-un alt proiect. Dacă ai nevoie de Windows, prima etapă e o verificare de compatibilitate care se termină cu un schelet care pornește la tine pe calculator, și abia după ea discutăm restul.

Dar pentru Linux sau Ubuntu?

Aceeași poziție, cu o precizare care contează. Aplicație cu ferestre pentru Linux: zero livrate, niciun ambalaj deb sau AppImage construit de noi. Servere Linux: zi de zi — unități de sistem, server web în față, jurnale, repornire la eșec. Sunt două discipline diferite și ar fi necinstit să o folosim pe prima ca dovadă pentru a doua.

De ce nu Electron, dacă așa e mai simplu?

Pentru că n-am livrat niciuna și n-o să pretindem că avem experiență acolo unde nu avem. Pe discurile noastre există două aplicații Electron funcționale, dar sunt depozite clonate de la alți autori, cu zero contribuții de-ale noastre — le-am verificat în istoricul de comituri exact ca să nu ajungă „dovadă” pe pagina asta. Într-un proiect real, alegerea între un mediu web împachetat și cod nativ se face după ce se știe ce cere aplicația de la sistem, cât cântărește pachetul și cine plătește întreținerea, nu după modă.

Ce blochează, la voi, publicarea unei aplicații de Mac?

Contul, nu codul. În configurația proiectului, câmpul echipei de dezvoltare e gol, iar semnătura pachetului construit e ad-hoc, fără identificator de echipă. Pentru distribuție în afara magazinului, macOS cere un certificat de dezvoltator identificabil și trecerea pachetului prin notarizare. Într-un proiect al tău, certificatul și contul sunt ale organizației tale, deci blocajul ăsta pur și simplu nu apare — dar trebuie prevăzut de la început, cu costul și timpul lui.

Nu e mai simplu să facem un site și să terminăm?

De multe ori da, și ți-o spunem noi înainte să ne-o spui tu. Un site nu are nevoie de semnare, de notarizare, de instalator, de canal de actualizare și nici de o versiune pentru fiecare sistem de operare. Aplicația instalată câștigă când ai nevoie de senzori, de periferice, de lucru fără internet, de prezență permanentă în bara de sistem sau de acces direct la fișierele omului. Dacă niciunul dintre acestea nu e pe listă, banii se cheltuiesc mai bine în altă parte.

Puteți prelua o aplicație desktop pe care o avem deja?

Cu o condiție verificată înainte de orice estimare: să o putem construi de la zero, pe mașina noastră, din sursele primite. Dacă build-ul nu se reproduce — lipsesc dependențe, certificate, pași nescriși — atunci nimeni nu poate estima cinstit nimic, iar prima lucrare devine reproducerea build-ului, nu funcționalitatea nouă. Spunem asta înainte, ca să nu se descopere la mijloc.

Pe ce se sprijină fiecare afirmație de mai sus (14 surse)
  1. Țintă macOS declarată separat în proiect: `AirPostureMac`, `platform: macOS`, `MACOSX_DEPLOYMENT_TARGET "14.0"`, identificator de pachet `md.megapromoting.AirPosture.mac`project.yml:41-43, 64, 67 (ținte macOS); 4-6 (deployment iOS 17.0 / macOS 14.0); 9 (Swift 6.0)
  2. 25 de fișiere Swift în directorul specific de macOS; aplicație de bară de meniu peste un delegat AppKitAirPostureMacApp.swift:1 (`import AppKit`), 85 (`NSObject, NSApplicationDelegate, NSWindowDelegate`), 162 (`@main`), 165 (`@NSApplicationDelegateAdaptor`), 169 (`MenuBarExtra`)
  3. Construcție cu un script care cheamă direct `swiftc`, țintă `arm64-apple-macosx14`, unsprezece cadre de sistem legate explicit, apoi semnare și verificarebuild-macos.sh:14-28 (swiftc, țintă, cadrele SwiftUI, CoreMotion, AppKit, AVFoundation, Accelerate, CoreAudio, CoreImage, Vision, SceneKit, UserNotifications, Security), 76 (`codesign --force --deep --sign -`), 79 (`codesign --verify --deep --strict`)
  4. Binar macOS construit: Mach-O arm64, 6.516.128 octeți; semnătură ad-hoc, `TeamIdentifier=not set`AirPosture:file → „Mach-O 64-bit executable arm64”; ls -l → 6516128, 16 iul.; codesign -dv → „flags=0x2(adhoc)”, „Signature=adhoc”, „TeamIdentifier=not set”
  5. `DEVELOPMENT_TEAM` este gol în configurația de proiect — blocajul de semnare e de cont, nu de codproject.yml:12 (`DEVELOPMENT_TEAM: ""`)
  6. A doua aplicație de macOS: proiect separat, doar macOS, runtime întărit activat, versiune de marketing 0.9.0project.yml:5 (`macOS: "14.0"`), 13-15 (o singură țintă, `platform: macOS`), 20-25 (identificator de pachet, fișier de drepturi, `ENABLE_HARDENED_RUNTIME: YES`)
  7. Aceeași aplicație e un înveliș WebKit de trei fișiere Swift, nu o aplicație nativă cu funcții de sistem:ContentView.swift, VerolDesktopApp.swift, VerolWebView.swift (3 fișiere); VerolWebView.swift:2 `import WebKit`, :23 `NSViewRepresentable`, :28 `makeNSView -> WKWebView`
  8. Sandbox activat cu o singură permisiune de rețea, cea de clientVerolDesktop.entitlements:`com.apple.security.app-sandbox` = true; `com.apple.security.network.client` = true (nicio altă cheie)
  9. Binar universal x86_64 + arm64 pentru a doua aplicațieVerol:file → „Mach-O universal binary with 2 architectures: [x86_64] [arm64]”
  10. Windows: zero proiecte proprii. Toate rezultatele .NET / .sln / Qt de pe disc aparțin bibliotecii C terțe PJSIP, vendorizată în alt proiectProjects (căutare pe tot discul de proiecte):căutări după *.csproj, *.sln, *.xaml, *.appxmanifest, *.msix, *.wxs, WPF, WinForms, WinUI → singurele potriviri sunt sub `pjproject-2.15.1` (SimplePjsua2CS.csproj, pjproject-vs14.sln, pjsip-apps/src/vidgui)
  11. Linux desktop: zero. Niciun `.desktop` scris de noi, niciun ambalaj deb / rpm / AppImage construit aici; niciun proiect Tauri propriu; niciun Electron propriumacbook_nou (căutare pe tot directorul personal):căutări după tauri.conf.json, src-tauri, *.desktop, AppImage, *.deb, *.rpm, pubspec.yaml, wails.json, *.spec (PyInstaller) → zero rezultate proprii
  12. Cele două aplicații Electron funcționale de pe disc sunt depozite clonate, cu zero comituri ale noastredesktop:git remote → github.com/Kilted-Kraken/-RohanKar-Launcher.git (28 comituri, un singur autor extern); github.com/nexu-io/open-design.git (autori externi)
  13. Niciun instalator construit vreodată: nici .dmg, nici .pkg, nici .msi, nici .deb, nici .AppImagemacbook_nou (căutare):singurele fișiere .pkg de pe disc sunt două ambalaje Symbian din interiorul bibliotecii terțe pjproject
  14. Experiență de Linux server, distinctă de Linux desktop: sisteme rulate ca unități de sistem cu repornire la eșec, în spatele unui server webtransport-dashboard.service:`ExecStart=/usr/bin/node server.js`, `Restart=on-failure`, `RestartSec=5`; alături de install-nginx.sh în același proiect

Citările interne arată numele fișierului și linia. Calea completă rămâne în depozitul nostru; o putem parcurge împreună, la cerere.

Ce ai vrea să funcționeze mai bine?

Povestește-ne despre procesul tău. Împreună stabilim ce merită construit, ce putem conecta și cum verificăm rezultatul.

Hai să discutăm