Search code examples
iosswiftswift2researchkit

Researchkit: How to switch to next step manually out of UIWebView


I want to implement my own ResearchKit step including a WebView in which a button gives the possibility to switch to the next step.

Is it therefore possible to

1) initiate switching to the next step manually?

2) manipulate the result, receiving some data from my WebView?

For learning purposes I created following so far, including my own ActiveStep:

import UIKit
import ResearchKit
class DemoView: UIWebView {

}
class DemoStepViewController : ORKActiveStepViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let demoView = UIWebView()
        demoView.loadHTMLString("<html><body><p>Hello!</p></body></html>", baseURL: nil)
        demoView.translatesAutoresizingMaskIntoConstraints = false
        self.customView = demoView
        self.customView?.superview!.translatesAutoresizingMaskIntoConstraints = false
        view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[demoView]-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["demoView": demoView]))
        view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[demoView]-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["demoView": demoView]))
    }
}
class DemoStep : ORKActiveStep {
    static func stepViewControllerClass() -> DemoStepViewController.Type {
        return DemoStepViewController.self
    }
}
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, ORKTaskViewControllerDelegate {
    var window: UIWindow?
    var taskResultFinishedCompletionHandler: (ORKResult -> Void)?
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        let activeStep = DemoStep(identifier: "webstep")
        activeStep.title = "Demo Step"
        var endStep = ORKCompletionStep(identifier: "endstep")
        endStep.title = "Well done"
        endStep.text = "thank you"
        let task = ORKOrderedTask(identifier: "orderedtask", steps: [activeStep, endStep])
        let taskViewController = ORKTaskViewController(task: task, taskRunUUID: nil)
        taskViewController.delegate = self
        taskViewController.outputDirectory = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! String, isDirectory: true)
        window?.rootViewController = taskViewController
        return true
    }
    func taskViewController(taskViewController: ORKTaskViewController, didFinishWithReason reason: ORKTaskViewControllerFinishReason, error: NSError?) {
        taskResultFinishedCompletionHandler?(taskViewController.result)
        taskViewController.dismissViewControllerAnimated(true, completion: nil)
    }
}

Solution

  • I solved the problem like this:

    1) Use a WKWebView scriptmessagehandler that receives the result from javascript. As soon as the messagehandler receives a result it starts the ActiveTask's timer which is set to a very small value.

    You can see my implementation here as answer to another stackoverflow question: Creating custom ORKStep with WKWebView

    2) To include the step result into the task, I just had to override the result property in my step view controller.

      var JS_Answer = ORKTextQuestionResult()
    
       override var result : ORKStepResult? {
        let step_Result = super.result!
        step_Result.results = [JS_Answer] // result will look up the answer here
        return step_Result
      }
    
      // Handle the message posted by Javascript
      func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
        if let widget_Result = message.body as? String {
          JS_Answer.identifier = step!.identifier
          // write the answer from WebView to JS_Answer, which will be accessed by result
          JS_Answer.textAnswer = widget_Result 
          start()
        }
      }