0

I've created a macOS console app in swift, but the code is never executed, =I have to use Semaphore but is there another way to do this ? my purpose is to create a method returning a json file

class test{
    func gizlo(){
        let config = URLSessionConfiguration.default // Session Configuration
        let session = URLSession(configuration: config) // Load configuration into Session
        let url = URL(string: "https://itunes.apple.com/fr/rss/topmovies/limit=25/json")!

        let task = session.dataTask(with: url, completionHandler: {
            (data, response, error) in

            if error != nil {

                print(error!.localizedDescription)

            } else {

                do {

                    if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]
                    {
                        print(json)
                    }

                } catch {

                    print("error in JSONSerialization")

                }


            }

        })

        task.resume()
    }
}

let tr=test()
tr.gizlo()

Thanks

David Tavan
  • 69
  • 1
  • 6
  • You can add RunLoop.main.run() in the and of file. But usage of the Semaphore is more preferable.The main point is to prevent consolidation from close. – Oleg Gordiichuk Apr 20 '17 at 08:18
  • Related (duplicate?): [CFRunLoop in Swift Command Line Program](http://stackoverflow.com/questions/25126471/cfrunloop-in-swift-command-line-program) – Martin R Apr 20 '17 at 08:51

2 Answers2

0

To avoid Semaphores you can use simple readLine() that will wait for input from the keyboard. Yes it is not obvious but it is woking because it prevent terminal app from exit.

Just add in the and of the file:

_ = readLine()
Oleg Gordiichuk
  • 15,240
  • 7
  • 60
  • 100
0

As Oleg points out, putting readLine() at the end of the top-level code will prevent the program for exiting until you hit Enter in the terminal or wherever FileHandle.standardInput is pointing. That's probably fine for just testing the code quickly in the debugger or in a Playground. An infinite loop would also work, though you'd have to actually terminate it in the debugger or with kill from the command line.

The real issue is why you don't want to use a semaphore. Since they're not difficult to use, I'm going to hazard a guess that it's just because you don't want to pollute your asynchronous data task completion handler with a semaphore when you probably only need it to wait for the data for testing purposes.

Assuming my guess is correct, the real issue isn't actually using a semaphore, it's where you think you need to put them. As David Wheeler once said, "Any problem can be solved by adding a layer of indirection."

You don't want the semaphore explicitly in the completion handler you pass to dataTask. So one solution would be to make gizlo accept a completion handler of its own, and then create a method that calls gizlo with a closure that handles the semaphore. That way you can decouple the two and even add some flexibility for other uses. I've modified your code to do that:

import Foundation
import Dispatch // <-- Added - using DispatchSemaphore

class test{
    func gizlo(_ completion: ((Result<[String: Any]?, Error>) -> Void)? = nil) { // <-- Added externally provided completion handler
        let config = URLSessionConfiguration.default // Session Configuration
        let session = URLSession(configuration: config) // Load configuration into Session
        let url = URL(string: "https://itunes.apple.com/fr/rss/topmovies/limit=25/json")!

        let task = session.dataTask(with: url, completionHandler: {
            (data, response, error) in

            let result: Result<[String: Any]?, Error>
            if let responseError = error { // <-- Changed to optional binding

                print(responseError.localizedDescription)
                result = .failure(responseError) // <-- Added this

            } else {

                do {

                    if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]
                    {
                        print(json)
                        result = .success(json) // <-- Added this
                    }
                    else { // <-- Added this else block
                        result = .success(nil)
                    }
                } catch {

                    print("error in JSONSerialization")
                    result = .failure(error) // <-- Added this
                }

            }

            completion?(result)  // <-- Added this call
        })

        task.resume()
    }

    func blockingGizlo() throws -> [String: Any]? // <-- Added this method
    {
        let sem = DispatchSemaphore(value: 1)
        sem.wait()
        var result: Result<[String: Any]?, Error>? = nil
        gizlo {
            result = $0
            sem.signal()
        }
        sem.wait() // This wait will block until the closure calls signal
        sem.signal() // Release the second wait.

        switch result
        {
            case .success(let json)  : return json
            case .failure(let error) : throw error
            case .none: fatalError("Unreachable")
        }
    }
}

let tr=test()

do {
    let json = try tr.blockingGizlo()
    print("\(json?.description ?? "nil")")
}
catch { print("Error: \(error.localizedDescription)") }
Chip Jarred
  • 2,600
  • 7
  • 12