Swift: como usar sinalizadores de PREPROCESSOR (como `#if DEBUG`) para implementar chaves de API?

100

Em Objective-Calgumas vezes era útil para usar as constantes string estática para definir chaves de API alternativos (por exemplo, para diferenciar entre a liberação e as chaves de depuração para pacotes de análise, como MixPanel, Flurry ou Crashlytics):

#if DEBUG
static NSString *const API_KEY = @"KEY_A";
#else
static NSString *const API_KEY = @"KEY_B";
#endif

e depois...

[Analytics startSession:API_KEY];

Como isso se traduz em Swift, já que o compilador Swift não usa mais um pré-processador?

inteligente
fonte

Respostas:

168

A Apple incluiu suporte completo para sinalizadores de pré-processador Swift a partir do Xcode 8 , então não é mais necessário definir esses valores em "Outros sinalizadores Swift".

A nova configuração é chamada de "Condições de compilação ativa", que fornece suporte de nível superior para o equivalente Swift dos sinalizadores de pré-processador. Você o usa exatamente da mesma maneira que faria com "Outros sinalizadores Swift", exceto que não há necessidade de acrescentar um "-D" ao prefixo (portanto, é apenas um pouco mais limpo).

Das notas de lançamento do Xcode 8 :

Active Compilation Conditionsé uma nova configuração de construção para passar sinalizadores de compilação condicional para o compilador Swift. Cada elemento do valor desta configuração passa para swiftc prefixado com -D, da mesma forma que elementos de Preprocessor Macrospassam para clang com o mesmo prefixo. (22457329)

insira a descrição da imagem aqui

Você usa a configuração acima assim:

#if DEBUG
    let accessToken = "DebugAccessToken"
#else
    let accessToken = "ProductionAccessToken"
#endif
Dan Loewenherz
fonte
2
Nota: você não deve especificar = 1 ou qualquer outro valor =. Em vez disso, você precisa apenas especificar o nome do sinalizador. :]
JRG-Developer
@ JRG-Developer Não discordo, mas não tenho certeza de como seu comentário se aplica aqui.
Dan Loewenherz
9
Esta é uma resposta útil, mas vindo de um background Objective-C (como eu imagino que muitos desenvolvedores iOS são), presumi que precisava especificar =1... Perdi um pouco de tempo tentando descobrir por que não estava funcionando quando eu fiz. Então, pensei em compartilhar este boato para ajudar o próximo companheiro. :] De qualquer forma, obrigado pela sua resposta aqui!
JRG-Developer
1
@ JRG-Developer, @Dan Loewenherz Eu configurei DEBUGem Active Compilation Conditionse DEBUG=1em Preprocessor Macrose esta configuração não funciona de todo. Devo remover DEBUG=1? Não está claro nos comentários acima.
Bhavin_m
2
@DanLoewenherz Você está absolutamente certo. Eu tinha definido "DEBUG" para a configuração do arquivo em minhas configurações de destino, portanto, toda vez que ele executa uma instrução Debug e nunca executa a condição de liberação. Qualquer pessoa que esteja enfrentando problemas, verifique Build Configurationprimeiro o seu alvo . Verifique esta resposta stackoverflow.com/questions/9063100/… para obter mais informações.
Bhavin_m
132

ATUALIZADO: o Xcode 8 agora suporta isso automaticamente, consulte a resposta de @DanLoewenherz acima.

Antes do Xcode 8, você ainda podia usar macros da mesma maneira:

#if DEBUG
let apiKey = "KEY_A"
#else
let apiKey = "KEY_B"
#endif

No entanto, para que eles sejam captados pelo Swift, você precisa definir "Outros sinalizadores Swift" nas configurações de construção do seu alvo:

  • Abra as configurações de compilação para seu destino
  • Pesquise por "outras sinalizações rápidas"
  • Adicione as macros que deseja usar, precedidas do -Dsinalizador

insira a descrição da imagem aqui

inteligente
fonte
você fez meu dia! para mim não funcionou sem -Dprefixo
nomnom
5

Como observação de acompanhamento, tente não manter as chaves / segredos da API em texto simples no repositório. Use um sistema de gerenciamento de segredos para carregar as chaves / segredos nas variáveis ​​de ambiente do usuário. Caso contrário, a etapa 1 é necessária, se aceitável.

  1. Coloque os "segredos" em um arquivo de texto simples acima no repositório anexo
  2. Crie um ../set_keys.shque contenha uma lista de export API_KEY_A='<plaintext_key_aef94c5l6>'(use aspas simples para evitar avaliação)
  3. Adicione uma fase de script de execução que possa source ../set_keys.she mova-a para o topo da ordem de execução
  4. Em Build Settings> Preprocessor Macros, adicione às definições conforme necessário, como API_KEY_A="$API_KEY_A"

Isso captura a variável de ambiente na definição do compilador, que é usada posteriormente em cada invocação do clang para cada arquivo de origem.

Exemplo de estrutura de diretório

[10:33:15] ~/code/memo yes? tree -L 2 .
.
├── Memo
│   ├── Memo
│   ├── Memo.xcodeproj
│   ├── Memo.xcworkspace
│   ├── Podfile
│   ├── Podfile.lock
│   └── Pods
└── keys
Michael Lorenzo
fonte
0

Em pacotes swift, você tem que fazer isso dentro do swiftSettingsargumento .targetem seu Package.swiftarquivo. Use o definemétodo (documentação da Apple) ou documentação do Swift

targets: [
.target(name: String,
            dependencies: [Target.Dependency],
            path: String?,
            exclude: [String]?,
            sources: [String]?,,
            cSettings: [CSetting]?,
            cxxSettings: [CXXSetting]?,
            swiftSettings: [SwiftSetting]?,
            linkerSettings: [LinkerSetting]?),

O meu é assim e funciona!

            swiftSettings: [
               .define("VAPOR")
            ]

no meu código, posso compilar condicionalmente usando isto:

#if VAPOR
garafajon
fonte