Criando frameworks iOS / OSX: é necessário codificá-los antes de distribuí-los para outros desenvolvedores?

91

Estou aprendendo a criar frameworks iOS e OSX. Vamos pegar o iOS, por exemplo, as seguintes etapas funcionam para mim até agora:

  1. Estrutura xcodebuild usando -sdk iphonesimulator e ação Build
  2. Estrutura xcodebuild usando -sdk iphoneos e ação Build
  3. Use a ferramenta lipo para criar binário universal para que lipo -infoproduza o esperado:

As arquiteturas no arquivo fat: Foo.framework / Foo são: i386 x86_64 armv7 arm64

As perguntas são:

  1. Eu li que meu framework pode ser assinado novamente pelo desenvolvedor que o está usando: "Code Sign on Copy" mas não entendo quais são as pré-condições para ele, ou seja , devo adicionar a etapa de codeign para codificar aquele binário universal com minha identidade de assinatura antes distribuir para outros desenvolvedores?

  2. se anterior for positivo - devo usar minha identidade "iPhone Distribution: ..." ou "iPhone Developer: ..." é o suficiente (para que meu framework como parte de algum projeto iOS passe em todos os tipos de validações, especialmente a validação da App Store ) ?.

O pano de fundo para minha resposta é o "erro de CodeSign: a assinatura de código é necessária para o tipo de produto 'Framework' no SDK 'iOS 8.3'", que vi em uma série de estruturas de terceiros e Carthage # 235 ou "objeto de código não assinado em tudo "(um exemplo: problema que relatei no Realm # 1998 .

Portanto, quero ter certeza de que os usuários de meus frameworks não encontrarão problemas de assinatura de código ao usá-los.

PS Esta questão fica ainda mais interessante quando aplicada não a um único desenvolvedor, mas a uma organização que é fornecedora de framework.

Stanislav Pankevich
fonte
Este comentário sugere o uso de 'CODE_SIGN_IDENTITY = iPhone Developer', mas não está claro por que 'Developer' é usado em vez de 'iPhone Distribution'.
Stanislav Pankevich
Tópico relacionado: Exportando aplicativo com estrutura integrada, mas sem resposta definitiva.
Stanislav Pankevich
Também enganei esta pergunta nos Fóruns de Desenvolvedores da Apple: forums.developer.apple.com/thread/6400 .
Stanislav Pankevich
Tenho uma pergunta quando você distribui sua estrutura, você distribui uma compilação de depuração ou uma compilação de lançamento? e se você distribuí-lo na versão de lançamento, como fazer isso?
niczm25
@ niczm25, esta é uma maneira de fazer isso: stanislaw.github.io/2015/11/23/… .
Stanislav Pankevich

Respostas:

138

Eu abri a recompensa: "Procurando uma resposta tirada de fontes confiáveis ​​e / ou oficiais." mas não recebo desde então.

Embora a resposta fornecida por @jackslash esteja correta, ela conta apenas uma parte da história, então quero escrever a minha própria de uma maneira que gostaria de ter visto no momento em que estava fazendo esta pergunta.

A realidade desta resposta é: julho de 2015. É mais provável que as coisas mudem.

Em primeiro lugar, vamos afirmar que as ações necessárias para a assinatura correta do código do framework devem ser divididas em etapas que o desenvolvedor do framework deve seguir e etapas que o consumidor do framework deve executar.

TLDR;

Para a estrutura OSX: O desenvolvedor é livre para distribuir a estrutura OSX sem codificá-la, pois o consumidor irá recodificá-la de qualquer maneira.

Para a estrutura iOS: o desenvolvedor é livre para distribuir a estrutura iOS sem codificá-la, pois o consumidor irá recodificá-la de qualquer maneira, mas o desenvolvedor é forçado pelo Xcode a codificar sua estrutura quando construir para um dispositivo iOS.

Por causa do radar: "frameworks iOS contendo fatias de simulador não podem ser enviados para a App Store" O consumidor de framework iOS é forçado a executar um script especial como "copy_frameworks" ou "strip_frameworks" que usa lipo -removepara retirar fatias de simulador de framework iOS e re -codesigns estrutura despojada porque neste ponto sua identidade de codesigning o que quer que fosse (ou não fosse) é removida como efeito colateral da lipo -removemanipulação.

Segue uma resposta mais longa.


Esta resposta não é um "desenho de fontes credíveis e / ou oficiais", mas sim baseada em uma série de observações empíricas.

Observação empírica # 1: O consumidor não se importa porque eles irão recodificar a estrutura que recebem do desenvolvedor

