Warning URLSession pour tests

Bonjour à tous,

Je suis sur un projet dans ma formation et j’ai un warning qui apparaît et je ne trouve pas de solution pour l’éviter…

C’est dans mon fichier URLSessionFake qui créé un sessionFake pour mes tests des appels réseau de mon projet.
ça me dit : ‹ init() › was deprecated in iOS 13.0: Please use +[NSURLSession sessionWithConfiguration:] or other class methods to create instances.

Lors de mes recherches, je ne trouve pas de résultats qui m’aident vraiment. Pas ou peu de documentation.
On me dit de passer (comme demandé dans les consignes) à iOS11 dans iOS Deployement Target mais ça ne change rien, le warning est toujours présent.

Si quelqu’un a déjà rencontré ce warning et qu’il a une solution…je suis preneur.

Voici le lien de mon projet sur github : https://github.com/ArnaudKif/P9-Le-Baluchon

Merci d’avance et bon courage à tous.

Bonjour @ArnaudK,

Je ne suis pas du tout expert dans le sujet, mais d’après ce que j’ai compris, les URLSession ont une gestion particulière. Effectivement, l’init n’est pas comme les class ‹ classiques ›.
J’ai trouvé plusieurs choses dans la documentation (effectivement, peu de choses), que peut-être tu n’as pas vu. D’après ce que je comprends, tu peux charger une URLSession par défaut, qui gère les data, responsables et error directement). Même la doc précise que c’est complexe, ceci dit… J’ai trouvé quelques pages, notamment celle-ci :

https://developer.apple.com/documentation/foundation/url_loading_system/fetching_website_data_into_memory

D’après ton code, je pense que le concept est la même dans cette doc, mais avec une autre méthode, particulière à URLSession pour ce faire. De ce que je vois, beaucoup de choses sont en fait gérées directement par la classe URLSession.

Apparemment, l’URLSession se repère avec une URLSessionConfiguration qui définit ton init (en gros).

https://developer.apple.com/documentation/foundation/url_loading_system

J’ai ça aussi pour une URLSessionConfiguration :
https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1411560-default

https://developer.apple.com/documentation/foundation/urlsession/1411474-init

Je ne sais pas si cela va t’aider plus ceci dit… Bon courage.

Merci pour ton aide. J’avais regardé sur la documentation mais j’avais du mal à voir comment l’utiliser… Je suis encore novice dans le développement :wink:.
Je suis en train de voir pour utiliser un URLprotocol « test » à la place de ma URLSessionFake…ce qui m’évitera la partie où se trouve mon warning.
La plupart des tests se valident pour le moment…j’attend de tout finir pour vérifier si ça marche comme ça…
Merci @+

Salut Bob,
J’ai 3 mentorés qui passent en évaluation sous quelques jours, avec chacun, un même projet implémentant 3 web services. Tout est nickel, taux de coverage de 98%, sauf qu’il reste les warnings objet de ce post, ci-dessous la copie d’écran :


Avec cette chose pas évidente :

'init()' was deprecated in iOS 13.0: Please use +[NSURLSession sessionWithConfiguration:] or other class methods to create instances

Je n’arrive pas à suivre le conseil de Tim : « Please use +[NSURLSession sessionWithConfiguration:] or other class methods to create instances » après des heures de recherches et de tentatives infructueuses, alors j’en appelle aux costauds de Swift v16, pour savoir comment répondre à cette dépréciation (autrement qu’en re-codant tout via des protocoles), ce qui serait très compliqué pour les élèves.

Ci-dessous le même code, autrement qu’en photo, mais sans les warnings.

import Foundation

class WeatherURLSessionFake: URLSession {

    var data: Data?
    var response: URLResponse?
    var error: Error?
    
    init(data: Data?, response: URLResponse?, error: Error?) {
        self.data = data
        self.response = response
        self.error = error
    }
    
    override func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
        let task = WeatherURLSessionDataTaskFake()
        task.completionHandler = completionHandler
        task.data = data
        task.urlResponse = response
        task.responseError = error
        return task
    }
    
    override func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
        let task = WeatherURLSessionDataTaskFake()
        task.completionHandler = completionHandler
        task.data = data
        task.urlResponse = response
        task.responseError = error
        return task
    }
}

class WeatherURLSessionDataTaskFake: URLSessionDataTask {
 
    var completionHandler: ((Data?, URLResponse?, Error?) -> Void)?
    
    var data: Data?
    var urlResponse: URLResponse?
    var responseError: Error?
    
    override func resume() {
        completionHandler?(data, urlResponse, responseError)
    }
    
    override func cancel() {
        // not applicable
    }
}


Un grand merci. Je reste à disposition si nécessaire. :unamused:

Salut Jean Michel,
Quand j’ai fais ce sujet, je n’avais pas trouvé d’autre solution qu’un protocole pour régler ce warning. Tes élèves ont simplement suivi le cours OCR mais malheureusement il est aussi déprécié (comme beaucoup d’ailleurs):stuck_out_tongue_winking_eye:.
N’hésite pas si tu as besoin de mon projet pour les inspirer :+1:.
Bon courage

