Para exibir um array Foundation em uma janela do Mac, valide os tipos dos elementos e, em seguida, crie uma view para cada valor. Fazer a ponte de NSArray para [String] fornece strings; exibi-las exige uma transformação separada em rótulos do AppKit.

Este passo a passo é para um desenvolvedor que está adicionando uma pequena lista ao próprio app macOS. A tarefa concreta é exibir três nomes sintéticos — Circle, Square e Triangle — em uma pilha vertical. O exemplo deixa explícitos os tipos da coleção e dos elementos, o layout e o ciclo de vida dos objetos.

O que a ponte entre coleções realmente muda?

A Apple documenta Array e NSArray como tipos correspondentes do Swift e do Foundation, assim como String e NSString são correspondentes. Ela recomenda usar tipos de valor do Swift no código Swift comum e acessar tipos de referência do Foundation quando seu comportamento for necessário. Para esta tarefa, mantenha a coleção Foundation no limite de entrada e use depois um array Swift tipado. Trabalhando com tipos do Foundation

O tipo externo de uma coleção e o tipo dos seus elementos respondem a perguntas diferentes. NSArray identifica uma coleção ordenada de objetos. [String] acrescenta um requisito específico: cada elemento precisa poder ser usado como uma string do Swift. Nenhuma das declarações descreve uma coleção de views. O inicializador da pilha da Apple exige explicitamente [NSView]. NSArray, init(views:)

Etapa Tipo neste exemplo Contrato
Entrada do Foundation NSArray Contém os objetos sintéticos fornecidos
Modelo validado [String] Cada elemento atende ao requisito de string
Objetos de apresentação [NSView] Cada elemento é um rótulo de texto recém-criado
Contêiner de layout NSStackView Organiza essas views de acordo com sua configuração

A distinção útil é entre fazer a ponte e construir. Fazer a ponte conecta representações compatíveis, como strings do Foundation e strings do Swift. Construir objetos NSTextField cria objetos de apresentação com propriedades e ciclo de vida próprios. Não há uma ponte documentada de string para rótulo; o caminho compatível é chamar o inicializador do rótulo para cada string. Array, init(labelWithString:)

Como validar os elementos sem descartar itens?

Use uma conversão condicional quando for necessário verificar o contrato dos elementos da coleção recebida. A Apple documenta que as? produz um resultado opcional e que uma conversão forçada malsucedida com as! provoca um erro em tempo de execução. Trate a falha explicitamente antes de construir a interface. Lidando com métodos e objetos de tipos dinâmicos no Swift

O código a seguir faz uma verificação completa usando apenas o Foundation e uma entrada sintética. Salve-o como BridgeCheck.swift e execute-o em um Mac com as ferramentas do Swift usando swift BridgeCheck.swift. As asserções expressam os resultados esperados.

import Foundation

let source: NSArray = [
    NSString(string: "Circle"),
    NSString(string: "Square"),
    NSString(string: "Triangle")
]

let names = source as? [String]
assert(names == ["Circle", "Square", "Triangle"])

let mixed: NSArray = [NSString(string: "Circle"), NSNumber(value: 7)]
assert((mixed as? [String]) == nil)

let empty: NSArray = []
assert((empty as? [String]) == [])

print("Collection checks passed.")

A primeira conversão solicita um array de strings, portanto verifica o tipo de elemento exigido ao obter a coleção tipada. A entrada mista não atende a esse contrato. A entrada vazia atende: ela não contém nenhum elemento incompatível. Essas são verificações ilustrativas do contrato de entrada escolhido, não medições nem resultados de um app.

Evite substituir por compactMap { $0 as? String } apenas para fazer uma coleção mista passar. A Apple define compactMap como uma operação que retorna somente os resultados da transformação que não sejam nil. Neste exemplo, essa abordagem descartaria o número e exibiria uma lista incompleta. Filtrar pode ser adequado quando isso é solicitado explicitamente; aqui, a tarefa exige preservar todos os itens fornecidos. compactMap(_:)

