1

I am Working Researchkit based application where i am navigating to the question based on the selected option of a single choice question in Xcode 8.3.3 . Unfortunately app crashing dynamically in setting the navigation rule. Earlier in Xcode 8.2.1 i have no issue and working smooth. Please let me know whats going wrong in my code, Following is the crash log:

Could not cast value of type '(predicate : __ObjC.NSPredicate, destinationStepIdentifier : Swift.String)' (0x15fe50570) to '(resultPredicate : __ObjC.NSPredicate, destinationStepIdentifier : Swift.String)' (0x15fe60530). 2017-10-10 07:37:57.530808 Turbo[7440:2981376] Could not cast value of type '(predicate : __ObjC.NSPredicate, destinationStepIdentifier : Swift.String)' (0x15fe50570) to '(resultPredicate : __ObjC.NSPredicate, destinationStepIdentifier : Swift.String)' (0x15fe60530).

           //Question0
    let textChoiceOneText = NSLocalizedString("Choice 1", comment: "")
    let textChoiceTwoText = NSLocalizedString("Choice 2", comment: "")
    let textChoiceThreeText = NSLocalizedString("Choice 3", comment: "")

    // The text to display can be separate from the value coded for each choice:
    let textChoices = [
        ORKTextChoice(text: textChoiceOneText, value: "choice_1" as NSCoding & NSCopying & NSObjectProtocol),
        ORKTextChoice(text: textChoiceTwoText, value: "choice_2" as NSCoding & NSCopying & NSObjectProtocol),
        ORKTextChoice(text: textChoiceThreeText, value: "choice_3" as NSCoding & NSCopying & NSObjectProtocol)
    ]
    let answerFormat = ORKAnswerFormat.choiceAnswerFormat(with: .singleChoice, textChoices: textChoices)
    let questionStepzero = ORKQuestionStep(identifier: String(describing:"singlechoice0"), title: "titel", answer: answerFormat)

    //question1
    let question1 = ORKQuestionStep(identifier: "question1")
    question1.answerFormat = ORKBooleanAnswerFormat()
    //question2
    let question2 = ORKQuestionStep(identifier: "question2")
    question2.answerFormat = ORKBooleanAnswerFormat()

    //Question6
    let question6 = ORKQuestionStep(identifier: "question6")
    question6.answerFormat = ORKBooleanAnswerFormat()


    //Question 7
    let defaultDate = Date()
    let minDate = Date()
    let maxDate = Date()
    let nameQuestionStepTitle = "title message"
    let dateAnswer = ORKDateAnswerFormat(style:ORKDateAnswerStyle.date, defaultDate: defaultDate, minimumDate: minDate, maximumDate: maxDate, calendar: nil)
    let dataPickerQuestionStep7 = ORKQuestionStep(identifier: "datequestion7", title:nameQuestionStepTitle, answer: dateAnswer)


    let steps = [questionStepzero,question1, question2,question6,dataPickerQuestionStep7]
    let task = ORKNavigableOrderedTask(identifier: "task", steps: steps)

    let predicate1 = ORKResultPredicate.predicateForChoiceQuestionResult(with: ORKResultSelector(resultIdentifier: "singlechoice0"), matchingPattern: "choice_1")
    let predicate2 = ORKResultPredicate.predicateForChoiceQuestionResult(with: ORKResultSelector(resultIdentifier: "singlechoice0"), matchingPattern: "choice_2")
    let predicate3 = ORKResultPredicate.predicateForChoiceQuestionResult(with: ORKResultSelector(resultIdentifier: "singlechoice0"), matchingPattern: "choice_3")


    let singleChulesArray:NSMutableArray =  NSMutableArray()

    var dict:NSMutableDictionary = NSMutableDictionary()
    dict.setObject(predicate1, forKey: "predicateInstance" as NSCopying)
    dict.setObject("datequestion7", forKey: "Destination" as NSCopying)
    singleChulesArray.add(dict)
    dict = NSMutableDictionary()

    dict.setObject(predicate2, forKey: "predicateInstance" as NSCopying)
    dict.setObject("question2", forKey: "Destination" as NSCopying)
    singleChulesArray.add(dict)

    dict = NSMutableDictionary()
    dict.setObject(predicate3, forKey: "predicateInstance" as NSCopying)
    dict.setObject("question6", forKey: "Destination" as NSCopying)
    singleChulesArray.add(dict)



    //Static loading of Predicates and Destintionidentifiers
    /*
    let predicateRule1 = ORKPredicateStepNavigationRule(resultPredicatesAndDestinationStepIdentifiers: [
        (resultPredicate: predicate1, destinationStepIdentifier: "datequestion7"),
        (resultPredicate: predicate2, destinationStepIdentifier: "question2"),
        (resultPredicate: predicate3, destinationStepIdentifier: "question6")
        ])
    */

    print("singleChulesArray",singleChulesArray)

    //Dynamic loading of Predicates and Destination identifiers
    var stuff:[(predicate: NSPredicate, destinationStepIdentifier: String)] = [(predicate: NSPredicate, destinationStepIdentifier: String)]()
    for (_, PredicateDict) in singleChulesArray.enumerated()
    {
        stuff += [(predicate: (PredicateDict as AnyObject).value(forKey: "predicateInstance") as! NSPredicate, destinationStepIdentifier: (PredicateDict as AnyObject).value(forKey: "Destination") as! String)]
    }

    let predicateRule = ORKPredicateStepNavigationRule(resultPredicatesAndDestinationStepIdentifiers: stuff as! [(resultPredicate: NSPredicate, destinationStepIdentifier: String)])

    task.setNavigationRule(predicateRule, forTriggerStepIdentifier: "singlechoice0")
    let taskViewController = ORKTaskViewController(task: task, taskRun: nil)
    taskViewController.view.tintColor = TurboConstants.globalAccess.primaryClr
    taskViewController.delegate = self
    present(taskViewController, animated: true, completion: nil)
