Where is my mistakes while I m using completion handler with multiple url?

103 Views Asked by At

I have many URLs that I have to scrape from the website. After I get them from my bundle file I create an array that includes many URLs. I passed this array my function which is called scrapeSanatcilar and I add completion handler there but something going wrong. I just want to get some random elements from my array after it has filled with data :(

I call scrapeWithPage in my viewdidLoad.

func scrapeWithPage(){
    
    let path = Bundle.main.path(forResource: "SanatciLink", ofType: "txt")!
    let text = try! String(contentsOfFile: path, encoding: String.Encoding.utf8)
    var links = text.components(separatedBy: CharacterSet.newlines)
    links.removeLast()
    
    scrapSanatcilar(links: links) { (result) in
        
        print(result.randomElement())
    }
    
}
    func scrapeSanatcilar(links: [String], completionHandler: @escaping (_ result: [String]) -> Void){
    
    for link in links{
       
        let baseUrl = link
        let url = URL(string: baseUrl)!
        
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else {
            self.makeAlert(titleInput: "Hata!", messageInput: "Üzgünüz ancak bir sorun oluştu lütfen daha sonra tekrar deneyin")
            return
            }
            guard let htmlString = String(data: data, encoding: .utf8) else {
            self.makeAlert(titleInput: "Hata!", messageInput: "Üzgünüz ancak bir sorun oluştu lütfen daha sonra tekrar deneyin")
            return
            }

           guard let songs: Elements = try? SwiftSoup.parse(htmlString).getElementsByClass("artist-list") else {return}

                do{

                       let hrefs : Elements  = try songs.select("a")

                       for href in hrefs{

                           self.scrapeSanatciNameWithPageLinks.append(try href.text())
                           self.scrapeSarkiNameWithPageLinks.append(try href.attr("href"))
                           
                       }
                    
                    
                   }catch{

               }

        };task.resume()
        
    }
    
    completionHandler(self.scrapeSanatciNameWithPageLinks)
    
}
2

There are 2 best solutions below

2
Carson Vo On

Please move your completionHandler into your Task because the Asynchronous like this:

func scrapeSanatcilar(links: [String], completionHandler: @escaping (_ result: [String]) -> Void){
        
        for link in links{
           
            let baseUrl = link
            let url = URL(string: baseUrl)!
            
            let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
                guard let data = data else {
                self.makeAlert(titleInput: "Hata!", messageInput: "Üzgünüz ancak bir sorun oluştu lütfen daha sonra tekrar deneyin")
                return
                }
                guard let htmlString = String(data: data, encoding: .utf8) else {
                self.makeAlert(titleInput: "Hata!", messageInput: "Üzgünüz ancak bir sorun oluştu lütfen daha sonra tekrar deneyin")
                return
                }
    
               guard let songs: Elements = try? SwiftSoup.parse(htmlString).getElementsByClass("artist-list") else {return}
    
                    do{
    
                           let hrefs : Elements  = try songs.select("a")
    
                           for href in hrefs{
    
                               self.scrapeSanatciNameWithPageLinks.append(try href.text())
                               self.scrapeSarkiNameWithPageLinks.append(try href.attr("href"))
                               
                           }

                        completionHandler(self.scrapeSanatciNameWithPageLinks)
                        
                       }catch{
    
                   }
    
            };task.resume()
            
        }
    }
1
Rangel Terraquio On

You can achive that using a DispatcGroup. DispatchGroup is used when you want to monitor a group of task as a single unit. You can dispatch multiple asynchronous tasks and to be notified when all of them are finished. Documentation.

func scrapeSanatcilar(links: [String], completionHandler: @escaping (_ result: [String]) -> Void){
    //Here we create a group
    let group = DispatchGroup()
    
    
    for link in links{
        
        let baseUrl = link
        let url = URL(string: baseUrl)!
        
        
        group.enter()//Here we are saying to the group a new request is starting.
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            defer {
                group.leave() //Defer is execute when the escope is finished. We call group.leave to notify our group this request is finished.
            }
            guard let data = data else {
                self.makeAlert(titleInput: "Hata!", messageInput: "Üzgünüz ancak bir sorun oluştu lütfen daha sonra tekrar deneyin")
                return
            }
            guard let htmlString = String(data: data, encoding: .utf8) else {
                self.makeAlert(titleInput: "Hata!", messageInput: "Üzgünüz ancak bir sorun oluştu lütfen daha sonra tekrar deneyin")
                return
            }
            
            guard let songs: Elements = try? SwiftSoup.parse(htmlString).getElementsByClass("artist-list") else {return}
            
            do{
                
                let hrefs : Elements  = try songs.select("a")
                
                for href in hrefs{
                    
                    self.scrapeSanatciNameWithPageLinks.append(try href.text())
                    self.scrapeSarkiNameWithPageLinks.append(try href.attr("href"))
                    
                }
                
                
            }catch{
                
            }
            
        }
        task.resume()
        
    }
    
    
    //When all request are finished this clousure will be execute notifying the main thread. Here you can call your completion handler
    group.notify(queue: .main) { [self] in
        completionHandler(self.scrapeSanatciNameWithPageLinks)
    }
    
    
    
    
    
}