2016-12-15 1 views
0

J'utilise un completionHandler dans cette fonction, mais il est imbriqué dans plusieurs boucles for (ci-dessous). Le problème est le gestionnaire où il est maintenant appelé chaque fois que la boucle dans laquelle il s'exécute, alors que je veux seulement que le gestionnaire passe dans le Set lorsque la fonction entière a terminé le traitement. Si je le place en dehors de la boucle, il est appelé trop tôt et est vide. Que devrais-je faire ici?Où placer un completionHandler à l'intérieur des boucles?

En ce moment, lorsque j'imprime à la console pour le tester imprime: Set article 1 Set article 1, 2 Set point 1, 2, 3, etc.

struct RekoRequest { 




    public func getRekos(rekoType: rekoCategory, handler: @escaping (Set<String>) -> Void) { 

     var urls = [NSURL]() 
     var IDs = Set<String>() 


     TwitterRequest().fetchTweets(searchType: "things") { result in 


      guard let tweets = result as? [TWTRTweet] else {print("Error in getRekos receiving tweet results from TwitterRequest.fetchTweets"); return} 

      for tweet in tweets { 





       let types: NSTextCheckingResult.CheckingType = .link 
       let detector = try? NSDataDetector(types: types.rawValue) 
       guard let detect = detector else { print("NSDataDetector error"); return } 

       let matches = detect.matches(in: text, options: .reportCompletion, range: NSMakeRange(0, (text.characters.count))) 



       for match in matches { 


        if let url = match.url { 

         guard let unwrappedNSURL = NSURL(string: url.absoluteString) else {print("error converting url to NSURL");return} 

         //Show the original URL 
         unwrappedNSURL.resolveWithCompletionHandler { 

          guard let expandedURL = URL(string: "\($0)") else {print("couldn't covert to expandedURL"); return} 


          guard let urlDomain = expandedURL.host else { print("no host on expandedURL"); return } 

          switch urlDomain { 


          case "www.somesite.com": 

           let components = expandedURL.pathComponents 

           for component in components { 
            if component == "dp" { 
             guard let componentIndex = components.index(of: component) else {print("component index error"); return} 
             let IDIndex = componentIndex + 1 
             let ID = components[IDIndex] 

             //Filter out Dups and add to Set 
             IDs.insert(ID) 


             handler(IDs) 

             print(ID) //this prints multiple sets of IDs, I only want one when the function is finished completely 

            } 
           } 

           break; 

          default: 
           break; 
          } 
         } 


        } else { print("error with match.url") } 

       } //for match in matches loop 




      } //for tweet in tweets loop 


     } 
    } 







} 


// Create an extension to NSURL that will resolve a shortened URL 
extension NSURL 
{ 
    func resolveWithCompletionHandler(completion: @escaping (NSURL) -> Void) 
    { 
     let originalURL = self 
     let req = NSMutableURLRequest(url: originalURL as URL) 
     req.httpMethod = "HEAD" 

     URLSession.shared.dataTask(with: req as URLRequest) 
     { 
      body, response, error in completion(response?.url as NSURL? ?? originalURL) 
      } 
      .resume() 
    } 
} 
+1

pourquoi ne pas mettre le gestionnaire d'achèvement après la boucle? –

+0

Quand je le mets après la boucle, j'obtiens un ensemble vide – GarySabo

+0

@GarySabo Où insérez-vous dans l'ensemble 'IDs'? Est-ce que 'setOfIDs' est supposé être' IDs'? –

Répondre

0

Appelez votre gestionnaire d'achèvement après la pour la boucle.

for component in components { 
    if component == "dp" { 
     ... 
    } 
} 
handler(IDs) 

Important: Le handler doit être appelé en dehors de la boucle, mais au sein la TwitterRequest().fetchTweets() fermeture arrière.


options de gestion du vide un ensemble

Votre IDs sont en cours d'initialisation à un ensemble vide. Ce n'est qu'après avoir rencontré certaines conditions dans votre boucle for que des valeurs sont insérées dans cet ensemble. Si ces conditions ne sont pas remplies, alors votre jeu IDs sera vide.

Si cela n'est pas souhaitable, vous devrez soit apporter des modifications à votre gestionnaire de complétion, soit modifier votre logique conditionnelle afin d'obtenir toujours un jeu non vide.

Une approche peut être d'avoir un ensemble facultatif dans votre rappel. Quelque chose comme:

(Set<String>?) -> Void

Si IDs sont vides, puis un rappel avec nil et votre code d'appel gérer la possibilité d'un ensemble nil.

Une autre approche pourrait être de créer une énumération pour encapsuler votre résultat et l'utiliser dans votre rappel.Quelque chose comme:

Enum

enum Result { 
    case success(Set<String>) 
    case failure 
} 

Callback

handler: (Result) -> Void

Utilisation

handler(.success(IDs)) 

// or 

handler(.failure) 

appelle le code

getReckos(rekoType: .someType) { result in 
    switch result { 
    case .success(let IDs): 
     // Use IDs 
    case .failure: 
     // Handle no IDs 
    } 
} 
+0

Merci, je ne suis peut-être pas en train de l'articuler correctement, mais le problème est que je veux que le completionHandler son résultat à sa fonction d'appel 'RekoRequest(). getRekos (rekoType: sometype) {résultat in' quand il est itéré dans la boucle et est terminé, de sorte que je reçois seulement 1 Set terminé .... à partir de maintenant je reçois un Réglez chaque fois à travers la boucle, c'est-à-dire 1, 1 et 2, 1 2 et 3 etc. – GarySabo

+0

@GarySabo Le 'handler' doit être appelé en dehors de la boucle for. De cette façon, lorsque la boucle est terminée, votre 'gestionnaire 'sera appelé une fois avec les' ID '. –

+0

@GarySabo Les points supplémentaires dans ma réponse essayent d'adresser votre commentaire sur l'ensemble vide. Un ensemble vide est possible avec la façon dont vous avez actuellement votre code écrit. Ma réponse offre quelques suggestions sur la façon de gérer un ensemble vide potentiel. –