Bonjour Arnaud,
Merci pour ta réponse, et bravo pour être toujours vigilant sur ce forum.
Effectivement, toutes les parties vidéos (quand Ambroise dit : « je vous montre ça directement dans le code … ») ont été reprises en mai 2022, avec Amandine Cousin. J’ai testé moi-même, au millimètre près, et j’ai obtenu immédiatement 98% de couverture. J’ai dit aux élèves, de faire comme moi, et ils y sont parvenus aussi. Nickel, propre, le tour semblait bien joué, sauf … sauf … ces warnings résiduels de dernière minute. Pourtant ces warnings apparaissent à la 8ème minute de la vidéo du cours (Préparez votre double - Lancez des appels réseaux en iOS - OpenClassrooms) puis disparaissent par magie. Perso, je soupçonne une désactivation des warnings et un petit montage adhoc, vite fait, bien fait. Mais bon?! En tout cas c’est pas cool pour les élèves. Comme tu sais ceux-ci sont toujours en retard, ils payent cher pour devoir se former eux-mêmes, et devoir reconsidérer tout leur code de tests les afflige. J’essaye de résoudre ça moi-même pour les aider le plus rapidement possible, mais je rame. D’ou mon appel à l’aide.
Ci dessous, la photo issue du cours :


Qui est le plus haut gradé en Swift ici? Je pense à @mbritto, bien sûr, alors au secours Maxime. Bien sûr, tous ceux qui auraient une solution radicale sont les bienvenus. Merci à tous. Je reste à l’écoute. :thinking:

Il semblerait que les warnings puissent venir d’un super.init à rajouter comme indiqué là : https://stackoverflow.com/questions/68756347/url-session-initilazition-issue
Super, sauf que cela engendre une vraie erreur rouge : `Must call a designated initializer of the superclass ‹ URLSession ›
Problème : Trouver ce « designated initializer of the superclass URLSession »
Peut-être que cette nouvelle erreur sera davantage connue et corrigeable?!
Merci à tous. Je reste à l’écoute.

SUITE DIMANCHE 13 NOVEMBRE 17H35

Salut Bob,

J’avance, j’avance (vers le ravin?! Peut-être !) mais j’avance.

J’ai posté le message suivant :

J’ai pu implémenter (non sans mal) la réponse cochée en vert et wrapper NSURSession dans une sou-classe. Et le résultat est celui attendu

Thus you will have more control and customization options.

Au passage, comme dans toute classe qui hérite, il faut prévoir un super.init de la classe parente. Après bien tracas, le super.init qui a marché est le suivant :
super.init(monParamètreWrapper: URLSession(configuration: .default))
pour éviter ce message d’erreur :annot convert value of type 'URLSessionDataTask.Type' to expected argument type 'URLSessionDataTask'

Bravo ! J’ai voulu reconduire le même principe avec le code ci-dessous :

// wrapper de la classe native URLSessionDataTask
class WeatherURLSessionDataTaskFakeWrapper {

     let weatherURLSessionDataTaskFakeWrapper: URLSessionDataTask
     
     init(weatherURLSessionDataTaskFakeWrapper: URLSessionDataTask) {
          self.weatherURLSessionDataTaskFakeWrapper = weatherURLSessionDataTaskFakeWrapper
     }
}

// sous classe de mon wrapper 
class WeatherURLSessionDataTaskFake: WeatherURLSessionDataTaskFakeWrapper {
     
     var completionHandler: ((Data, URLResponse, Error) -> Void)?
     
     var data: Data
     var urlResponse: URLResponse
     var responseError: Error
     
     init( data: Data, urlResponse: URLResponse, responseError: Error) {
          
          self.data = data
          self.urlResponse = urlResponse
          self.responseError = responseError
          super.init(weatherURLSessionDataTaskFakeWrapper: URLSessionDataTaskXXXXXXXXXXXXX)
      }
     
     func resume() {
          completionHandler?(data, urlResponse, responseError)
     }
     
     func cancel() {
          // not applicable
     }
}

et c’est au niveau de URLSessionDataTaskXXXXXXXXXXXXX que je retrouve mon message :
Cannot convert value of type 'URLSessionDataTask.Type' to expected argument type 'URLSessionDataTask'

Je cherche depuis des heures et des heures (bon, je sais, je suis peut-être mauvais :joy: dans ce cas, je mérite davantage d’aide) à trouver les bon argument à renvoyer à la classe mère pour initialiser sa propriété :

let weatherURLSessionDataTaskFakeWrapper: URLSessionDataTask

A vot’ bon coeur M’ieux Dames.

Je reste à disposition avec plaisir.

Salut Bob,
Je lutte toujours, mais toujours sans solution.

Les solutions de ce post ne marchent pas : ios - URL session initilazition issue - Stack Overflow

D’autres faisceaux convergent vers ce post : Subclassing factory methods of URLSession in Swift lequel conclu :

According to Swift Language Guide rule 1 for Automatic Initializer Inheritance:

* *If your subclass doesn’t define any designated initializers, it * *automatically inherits all of its superclass designated initializers.* *

So, technically MyURLSession should inherit all designated initializers, but it doesn’t, and it only inherits init() from NSObject. Looking into documentation of URLSession:

public /not inherited/ init(configuration: URLSessionConfiguration)

There is nothing visible aside from the comment, that it is not inherited. Looking into it’s Objective-C definitions, we can notice that they are not initializers, but rather factory methods, which are imported into Swift as inits.

```
*+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration )configuration;
*+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration

So the question is, how to override and/or correctly call these methods of superclass in initialization?

Comment implémenter ce conseil de Tim :
Please use +[NSURLSession sessionWithConfiguration:] or other class methods to create instances

Si quelqu’un est assez pointu pour me commenter ça, je prends, car pour l’instant je ne sais pas faire. Je rappelle que le code marche super bien, c’est juste une question d’éradiquer ces warnings jaunes non bloquants (mais éliminatoires à l’examen)

Merci. Je cherche et reste à l’écoute.

--------------------------------------------------------------------------------------------------------
DIMANCHE 13 NOVEMBRE 17H42 (j’écris là suite à la limitation de 3 réponses)
-----------------------------------------------------------------------------------------------------------

Salut Bob,

J’avance, j’avance (vers le ravin?! Peut-être !) mais j’avance.

J’ai posté le message suivant :

qui m’a redirigé vers :

J’ai pu implémenter (non sans mal) la réponse cochée en vert et wrapper NSURSession dans une sous-classe. Et le résultat est celui attendu

Thus you will have more control and customization options.

Au passage, comme dans toute classe qui hérite, il faut prévoir un super.init de la classe parente. Après bien tracas, le super.init qui a marché est le suivant :
super.init(monParamètreWrapper: URLSession(configuration: .default))
pour éviter ce message d’erreur :
Cannot convert value of type 'URLSessionDataTask.Type' to expected argument type 'URLSessionDataTask'

Bravo ! J’ai voulu reconduire le même principe avec le code ci-dessous :

// wrapper de la classe native URLSessionDataTask
class WeatherURLSessionDataTaskFakeWrapper {

     let weatherURLSessionDataTaskFakeWrapper: URLSessionDataTask
     
     init(weatherURLSessionDataTaskFakeWrapper: URLSessionDataTask) {
          self.weatherURLSessionDataTaskFakeWrapper = weatherURLSessionDataTaskFakeWrapper
     }
}

// sous classe de mon wrapper
class WeatherURLSessionDataTaskFake: WeatherURLSessionDataTaskFakeWrapper {
     
     var completionHandler: ((Data, URLResponse, Error) -> Void)?
     
     var data: Data
     var urlResponse: URLResponse
     var responseError: Error
     
     init( data: Data, urlResponse: URLResponse, responseError: Error) {
          
          self.data = data
          self.urlResponse = urlResponse
          self.responseError = responseError
          super.init(weatherURLSessionDataTaskFakeWrapper: 
URLSessionDataTaskXXXXXXXXXXXXX)
      }
     
     func resume() {
          completionHandler?(data, urlResponse, responseError)
     }
     
     func cancel() {
          // not applicable
     }
}

et c’est au niveau de URLSessionDataTaskXXXXXXXXXXXXX que je retrouve mon message :

Cannot convert value of type 'URLSessionDataTask.Type' to expected argument type 'URLSessionDataTask'

Je cherche depuis des heures et des heures (bon, je sais, je suis peut-être mauvais :thinking: dans ce cas, je mérite davantage d’aide :joy: ) à trouver la meilleure forme de l’argument à renvoyer à la classe mère pour initialiser sa propriété de type URLSessionDataTask

Je rappelle qu’au niveau URLSession (voir le début) l’argument :
super.init(monParamètreWrapper: URLSession(configuration: .default))
a marché.

A vot’ bon coeur M’ssieux Dames.

Je reste à disposition avec plaisir.

Nouvelle étape :

Swift - Subclassing URLSessionDataTask - Which super.init(DataTaskFakeWrapper: URLSessionDataTask ???) Apply?

--------------------------------------------------------------------------------------------------------
MARDI 15 NOVEMBRE 15H30 - EPILOGUE
-----------------------------------------------------------------------------------------------------------

Salut Bob,J’ai résolu (et compris) le dernier problème de comment trouver la meilleure forme du super.init à renvoyer à la classe mère

Du coup, j’ai pu cloner URLSession et URLSessionTask (et non pas créer des sous classes), ça m’a beaucoup appris aux travers des ressources. Toutefois, mon problème de tests unitaires n’est pas résolu, car le web service travaille avec les types URLSession et URLSessionTask et non avec des types clonés URLSessionFake et URLSessionTaskFake (… de Charybde en Scylla).

Mais j’arrête là et je jette l’éponge :exploding_head:

Au départ, je voulais juste comprendre et traiter ce simple anodin warning :

'init()' was deprecated in iOS 13.0: Please use +[NSURLSession sessionWithConfiguration:] or other class methods to create instances

dans un code opérationnel basé sur de l’overriding de URLSession et URLSessionTask, et vers quoi Apple nous renvoyait, mais je n’ai pas réussi. :pensive:

Bien à vous.