web-dev-qa-db-fra.com

Supprimer println () pour la version iOS Swift

Je voudrais ignorer globalement tous les appels println() dans mon code Swift si je ne suis pas dans une construction Debug. Je ne trouve pas d'instructions détaillées étape par étape pour cela et apprécierais des conseils. Est-il possible de le faire globalement ou dois-je entourer chaque println() par des instructions #IF DEBUG/#ENDIF?

69
Nate Birkholz

Comme indiqué, je suis un étudiant et j'ai besoin que les choses soient définies un peu plus clairement pour pouvoir les suivre. Après de nombreuses recherches, la séquence que je devais suivre était la suivante:

Cliquez sur le nom du projet en haut du navigateur de fichiers, à gauche de la fenêtre du projet Xcode. Cette ligne porte le nom du projet, le nombre de cibles de génération et la version du SDK iOS.

Choisissez l'onglet Paramètres de construction et faites défiler jusqu'à la section " Compilateur rapide - Indicateurs personnalisés " située près du bas. Cliquez sur la flèche vers le bas en regard de Autres drapeaux pour agrandir la section.

Cliquez sur la ligne Debug pour le sélectionner. Placez le curseur de votre souris sur le côté droit de la ligne et double-cliquez dessus. Une liste apparaîtra. Cliquez sur le bouton + en bas à gauche de la liste pour ajouter une valeur. Un champ de texte deviendra actif.

Dans le champ de texte, entrez le texte -D DEBUG et appuyez sur Retour pour valider la ligne.

Ajoutez un nouveau fichier Swift à votre projet. Vous allez vouloir créer une classe personnalisée pour le fichier, entrez donc le texte comme suit:

class Log {

  var intFor : Int

  init() {
    intFor = 42
   }

  func DLog(message: String, function: String = __FUNCTION__) {
    #if DEBUG
      println("\(function): \(message)")
    #endif
  }
}

J'avais du mal à faire accepter le cours aujourd'hui par Xcode, donc l'init peut être un peu plus lourd que nécessaire. 

Vous devez maintenant référencer votre classe personnalisée dans toute classe dans laquelle vous souhaitez utiliser la nouvelle fonction personnalisée à la place de println(). Ajoutez ceci en tant que propriété dans chaque classe applicable:

   let logFor = Log()

Vous pouvez maintenant remplacer toutes les instances de println() par logFor.DLog(). La sortie inclut également le nom de la fonction dans laquelle la ligne a été appelée.

Notez que dans les fonctions de classe, je ne pouvais pas appeler la fonction à moins de faire une copie de la fonction en tant que fonction de classe dans cette classe, et println() est également un peu plus flexible avec l'entrée; dans mon code.

16
Nate Birkholz

Le moyen le plus simple est de placer votre propre fonction globale devant la println de Swift:

func println(object: Any) {
    Swift.println(object)
}

Quand il est temps d'arrêter de vous connecter, commentez le corps de cette fonction:

func println(object: Any) {
    // Swift.println(object)
}

Ou vous pouvez le rendre automatique en utilisant un conditionnel:

func println(object: Any) {
    #if DEBUG
        Swift.println(object)
    #endif
}

EDITDans Swift 2.0, println est remplacé par print. Malheureusement, il a maintenant un premier paramètre variadique; c’est cool, mais cela signifie que vous ne pouvez pas le remplacer facilement car Swift n’a pas d’opérateur "splat", vous ne pouvez donc pas transmettre de code variadique (il ne peut être créé que littéralement). Mais vous pouvez créer une version réduite qui fonctionne si, comme ce sera généralement le cas, vous n'imprimez qu'une seule valeur:

func print(items: Any..., separator: String = " ", terminator: String = "\n") {
    Swift.print(items[0], separator:separator, terminator: terminator)
}

Dans Swift 3, vous devez supprimer l’étiquette externe du premier paramètre:

func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
    Swift.print(items[0], separator:separator, terminator: terminator)
}
94
matt

Mise à jour pour Swift 4.x:

Swift 2.0/3.0 et Xcode 7/8 étant désormais hors de la version bêta, la façon dont vous désactivez la fonction d'impression dans les versions de version a été modifiée.

Certains points importants mentionnés par @matt et @Nate Birkholz ci-dessus sont toujours valables.

  1. La fonction println() a été remplacée par print()

  2. Pour utiliser la macro #if DEBUG, vous devez définir le "Compilateur Swift - Indicateurs personnalisés - Autres indicateurs" de manière à contenir la valeur -D DEBUG.

  3. Je recommanderais de remplacer la fonction Swift.print() dans la portée globale afin que vous puissiez utiliser la fonction print() comme d'habitude dans votre code, mais cela supprimera la sortie pour les versions non déboguées. Voici une signature de fonction que vous pouvez ajouter au niveau global pour le faire dans Swift 2.0/3.0:

    func print(items: Any..., separator: String = " ", terminator: String = "\n") {
    
        #if DEBUG
    
        var idx = items.startIndex
        let endIdx = items.endIndex
    
        repeat {
            Swift.print(items[idx], separator: separator, terminator: idx == (endIdx - 1) ? terminator : separator)
            idx += 1
        }
        while idx < endIdx
    
        #endif
    }
    

