web-dev-qa-db-fra.com

Utilisation de Cocoapods dans une extension d'application utilisant un framework

J'ai une application (appelons-la MyApp) écrite en Swift avec les cibles suivantes:

  • MyApp: la cible principale
  • MyAppKit: une cible construisant un framework pour le code qui est partagé entre l'application et ses extensions, principalement le backend API et la gestion de la base de données
  • MyAppWidget: un Widget de Vue Aujourd'hui (ou son nom actuel) qui utilise le framework MyAppKit.

Le framework MyAppKit est lié à chaque cible qui l'utilise, à savoir MyApp et MyAppWidget. Entrez Cocoapods: J'avais auparavant la structure Podfile suivante:

platform :ios, '8.0'
use_frameworks!

target 'MyApp' do
    # Mostly UI or convenience pods
    pod 'Eureka', '~> 2.0.0-beta'
    pod 'PKHUD', '~> 4.0'
    pod '1PasswordExtension', '~> 1.8'
end

target 'MyAppKit' do
    # Backend pods for networking, storage, etc.
    pod 'Alamofire', '~> 4.0'
    pod 'Fuzi', '~> 1.0'
    pod 'KeychainAccess', '~> 3.0'
    pod 'RealmSwift', '~> 2.0'
    pod 'Result', '~> 3.0'
end

target 'MyAppWidget' do
    # Added here event though the target only imports MyAppKit but it worked
    pod 'RealmSwift', '~> 2.0'
end

Le but ici était d'exposer uniquement le framework MyAppKit aux autres parties et pas tous ses pods (par exemple, je ne veux pas pouvoir import Alamofire dans l'application principale). Cependant, en commençant par les Cocoapods 1.2.0 RC, pod install a échoué avec l'erreur suivante: [!] The 'Pods-MyApp' target has frameworks with conflicting names: realm and realmswift.. Cela fonctionnait parce que les pods ont été déclarés pour l'extension mais uniquement intégrés dans l'application hôte (voir ce problème pour plus d'informations). J'ai donc supprimé les pods de la cible du widget, me laissant juste un _ target 'MyAppWidget' ligne.

Avec cette configuration, pod install s'exécute correctement mais la compilation échoue au stade de la liaison pour la cible MyAppWidget: ld: framework not found Realm for architecture x86_64. Cela peut être résolu en ajoutant explicitement les deux Realm.framework et RealmSwift.framework à la section "Link Binary With Libraries" et au paramètre de génération suivant dans le Pods-MyAppWidget.[debug/release].xcconfig:

FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Realm" "$PODS_CONFIGURATION_BUILD_DIR/RealmSwift"`

Cependant, chaque fois que je lance pod install, les paramètres de build sont naturellement rétablis et je dois à nouveau ajouter les paramètres de build.

Je vois les solutions suivantes:

  • Ajouter un post_install hook ajoutant ces paramètres à chaque fois mais cela semble "hacky" et après quelques tentatives malencontreuses, je n'ai trouvé aucune référence API et je ne sais pas comment ajouter ces paramètres à la cible MyAppWidget via le script.
  • Changez le Podfile en la structure suivante (ou même en l'enveloppant dans une cible abstraite):

    [...]
    target 'MyAppKit' do
        # Backend pods for networking, storage, etc.
        pod 'Alamofire', '~> 4.0'
        pod 'Fuzi', '~> 1.0'
        pod 'KeychainAccess', '~> 3.0'
        pod 'RealmSwift', '~> 2.0'
        pod 'Result', '~> 3.0'
    
        target 'MyAppWidget' do
            inherit! :search_paths # Because else we get the "conflicting names" error
        end
    end
    

    Ce qui me semble logique dans le sens de "le widget devrait savoir où chercher lors de la liaison mais n'a pas besoin des pods en soi" mais cela n'ajoute pas les paramètres de construction susmentionnés (je comprends probablement mal le :search_paths héritage) (modifier: cela fonctionne réellement, mais pas avec une cible abstraite). Cette idée m'est venue parce que dans les anciennes versions de CocoaPods, la solution devait apparemment ajouter link_with, désormais obsolète.

  • Exposer également Realm dans la cible MyApp, mais cela entre en conflit avec mon objectif de ne pas avoir accès au code "backend" dans le code principal (cela pourrait être purement esthétique?).

Alors, voici ma question: quelle est la meilleure façon d'intégrer des pods dans un cadre partagé entre l'application principale et l'extension tout en étant en mesure de compiler, sans peaufiner et ajouter manuellement des choses?

Santé et merci d'avance!


ÉDITER

Après Commentaire de Prientus J'ai exploré les possibilités d'abstraction et d'héritage. Les problèmes sous-jacents que j'ai maintenant découverts sont en fait multiples:

  • Il fonctionnait avant Cocoapods 1.2.0 car les pods déclarés sous la cible du widget étaient intégrés dans l'application Host mais toujours liés au widget. Non, il refuse simplement d'avoir des pods avec le même nom pour différentes cibles dans une relation "principale vs extension"
  • L'utilisation de cibles abstraites est insuffisante car les cibles ne peuvent pas hériter uniquement des chemins de recherche (inherit! :search_paths) à partir d'une cible abstraite.
  • Les chemins de recherche peuvent être hérités d'une cible réelle comme MyAppKit, mais cela expose tous ces pods au code de MyApp (que je veux éviter), et il y a toujours le problème de la liaison du framework Realm (car en fait, le widget utilise le plus petit bit d'un getter et en a donc besoin).