Quando a validação for concluída com sucesso, use map para criar um rótulo para cada nome. A Apple documenta map como uma operação que aplica uma transformação aos elementos da sequência e NSTextField(labelWithString:) como a criação de um rótulo estático, sem quebra de linha e com texto não selecionável. Isso torna a transformação adequada para esses três nomes curtos. map(_:), init(labelWithString:)

Como construir a janela completa do Mac?

Este exemplo independente requer o macOS 14 ou posterior e as ferramentas de linha de comando do Xcode instaladas. Ele cria a interface por meio de código, portanto não precisa de storyboard, nib, recurso ou arquivo-fonte adicional. O requisito do macOS 14 contempla o uso de NSApplication.activate() no exemplo, que solicita a ativação sem garanti-la. activate()

Salve o programa completo abaixo como CollectionDemo.swift. Ele inclui deliberadamente seu próprio ponto de entrada. Ao adaptá-lo a um app existente, use o ponto de entrada e o ciclo de vida já estabelecidos pelo app, em vez de adicionar outro.

import Foundation
import AppKit

@MainActor
final class DemoDelegate: NSObject, NSApplicationDelegate {
    var window: NSWindow?

    func applicationDidFinishLaunching(_ notification: Notification) {
        let source: NSArray = [
            NSString(string: "Circle"),
            NSString(string: "Square"),
            NSString(string: "Triangle")
        ]

        guard let names = source as? [String] else {
            print("Expected a collection containing only strings.")
            NSApplication.shared.terminate(nil)
            return
        }

        let labels: [NSView] = names.map { name in
            NSTextField(labelWithString: name)
        }

        let stack = NSStackView(views: labels)
        stack.orientation = .vertical
        stack.alignment = .leading
        stack.spacing = 8
        stack.translatesAutoresizingMaskIntoConstraints = false

        let content = NSView(frame: NSRect(
            x: 0, y: 0, width: 360, height: 180
        ))
        content.addSubview(stack)

        NSLayoutConstraint.activate([
            stack.leadingAnchor.constraint(
                equalTo: content.leadingAnchor, constant: 20
            ),
            stack.topAnchor.constraint(
                equalTo: content.topAnchor, constant: 20
            ),
            stack.trailingAnchor.constraint(
                lessThanOrEqualTo: content.trailingAnchor, constant: -20
            ),
            stack.bottomAnchor.constraint(
                lessThanOrEqualTo: content.bottomAnchor, constant: -20
            )
        ])

        let window = NSWindow(
            contentRect: content.frame,
            styleMask: [.titled, .closable, .miniaturizable],
            backing: .buffered,
            defer: false
        )
        window.title = "Synthetic Shapes"
        window.isReleasedWhenClosed = false
        window.contentView = content
        self.window = window
        window.center()
        window.makeKeyAndOrderFront(nil)
        NSApplication.shared.activate()
    }

    func applicationShouldTerminateAfterLastWindowClosed(
        _ sender: NSApplication
    ) -> Bool {
        true
    }
}

@main
@MainActor
enum CollectionDemo {
    static let appDelegate = DemoDelegate()

    static func main() {
        let app = NSApplication.shared
        app.setActivationPolicy(.regular)
        app.delegate = appDelegate
        app.run()
    }
}

Compile e inicie o programa pelo Terminal, no diretório que contém o arquivo:

xcrun swiftc -parse-as-library CollectionDemo.swift -o CollectionDemo
./CollectionDemo

Com a entrada fornecida, o resultado esperado é uma janela intitulada Synthetic Shapes contendo os três rótulos na ordem original. Fechar a janela solicita o encerramento do app. O exemplo retorna true do método delegado que a Apple fornece para esse comportamento. applicationShouldTerminateAfterLastWindowClosed(_:)

Os exemplos foram inspecionados, mas não foram compilados nem executados aqui: o ambiente de pesquisa não oferece um compilador Swift nem o AppKit do macOS. Execute as verificações do Foundation e inspecione a janela no Mac de destino antes de integrar o exemplo.

Quais contratos de layout e ciclo de vida precisam ser preservados?