Distribuições de estruturas binárias de projetos de código aberto conhecidos no Github não são codificadas . O comando codesign -d -vvvvfornece: "o objeto de código não está assinado de forma alguma" em todas as estruturas binárias do iOS e OSX que usei para explorar. Alguns exemplos: ReactiveCocoa and Mantle , Realm , PromiseKit .

A partir dessa observação, fica claro que os autores desses frameworks pretendem que eles sejam codificados pelo Consumidor, em seu nome, ou seja, um Consumidor deve usar a sinalização "Code Sign on Copy" na fase de construção "Embed frameworks" fornecida pelo Xcode ou usar algum shell personalizado script que faz a mesma coisa manualmente: framework de codesigns em nome do consumidor.

Não encontrei nenhum exemplo do oposto: estrutura de código aberto que seria distribuída com identidade de assinatura de código nela, portanto, no restante da resposta, estou assumindo essa abordagem amplamente adotada como correta: não há necessidade do desenvolvedor de estrutura distribuir sua estrutura a outros desenvolvedores com identidade de codificação, porque o consumidor irá recodificá-la de qualquer maneira .

Observação empírica # 2 que se aplica apenas ao iOS e que é inteiramente uma preocupação do desenvolvedor

Enquanto consumidor não se importa se estrutura que recebem de desenvolvedor é codesigned ou não, desenvolvedor ainda precisa CodeSign seu quadro iOS como parte de seu processo de criação quando construí-lo para o dispositivo iOS , porque caso contrário Xcode não construir: CodeSign error: code signing is required for product type 'Framework' in SDK 'iOS 8.1'. Para citar Justin Spahr-Summers :

Os frameworks do OS X não precisam ser codificados na construção ... Infelizmente, o Xcode exige que os frameworks iOS sejam codificados no momento da construção.

Isso responde bem à minha pergunta nº 2: a identidade de "desenvolvedor de iPhone" é o suficiente para persuadir o Xcode de modo que ele crie uma estrutura iOS para o dispositivo. Este comentário em Cartago # 339 diz a mesma coisa.

Observação empírica # 3: ferramenta lipo

Comportamento específico da ferramenta lipo: quando aplicado a binário quadro, sempre de forma recursiva remove quaisquer identidades CodeSign dele : lipo -create/-remove codesigned framework ... -> not codesigned framework.

Esta poderia ser uma resposta por que todos os exemplos na observação nº 1 não são codificados de forma alguma: sua identidade de codificação é perdida após a aplicação da lipo, mas desde que, de acordo com a observação nº 1, o consumidor não se importa, está tudo bem.

Esta observação é especialmente relevante para a próxima observação nº 4 sobre a AppStore.

Observação empírica nº 4: frameworks iOS contendo fatias de simulador não podem ser enviados para a App Store

Isso é amplamente discutido em: Realm # 1163 e Carthage # 188 e o radar é aberto: rdar: // 19209161 .

Essa é uma preocupação inteiramente do Consumidor: para a estrutura universal do iOS que o Consumidor inclui em seu aplicativo, quando o aplicativo está sendo construído, ele deve executar um script especial (Fase Run Script customizada) que remove a fatia do simulador do binário dessa estrutura para que o aplicativo possa passar na validação do AppStore.

O bom exemplo de frameworks binários que encontrei em Realm: strip-frameworks.sh .

Ele usa lipopara remover todas as fatias de arquiteturas que não sejam ${VALID_ARCHS}e então recodifica-as com a identidade do consumidor - é aqui que a observação # 3 entra em ação: a estrutura deve ser recodificada por causa de manipulações lipo nela.

Carthage tem o script CopyFrameworks.swift que faz a mesma coisa com todos os frameworks incluídos pelo Consumer: ele remove as fatias do simulador e recodifica o framework em nome do Consumer.

Também há um bom artigo: Removendo arquiteturas indesejadas de bibliotecas dinâmicas no Xcode .


Agora, a visão geral das etapas necessárias para produzir iOS e OSX a partir da perspectiva do desenvolvedor e do consumidor. Primeiro o mais fácil:

OSX

Desenvolvedor:

  1. Constrói estrutura OSX
  2. Dá ao consumidor

Nenhuma atividade de codesigning é exigida do desenvolvedor.

Consumidor:

  1. Recebe estrutura OSX do desenvolvedor
  2. Copia a estrutura para Frameworks / diretório e a codifica automaticamente em seu nome, do Consumidor, como parte do processo de "Assinatura de código na cópia".

iOS

