Return...

Um cronômetro zen de meditação e foco em cinco telas: iPhone, iPad, Apple Watch, Apple TV e Mac.

Lançado em 21 de abril de 2026. Uma única base de código. Vinte e sete idiomas, incluindo árabe e hebraico. Quatro temas, três sinos, zero analytics. O que se segue é como tudo se juntou: as escolhas técnicas, os compromissos de design e o longo processo silencioso de editar centenas de gotas de água geradas por IA até chegar a uma.

Universal

Uma base de código, cinco telas.

O Return é o primeiro app que lancei que roda em todas as classes de tela da Apple a partir de um único projeto Xcode: iPhone, iPad, Apple Watch, Apple TV e Mac. Cinquenta e sete arquivos Swift, cerca de 12.700 linhas de código e zero dependências externas. SwiftUI puro, AVFoundation, HealthKit, ActivityKit e WidgetKit.

A forma ingênua de fazer isso é um TimerManager universal com ramificações #if para cada diferença de plataforma. Não fiz assim. O Return entrega três classes de cronômetro (TimerManager no iOS e macOS, TVTimerManager no tvOS, WatchTimerManager no watchOS) que compartilham a semântica de estado, mas respeitam aquilo em que cada plataforma é realmente boa. Live Activities apenas no iOS. HealthKit apenas onde a API existe. Sessões de tempo de execução estendido apenas no Watch. Cada gerenciador é mais curto e mais honesto do que seria uma única classe polimórfica.

Return rodando no iPhone 17 Pro Max com o tema Fogo
iPhone
Return rodando no macOS com o tema Fogo
Mac
Return rodando no Apple Watch Series 11 com o tema Fogo
Watch
Return rodando no Apple TV com o tema Fogo
Apple TV
Return rodando no iPad Pro de 13 polegadas com o tema Fogo
iPad

Compartilhado onde importa.

Uma única pasta Shared/ carrega as peças com as quais todos os alvos precisam concordar: o modelo de dados MeditationSession, o wrapper de iCloud SessionStore e a SessionHistoryView. As configurações sincronizam entre Watch e telefone através de um App Group (group.com.941apps.Return). O restante é específico de plataforma, propositalmente.

O exemplo mais claro é a única linha que decide se uma sessão já foi registrada no HealthKit. O iPhone escreve diretamente, então "synced" é verdadeiro no momento em que a sessão termina. Mac e TV não conseguem escrever no HealthKit de forma alguma, então "synced" é falso até que o iPhone pegue a sessão pendente mais tarde. Mesma intenção, booleano oposto, um único #if:

Swift · TimerManager.swift:120-138
/// Save session to SessionStore for cross-device sync and HealthKit syncing
private func saveSessionToStore(startTime: Date, endTime: Date) {
    // On iOS: if healthKitEnabled, we save directly to HealthKit, so mark as synced
    // On Mac: if healthKitEnabled, we want to sync to iPhone, so mark as NOT synced
    #if os(iOS)
    let alreadySynced = settings.healthKitEnabled
    #else
    let alreadySynced = !settings.healthKitEnabled
    #endif

    let session = MeditationSession(
        startDate: startTime,
        endDate: endTime,
        sourceDevice: .current,
        syncedToHealthKit: alreadySynced
    )

    SessionStore.shared.addSession(session)
}

Volto a esse padrão constantemente: o menor número de linhas que ainda torna a intenção legível. Quando o mesmo booleano significa coisas diferentes em plataformas diferentes, escreva-o como booleanos diferentes. O #if passa a fazer parte da documentação.

Localizado

Vinte e sete idiomas, e suporte da direita para a esquerda.

O Return é o primeiro app da Apple que lancei em todos os idiomas com os quais eu me importava. Vinte e sete localidades passaram por uma revisão completa, incluindo árabe e hebraico. Tudo isso vive em um único arquivo Localizable.xcstrings, o que é menos heroico do que parece. O Xcode faz a maior parte do trabalho se você concordar em parar de escrever strings à mão.

