Swift is giving non-void return error even though

2019-09-20 14:14发布

问题:

I am getting this error:

/Users/xxxx/Desktop/xxx/xxx/ViewController.swift:43:38: Unexpected non-void return value in void function

And others like this even though my functions are set to return Double.

This is one of the functions of which this error is appearing in every return.

func readWeight() -> Double {
    let quantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bodyMass)

    let weightQuery = HKSampleQuery(sampleType: quantityType!, predicate: nil, limit: 1, sortDescriptors: nil) {

        query, results, error in

        if (error != nil) {
            print(error!)
            return 0.0
        }

        guard let results = results else {
            print("No results of query")
            return 0.0
        }

        if (results.count == 0) {
            print("Zero samples")
            return 0.0
        }

        guard let bodymass = results[0] as? HKQuantitySample else {
            print("Type problem with weight")
            return 0.0
        }
        return bodymass.quantity.doubleValue(for: HKUnit.pound())
    }

    healthKitStore.execute(weightQuery)
}

An example of how this is used:

print(readWeight())

Thanks!

回答1:

You need to use block. So the function itself will return void. When the HKSampleQuery starts, it get executed and waiting for result while your readWeight function keep executing and then end returning void. By this time, your HKSampleQuery is still executing. When it is done, it posts result by the completion handler. So if you want to do anything with the result Double, you need to do it in the completion handler. So your function will be

func readWeight(completion: @escaping (Double) -> Void) {
   let quantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bodyMass)

   let weightQuery = HKSampleQuery(sampleType: quantityType!, predicate: nil, limit: 1, sortDescriptors: nil) {

    query, results, error in

    if (error != nil) {
        print(error!)
        completion(0.0)
    }

    guard let results = results else {
        print("No results of query")
        completion(0.0)
    }

    if (results.count == 0) {
        print("Zero samples")
        completion(0.0)
    }

    guard let bodymass = results[0] as? HKQuantitySample else {
        print("Type problem with weight")
        completion(0.0)
    }
    completion(bodymass.quantity.doubleValue(for: HKUnit.pound()))
}

   healthKitStore.execute(weightQuery)
}

To use the result:

self.readWeight(){ (result) in
    //This result is a double value that is returned from HKSampleQuery
}


回答2:

Have to return Double value at the end of function.

 func readWeight() -> Double {
 let quantityType = HKQuantityType.quantityType(forIdentifier:HKQuantityTypeIdentifier.bodyMass)

let weightQuery = HKSampleQuery(sampleType: quantityType!, predicate: nil, limit: 1, sortDescriptors: nil) {

    query, results, error in

    if (error != nil) {
        print(error!)
        return 0.0
    }

    guard let results = results else {
        print("No results of query")
        return 0.0
    }

    if (results.count == 0) {
        print("Zero samples")
        return 0.0
    }

    guard let bodymass = results[0] as? HKQuantitySample else {
        print("Type problem with weight")
        return 0.0
    }
    return bodymass.quantity.doubleValue(for: HKUnit.pound())
}

healthKitStore.execute(weightQuery)
return some double value here
}


回答3:

The lines inside the braces at the end of:

let weightQuery = HKSampleQuery(sampleType: quantityType!, predicate: nil, limit: 1, sortDescriptors: nil) { ... }

are actually part of another closure sent as an argument to HKSampleQuery's initializer. Think of them as a separate function which will be executed when you call healthKitStore.execute(weightQuery). As noted in Apple's documentation the signature of that closure is: (HKSampleQuery, [HKSample]?, Error?) -> Void. This means that you can't return anything from the statements inside that closure.

Keeping that in mind, you can modify your method as follows:

func printWeight() {
    let quantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bodyMass)

    let weightQuery = HKSampleQuery(sampleType: quantityType!, predicate: nil, limit: 1, sortDescriptors: nil) {

        query, results, error in

        if (error != nil) {
            print(error!)
        }

        guard let results = results else {
            print("No results of query")
        }

        if (results.count == 0) {
            print("Zero samples")
        }

        guard let bodymass = results[0] as? HKQuantitySample else {
            print("Type problem with weight")
        }

        print("Bodymass:",  bodymass.quantity.doubleValue(for: HKUnit.pound()))
    }

    healthKitStore.execute(weightQuery)
}

and then just call printWeight() to get the results.



回答4:

I suppose that what you want to achieve is something like that:

func readWeight(result: @escaping (Double) -> Void) {
   let quantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bodyMass)

   let weightQuery = HKSampleQuery(sampleType: quantityType!, predicate: nil, limit: 1, sortDescriptors: nil) {

    query, results, error in

    if (error != nil) {
        print(error!)
        result(0.0)
    }

    guard let results = results else {
        print("No results of query")
        result(0.0)
    }

    if (results.count == 0) {
        print("Zero samples")
        result(0.0)
    }

    guard let bodymass = results[0] as? HKQuantitySample else {
        print("Type problem with weight")
        result(0.0)
    }
    result(bodymass.quantity.doubleValue(for: HKUnit.pound()))
}

   healthKitStore.execute(weightQuery)
}

Then you can use readWeight method like that:

readWeight(result: { myResult in
    let comparisonResult = myResult == 0.0
})

So that myResult here is the value you are looking for.

This is all caused by the fact that HKSampleQuery executes asynchronously, so you have no way of immediately knowing the return value. You have to wait for its result, and then process the result in a closure that was given as your method's argument.

Probably you should read more about closures:

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html

Swift: How to pass in a closure as a function argument