0

I've been pouring over stack overflow for ages trying to find a way out of this error:

unexpected non void return value in void function

that I am getting with returning a Bool within my function.

i just can't seem to dig my way out of this one. I'm sure its something to do with async but I'm not very familiar with these types of functions.

class CheckReachability {


    class func setupReachability (hostName:String?, useClosures: Bool) -> Bool{

        var reachability : Reachability!
        var connected = false

                let reachability2 = hostName == nil ? Reachability() : Reachability(hostname: hostName!)
                reachability = reachability2
                try! reachability?.startNotifier()

                if useClosures {
                    reachability2?.whenReachable = { reachability in
                        DispatchQueue.main.async {
                            connected = true
                            print("Reachable....")
                        }
                    }
                    reachability2?.whenUnreachable = { reachability in
                        DispatchQueue.main.async {
                            connected = false
                            print("Not Connected....")
                        }
                    }

                } else {
                    NotificationCenter.default.addObserver(self, selector: Selector(("reachabilityChanged:")), name: ReachabilityChangedNotification, object: reachability2)
                }
            return connected
            }

}

calling this from viewdidload on another vc doesn't allow enough time to get a true result

        let connected = CheckReachability.setupReachability(hostName: nil, useClosures: true)
if connected {
Pippo
  • 1,439
  • 1
  • 18
  • 35
  • 1
    Your code doesn't give me that error. Check for unbalanced { } in code not shown – Paulw11 Jan 01 '17 at 12:13
  • What exactly do you want to reach? Each time when reachability status changes to know it? – Nikita Ermolenko Jan 01 '17 at 12:22
  • you are correct I've been looking at this too long. Problem i now have is initialising the function from my viewDidLoad in another VC with doesn't allow enough time return a true result. added some details on edit. – Pippo Jan 01 '17 at 12:22
  • yes pretty much the change in reachability. I'm trying to initialise it in viewdidload in another viewcontroller but because its asynchronous its not allowing enough time to actually get a true result – Pippo Jan 01 '17 at 12:25
  • It's derived from the well-liked and famous issue [returning data from an asynchronous call](http://stackoverflow.com/questions/25203556/returning-data-from-async-call-in-swift-function) – vadian Jan 01 '17 at 12:50

2 Answers2

2

Your question is confusing because the code you posted does not have the error you describe. However, you're trying to create a function that returns a result from an async function. That is not how async works.

Async functions start to do a task in the background, where that task won't be finished before it's time to return.

You need to adjust your thinking. Instead of trying to return a result from your function, you need to write your function to take a completion handler. You then call the completion handler once the long-running task has finished (which is after your function has returned.)

@bubuxu provided you with code showing how to modify your function as I described.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
1

If you want to write a checking class to listen to the reachability, define it as a singleton and pass the completeBlock to it like this:

class CheckReachability {

    static let shared = CheckReachability()

    var reachability: Reachability?

    func setupReachability(hostName:String?, completeBlock: ((Bool) -> Void)? = nil) {

        reachability = hostName == nil ? Reachability() : Reachability(hostname: hostName!)

        try? reachability?.startNotifier()

        if let block = completeBlock {
            reachability?.whenReachable = { reachability in
                DispatchQueue.main.async {
                    print("Reachable....")
                    block(true)
                }
            }
            reachability?.whenUnreachable = { reachability in
                DispatchQueue.main.async {
                    print("Not Connected....")
                    block(false)
                }
            }
        } else {
            // If we don't use block, there is no point to observe it.
            NotificationCenter.default.addObserver(self, selector: #selector(reachabilityChanged(_:)), name: .ReachabilityChangedNotification, object: nil)
        }

    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    @objc func reachabilityChanged(_ notification: Notification) {
        // ?? what should we do here?
    }

} 
Duncan C
  • 128,072
  • 22
  • 173
  • 272
bubuxu
  • 2,187
  • 18
  • 23
  • Thanks for the solution and apologies for the scrappy original post. i had been staring at it for hours and i wasn't thinking clearly. I am having trouble understanding the singleton and completion block approach. i can see that you only want something like this to run once and then notify if there is a change, but i can't figure out how to access the result from other classes? am i going to have to go down the delegation path here? – Pippo Jan 01 '17 at 21:22
  • originally i just had this being called from viewdidload in one viewcontroller and it would always update the variable connected when there was a change in the connection which would then let me either fetch data from network or local store. – Pippo Jan 01 '17 at 21:22
  • You can take a look on this library: https://github.com/Alamofire/Alamofire, it contains a NetworkReachabilityManager to help to detect the Reachability. In your view controller, declare the iVar of this class (e.g. named `manager`), then call `manager?.startListening()` and set `manager?.listener = ...` on your `viewDidLoad` , `manager?.stopListening()` on `deinit`. – bubuxu Jan 02 '17 at 02:19