Tela inicial do Return, tema Água, inglês
EnglishInício · Água
Tela inicial do Return, tema Fogo, japonês
日本語Início · Fogo
Tela inicial do Return, tema Floresta, chinês simplificado
简体中文Início · Floresta
Tela de configurações do Return, alemão
DeutschConfigurações
Tela de permissão do HealthKit no Return, coreano
한국어HealthKit

RTL é uma vitória grátis se você parar de lutar contra.

O SwiftUI trata .leading e .trailing como direções semânticas em vez de .left e .right como direções fixas. Monte uma tela com direções semânticas uma vez, e a mesma tela é espelhada automaticamente em árabe, hebraico, persa ou urdu, sem um caminho de código dedicado. Os rótulos de configurações viram, a seta de voltar inverte, as posições dos switches se invertem. Os ícones de tema (gota, chama, folha) ficam no lugar. Não escrevi uma única linha de código RTL para esse comportamento.

Tela inicial do Return, tema Floresta, inglês, layout da esquerda para a direita
Inglês · LTR
Tela inicial do Return, tema Floresta, árabe, layout da direita para a esquerda
Árabe · RTL
Tela inicial do Return, tema Floresta, hebraico, layout da direita para a esquerda
Hebraico · RTL

Uma exceção que peguei antes do lançamento: o SwiftUI aplica a direção do layout também a visualizações Text, o que significou que o primeiro corte das capturas de tela em árabe e hebraico tinha o cronômetro mostrando "00:02" em vez de "20:00" — dígitos latinos dispostos da direita para a esquerda. Um único modificador .environment(\.layoutDirection, .leftToRight) em toda visualização Text que contém tempo ou conteúdo numérico resolve. As capturas de tela acima são da versão que entrega esse modificador em funcionamento.

O conjunto de capturas de tela foi gerado pelo fastlane rodando os mesmos testes de UI com diferentes argumentos -AppleLanguages. O próprio padrão effectiveLocale do app lê a flag, reconstrói a hierarquia de visualização e captura o resultado. Um único auxiliar, vinte e sete localidades, quatro classes de dispositivos, tudo em uma única execução noturna.

Swift · ReturnWatchApp.swift:92-111
/// The locale to use for the app - either user-selected or system default
/// In snapshot mode, always use system language (set by -AppleLanguages)
/// to allow screenshot generation for different locales
private var effectiveLocale: Locale {
    if isSnapshotMode || appLanguage.isEmpty {
        if let preferredLanguage = Locale.preferredLanguages.first {
            return Locale(identifier: preferredLanguage)
        }
        return .current
    }
    return Locale(identifier: appLanguage)
}

var body: some Scene {
    WindowGroup {
        WatchContentView()
            .preferredColorScheme(.dark)
            .environment(\.locale, effectiveLocale)
            .id(appLanguage) // Force rebuild when locale changes
    }
}

O .id(appLanguage) é o detalhe que justifica sua presença. Sem ele, o SwiftUI faz cache da hierarquia de visualização antiga e as strings não são atualizadas quando você troca de idioma em tempo de execução. Com ele, a árvore inteira é descartada e reconstruída, e tudo relê suas strings localizadas automaticamente. Uma linha, uma categoria inteira de bugs apagada.

HealthKit

Minutos conscientes, finalmente.

O app nativo de Atenção Plena do Watch da Apple limita as sessões integradas de Reflexão e Respiração em cinco minutos. A própria API do HealthKit não tem esse limite. Ela aceita alegremente qualquer HKCategorySample em que a data final seja posterior à data inicial. O limite está na UI, não no sistema. O Return coloca um seletor de 5 a 60 minutos em todos os dispositivos e registra o tempo que você realmente ficou sentado.

Swift · HealthKitManager.swift:92-103
/// Save a mindful session with the given start and end time
func saveMindfulSession(start: Date, end: Date) async -> Bool {
    guard isAvailable else { return false }

    // Don't save if end is before or equal to start
    guard end > start else { return false }

    let sample = HKCategorySample(
        type: mindfulType,
        value: HKCategoryValue.notApplicable.rawValue,
        start: start,
        end: end
    )
    ...
}

A única validação é end > start. É tudo o que o próprio HealthKit valida. A API da Apple sempre esteve disposta a registrar uma meditação de quarenta e cinco minutos. O botão para solicitar isso é que estava faltando.