Utiliser cette dernière option et lier manuellement Realm.framework fonctionne mais n'est pas optimal en ce qui concerne mes intentions et ce qui fonctionnait auparavant. Certains de ces problèmes semblent être un bogue selon diversproblèmeson GitHub de Cocoapods. J'ai ajouté mon propre problème et je mettrai à jour quand j'aurai des nouvelles.

14
Jonas Zaugg

Alors, qu'est-ce qui donne:

  • Ma préoccupation de "séparer les pods entre les cibles" est absurde car vous pouvez toujours les importer n'importe où.
  • Le problème "vous devez lier manuellement" est résolu par un simple import RealmSwift déclaration.

Le Podfile fixe et fonctionnel est donc:

platform :ios, '8.0'
use_frameworks!

target 'MyApp' do
    pod 'Eureka', '~> 2.0.0-beta'
    pod 'PKHUD', '~> 4.0'
    pod '1PasswordExtension', '~> 1.8'
end

target 'MyAppKit' do
    pod 'Fuzi', '~> 1.0'
    pod 'RealmSwift', '~> 2.0'
    pod 'Alamofire', '~> 4.0'
    pod 'KeychainAccess', '~> 3.0'
    pod 'Result', '~> 3.0'

    target 'MyAppWidget' do
        inherit! :search_paths
    end
end

Et c'est tout. Je dirais que l'ancien comportement était plus évident et ne nécessitait pas de lecture sur "l'héritage de la cible du podfile". J'ai cependant beaucoup appris. À votre santé!

12
Jonas Zaugg

Je ne te connais pas. Mais pour moi, c'est totalement légitime et raisonnable d'avoir l'extension et l'application Host contient tous les pods qu'un framework définit. Et c'est ce que je veux dire:

def shared_pods
    pod 'Alamofire'
end

target 'Framework' do
    shared_pods
end

target 'Host' do
    shared_pods
    // Some other pods
end

target 'Extension' do
    shared_pods
end

Je sais que vous êtes inquiet, mais si vous y réfléchissez, tous ces cadres tiers que vous utilisez, ils ont tous des dépendances. Vous n'avez pas à vous en préoccuper car Cocoapods s'en occupe pour vous. Si vous souhaitez utiliser cela, vous devrez mettre une entrée de pod local dans la liste.

target 'Host' do
    pod Framework, :path => '../Framework'
end

Mais alors vous devez maintenir le fichier podspec.

6
J.Wang

Il s'agit d'un exemple de profil d'un projet Swift-3.0.

platform :ios, '8.0'

def import_public_pods

  pod 'SwiftyJSON'

end


target 'Demo' do
  use_frameworks!

  # Pods for Demo
  import_public_pods 
  pod 'Fabric'
  pod 'Crashlytics'

  target 'DemoTests' do
    inherit! :search_paths
    # Pods for testing
  end

  target 'DemoUITests' do
    inherit! :search_paths
    # Pods for testing
  end

end


target 'DemoKit' do
  use_frameworks!

  # Pods for DemoKit
  import_public_pods
  pod 'RealmSwift'

  target 'DemoKitTests' do
    inherit! :search_paths
    # Pods for testing
  end

end
1
york