Remarque: Nous avons défini le séparateur par défaut sur un espace et le terminateur par défaut sur une nouvelle ligne. Vous pouvez configurer cela différemment dans votre projet si vous le souhaitez.

J'espère que cela t'aides.

Mettre à jour:

Il est généralement préférable de placer cette fonction dans la portée globale afin qu'elle soit placée devant la fonction print de Swift. Je trouve que la meilleure façon d’organiser cela consiste à ajouter un fichier d’utilitaire à votre projet (tel que DebugOptions.Swift) dans lequel vous pouvez placer cette fonction au niveau global.

A partir de Swift 3, l'opérateur ++ sera obsolète. J'ai mis à jour le fragment ci-dessus pour refléter ce changement.

42
Glavid

Le problème avec toutes ces approches, y compris la mienne, est qu’elles ne suppriment pas la surcharge liée à l’évaluation des arguments print. Peu importe lequel vous utilisez, cela va coûter cher:

print(myExpensiveFunction())

La seule solution décente consiste à encapsuler l'appel d'impression dans la compilation conditionnelle (supposons que DEBUG soit défini uniquement pour les versions de débogage):

#if DEBUG
print(myExpensiveFunction())
#endif

Cela, et seulement cela, empêche myExpensiveFunction d'être appelé dans une version release.

Cependant, vous pouvez repousser l'évaluation d'un niveau en utilisant autoclosure . Ainsi, vous pouvez réécrire ma solution (il s’agit de Swift 3) comme suit:

func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") {
    #if DEBUG
    Swift.print(item(), separator: separator, terminator: terminator)
    #endif
}

Cela résout le problème dans le cas où vous imprimez une seule chose, ce qui est généralement vrai. C'est parce que item() n'est pas appelé en mode release. print(myExpensiveFunction()) cesse donc d'être coûteux, car l'appel est encapsulé dans une fermeture sans être évalué, et en mode de libération, il ne sera pas évalué du tout.

31
matt

Voici une fonction que j'utilise et qui fonctionne parfaitement dans Swift 3:

func gLog<T>( _ object: @autoclosure() -> T, _ file: String = #file, _ function: String = #function, _ line: Int = #line)
    {
    #if DEBUG
        let value = object()
        let stringRepresentation: String

        if let value = value as? CustomDebugStringConvertible
            {
            stringRepresentation = value.debugDescription
            }
        else if let value = value as? CustomStringConvertible
            {
            stringRepresentation = value.description
            }
        else
            {
            fatalError("gLog only works for values that conform to CustomDebugStringConvertible or CustomStringConvertible")
            }

        let fileURL = NSURL(string: file)?.lastPathComponent ?? "Unknown file"
        let queue = Thread.isMainThread ? "UI" : "BG"
    let gFormatter = DateFormatter()
    gFormatter.dateFormat = "HH:mm:ss:SSS"
        let timestamp = gFormatter.string(from: Date())

        print("✅ \(timestamp) {\(queue)} \(fileURL) > \(function)[\(line)]: " + stringRepresentation + "\n")
    #endif
    }

Voici un exemple de la sortie qu'il génère:

 screenshot of output

Explication:

  • la coche verte est utilisée pour vous permettre de voir rapidement vos messages d'impression (gLog) dans la console, où ils peuvent parfois se perdre dans une mer d'autres messages

  • l'horodatage

  • le fil sur lequel il est exécuté - dans mon cas, il s’agit du MainThread (que j’appelle UI), ou pas du MainThread (que j’appelle BG, pour le fil d’arrière-plan)

  • le nom du fichier dans lequel se trouve le message gLog

  • la fonction dans le fichier dans laquelle se trouve le message gLog

  • le numéro de ligne du message gLog

  • le message gLog que vous souhaitez imprimer

J'espère que cela sera utile à quelqu'un d'autre!

9
Gene Loparco

Testé avec Swift 2.1 & Xcode 7.1.1

Il existe un moyen simple d'exclure toutes les instructions d'impression des versions, une fois que vous savez que les fonctions empty sont supprimées par le compilateur Swift.

Note latérale: à l'ère d'Objective-C, il existait un pré-analyseur qui pouvait être utilisé pour supprimer les instructions NSLog avant le compilateur, comme décrit dans ma réponse ici . a un pré-analyseur cette approche n’est plus valide.

Voici ce que j'utilise aujourd'hui comme fonction de journalisation avancée et facilement configurable, sans avoir à vous soucier de la supprimer dans les versions. De plus, en définissant différents indicateurs de compilateur, vous pouvez modifier les informations consignées selon vos besoins.

Vous pouvez modifier la fonction au besoin, toute suggestion d'amélioration est la bienvenue!

// Gobal log() function
//
// note that empty functions are removed by the Swift compiler -> use #if $endif to enclose all the code inside the log()
// these log() statements therefore do not need to be removed in the release build !
//
// to enable logging
//
// Project -> Build Settings -> Swift Compiler - Custom flags -> Other Swift flags -> Debug
// add one of these 3 possible combinations :
//
//      -D kLOG_ENABLE
//      -D kLOG_ENABLE -D kLOG_DETAILS
//      -D kLOG_ENABLE -D kLOG_DETAILS -D kLOG_THREADS
//
// you can just call log() anywhere in the code, or add a message like log("hello")
//
func log(message: String = "", filePath: String = #file, line: Int = #line, function: String = #function) {
            #if kLOG_ENABLE

            #if kLOG_DETAILS

            var threadName = ""
            #if kLOG_THREADS
                threadName = NSThread.currentThread().isMainThread ? "MAIN THREAD" : (NSThread.currentThread().name ?? "UNKNOWN THREAD")
                threadName = "[" + threadName + "] "
            #endif

            let fileName = NSURL(fileURLWithPath: filePath).URLByDeletingPathExtension?.lastPathComponent ?? "???"

            var msg = ""
            if message != "" {
                msg = " - \(message)"
            }

            NSLog("-- " + threadName + fileName + "(\(line))" + " -> " + function + msg)
        #else
            NSLog(message)
        #endif
    #endif
}

Voici où vous définissez les drapeaux du compilateur:

 enter image description here

Un exemple de sortie avec tous les drapeaux ressemble à ceci:

   2016-01-13 23:48:38.026 FoodTracker[48735:4147607] -- [MAIN THREAD] ViewController(19) -> viewDidLoad() - hello

Le code avec le log () ressemble à ceci:

    override func viewDidLoad() { log("hello")
    super.viewDidLoad()

   // Handle the text field's user input through delegate callbacks
   nameTextField.delegate = self
}
8
Ronny Webers

XCode 8 introduit quelques nouveaux paramètres de construction .
En particulier, une référence à Active Compilation Conditions fait de manière similaire ce que les paramètres Other Flags ont fait.

"Conditions de compilation actives" est un nouveau paramètre de construction permettant de transmettre des indicateurs de compilation conditionnels au compilateur Swift.

Selon XCode 8 (testé en 8.3.2), vous obtiendrez ceci par défaut:

 enter image description here

Donc, sans aucune configuration, vous pouvez écrire ce qui suit:

#if DEBUG
    print("⚠️ Something weird happened")
#endif

Je vous recommande fortement de créer une classe/structure/fonction qui englobe cette logique de journalisation si vous utilisez cette approche de manière intensive. Vous voudrez peut-être prolonger cela plus tard.

6
Javier Cadiz

Encore plus simple, après avoir vérifié que -D DEBUG est défini pour les paramètres de construction OTHER_Swift_FLAGS Debug:

#if !DEBUG
    func println(object: Any) {}
    func print(object: Any){}
#endif

Dans Swift 2/Xcode 7, vous n’avez plus besoin/n’utilisez plus println mais vous pouvez ajouter des lignes pour:

func print(_ items: Any..., separator separator: String = default, terminator terminator: String = default)
4
Rivera

Swift 4 Xcode 10.0

peut-être que tu pourrais utiliser ça

func dPrint(_ message: @autoclosure () -> Any) {
    #if DEBUG
    print(message())
    #endif
}

La raison d'utiliser @autoclosure est que si vous transmettez une fonction en tant que paramètre de message, la fonction sera appelée uniquement en mode débogage, ce qui affectera les performances.

contrairement à la fonction Swift.print(_ items: Any..., separator: String = default, terminator: String = default), ma solution ne comporte qu'un seul paramètre, car dans la plupart des cas, nous ne transmettons pas plusieurs paramètres, car la fonction print affiche uniquement les informations dans la console. Nous pouvons simplement convertir les paramètres en String: "\(param1)"+"\(param2)", à droite? espère que tu aimes ma solution

2
Jiangshi Fresh

Swift 4.2

Le code ci-dessous fonctionne parfaitement pour moi:

func print(_ items: Any...) {
    #if DEBUG
    items.forEach { item in
        Swift.print(item)
    }
    #endif
}

Imprimer chaque élément de cette manière supprime les crochets de tableau agaçants autour des instructions d'impression :)

2
Trev14

Ma solution est d'utiliser ce code dans AppDelegate avant le cours.

// Disable console log in live app
#if !Arch(x86_64) && !Arch(i386)
    public func debugPrint(items: Any..., separator: String = " ", terminator: String = "\n") {

    }
    public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {

    }
#endif

class AppDelegate: UIResponder, UIApplicationDelegate {
// App Delegate Code 

}
0
Varun Naharia

Vous pouvez définir debug_println dont le contenu serait approximativement:

#if DEBUG
  println()
#endif
0
Ian MacDonald

pour ma solution je le simplifie

import UIKit

class DLog: NSObject {

   init(title:String, log:Any) {
       #if DEBUG
           print(title, log)
       #endif

   }

}

puis pour le montrer il suffit d'appeler 

_ = DLog(title:"any title", log:Any)