Entre dispositivos sem HealthKit em três deles.

Mac e Apple TV não têm HealthKit. A resposta óbvia é "então não se preocupe em registrar sessões lá". A resposta menos óbvia e correta é registrá-las de qualquer maneira, no Key-Value Store do iCloud, e deixar que o telefone as pegue da próxima vez que acordar. O SessionStore do Return é o armazenamento compartilhado, MeditationSession.syncedToHealthKit é a flag de pendente, e HealthKitManager.syncPendingSessions() roda toda vez que o app iOS volta ao primeiro plano.

SessionStore
Key-Value Store do iCloud
Sessões pendentes
iPhone grava no HealthKit ♥
Gráfico de barras de Minutos Conscientes do Apple Health mostrando uma média de 20 minutos ao longo de um mês
Minutos Conscientes do Apple Health, visualização em barras. O próprio app Atenção Plena da Apple é limitado a uma sessão de Reflexão de cinco minutos. O armazenamento subjacente não se importa com o que você grave nele.
Calendário de Minutos Conscientes do Apple Health mostrando 18 dias de prática nas últimas 4 semanas
Mesmos dados, visualização de calendário: 18 dias nas últimas 4 semanas, cada sessão registrada pelo Return.
Tela de Histórico de Sessões do Return mostrando uma lista de sessões de meditação de 20 minutos
O próprio histórico de sessões do Return. Todo dispositivo contribui, e toda sessão carrega um marcador de origem.

Essa é a parte que acho que a Apple deveria lançar por conta própria: um gravador de Minutos Conscientes realmente multiplataforma, que não exija que um telefone esteja ativo quando você quer meditar em um Mac. Até que façam isso, o Return faz.

Generativo

De onde veio a água.

Quatro temas. Quatro loops ambientes. Três sinos. Tudo gerado, a maior parte descartada. Os vídeos são do Midjourney, o áudio é do ElevenLabs, e o trabalho que importou não foi o prompting. Foi a edição. Olhar para uma grade de duzentas gotas de água e escolher a única que faz loop de forma limpa sem uma emenda visível. Ouvir quarenta variações de um sino de templo até que uma tenha o ataque certo e o decaimento certo e não soe como uma notificação de celular.

Folha de contato do Midjourney: centenas de variações de gotas de água, algumas marcadas com corações e triângulos de reprodução
Água · 128 exibidas
Folha de contato do Midjourney: dezenas de variações de fogo
Fogo · 96 exibidas
Folha de contato do Midjourney: variações de copa de árvore e folhas
Floresta · 60 exibidas
Folha de contato do Midjourney: exploração de nuvens e céu que não entrou na versão final
Exploração inédita · 128 exibidas

Cada quadrado é uma geração. Os corações são os que sobreviveram a uma primeira triagem. Os triângulos de reprodução são os que levei para o vídeo. Quatro temas lançados. Todo o resto ficou na grade, e esse é o ponto central do processo: a proporção importa.

Os sinos seguiram o mesmo arco no áudio. Prompt, ouvir, refinar, prompt de novo. Mantive três: Singing Bowl, Temple Bell, Soft Chime. Cada um iterado até parar de soar sintético.

Não vou fingir que contei o total de gerações. Centenas por tema é honesto. A disciplina não está nos prompts. Está em descartar tudo o que é apenas bom, e manter apenas aquelas que conseguem ficar atrás de um cronômetro por vinte minutos silenciosos sem nunca se tornarem a coisa que você percebe.

Prática

Por que um cronômetro, e não um professor.

Esta parte é pessoal. Construí o Return porque já tenho uma prática de meditação e não conseguia encontrar um cronômetro que saísse do caminho. O que pratico é o Zen japonês em sua corrente marcial: Takuan, Yagyu, Musashi, Dogen, Hakuin. Não o mindfulness terapêutico que os grandes apps vendem. Intenção diferente, textura diferente.

