EXIF.ER

publicado

Categories iOS
Stack SwiftUI, PhotosUI, PhotoKit, ImageIO, AVFoundation

visão geral

EXIF.ER começou como um script baseado na ferramenta exiftool de Phil Harvey. Eu criei essa automação inicialmente para organizar meus backups de fotos. Como tiro muitas fotos com o iPhone, logo adaptei o script para funcionar via o app Atalhos.

Mais tarde, na Apple Developer Academy, recebemos o desafio de criar uma ferramenta que resolvesse um problema prático real. A escolha foi natural: o que começou como um script era a base perfeita para virar um aplicativo completo.

Assim nasceu o EXIF.ER. Ele renomeia fotos e vídeos automaticamente usando as datas originais de captura (EXIF para imagens, QuickTime para vídeos), permitindo escolher entre dezenas de padrões de nome para facilitar a organização.

Todo o processamento é local (on-device, sem uploads ou servidores). O app integra-se tanto ao sistema de arquivos quanto à Fototeca e é compatível com uma ampla gama de formatos, de fotos RAW como o .DNG (Apple ProRAW) a vídeos .MOV e .MP4. Os arquivos são renomeados preservando a qualidade e os metadados originais.

trechos de código

Os arquivos escolhidos são movidos para o diretório temporário do app para sobreviver à limpeza do sistema. Também declaro tipos de conteúdo para vídeo e imagem para obter o arquivo original sem transcodificação lenta.

struct PhotoFile: Transferable {
    let url: URL

    /// Move para o diretório temporário para sobreviver à limpeza do sistema.
    private static func importFile(from received: ReceivedTransferredFile) throws -> PhotoFile {
        let fileName = received.file.lastPathComponent
        let destURL  = FileManager.default.temporaryDirectory.appending(path: fileName)

        if FileManager.default.fileExists(atPath: destURL.path) {
            try? FileManager.default.removeItem(at: destURL)
        }

        // Mover é instantâneo no mesmo sistema de arquivos.
        try FileManager.default.moveItem(at: received.file, to: destURL)
        return PhotoFile(url: destURL)
    }

    static var transferRepresentation: some TransferRepresentation {
        // Declara tipos específicos para obter o arquivo original.
        // O tipo genérico `.item` causaria uma transcodificação lenta (HEVC → H.264).

        FileRepresentation(contentType: .movie) { SentTransferredFile($0.url) }
            importing: { try importFile(from: $0) }

        FileRepresentation(contentType: .image) { SentTransferredFile($0.url) }
            importing: { try importFile(from: $0) }
    }
}

escolhas de design

cores

fosco #F2F2F7
obturador #1C1C1E
flash #FFFFFF

tipografia

SF Pro / interface via estilos semânticos do SwiftUI
Zebras jogam xadrez com o velho faquir
SF Pro · dígitos monoespaçados / timers e contadores
00:42 · 137 / 248

algumas fontes utilizadas neste projeto são proprietárias e podem não ser exibidas corretamente caso não estejam instaladas em seu sistema.

fundamentação

O EXIF.ER é um utilitário simples: escolha arquivos, escolha um padrão, execute. O design existe para não atrapalhar esse fluxo. Não há cromos de marketing, nem ilustração decorativa, nem animação comemorativa ao final: apenas a lista de arquivos, a prévia da renomeação e um estado de progresso claro. Tratar a interface como suporte, e não como o produto em si, espelha o jeito como o exiftool e outras ferramentas forenses funcionam, e mantém o foco em saber se as datas certas serão gravadas nos arquivos certos.

Esse minimalismo se estende à paleta. O app se apoia inteiramente nas cores semânticas de fundo do iOS, como .secondarySystemBackground e .tertiarySystemBackground, fazendo a interface se adaptar ao modo claro e escuro sem que eu precise escolher manualmente um segundo conjunto de valores. Os destaques em branco de alto contraste e o tom quase preto do obturador fazem o resto, mantendo a lista de arquivos legível tanto na cama quanto sob o sol.

A tipografia segue a mesma disciplina. Cada elemento usa um estilo semântico do SF Pro (.body, .headline, .subheadline, .caption), o que mantém a hierarquia nativa e entrega de graça peso, escalonamento e Dynamic Type pelo sistema. A única exceção deliberada é o .monospacedDigit() no tempo decorrido e no contador de arquivos durante o processamento: os números ali mudam várias vezes por segundo, e travar a largura dos dígitos impede que o layout fique tremendo enquanto a fila passa.

créditos

pessoas

design · desenvolvimento · lançamento
pedro wiezel

recursos

iconografia