2016-07-21 4 views
0

In meinem Projekt für OS X Befehlszeilentool mit Swift 3.0 (Beta 2) muss ich HTML-Daten in String aus mehreren URLs konvertieren. Es gibt ein Problem bei der Verwendung einer solchen Funktion mit vielen Hintergrundaufgaben (es funktioniert nicht, außer dem Hauptthread, also gibt es vielleicht elegantere Möglichkeiten, den Abschluss aller Aufgaben zu kontrollieren und HTML-Daten in einem solchen Tool mit oder ohne Parser zu lesen, die ich brauche Swift 3 und Mac OS X (Linux in naher Zukunft)):URLSession im Befehlszeilentool: mehrere Aufgaben steuern und HTML in Text konvertieren

func html2text (html: String, usedEncoding: String.Encoding) -> String { 

    let data = html.data(using: usedEncoding)! 

    if let htmlString = AttributedString(html: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: usedEncoding.rawValue], documentAttributes: nil)?.string { 
     return htmlString 
    } else { 
     return "" 
    } 
} 

Also lese ich Daten zunächst in ein Array warten, wenn alle DataTasks fertig und dann im Hauptthread zu konvertieren. Verwendung auch globale Variable (Set von Urls) Abschluss jeder Aufgabe zu steuern:

import Foundation 
import WebKit 

var urlArr = [String]() 
var urlSet = Set<String>() 
var htmlTup : [(url : String, html : String, encoding : String.Encoding)] = [] 

let session = URLSession.shared 

For-in-Schleife mit mehreren URLSession DataTasks

for myurl in urlArr { 
     if urlSet.insert(myurl).inserted { 
      print ("Loading \(myurl)...") 

      let inputURL = URL(string: myurl)! 
      let task = session.dataTask(with: inputURL, completionHandler: {mydata, response, error in 

lesen Kodierung von HTML Erste

  var usedEncoding = String.Encoding.utf8 
      if let encodingName = response!.textEncodingName { 

        let encoding = CFStringConvertIANACharSetNameToEncoding(encodingName) 
        if encoding != kCFStringEncodingInvalidId {        
         usedEncoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(encoding))        
        } 
       } 

Arbeiten Sie mit HTML String und lesen Sie Daten in ein Array

if let myString = String(data: mydata!, encoding: usedEncoding) {     
       htmlTup += [(url: myurl,html: myString, encoding: usedEncoding)] 
       } 
       // The end of task removing URL from Set 
       urlSet.remove(myurl)      
      }) 
      //Run Task 
      task.resume() 
     } 
    } 
} 

Warten auf Aufgaben HTML zu vervollständigen und konvertieren

while !urlSet.isEmpty { 
// Do nothing 
} 

for (url,html,encoding) in htmlTup { 
    print ("Writing data from \(url)...")  
    print (html2text(html: html, usedEncoding: encoding)) 
} 

Update 1 in Text: Runloop im Hauptthread von this Ein solcher Code zu überprüfen, wenn jede Aufgabe beendet:

var taskArr = [Bool]() 
let task = session.dataTask(with: request) { (data, response, error) in 
} 
          taskArr.removeLast() 
         } 
         taskArr.append(true) 
         task.resume() 

// Waiting for tasks to complete 
      let theRL = RunLoop.current 
      while !taskArr.isEmpty && theRL.run(mode: .defaultRunLoopMode, before: .distantFuture) { } 

Antwort

0

Sie Sie können nicht einfach in einer aktiven Schleife warten, um auf Ergebnisse zu warten, da Sie dadurch die Hauptlaufschleife/Thread/Dispatch-Warteschlange blockieren.

Stattdessen an diesem Punkt zurückkehren und somit die Hauptlaufschleife laufen lassen. Überprüfen Sie dann in Ihrem Completion-Handler, ob Sie alle Antworten erhalten haben, die Sie erwarten, und wenn ja, tun Sie die Dinge, die Sie nach dieser Aktivität haben, während der while-Schleife.

+0

Vielen Dank, ich brauche mehr Verständnis dafür, wie die Dinge funktionieren –