Swift, Cocoa Framework from scratch

idomyjobidomyjob Membre
juillet 2014 modifié dans Objective-C, Swift, C, C++ #1

-- Edit


 


J'apporte un début de solution à  ma question initialement posée. Pour développer un framework utilisant une classe swift, dans une application basique (single view), voici les étapes à  suivre.


 


Mon environnement xCode Beta4. Le code a été testé uniquement pour et sur cet environnement.


 


Veillez à  créer un dossier en local "CocoacafeFolder" pour ce projet afin d'y stocker l'ensemble des ressources que nous allons créer dans la procédure qui suit.


 


Attention, le langage Swift n'en est à  un stade peu avancé, et il se peut que certains fonctions soient amenées à  bouger avec le temps.


  • Démarrez xCode, puis créez un nouveau workspace "Cocoacafe"
  • Dans ce workspace, ajoutez un nouveau projet. Sélectionnez parmi les patterns iOS proposés, le Framework Touch Cocoa.
  • Dans la fenêtre suivante, nommez votre product name : "CocoacafeKit", puis assurez-vous de bien enregistrer ce framework dans le dossier local "CocoacafeFolder", qu'il soit associé avec votre espace de travail "Cocoacafe" (add to : Cocoacafe), et enfin que le langage sélectionné soit swift.
  • Vous devriez voir votre framework dans le navigateur de xCode à  votre gauche.
  • Dans ce navigateur, dépliez "CocoaKit", puis dans le dossier Cocoakit, ajoutez un nouveau fichier .swift (Swift File). Nommez ce fichier "CocoacafeClass". De nouveau assurez-vous que ce soit associé au groupe Cocoakit, avec pour cible Cocoakit.
  • à‰crivez votre classe dans ce fichier.

import Foundation
public class CocoacafeClass: NSObject {
public init() {
super.init()
}
public func hello() -> Void {
println("Hello World !")
}
}

  • Sauvegardez et buildez ce projet.
  • Dans votre espace de travail, créez un nouveau projet iOS : Application Single View. Nommez cette application "CocoacafeApp", et veillez à  sélectionner le langage Swift. Associez cette application à  votre espace de travail, et également au groupe de votre espace de travail.
  • Dans le navigateur d'xCode à  gauche, cliquez sur le nom de projet "CocoacafeApp". Vous aurez accès aux préférences. Dans l'onglet "General", rendez-vous dans la section "Linked Frameworks and Libraries", puis à  l'aide du bouton "+", associez "Cocoakit.framework"
  • Dans votre projet CocoacafeApp, ouvrez le fichier ViewController.swift, puis importez votre framework

import UIKit
import Cocoakit

class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var kit = CocoacafeClass()
kit.hello()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

  • Buildez, et executer sur le simulateur iPhone 5S. Vous devriez théoriquement obtenir un log "Hello World !"

