Cinco plataformas Apple, três arquivos compartilhados: como o Return realmente entrega SwiftUI multiplataforma
O Return, meu timer de meditação, roda em cinco plataformas Apple: iPhone, iPad, Mac, Apple Watch e Apple TV.1 O código-fonte tem 40 arquivos Swift (sem contar os testes). Três deles são compartilhados entre as cinco plataformas. O resto está dividido em targets separados do Xcode que duplicam conceitos como TimerManager, AudioManager e ContentView em vez de compartilhá-los por compilação condicional com #if os(...).
A taxa de compartilhamento é de cerca de 7,5% — e é proposital.
Este ensaio trata de como realmente é entregar um app SwiftUI multiplataforma em 2026, por que o compartilhamento agressivo de código é superestimado e o que os três arquivos que de fato foram compartilhados têm em comum.
As cinco plataformas que o Return atende, como a Apple as apresenta em developer.apple.com. Cada uma é um target de plataforma distinto no Xcode, não uma ramificação em tempo de execução.
Resumo
- Return: 18 arquivos Swift no target principal (iOS + iPadOS + macOS), 10 arquivos no target tvOS, 7 no target watchOS, 2 arquivos de widget (Live Activities) e 3 arquivos verdadeiramente multiplataforma em
Return/Shared/. Total: 40. - Os três arquivos compartilhados são os ligados à persistência:
MeditationSession,SessionStore,SessionHistoryView. Estado que viaja pelo iCloud, não UI que se adapta à plataforma. - tvOS e watchOS são targets separados do Xcode, não ramificações
#if os(tvOS)dentro do target principal. Os modelos de controle são diferentes demais para caber em um único ContentView. - Mesmo dentro do target principal iOS/iPadOS/macOS, os blocos
#if osproliferam: 10 emContentView.swift, 8 emLiveActivityManager.swift, 8 emVideoBackgroundView.swift, 6 emAudioManager.swift. - A avaliação honesta: compartilhar de forma agressiva entre cinco plataformas Apple é um passivo de manutenção. Um núcleo compartilhado pequeno (a camada de persistência) mais interfaces separadas por plataforma entrega mais rápido e quebra menos do que um arquivo gigante entupido de
#if.
Para os textos complementares específicos de cada plataforma, leia a matriz de plataformas Apple, o contrato de execução do watchOS e os padrões de Liquid Glass em SwiftUI.
Os números
O formato da base de código, por contagem de arquivos Swift, depois de descartar testes e testes de UI:
Return/ 18 files (iPhone + iPad + Mac, single target)
├── Shared/ 3 files ← cross-platform truth
│ ├── MeditationSession.swift
│ ├── SessionStore.swift
│ └── SessionHistoryView.swift
├── ContentView.swift (10 #if os branches)
├── TimerManager.swift (2 #if os branches)
├── AudioManager.swift (6 #if os branches)
├── HealthKitManager.swift
├── LiveActivityManager.swift (8 #if os branches, iOS-only)
├── ThemeManager.swift
├── VideoBackgroundView.swift (8 #if os branches)
├── GlassTextShape.swift (Liquid Glass, see prior post)
├── GlassTimerText.swift
└── … (settings, theme, audio assets, etc.)
ReturnTV/ 10 files (tvOS, separate target)
├── TVContentView.swift
├── TVTimerManager.swift ← duplicates main TimerManager
├── TVAudioManager.swift ← duplicates main AudioManager
├── TVDurationPicker.swift
├── TVFocusModifier.swift ← tvOS button styles for focus
├── TVSettingsView.swift
└── …
ReturnWatch Watch App/ 7 files (watchOS, separate target)
├── WatchContentView.swift
├── WatchTimerManager.swift ← duplicates main TimerManager
├── WatchAudioManager.swift ← duplicates main AudioManager
├── WatchHealthKitManager.swift ← duplicates main HealthKitManager (mostly)
├── WatchSettingsView.swift
└── …
ReturnWidgets/ 2 files (Live Activity + bundle)
├── ReturnLiveActivity.swift
└── ReturnWidgetsBundle.swift
Cinco plataformas, três arquivos compartilhados, dois targets separados por plataforma mais um target de widget, e ainda compilação condicional pesada dentro do target principal. A proporção de compartilhamento fica em torno de 7,5%. A maioria dos tutoriais de “SwiftUI multiplataforma” sugere o oposto: escrever um único ContentView que se adapta a cada plataforma via @Environment(\.horizontalSizeClass) e #if os(...).2 Isso funciona para duas plataformas (iPhone + iPad). Desmorona com cinco.
O que os três arquivos compartilhados têm em comum
Return/Shared/MeditationSession.swift define o tipo de valor adjacente ao SwiftData:3
struct MeditationSession: Codable, Identifiable, Equatable {
let id: UUID
let startDate: Date
let endDate: Date
let durationSeconds: Int
let sourceDevice: DeviceType
var syncedToHealthKit: Bool
enum DeviceType: String, Codable, CaseIterable {
case iPhone, iPad, mac, appleTV, appleWatch
}
}
O comentário no cabeçalho do arquivo carrega peso real: // Add this file to: Return, ReturnTV, ReturnWatch Watch App targets. O mesmo arquivo-fonte é referenciado pelos três targets do Xcode — sem symlink, sem estar embutido em um pacote Swift. O sistema de build da Apple compila alegremente um único arquivo em três binários.
SessionStore.swift é a camada de persistência: um invólucro fino sobre o NSUbiquitousKeyValueStore (o armazenamento chave-valor do iCloud) que lê e escreve arrays de MeditationSession. A escolha faz diferença: a sincronização por KV-store dá ao Return um histórico de sessões entre dispositivos sem precisar provisionar um contêiner do CloudKit, com a contrapartida de que o armazenamento inteiro é limitado a 1 MB no total.12 Para uma lista de sessões de meditação com algumas centenas de bytes cada, esse limite sobra. Já SessionHistoryView.swift é uma lista SwiftUI que renderiza as sessões. Ambos são usados de forma idêntica pelos targets de iPhone, iPad, Mac, Watch e TV.
O que esses três arquivos têm em comum: eles descrevem estado, não interação. Um MeditationSession é o mesmo conceito em qualquer dispositivo. A lista de sessões passadas se lê da mesma forma em qualquer dispositivo. Nenhum dos dois envolve uma superfície de controle, um gerenciador de janelas, uma decisão de roteamento de áudio, um mecanismo de foco ou a Digital Crown. No instante em que um arquivo precisa saber em qual plataforma está rodando, ele deixa de ser compartilhável.
Por que o resto não foi compartilhado
Pegue o TimerManager. A versão de iOS/iPadOS/macOS usa Timer.publish(every: 1, ...) e encaminha notificações pelo UserNotifications. A versão tvOS (TVTimerManager) lida com o caso em que a pessoa pausou pelo Siri Remote e a proteção de tela entra em ação. A versão watchOS (WatchTimerManager) delega a um WKExtendedRuntimeSession (via WatchSessionManager) para que o sistema mantenha o app responsivo enquanto a tela escurece, e recebe entrada pela Digital Crown em vez do toque. Três plataformas, três comportamentos de timer profundamente diferentes.
Dava sim para unificar tudo em class TimerManager { #if os(watchOS) ... #elif os(tvOS) ... }. O resultado seria uma classe com três modos, cada um com quarenta linhas de código cercado por #if, em que mexer no caminho do iOS arrisca quebrar o caminho do watchOS. Isso é um pesadelo de manutenção.
Três classes separadas com três nomes de arquivo significam mais código em disco e menos código na sua cabeça. Duplicação que você consegue ler ganha de abstração que você não consegue.
A mesma lógica vale para:
ContentViewvs.TVContentViewvs.WatchContentView: os modelos de navegação são diferentes (baseado em push no iPhone, em foco na TV, em lista no Watch).AudioManagervs.TVAudioManagervs.WatchAudioManager: as categorias de sessão de áudio divergem, o watchOS tem regras mais rígidas para áudio em segundo plano e o tvOS roteia de outro jeito para o AirPlay.VideoBackgroundViewtem 8 ramificações#if os(iOS)no target principal (com um#elseif os(macOS)acompanhando), cobrindo assets de vídeo diferentes (fire_phone.mp4vs.fire_mac.mp4), tipos de camada diferentes e proporções diferentes.4
Vale registrar: o target principal Return/ realmente junta iOS, iPadOS e macOS. Essas três plataformas compartilham mais código do que deixam de compartilhar. O NavigationStack do SwiftUI funciona nas três. O .glassEffect() funciona nas três. As diferenças de gerenciamento de janelas são reais, mas tratáveis dentro de um único target. tvOS e watchOS foram onde tracei a linha do target separado.
O caso do tvOS: por que o mecanismo de foco exigiu um target separado
A navegação na Apple TV é construída em torno do mecanismo de foco.5 Todo elemento de interface com o qual dá para interagir se declara focável; as setas do sistema no Siri Remote movem o foco entre os elementos; pressionar select ativa o elemento em foco. No tvOS, o SwiftUI expõe isso por meio de .focusable(), .focusEffect e tipos personalizados de ButtonStyle que reagem a @Environment(\.isFocused) para produzir aquele efeito de inclinação em parallax que os apps da própria Apple usam. Código de produção real em TVFocusModifier.swift:6
struct TVCapsuleButtonStyle: ButtonStyle {
var accentColor: Color = .white
@Environment(\.isFocused) private var isFocused
func makeBody(configuration: Configuration) -> some View {
configuration.label
.colorMultiply(isFocused ? focusedTextColor : accentColor)
.background(
Capsule().fill(isFocused
? AnyShapeStyle(accentColor)
: AnyShapeStyle(.ultraThinMaterial))
)
.clipShape(Capsule())
.scaleEffect(isFocused ? 1.1 : 1.0)
.scaleEffect(configuration.isPressed ? 0.95 : 1.0)
.shadow(color: .black.opacity(isFocused ? 0.3 : 0.1),
radius: isFocused ? 20 : 5, y: isFocused ? 10 : 2)
.animation(.easeInOut(duration: 0.2), value: isFocused)
}
}
O mesmo arquivo também define TVCircleButtonStyle para controles quadrados/circulares. Os dois estilos invertem cor e translucidez no foco: botões sem foco ficam sobre .ultraThinMaterial, botões em foco se preenchem com a cor de destaque e ganham escala e sombra. Nesse app, o padrão é estruturalmente específico do tvOS. @Environment(\.isFocused) existe em iOS, iPadOS, macOS, watchOS e tvOS,13 mas a navegação guiada por foco é o modelo de interação primário apenas no tvOS, onde o Siri Remote não produz nenhum evento de ponteiro ou toque. No iPhone ou no iPad, o controle equivalente é acionado pelo toque, via hit test; no Mac, ao passar o cursor ou clicar. Os estilos de botão em TVFocusModifier.swift partem do princípio de que o foco é a principal affordance da pessoa e desenham toda a resposta visual em cima disso. Não existe um jeito bom de escrever um ContentView que trate toque no iOS, cursor no Mac e navegação por foco no tvOS tudo no mesmo lugar. A estrutura da view é genuinamente diferente: um ContentView de tvOS é um grafo de linhas focáveis; um ContentView de iOS é uma pilha de toque-e-age.
O mesmo vale para o seletor de duração. No iPhone, ele desliza de baixo para cima e aceita toques. Na Apple TV, é uma linha horizontal de células focáveis pelas quais a pessoa navega com o controle remoto. TVDurationPicker.swift é um arquivo próprio porque o design de foco baseado em células não tem análogo no iPhone. Forçar os dois no mesmo arquivo significaria duas interfaces sem relação alguma coladas por um #if os(tvOS).
O caso do watchOS: sessões de execução estendida, HealthKit e uma superfície menor
O watchOS acrescenta duas restrições estruturais que as outras plataformas não têm:
WKExtendedRuntimeSessionpara manter o app responsivo enquanto a tela do relógio está escurecida.8 Sem isso, o watchOS suspende o app agressivamente entre cada tique de segundo e o timer acumula atraso. O Return declaraWKBackgroundModes: mindfulnessnoInfo.plistdo target watchOS para que o sistema reconheça o caso de uso e conceda o orçamento de execução; a própria sessão de execução é criada com o inicializador padrãoWKExtendedRuntimeSession().- Sincronização via iCloud com
NSUbiquitousKeyValueStore, não WatchConnectivity.7 A sincronização do histórico de sessões do Return anda no mesmo armazenamento chave-valor que os targets de iPhone, iPad e Mac usam, então uma meditação registrada no relógio aparece na tela de histórico do iPhone sem nenhuma troca direta de mensagens entre relógio e telefone. O WatchConnectivity poderia ser uma opção futura para sincronizar estado ao vivo, mas o Return escolheu o modelo mais simples: cada dispositivo escreve no mesmo KV-store do iCloud, e a próxima leitura em qualquer dispositivo enxerga a união.
WatchTimerManager.swift é o timer do lado do relógio; ele delega o trabalho de execução estendida ao WatchSessionManager, definido em ReturnWatchApp.swift como final class WatchSessionManager: NSObject, WKExtendedRuntimeSessionDelegate. O TimerManager do iOS não tem análogo, porque apps de iOS continuam responsivos em primeiro plano sem uma sessão de execução explícita. Enfiar a lógica do relógio dentro do TimerManager do iOS via #if os(watchOS) faria o caminho de código do iOS importar símbolos do WatchKit que ele nunca usa, além de exigir para o caminho watchOS inicializações que o caminho iOS não precisa.
WatchHealthKitManager.swift é uma variante menor do HealthKitManager principal. Ele registra minutos de atenção plena do mesmo jeito, mas a UX do pedido de autorização é outra (o relógio não consegue exibir um HealthKitPermissionSheet). A classe do Watch tem cerca de metade do tamanho da principal.
O que acontece dentro do target principal iOS/iPadOS/macOS
Mesmo dentro do target principal, o compartilhamento não é automático. ContentView.swift tem dez blocos #if os(macOS) ou #if !os(macOS); LiveActivityManager.swift tem oito; VideoBackgroundView.swift, oito; AudioManager.swift, seis. Live Activities é um recurso exclusivo do iPhone, então o LiveActivityManager inteiro está embrulhado em #if os(iOS). O seletor de duração no iPhone usa um layout diferente do seletor no iPad e no Mac, então o ContentView tem ramificações paralelas de layout.
O padrão que tem funcionado: #if os(...) para pequenas diferenças de plataforma (comportamento de teclado distinto, padding distinto, API ausente); target separado para diferenças estruturais grandes (foco vs. toque, sessão de treino vs. timer). O limiar que acabei adotando é “mais de ~10 linhas de ramificação”. Abaixo disso, compilação condicional está de bom tamanho. Acima disso, o arquivo está fazendo dois trabalhos ao mesmo tempo — e o segundo pertence a outro target.
Quando não vale entregar nas cinco plataformas
A avaliação honesta.
Pule o Apple Watch se o seu app for denso em informação. A tela de 46 mm não tem espaço para uma lista de 30 itens, um seletor de duração e uma tela de configurações. O Return sobrevive no watchOS porque a interação central é um botão só (iniciar/parar um timer). Um app de produtividade, um app financeiro ou um app cheio de mídia não sobrevive.
Pule a Apple TV se o seu app for interativo. A TV é para experiências ambientes (um timer rodando numa tela do outro lado da sala, reprodução de música). Qualquer coisa que exija entrada frequente está brigando com a plataforma. O Return está no tvOS porque “colocar um timer de 20 minutos e olhar o fogo na tela” é exatamente o caso ambiente certo. Um app de anotações seria um martírio.
Pule o Mac se o seu app for uma interface pensada para o celular. SwiftUI no Mac funciona, mas o modelo de push do NavigationStack parece de brinquedo perto de uma sidebar de Mac de verdade. Se o app for parecer mal-acabado no Mac, entregue via Catalyst (que converte o app de iPad) ou pule o Mac por completo até conseguir construir uma interface nativa de Mac.
Pule o iPad se você não fez a adaptação por size class. Um app de iPhone esticado para preencher o iPad passa a impressão de coisa barata. O iPad precisa, no mínimo, de um NavigationSplitView com sidebar; idealmente, de um layout real de dois painéis. O Return usa split views no iPad e stacks no iPhone. O código está no mesmo target, mas a interface é genuinamente diferente.
A regra que tracei: entregue numa plataforma quando a interação central do app corresponder ao modelo de entrada daquela plataforma. Entregue um timer de meditação no Apple Watch (um toque para começar). Entregue um timer de meditação na Apple TV (configure e esqueça). Não entregue um quadro kanban em nenhuma das duas.
O que viaja sem esforço
As três coisas que de fato foram compartilhadas entre as cinco plataformas no Return:
- O modelo de dados (
MeditationSession). A struct é idêntica em toda plataforma, sincroniza viaNSUbiquitousKeyValueStore, e qualquer plataforma consegue ler o que qualquer outra escreveu. - A tela de histórico de sessões (
SessionHistoryView). UmaListde sessões passadas renderiza igual em iPhone, iPad, Mac, Apple Watch e Apple TV. AListdo SwiftUI é uma das poucas primitivas que se adapta com elegância aos cinco formatos. - O invólucro de persistência (
SessionStore). Leituras e escritas independem da plataforma; o armazenamento por baixo (NSUbiquitousKeyValueStore) é a mesma API em todo lugar.
Três conceitos. Estado, renderização de lista e persistência. Tudo que é estado e apresentação, sem envolver um modelo de entrada específico de hardware, é compartilhável. Tudo que encosta em entrada, foco, roteamento de áudio, tamanho de tela ou execução em segundo plano não é.
Esse padrão aparece no guia de desenvolvimento iOS com agentes, onde defendi a mesma coisa com outras palavras: as partes de um app iOS que um agente consegue escrever compartilham a maior parte do código com as partes que um humano escreve; e as partes que exigem julgamento humano (assinatura, acabamento visual, desempenho) são exatamente as que também não se compartilham bem entre plataformas.9 As duas fronteiras coincidem. Ambas dizem respeito ao ponto em que o conhecimento de domínio passa a importar.
Quanto custa o multiplataforma
O ROI é assimétrico. Adicionar iPad a um app de iPhone custa talvez 20% mais código (ramificações por size class, split view em alguns lugares). Adicionar Mac a esse mesmo target soma outros 15% a 20% (ramificações #if os(macOS), barra de menus, gerenciamento de janelas). Cada target principal acrescenta uns 10 arquivos num app pequeno.
Apple Watch e Apple TV são os caros. Adicionar watchOS ao Return exigiu 11 arquivos novos num target separado, incluindo gerenciadores dedicados de áudio, timer e HealthKit. Adicionar tvOS exigiu 10 arquivos novos em outro target separado, incluindo gerenciamento de foco e um seletor de duração personalizado. Juntos, quase dobraram a superfície de código Swift para o que é, no nível de recursos visíveis, o mesmo app.
A decisão de entregar nas cinco não foi “queremos ser multiplataforma por esporte”. Foi uma série de decisões separadas: Apple Watch porque timers de meditação genuinamente pertencem ao pulso; Apple TV porque o formato de tela ambiente combina com sessões longas em uma sala; Mac porque parte das pessoas medita na mesa de trabalho entre reuniões. Cada plataforma conquistou seu target por ter um caso de uso real.
Se um recurso não conquista seu target, o movimento mais barato é pular a plataforma e reforçar aquelas em que o app é excelente.
O que isso significa para o seu app
Três lições.
- Comece com um target por grande grupo de plataformas. iOS + iPadOS + macOS num target só funciona porque a interação central (toque + cursor) é parecida. tvOS num target separado. watchOS num target separado. Cada target separado custa ~10 arquivos, mas te livra de uma classe-Deus com ramificações
#ifque crescem sem limite. - Compartilhe estado agressivamente, interação não. Structs
Codablede modelo, invólucros de persistência e renderizações deListviajam quase de graça. Gerenciadores de timer, gerenciadores de áudio e content views, não. - Faça cada plataforma merecer seu lugar. Não entregue no watchOS só porque dá. Entregue quando a interação central do seu app corresponder ao modelo de entrada da plataforma. Pule o resto.
Esse padrão convive com as outras três superfícies sobre as quais escrevi para a mesma família de apps: App Intents tipados para o Apple Intelligence, servidores MCP para agentes entre LLMs, e Liquid Glass para o humano diante do dispositivo. A camada mais externa dessa mesma pilha é a plataforma: em quais telas o app sequer roda. Escolha isso com a mesma deliberação com que você escolhe a superfície de IA.
Perguntas frequentes
Por que não usar um pacote Swift para o código compartilhado?
Cheguei a considerar. Para três arquivos, um pacote Swift adiciona mais cerimônia do que economiza. O sistema de build do Xcode 26 compila alegremente um mesmo arquivo-fonte em vários targets quando você marca as caixas de Target Membership. Um pacote acrescenta um Package.swift à parte, um target de testes à parte e um passo de indireção que toda refatoração tem que atravessar. Para um núcleo compartilhado pequeno, a resposta mais simples ganha.10
O SwiftData funciona no watchOS e no tvOS?
O SwiftData está disponível em iOS 17+, macOS 14+, watchOS 10+ e tvOS 17+, cobrindo todas as plataformas que o Return atende.11 A struct MeditationSession é Codable puro, não um @Model, porque o Return usa NSUbiquitousKeyValueStore para sincronizar o histórico de sessões em vez de um contêiner do SwiftData. O padrão funciona da mesma forma para tipos @Model: o arquivo do modelo é compartilhado e o contêiner de persistência varia por plataforma, se precisar variar.
Devo usar Mac Catalyst ou um target Mac nativo?
O Catalyst é a ferramenta certa quando o app de iPad é bom o bastante para que a versão Mac reconstruída por ele passe por nativa. O target principal do Return é um target multiplataforma de verdade (não Catalyst), construído com SwiftUI para iOS, iPadOS e macOS num único binário. A interface do Mac usa #if os(macOS) para renderizar diferente do iPad: sidebar em vez de sheet, atalhos de teclado nos botões e assim por diante. O Catalyst teria sido mais simples, mas a interface do Mac teria cara de app de iPad rodando num Mac — que é justamente o modo de falha pelo qual o Catalyst é mais conhecido.
Vale a pena entregar na Apple TV num app pequeno?
Provavelmente não. Apps de Apple TV têm casos de uso bem específicos (ambiente, mídia, jogo casual). Se o seu app não se encaixa em um desses, o público da plataforma é pequeno demais para justificar os 10 arquivos Swift por app. O Return atende ao tvOS justamente porque sessões longas de meditação numa tela do outro lado da sala são um dos poucos casos de uso vizinhos à produtividade que combinam com a plataforma.
Quanto tempo leva para entregar nas cinco plataformas?
Difícil dar um número preciso; depende do app. O Return nasceu multiplataforma desde o primeiro dia, em vez de ganhar plataformas aos poucos — o que é mais rápido do que adaptar depois. Como regra de bolso: um MVP só para iPhone, mais suporte a iPad, mais suporte a Mac dá aproximadamente 1,5x o tempo da versão só para iPhone. Adicionar Apple Watch soma outros 0,5x. Adicionar Apple TV, mais 0,5x. Ou seja, um primeiro lançamento em cinco plataformas custa por volta de 2,5x o esforço da versão só para iPhone — com a ressalva de que esta foi uma construção assistida por agentes, em que a maior parte do código duplicado foi editada em massa pelo Claude Code em vez de digitada à mão.
Referências
-
Return, do autor, um app de timer de meditação publicado na App Store em 21 de abril de 2026. Targets nativos: iOS 26+, iPadOS 26+, macOS 26+, watchOS 26+, tvOS 26+. SwiftUI do começo ao fim.
NSUbiquitousKeyValueStorepara o histórico de sessões entre dispositivos. ↩ -
Apple Developer, “Configuring a Multi-Platform App” e a sessão “SwiftUI essentials” da WWDC 2024. A orientação padrão da Apple pende para um único target com adaptação guiada pelo environment; o caminho multi-target adotado neste artigo é um desvio deliberado. ↩
-
Código de produção em
Return/Return/Shared/MeditationSession.swift,SessionStore.swift,SessionHistoryView.swift. O comentário de cabeçalho emMeditationSession.swiftdiz: “Add this file to: Return, ReturnTV, ReturnWatch Watch App targets.” ↩ -
Código de produção em
Return/Return/VideoBackgroundView.swift(8 ramificações#if os(iOS)mais uma ramificação#elseif os(macOS)),Return/Return/ContentView.swift(10 ramificações#if os),Return/Return/AudioManager.swift(6 ramificações#if os),Return/Return/LiveActivityManager.swift(8 ramificações#if os, arquivo exclusivo de iOS). Contagens de ramificações obtidas executandogrep -Ec '^\s*#if os\\(' <file>. ↩ -
Apple Developer, “Focus interactions” Human Interface Guidelines. O mecanismo de foco do tvOS é um modelo de navegação fundamentalmente distinto do toque no iOS ou do ponteiro no Mac. ↩
-
Código de produção em
Return/ReturnTV/TVFocusModifier.swift. Define dois tipos deButtonStyle(TVCapsuleButtonStyleeTVCircleButtonStyle) que usam@Environment(\.isFocused)para inverter cor e translucidez no foco e aplicar escala + sombra. ↩ -
Apple Developer, “WatchConnectivity”. O framework para comunicação entre iPhone e Watch pareados; o Return não o usa para sincronizar sessões, apoiando-se no armazenamento chave-valor do iCloud. ↩
-
Apple Developer, “WKExtendedRuntimeSession” e a chave de Info.plist “WKBackgroundModes”. O valor
mindfulnessestá documentado como: “Enables extended runtime sessions for silent meditation” — exatamente o encaixe para um timer de meditação. O Return cria umWKExtendedRuntimeSession()padrão e declaraWKBackgroundModes: mindfulnessnoInfo.plistdo target watchOS. Código de produção:Return/ReturnWatch Watch App/ReturnWatchApp.swiftdefineWatchSessionManager: NSObject, WKExtendedRuntimeSessionDelegate;WatchTimerManager.swiftdelega a ele o trabalho de execução estendida. ↩ -
Análise do autor em Construindo apps iOS com agentes de IA, o guia prático de desenvolvimento iOS assistido por agentes, baseado em 8 apps em produção. ↩
-
Apple Developer, “Configuring a Multi-Platform App”. O Target Membership permite compilar um mesmo arquivo-fonte em vários targets sem um pacote Swift. Ferramenta certa para núcleos compartilhados pequenos. ↩
-
Apple Developer, disponibilidade de plataformas do “SwiftData”. Disponível em iOS 17+, iPadOS 17+, macOS 14+, watchOS 10+, tvOS 17+, visionOS 1+, cobrindo todas as cinco famílias de plataformas Apple. ↩
-
Apple Developer, “NSUbiquitousKeyValueStore”. O armazenamento chave-valor do iCloud da Apple, para sincronizar pequenas quantidades de estado entre os dispositivos de uma pessoa. Tamanho total limitado a 1 MB somando todas as chaves, conforme os limites publicados pela Apple. Código de produção:
Return/Return/Shared/SessionStore.swift. ↩ -
Apple Developer,
EnvironmentValues.isFocused. Disponível em iOS 14+, iPadOS 14+, macOS 11+, tvOS 14+, watchOS 7+. A API é multiplataforma; o que muda é se o foco é ou não a principal affordance de navegação da pessoa. ↩