hemanth
  • 13
  • 4

1 Answers1

1

You are using the ORKPredicateStepNavigationRule initializer incorrectly. The initializer takes an Array of tuples as parameter. You need to pass the tuple's element's names in the initializer:

let predicateRule = ORKPredicateStepNavigationRule(resultPredicatesAndDestinationStepIdentifiers: [(resultPredicate: predicate1, destinationStepIdentifier: "datequestion7")])

Or with multiple tuples:

let predicateRule = ORKPredicateStepNavigationRule(resultPredicatesAndDestinationStepIdentifiers: [
            (resultPredicate: predicate1, destinationStepIdentifier: "datequestion7"),
            (resultPredicate: predicate2, destinationStepIdentifier: "question2"),
            (resultPredicate: predicate3, destinationStepIdentifier: "question6"),
            ])

Or dynamically (as in your code above):

var stuff = [(resultPredicate: NSPredicate, destinationStepIdentifier: String)]()
for predicateDict in singleChulesArray
{
    guard
        let predicateDict = predicateDict as? [String: Any],
        let predicate = predicateDict["predicateInstance"] as? NSPredicate,
        let identifier = predicateDict["Destination"] as? String
        else { continue }
    stuff.append((resultPredicate: predicate, destinationStepIdentifier: identifier))
}

To remove the crash you only needed to change the tuple element name predicate to the correct name resultPredicate. When tuples are used as parameters in a method the naming of the element names has to be exactly as in the method signature. Or you will get a crash.

I took the liberty to change a bit more in that part of your code. I removed the force casting (which is almost never a good idea) and used optional unwrapping instead. Also I used append to add a new tuple to the stuff array instead of creating a new array (with the tuple as its only element) and then adding that array to the stuff array.

joern
  • 27,354
  • 7
  • 90
  • 105
  • Yes, its working fine with your answer for only single predicate, but i am not able navigate single choice questions with multiple options(Multiple predicates) .facing issue in framing from dynamic JSON. Below is the actual code – hemanth Oct 09 '17 at 09:23
  • Please have a look at the edited answer. Does that fix your problem? – joern Oct 09 '17 at 09:49
  • var predicateAndDestinationIdentifierArray:[(predicate: NSPredicate, destinationString: String)] = [(predicate: NSPredicate, destinationString: String)]() for (index, PredicateDict) in singleChoicePredicateRulesArray.enumerated() { let PredicateDic:NSMutableDictionary = PredicateDict as! NSMutableDictionary predicateAndDestinationIdentifierArray += [(predicate: PredicateDic.value(forKey: "predicateInstance") as! NSPredicate, destinationString: PredicateDic.value(forKey: "Destination") as! String)] } – hemanth Oct 09 '17 at 12:18
  • let singleChoicePredicate = ORKPredicateStepNavigationRule(resultPredicatesAndDestinationStepIdentifiers: predicateAndDestinationIdentifierArray as! [(resultPredicate: NSPredicate, destinationStepIdentifier: String)]) – hemanth Oct 09 '17 at 12:18
  • Thanks for your response, pls go through my code and can you let me know why it is crashing. – hemanth Oct 09 '17 at 12:20
  • Please add that code to your answer. I tried to add it and it does not even compile. Also please add the error that you get when the app crashes. – joern Oct 09 '17 at 12:29
  • @ joern, i have updated my compiled code and error (crash )that i am facing with my question. Please look in to it. Thanks!! – hemanth Oct 10 '17 at 02:16
  • @hemanth Still the same problem: you are not using the correct tuple element name. Please check my updated answer for details and a fix. – joern Oct 10 '17 at 07:21
  • @ joern, it worked perfectly . Thank you very very much!!. – hemanth Oct 10 '17 at 18:09