Réponses

  • Joanna CarterJoanna Carter Membre, Modérateur
    juillet 2014 modifié #2



    import UIKit
    import myFramework

    class ViewController: UIViewController {

    override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    var test: NSObject = myClass()
    test.hello
    }



     


    C'est pourquoi tu instances myClass dans un var NSObject ? NSObject ne possède pas la méthode hello(). En plus, je crois qu'il te faut ajouter les parenthèses après le nom de la méthode.


     


    Et, en plus de cet en plus - on est conseillé de commencer les noms des types ave une majuscule  :-*


  • Merci pour les précisions, j'ai corrigé le code dans le message ci-dessus. Concernant les conventions d'écriture, je ne souhaite pour le moment pas m'y attarder. Du moment que cela reste cohérent. En revanche, ça ne fonctionne toujours pas.


  • Joanna CarterJoanna Carter Membre, Modérateur


    Merci pour les précisions, j'ai corrigé le code dans le message ci-dessus. Concernant les conventions d'écriture, je ne souhaite pour le moment pas m'y attarder. Du moment que cela reste cohérent. En revanche, ça ne fonctionne toujours pas.




     


    Pouah ! J'attends le téléchargement de Xcode 6b4 ; puis je pourrai essayer de faire la même chose.

  • MayerickMayerick Membre
    juillet 2014 modifié #5


    Merci pour les précisions, j'ai corrigé le code dans le message ci-dessus. Concernant les conventions d'écriture, je ne souhaite pour le moment pas m'y attarder. Du moment que cela reste cohérent. En revanche, ça ne fonctionne toujours pas.


    Tu ne souhaites pas t'y attarder pour le moment ? C'est à  dire ? Tu penses t'y mettre plus tard ? Mauvaise idée d'après moi. Si tu penses qu'un beau matin il sera enfin temps de respecter les conventions d'écriture, alors que beaucoup de code sera déjà  écrit et bien il sera trop tard. ^^ ça va rapidement devenir le bordel, et dans quelques mois quand tu te reliras, le fait de ne pas avoir respecté les conventions pourra apporter des ambiguà¯tés et être source d'erreur. Enfin ça fait le même l'effet qu'une grosse faute d'orthographe quand on lit du code qui ne respecte aucune convention, donc on est moins enclin à  tout lire ou à  vraiment chercher. Ce sont juste des conseils, tu en fais évidement ce que tu veux !!


    Bonne journée.
  • AliGatorAliGator Membre, Modérateur
    juillet 2014 modifié #6


    Concernant les conventions d'écriture, je ne souhaite pour le moment pas m'y attarder.

    Grave erreur.


    1. Tu vas prendre de mauvaises habitudes dès le début si tu ne t'y mets pas tout de suite (et c'est pas comme si elles étaient compliquées à  mémoriser en +, c'est les mêmes que dans la plupart des langages)


    2. Il y a un peu de Convention-Over-Configuration dans Cocoa (KVO, etc) ce qui fait que certains mécanismes sont basés sur le respect de ces conventions justement, donc ne pas les respecter pourrait faire que ces mécanismes ne fonctionnent pas comme attendu dans ce cas


    3. Et enfin, c'est quasi impossible pour un dev qui pratique déjà  Cocoa de lire un code ne respectant pas les conventions, car ça devient tellement naturel que quand on lit "myClass" on pense automatiquement (limite réflexe) que c'est une variable / un objet et pas une classe, du coup ça gène à  la sémantique et compréhension de ton code.



    Sinon tu as essayé MyFramework.MyClass() plutôt que juste MyClass() ? (désolé, impossible pour moi de tester moi-même pour le moment)
  • Joanna CarterJoanna Carter Membre, Modérateur

    Attendez mes petits, Xcode est maintenant téléchargé il ne faut qu'attendre l'installation/démarrage  :o


  • Je vous rejoins sur les conventions d'écriture et la syntaxe.


     


    Je mets juste l'accent sur le fait que je souhaite simplement utiliser une classe Swift dans un framework, puis importer ce framework dans une app pour tenter d'y instancier la classe et appeler sa méthode.


     


    J'en suis pas à  faire une usine à  gaz à  ce stade... (à  moins que ça ne le soit déjà )


     


    J'essaie de faire vite et salement, à  mes dépends. Je vais tenir compte de vos remarques (j'ignorais le Convention-Over-Configuration étant donné que je manipule habituellement du langage de plus haut niveau).


     


    Jusqu'ici personne n'a essayé de coder le résultat que j'attends ? Car malgré ta suggestion Ali, ce n'est toujours pas probant.


  • Joanna CarterJoanna Carter Membre, Modérateur

    Je viens d'essayer plusieurs choses avec ton projet mais sans réussit. Il me paraà®t qu'on manque quelque choses dans la configuration du projet.


     


    Erreur : /../AppDelegate.swift:24:22: Use of unresolved identifier 'TestClass'


  • idomyjobidomyjob Membre
    juillet 2014 modifié #10

    Ne tenez pas compte de mon code sinon. Essayez simplement de réaliser ceci :



    - utiliser une classe Swift dans un framework cocoa touch
    - importer ce framework dans une app single view
    - instancier la classe, appeler sa méthode

  • Joanna CarterJoanna Carter Membre, Modérateur

    C'est exactement ce que j'ai fait. Jusqu'à  ici, je n'arrives pas à  réussir


  • Très bien, mais peut-être qu'à  ce stade ce n'est pas possible ? Ou bien, Swift ne le permet pas ?


  • Joanna CarterJoanna Carter Membre, Modérateur
    juillet 2014 modifié #13

    Enfin !!!!


    Depuis Xcode 6b4, l'accès aux types, méthodes, etc est, par défaut, internal - vu, si on ne marque pas ces choses comme public, ils ne seront pas visibles dehors le module (framework).


    Du coup, si tu écrivais la classe comme ci...



    public class TestClass: NSObject
    {
    public init()
    {
    super.init()


    }

    public func test()
    {
    println("Test")
    }
    }

    ... elle serait visible de l'app.


  • Indeed. Bon en revanche maintenant lorsque je suis dans l'application, que j'instancie, fait appel à  la méthode puis build, je suis confronté à  une nouvelle erreur :


     



     


    dyld: Library not loaded: @rpath/MyFramework.framework/ ... Reason: image not found


  • Joanna CarterJoanna Carter Membre, Modérateur

    C'est comment tu as organisé tes projets ?


     


    Moi j'ai fait un workspace et ajouté les deux projets là  dedans, puis, je modifie le scheme de l'app.


     


    ça se voit dans les images ci-jointes.


     


     


     

  • Je vais essayer cela ce soir. Mais en effet, je n'ai pas travaillé dans un workspace. J'ai créé deux projets indépendants. Merci :-)


  • AliGatorAliGator Membre, Modérateur
    juillet 2014 modifié #17

    Je vais essayer cela ce soir. Mais en effet, je n'ai pas travaillé dans un workspace. J'ai créé deux projets indépendants. Merci :-)

    Ah ben voilà  ne cherche pas. Si tu as 2 projets totalement indépendants, Xcode ne peux pas deviner tout seul les dépendances implicites, autrement dit ne peux pas savoir qu'il faut qu'il compile d'abord le framework avant de pouvoir compiler l'application qui utilise ledit framework.

    Et de plus, les 2 projets ne partagent du coup pas les mêmes dossiers de build (ils ne sont pas compilés dans le même sous-dossier de DerivedData), donc l'application, compilée dans un dossier, ne peut pas simplement trouver le framework, compilé dans un autre, puisque ce sont des projets indépendants.

    L'utilisation d'un workspace est faite pour ça justement, grouper les projets qui travaillent ensemble, pour que Xcode détecte tout seul les dépendances, fasse les build dans le bon ordre et dans un répertoire de build commun pour pouvoir retrouver tout le monde, donc compiler d'abord les frameworks et libs dont vont dépendre ton appli, et compiler l'appli en dernier, et faire en sorte qu'elle retrouve ses petits.

    ---


    PS : Pour information, ce fonctionnement et utilisation des workspaces n'est pas nouveau (ça date facile de Xcode4), donc n'a pas vraiment de lien direct avec Swift. Ca fait un bail que ça existe et qu'on utilise ça dès qu'on a besoin de faire des projets qui dépendent d'autres composants. Comme des frameworks maisons créés pour l'occasion comme tu fais là , ou comme des composants OpenSource que tu voudrais utiliser dans ton projet (c'est comme ça que fonctionne CocoaPods entre autres), etc.

    Tu as toujours ton appli d'un côté dans un projet, les composants tierces dans un projet, et un workspace pour regrouper tous les projets. Swift ou pas Swift, et c'était pareil avant quand on n'avait pas les frameworks pour iOS mais que les libs statiques.

    Je ne sais pas depuis combien de temps j'ai pas créé un xcodeproj tout seul isolé, ça fait des lustres que tout le monde travaille toujours avec les workspaces, car même pour une appli simple tu as forcément rapidement besoin de quelques composants tierces comme MagicalRecord ou AFNetworking ou autre
  • idomyjobidomyjob Membre
    juillet 2014 modifié #18

    Merci pour ces précisions. Je suis novice en la matière, je découvre


     


    J'imagine qu'une fois mon framework réalisé, je pourrais en faire un composant externe distribuable et que d'autres personnes pourront l'intégrer dans leur application du coup ?


     


    Edit : J'en profite pour remercier également Joanna, ça fonctionne bien :-)


     


    Si je souhaite tester l'application sur un device, je suppose qu'une manipulation est à  faire ? Sur le simulateur c'est ok, mais étant donné que j'aurais besoin des notifications, le simulateur risque d'être limité dans son usage.


  • AliGatorAliGator Membre, Modérateur
    juillet 2014 modifié #19
    Sur le principe oui.


    Mais pour instant comme expliqué sur le blog Swift d'Apple et déjà  ici aussi l'ABI (façon dont le binaire est agencé en interne lors de la compilation) est sujette à  changer.

    Et du coup pour la distribution de frameworks il faut mieux distribuer le code que la version compilée de tes frameworks car la version compilée pourrait ne plus être utilisable/compatible si l'ABI à  changé depuis que tu as compilé ton framework.

    Et tout cas jusqu'à  ce que Swift ait un peu d'expérience dans les jambes et que l'ABI soit stabilisée


    Pour + d'infos voir le blog Swift d'Apple avec justement l'article sur le sujet (qui était le tout premier du blog ;-))
  • idomyjobidomyjob Membre
    juillet 2014 modifié #20

    La logique reste la même si je développe en Objective C ? J'entends par là , seule l'écriture du code va changer. Mais l'on reste bien dans un workspace contenant d'un côté le framework et l'autre mon application ?


     


    En fait, mon objectif à  court terme, c'est pouvoir réaliser une démo de la solution que je développe. Je pense que par soucis de compatibilité et distribution, il serait plus convenable de partir sur du code Objective C. Mais travaillant sous les méthodes agiles, c'est pour plus simple d'aller au but par le biais de Swift.


  • AliGatorAliGator Membre, Modérateur
    juillet 2014 modifié #21
    Oui pareil workspace aussi.


    Avec d'un côté dans un des projets du workspace, tu dev l'appli et de l'autre côté dans un autre projet (ou plusieurs si tu as plusieurs composants perso sépares, un projet par lib quoi) du workspace, le framework (ou la lib statique) contenant ton composant.


    Bref comme dans plein d'autres langages et IDE (en Java aussi c'est pareil avec les composants sépares dans des packages distincts, etc) " après tout c'est la base de tout langage orienté objet que d'encapsuler et isoler les composants pour les réutiliser ailleurs sans dépendance/adhérence forte à  l'app.
  • Oui tout à  fait. Je posais cette question plutôt en rapport avec le comportement d'xCode. Je suis plutôt spécialisé dans le développement web. Et on peut indépendamment développer des ressources facilement intégrables à  des projets.


  • AliGatorAliGator Membre, Modérateur
    Bah dans tous les langages c'est pareil, la seule exception est dans les langages non-compilés comme les langages de scripting ou le web, où il n'y a pas besoin de compiler les composants avant de pouvoir compiler l'application qui les utilise, puisque tout est en langage interprété et non compilé.

    Mais même en Web, si tu fais des composants, par exemple si tu utilises Symphony et que tu utilises Composer pour importer tes composants (.phar, etc), il faut bien qu'ils aient été packagés avant que l'appli puisse les récupérer. Pareil si tu fais du Java avec un serveur Tomcat, faut bien que tu compiles tes .jar ou .war et les mette sur le serveur pour que ton application web puisse utiliser les composants packagés.

    Par contre si tu fais du web "en dillétante" façon quelques pages HTML, PHP, CSS et JS, sans gestion de dépendances et sans outil de gestion de composants, ouais là  tu mets tout ensemble en mode interprété ça marche, mais c'est parce qu'il n'y a pas de compilation, donc pas non plus de vérification quand tu lances le code de l'appli que le code des composants qu'elle est sensée utilisée est bien compatible et fonctionne. Si c'est pas le cas, c'est au runtime que tu t'en rendras compte, en testant, puisqu'il n'y a pas de compilation... mais le problème aura été le même, une appli qui essaye de se lancer sans que l'un de ses composants tierce soit prêt ou fonctionnel, c'est juste que le problème est silencieusement ignoré et que tu t'en rends compte trop tard dans le cas de langages interprétés ;)
  • idomyjobidomyjob Membre
    juillet 2014 modifié #24

    C'est exactement ça :-) J'utilise un framework MVC (PhalconPHP) qui tourne sur un serveur Apache avec une base MySQL derrière. En effet, la logique veut que sur un environnement de dev/pre-prod, tu sois suffisamment vigilant pour qu'une erreur n'arrive pas en prod. Cela n'empêche pas ce framework de cloisonner un peu son fonctionnement : utilisation d'espace de noms, déclaration de modules etc. et d'intercepter de mauvais comportements. Mais ça reste moins performant que de compiler.


     


    Pour en revenir au sujet, que ce soit en objective c ou en swift côté framework, si je souhaite tester ça sur mon iPhone, il y a-t-il une manipulation particulière à  effectuer ?



    dyld: Library not loaded: @rpath/MyFramework.framework/MyClass
      Referenced from: /private/var/mobile/Containers/Bundle/Application/1876BA80-3ED6-4248-BB6E-7E95CEC1B164/App.app/App
      Reason: image not found

  • Joanna CarterJoanna Carter Membre, Modérateur
    juillet 2014 modifié #25

    Pour installer le framework dans l'app bundle sur un appareil, il faut ajouter @rpath/../Frameworks pour le "Build Setting" INSTALL_PATH.


     


    La problème avec les frameworks Swift distribuables est, actuellement, que le format binaire pourrait changer pendant les prochaines années pendant que le compilateur " mûrisse ". 


     


    Côté workspaces - je suis en train de développer une suite d'apps qui fonctionneront sur OS X et iOS. Dans le même workspace j'ai sept projets : 


     


    1. avec deux cibles - lib statique pour iOS et framework pour OS X


    2. Framework pour OS X qui utilise framework 1


    3. QuickLook framework pour framework 3 qui utilise frameworks 1 & 2


    4. App OS X qui utilise framework 1


    5. Deuxième app OS X qui utilise framework 1


    6. Troisième app OS X qui utilise frameworks 1, 2 & 3


    7. App iPad qui utilise lib statique 1


     


    Sans un workspace, ça aurait été un vrai cauchemar 


  • Ou puis-je apprendre / connaitre ses subtilités du déploiement ? Connaissez-vous un bon tuto Objective C pour réaliser un framework simple. Du stade de l'écriture, au stade de l'intégration in-app + déploiement ? Merci


  • Joanna CarterJoanna Carter Membre, Modérateur
    juillet 2014 modifié #27

    Moi, j'ai souffert beaucoup dans la recherche des infos sur ça. Le plupart que j'ai trouvé était sur Stack Overflow par Google  :(


     


    Peut-être, ça irait mieux de demander plus de renseignements ici.


     


    Tu habites à  Saint-Grégoire près de Rennes ? Je assisterai la réunion CocoaHeads en septembre


  • En effet, je suis bien à  proximité de Rennes.


     


    D'après ce que j'ai pu voir sur le mur de la cantine c'est le 26 septembre ? Je tâcherais de m'y rendre :-) C'est vrai que cela semble assez douloureux au départ.


     


    Je vais en profiter pour éditer mon message initial, afin d'y apporter un début de solution.


  • Concernant l'erreur que je soulevais quelques posts plus haut sur le fait de tester l'application sur un vrai device, il suffit en fait d'aller sur les préférences de l'application. Dans l'onglet "General", vous retrouvez l'option "Embedded Binaries". Il suffit ensuite d'y associer le framework.


Connectez-vous ou Inscrivez-vous pour répondre.