Os valores padrão documentados do inicializador da pilha são importantes: ele cria uma pilha horizontal, coloca as views fornecidas na área de gravidade inicial e desativa a tradução da máscara de redimensionamento automático para a pilha. O exemplo altera explicitamente a orientação para vertical e o alinhamento para inicial. Uma conversão de array não escolhe nenhuma dessas configurações de layout. init(views:), alignment

A pilha gerencia a disposição das views que contém. As restrições entre a pilha e a view de conteúdo que a envolve determinam onde essa disposição fica na janela. Aqui, as restrições inicial e superior definem um recuo de 20 pontos; as desigualdades final e inferior reservam espaço sem exigir que a lista curta preencha a área de conteúdo. A Apple documenta que as views de pilha usam Auto Layout e explica que desativar a tradução da máscara de redimensionamento automático permite definir as próprias restrições. NSStackView, translatesAutoresizingMaskIntoConstraints

Esta é uma demonstração de tamanho fixo com três rótulos curtos. Textos mais longos, muitas linhas ou uma interface redimensionável exigem uma decisão de layout separada — como usar quebra de linha ou rolagem — antes de ampliar o exemplo. Fazer corretamente a ponte de uma coleção maior não garante que ela caiba na apresentação.

O ciclo de vida também é separado da conversão de tipos. A Apple declara como fraca a propriedade de delegado do app. Portanto, a propriedade estática appDelegate fornece um proprietário forte explícito. O delegado também retém a janela, e a janela define isReleasedWhenClosed como false, seguindo as orientações da Apple para clientes do Swift e do ARC. delegate, isReleasedWhenClosed

A construção da interface ocorre em applicationDidFinishLaunching, que a Apple documenta como um ponto para inicialização adicional depois que o loop principal começa e antes de processar eventos. O ponto de entrada do programa chama run() para iniciar esse loop de eventos. Preserve essas responsabilidades ao mover a conversão para um app AppKit existente. applicationDidFinishLaunching(_:), run()

Os rótulos também representam um retrato estático construído. O exemplo atribui o texto de cada rótulo uma única vez. Alterar a coleção de origem depois disso exigiria código explícito para atualizar os rótulos ou reconstruir as views organizadas; a ponte e a operação de mapeamento não estabelecem uma conexão contínua.

Como localizar rapidamente um erro entre o contêiner e os elementos?

Use a sequência a seguir para identificar qual contrato falhou. Ela separa a validação de tipos das verificações de apresentação e ciclo de vida, para que um problema de layout não leve você a reescrever uma ponte que funcionou.

Sintoma Verificação Próximo passo compatível
Uma conversão para [String] retorna nil Cada elemento da entrada atende ao requisito de string? Rejeite a coleção explicitamente ou defina uma política intencional de conversão.
Um array inteiro está sendo convertido para String O destino precisa de um valor ou de uma coleção? Valide [String]; escolha um elemento somente se a tarefa realmente precisar de um.
Um inicializador de pilha rejeita o argumento O argumento é [String], em vez de [NSView]? Primeiro, mapeie cada string para um rótulo.
Os rótulos aparecem na horizontal A orientação da pilha foi configurada? Defina .vertical para esta lista.
As views estão mal posicionadas ou há conflito entre restrições As restrições da view que envolve a pilha e as configurações da máscara de redimensionamento automático são consistentes? Inspecione o layout independentemente da conversão de entrada.
As chamadas de retorno do delegado não chegam Um proprietário forte mantém o delegado retido? Mantenha o delegado vivo além da atribuição à propriedade fraca.
Alterações posteriores no modelo não aparecem Há código explícito de atualização? Atualize deliberadamente os valores dos rótulos ou as views organizadas.

Para esta tarefa, o caminho confiável é validar a entrada do Foundation uma vez, manter o modelo tipado resultante, construir um rótulo para cada valor e configurar explicitamente a pilha e o ciclo de vida do app. Cada etapa tem um contrato visível, o que facilita diagnosticar falhas à medida que a interface do Mac cresce.

Fontes