Desenvolvedor:

  1. Constrói estrutura iOS para dispositivo. O Codesigning é exigido pelo Xcode, a identidade "iPhone Developer" é suficiente.
  2. Constrói estrutura iOS para simulador.
  3. Usa lipo que produz estrutura iOS universal dos dois anteriores. Neste ponto, a identidade de codificação de 1 etapa é perdida: binário de estrutura universal "não é assinado", mas tudo bem, já que "O consumidor não se importa".
  4. Dá ao consumidor

Consumidor:

  1. Recebe estrutura iOS do desenvolvedor
  2. Copia a estrutura para Frameworks / diretório (esta etapa pode ser redundante, dependendo de qual script é a etapa 3).
  3. Usa script especial como parte do processo de construção: este script remove fatias do simulador da estrutura do iOS e, em seguida, recodifica-a em seu nome, o consumidor.
Stanislav Pankevich
fonte
10
Este é um artigo valioso. Boa
jackslash de
@Stanislaw obrigado pelos insights e pelos dados valiosos. Como estou escrevendo um framework projetado para desenvolvedores, quero que eles sejam capazes de pegar o framework como está e usá-lo sem precisar fazer um script especial para carregá-lo na app-store. Acho que o GoogleMobileAd funciona assim. mas você diz que eles devem executar determinado script? você tem ideia de como o GoogleMobileAds não exige isso? obrigado
Michael A
@MichaelA, realmente isso é interessante. Eu vou procurar.
Stanislav Pankevich
Você poderia me fornecer um link para o GoogleMobileAds que está usando?
Stanislav Pankevich
1
Obrigado por economizar meu tempo, que funcionou para mim. Xcode 7.3, Mac OS X 10.11.4.
eugen,
21

Lendo o tópico vinculado no repositório de Carthage, parece relativamente simples. Se você estiver distribuindo a estrutura binária, precisará assiná-la por código e se estiver distribuindo o código-fonte por meio de cartago ou cacau, não, pois essas ferramentas cuidam disso por meio de métodos diferentes.

O motivo pelo qual você precisa assinar o código ao distribuir a estrutura binária é que o Xcode não produzirá um binário da estrutura sem a assinatura do código. Se você tentar não codificar a assinatura da estrutura binária, receberá este erro:

CodeSign error: code signing is required for product type 'Framework' in SDK 'iOS 8.1'

Não importa com qual identidade você assina o código do framework (iPhone Developer ou iPhone Distribution) porque, como você ressaltou, o framework será recodificado com a configuração "code sign on copy". Isso significa que sua estrutura será recodificada pelo certificado apropriado do perfil de desenvolvedor do consumidor da estrutura quando sua estrutura for copiada para o aplicativo. Isso significa que não haverá problemas com a App Store, pois ela verá apenas a assinatura do código final do consumidor da estrutura.

No final do dia, você também pode assinar o código do seu binário .framework, já que não quer ter que manter um processo de compilação exótico, e como o Xcode só produzirá estruturas assinadas, você não deve se afastar muito do padrões. De qualquer forma, isso realmente não importa porque o consumidor final assinará novamente.

jackslash
fonte
em sua resposta, no segundo parágrafo, você está dizendo que não há necessidade de codificar a estrutura porque ela será recodificada pelo consumidor e agora também estou 95% de certeza que é verdade, mas não entendo por que, ao mesmo tempo, no primeiro parágrafo, você está dizendo: "Se você está distribuindo a estrutura binária, você precisa assiná-la pelo código" - qual é a diferença - se eu distribuir a estrutura binária (ou seja, não sua fonte via cartago ou cocoapods) por que Eu precisaria assiná-lo com código?
Stanislav Pankevich
Acho que não devo codificar meu framework em qualquer tipo de distribuição que uso (arquivo .framework compactado ou carthage ou cocoapods). Exemplos: todas as seguintes distribuições de estruturas binárias não são codificadas: Realm , ReactiveCocoa , OCMockito .
Stanislav Pankevich
Portanto, estou um pouco confuso com o seu "Se você está distribuindo a estrutura binária, você precisa assiná-la por código", que (imho) contradiz o resto da sua resposta. Por favor, esclareça. Observe também que abri esta recompensa como "Procurando uma resposta tirada de fontes confiáveis ​​e / ou oficiais.".
Stanislav Pankevich
1
Sim, eu também olhei para Realm e OCMockito. Realm tem identidades de "desenvolvedor de iPhone" e sim, OCMockito é uma biblioteca estática. Acho que entendo o problema - é o lipo que remove a assinatura de código do iphoneos quando combina iphoneos com código e iphonesimulator não codificado.
Stanislav Pankevich
1
Tenho uma pergunta quando você distribui sua estrutura, você distribui uma compilação de depuração ou uma compilação de lançamento? e se você distribuí-lo na versão de lançamento, como fazer isso?
niczm25