O que circula em uma semana típica:

  • Susokukan (contagem da respiração). Contar de um a dez na respiração, retornar a um sempre que a contagem se perde. Fundamento. Concentração, joriki, primeiro.
  • Shikantaza (apenas sentar). Sem objeto. Sem contagem, sem questão, sem visualização. A mente que não se fixa. Forma central de zazen de Dogen e a aproximação formal mais próxima do estado que realmente quero.
  • Koan. Principalmente o Mu de Joshu. Uma pergunta que não pode ser resolvida pelo pensamento, sustentada até que o pensamento desista.
  • Maranasati (contemplação da morte). Enquadramento do Hagakure. Usado com parcimônia. A sobrevivência aperta a mente; isto atravessa esse aperto.
  • Isshin (uma mente). Território de Takuan e Yagyu: relaxado mas comprometido, assentado mas móvel. A ponte entre a almofada e o que vier a seguir.
  • Dias de integração. Gratidão, compaixão, linhagem. Jihi. Katsujinken: a espada que dá vida, não a espada que mata. Sábados, geralmente.
  • Sakki (consciência de intenção hostil). Cinco minutos de escuta em campo aberto anexados a cada sessão. Tira o shikantaza da almofada e o testa sob pressão em ambientes comuns.

A rotação não é rígida. Contagem da respiração quando preciso estabilizar. Koan quando preciso romper. Shikantaza quando preciso descansar na abertura. Contemplação da morte quando as apostas precisam ser esclarecidas. A variedade pertence ao treinamento.

O Return é um cronômetro porque não preciso de um professor no meu telefone. Preciso de algo que segure o relógio para que eu não precise, que marque o começo e o fim com um sino que eu respeite, e que saia do caminho no meio. Se você já tem uma prática, provavelmente é isso que também quer. Se você é totalmente iniciante, encontre um professor em uma sala. Depois volte.

Restrição

O que não está no Return.

O Return não é o Calm. Não é o Headspace. Não há um narrador britânico te guiando por um escaneamento corporal. Não há um avatar animado comemorando sua sequência. Não há assinatura que desbloqueia novos programas guiados. O Return é um cronômetro. A ideia é que se você já tem uma prática, não precisa de um professor no app. Você precisa de uma ferramenta que segure o tempo para você e saia do caminho.

  • Sem voz guiada ou narração
  • Sem sequências, pontuações ou gamificação
  • Sem assinatura ou compras dentro do app
  • Sem anúncios, nunca
  • Sem analytics; o app não rastreia nada
  • Sem login ou compartilhamento social
  • Sem telas de insistência, sem modais de abertura
  • Sem padrões obscuros no fluxo de IAP, porque não existe fluxo de IAP

O que está no Return, mantido deliberadamente pequeno: quatro modos de repetição (Uma Vez, Até Parar, Até o Tempo, Repetir N Vezes), uma pausa respiratória de dois segundos entre ciclos, de um a três toques de sino em cada transição, escolha entre três sinos, quatro temas, opt-in do HealthKit e um seletor de idioma. Isso é o produto inteiro.

O custo de ser tão rigoroso aparece no modelo de configurações. Toda preferência exposta ao usuário é restringida a um intervalo válido pela própria propriedade, não pela validação na UI. Validação na UI é mais um padrão obscuro se você não tomar cuidado. O getter de bellRepeatCount não consegue retornar nada além de 1, 2 ou 3. Escrever 0 ou 47 no @AppStorage subjacente silenciosamente restringe de volta ao intervalo permitido.

Swift · Settings.swift:74-81
@ObservationIgnored
@AppStorage("bellRepeatCount") private var _bellRepeatCount = 1

/// Validated bell repeat count (1-3)
var bellRepeatCount: Int {
    get { max(1, min(3, _bellRepeatCount)) }
    set { _bellRepeatCount = max(1, min(3, newValue)) }
}

O Return custa US$ 2,99. Você paga uma vez e ele é seu. Sem custos de servidor para sustentar, sem assinatura para renovar, sem pipeline de analytics observando o que você faz. O produto é o produto. Se quiser a versão mais longa de por que continuo construindo apps desta forma, leia Minimum Worthy Product e The Steve Test. A versão curta está nesta seção.

Return.

Disponível agora na App Store para iPhone, iPad, Apple Watch, Apple TV e Mac.