ResearchKit stepResult for a TextChoiceQuestion - ios

I'm using ResearchKit's stepResultForStepIdentifier method successfully for other question types, but can't find correct syntax to pre-populate the results for a TextChoiceQuestion.
Below is an unsuccessful attempt at setting the result for the sample TextChoice question in ORKCatalog. Any advice on correct approach?
func stepResultForStepIdentifier(stepIdentifier: String) -> ORKStepResult? {
var stepResults = [ORKQuestionResult]()
if stepIdentifier == "TextChoiceQuestionStep" {
var questionDefault = ORKChoiceQuestionResult(identifier: stepIdentifier)
questionDefault.choiceAnswers?.append("choice_2")
stepResults.append(questionDefault)
}
var defaults = ORKStepResult(stepIdentifier: stepIdentifier, results: stepResults)
return defaults
}

Is the choiceAnswers array nil? When you do questionDefault.choiceAnswers?.append, choiceAnswers might be nil, so this might do nothing.
Instead do questionDefault.choiceAnswers = ["choice_2"]

Related

keeping two arrays in sync?

I am currently trying to keep my two arrays in sync. One question label and a one text label..
For my question label, I used arc4Random to pick a random question from my array. However, in order to set the label just above it in the UI, I need it to match my other array.. (titleQuestions)
How do I pull out the titleQuestion by using the arc4random number I generate from the questions array..
Sorry in advance, I am relatively new to Swift and still learning.. first year..
//just vars to get data from my two models(i.e two arrays)
var questionArray = DataModel()
var tArray = TitleLabel()
// this here is questions for the lower UI label. random No generate here!
func questions() {
let questions = questionArray.questions
let randomQuestions = questions.index(questions.startIndex , offsetBy : questions.count.arc4random)
questionLabel.text = questions[randomQuestions]
}
// the questions marks is because I dont know what to index here because I want this number but cant seem to get it!
func titles() {
let titlesArray = tArray.titleQuestions
titleLabel.text = titlesArray[?????]
}
// When this button is pressed both UIlabel will be updated with a random question and the associated title
#IBAction func questionsButton(_ sender: UIButton) {
questions()
titles()
}
I understand this could might be a stupid question but I have wasted a better part of a week trying to work this out.. any help is encouraged! thought about using a closure to take the questions function but really dont know how to implement it..
Welcome to SO. this is a trivial problem, and one you should really work out on your own, but it sounds like you're stuck, so I'll take pity on you.
At the simplest, take the code that generates a random number out of the line that fetches an item, and instead assign the random number to a temp variable, and set both labels from a single function:
func questionsAndTitles() {
let randomInt = Int(arc4random_uniform(Uint32(questions.count)))
let questions = questionArray.questions
questionLabel.text = questions[randomInt]
let titlesArray = tArray.titleQuestions
titleLabel.text = titlesArray[randomInt]
}
(Assuming questions is of type Array, there's no reason to create an Index object in order to fetch an item from the array. Arrays allow integer subscripting.)
However, it might make more sense to create an array of structures that contain both a question and an answer. That way you would fetch an instance of a question-and-answer struct from your array and use it to populate both.
Edit:
If you wanted to create an array of structs it might look like this:
struct QuestionStruct {
let question: String
let possibleAnswers: [String]
let correctAnswerIndex: int
}
var questions: [QuestionStruct] =
[
QuestionStruct(
question: "What is the airspeed velocity of an unladen swallow?",
possibleAnswers: ["3 ft/sec", "2.2 ft/sec", "European or African"],
correctAnswerIndex: 2),
QuestionStruct(
question: "What is Your favorite color?",
possibleAnswers: ["Blue", "Green", "Blue. No, Green! Ieee!"],
correctAnswerIndex: 2)
]
//And then you might use code like this:
let randomIndex = Int(arc4random_uniform(UInt32(questions.count)))
let aQuestion = questions[randomIndex]
let correctAnswerIndex = aQuestion. correctAnswerIndex
questionLabel.text.text = aQuestion.question
correctAnswerLabel.text = aQuestion.possibleAnswers[correctAnswerIndex]
You can store your picked random number in a class variable so it is accessible both from your questions() and titles() methods.
Use a property declared at the class scope :
var lastRandomSeed: Int?
Reuse it in both of your methods :
if let lastRandomSeed = lastRandomSeed {
let randomQuestions = questions.index(questions.startIndex , offsetBy : lastRandomSeed)
lastRandomSeed = nil
} else {
lastRandomSeed = questions.count.arc4random
let randomQuestions = questions.index(questions.startIndex , offsetBy : lastRandomSeed)
}
and :
if let lastRandomSeed = lastRandomSeed {
titleLabel.text = titlesArray[lastRandomSeed]
lastRandomSeed = nil
} else {
lastRandomSeed = questions.count.arc4random
titleLabel.text = titlesArray[lastRandomSeed]
}
This also ensures you can call your methods in both order.
A better approach is to refactor your code, use computed property that will update the view :
var lastRandomSeed: Int = -1 {
didSet {
let questions = questionArray.questions
let randomQuestions = questions.index(questions.startIndex , offsetBy : lastRandomSeed)
let titlesArray = tArray.titleQuestions
questionLabel.text = questions[lastRandomSeed]
titleLabel.text = titlesArray[lastRandomSeed]
}
}
Just write it in your IBAction :
let questions = questionArray.questions
lastRandomSeed = questions.count.arc4random
You had to discipline yourself first. Try to name your variables, functions and even local variables by it's purpose. So at any point you can understand whats this variable/function/Type is used for.
So in you example I can assume that DataModel type is subclass of an array, because you assigning it's instance to variable questionArray. It is not very straightforward, but more or less ok (however better to name it DataArray). But then one who looking at you code can realise that your array not array. It is object that have array of questions in it. So you either had to name it let questionArrayHolder = DataModel() or fill it with questions: let questionArray = DataModel().questions.
But then you assign instance of TitleLabel to variable tArray. t can mean anything, and it definitely doesn't look like array. If you want to create array with single object in it, declare it titleArray = [TitleObjects]
Then you have function question()
It's name naturally suggests that it had to return some questions. But it returns nothing. If you want to generate questions name you function appropriate: func generateQuestions() . But your function do not generate questions, it picking up one question and assigning it to title. So you had name it like func pickupRandomQuestion().
You have lot of opportunities to share soothing between functions. You can declare member variable var selectedQuestion: Int? = 0 and assign your arc4rand number to it before retrieving you question.
You can find index of you question in array, assuming that all questions are unique, questions.index(of: questionLabel.text)
And you can return question number from you function:
func pickupRandomQuestion() -> Int {
let randNumber = questions.count.arc4random // I'm assuming that you have that function, and it works for you as you expect
let randomQuestion = questions.index(questions.startIndex , offsetBy : randNumber)
questionLabel.text = questions[randomQuestion]
return randNumber
}
And then supply it to other function
func pickupTitleForQuestion(at index: Int) {
titleLabel.text = titlesArray[index]
}

Swift: Appending object inside dictionary

I'd like to add my dictionary with array in it to my arrayOfDictionary, i tried the suggested answer on this link, but it didn't work for me. I'm having problem on appending object in my dictionary.
Here's my code:
func formatADate() {
var dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = NSDateFormatterStyle.LongStyle
var journalDictionary = Dictionary<String, [Journal]>()
for index in 0..<self.sortedJournal.count {
let dateString = dateFormatter.stringFromDate(self.sortedJournal[index].journalDate)
var oldDateString = ""
// Split Date
var dateStringArr = split(dateString){$0 == " "}
var newDateString = "\(dateStringArr[0]) \(dateStringArr[2])"
// Append journal to corresponding dates
// journalDictionary[newDateString]? += [self.sortedJournal[index]]
// journalDictionary[newDateString]!.append(self.sortedJournal[index])
journalDictionary[newDateString]?.append(self.sortedJournal[index])
// add to array of journal
if !(newDateString == oldDateString) || (oldDateString == "") {
arrayOfJournalDictionary.append(journalDictionary)
}
}
println(arrayOfJournalDictionary)
}
With this line of code, it gives me empty object.
journalDictionary[newDateString]?.append(self.sortedJournal[index])
Any suggestion on how I can accomplish/fix this? I'm pretty amateur in Swift, so any help would be appreciated! Thank you.
Woah, date magic with string splits and stuff, yeiks. You already are using NSDateFormatters, for the wrong reasons though. Since journalDate is a date, use the formatter to give you the exact string you want from that date, dont let it give you some generic string and split and concatenate and argh... NOBODY will understand what you are trying to do there.
Back to the question:
Your journalDictionary is empty, if you write
journalDictionary[newDateString]?.append(self.sortedJournal[index])
You tell the program to read at "index" newDateString and if there is something take that and append a new value to it.
Do you see the mistake already?
if there is something -> You have no logic yet to insert the initial empty [Journal] for any given key.
At some point in your code you have to set up the array like so:
journalDictionary["myKey"] = []
Or probably more likely:
if journalDictionary[newDateString] == nil {
journalDictionary[newDateString] = [self.sortedJournal[index]]
} else {
journalDictionary[newDateString]?.append(self.sortedJournal[index])
}

Cannot Invoke Filter With Argument List '((_) -> _)' [already implemented equatable]

I am trying to use filter on an array, but I keep getting this error. I checked the earlier answer, but I have already implemented "equatable" on my object.
Btw, what does this error mean anyway?
// trying to use filter
var distance_array = [
FilterOption(title: "0.5 miles", value:500, currentSetting: false)...]
var filtered_distance: [FilterOption]!
filtered_distance = distance_array.filter({ ($0.currentValue == true) })
// FilterOption Class
class FilterOption: NSObject, Equatable {
var title:String!
var value: AnyObject?
var currentSetting: Bool!
init(title:String, value:AnyObject?, currentSetting:Bool){
self.title = title
self.value = value
self.currentSetting = currentSetting
}
class func convertDictionaryToFilterOption(dict:Dictionary<String, String>) -> FilterOption{
return FilterOption(title:dict["name"]!, value:dict["code"]!, currentSetting: false)
}
}
func == (lhs: FilterOption, rhs: FilterOption) -> Bool {
var title = (lhs.title == rhs.title)
var setting = (lhs.currentSetting! == rhs.currentSetting!)
return title && setting
}
That compiler error is a little misleading. If you were really testing to see if one FilterOption was equal to another (a common problem that rears its head when comparing objects in filter closure), then you'd have to make it conform to Equatable. But that's not the problem here.
In this case the problem is simply that the code refers to currentValue, rather than currentSetting. If you fix that, the compiler error goes away.
By the way, you can simplify that filter statement:
let filteredDistance = distanceArray.filter { $0.currentSetting }
The currentSetting is already a Bool, so if you're testing to see if it's true, you can just return it directly. There were also some extraneous parentheses.

Find Object with Property in Array

is there a possibility to get an object from an array with an specific property? Or do i need to loop trough all objects in my array and check if an property is the specific i was looking for?
edit: Thanks for given me into the correct direction, but i have a problem to convert this.
// edit again: A ok, and if there is only one specific result? Is this also a possible method do to that?
let imageUUID = sender.imageUUID
let questionImageObjects = self.formImages[currentSelectedQuestion.qIndex] as [Images]!
// this is working
//var imageObject:Images!
/*
for (index, image) in enumerate(questionImageObjects) {
if(image.imageUUID == imageUUID) {
imageObject = image
}
}
*/
// this is not working - NSArray is not a subtype of Images- so what if there is only 1 possible result?
var imageObject = questionImageObjects.filter( { return $0.imageUUID == imageUUID } )
// this is not working - NSArray is not a subtype of Images- so what if there is only 1 possible result?
You have no way to prove at compile-time that there is only one possible result on an array. What you're actually asking for is the first matching result. The easiest (though not the fastest) is to just take the first element of the result of filter:
let imageObject = questionImageObjects.filter{ $0.imageUUID == imageUUID }.first
imageObject will now be an optional of course, since it's possible that nothing matches.
If searching the whole array is time consuming, of course you can easily create a firstMatching function that will return the (optional) first element matching the closure, but for short arrays this is fine and simple.
As charles notes, in Swift 3 this is built in:
questionImageObjects.first(where: { $0.imageUUID == imageUUID })
Edit 2016-05-05: Swift 3 will include first(where:).
In Swift 2, you can use indexOf to find the index of the first array element that matches a predicate.
let index = questionImageObjects.indexOf({$0.imageUUID == imageUUID})
This is bit faster compared to filter since it will stop after the first match. (Alternatively, you could use a lazy sequence.)
However, it's a bit annoying that you can only get the index and not the object itself. I use the following extension for convenience:
extension CollectionType {
func find(#noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
return try indexOf(predicate).map({self[$0]})
}
}
Then the following works:
questionImageObjects.find({$0.imageUUID == imageUUID})
Yes, you can use the filter method which takes a closure where you can set your logical expression.
Example:
struct User {
var firstName: String?
var lastName: String?
}
let users = [User(firstName: "John", lastName: "Doe"), User(firstName: "Bill", lastName: "Clinton"), User(firstName: "John", lastName: "Travolta")];
let johns = users.filter( { return $0.firstName == "John" } )
Note that filter returns an array containing all items satisfying the logical expression.
More info in the Library Reference
Here is a working example in Swift 5
class Point{
var x:Int
var y:Int
init(x:Int, y:Int){
self.x = x
self.y = y
}
}
var p1 = Point(x:1, y:2)
var p2 = Point(x:2, y:3)
var p3 = Point(x:1, y:4)
var points = [p1, p2, p3]
// Find the first object with given property
// In this case, firstMatchingPoint becomes p1
let firstMatchingPoint = points.first{$0.x == 1}
// Find all objects with given property
// In this case, allMatchingPoints becomes [p1, p3]
let allMatchingPoints = points.filter{$0.x == 1}
Reference:
Trailing Closure
Here is other way to fetch particular object by using object property to search an object in array.
if arrayTicketsListing.contains({ $0.status_id == "2" }) {
let ticketStatusObj: TicketsStatusList = arrayTicketsListing[arrayTicketsListing.indexOf({ $0.status_id == "2" })!]
print(ticketStatusObj.status_name)
}
Whereas, my arrayTicketsListing is [TicketsStatusList] contains objects of TicketsStatusList class.
// TicketsStatusList class
class TicketsStatusList {
internal var status_id: String
internal var status_name: String
init(){
status_id = ""
status_name = ""
}
}

Detect a Null value in NSDictionary

I have an NSDictionary that's populated from a JSON response from an API server. Sometimes the values for a key in this dictionary are Null
I am trying to take the given value and drop it into the detail text of a table cell for display.
The problem is that when I try to coerce the value into an NSString I get a crash, which I think is because I'm trying to coerce Null into a string.
What's the right way to do this?
What I want to do is something like this:
cell.detailTextLabel.text = sensor.objectForKey( "latestValue" ) as NSString
Here's an example of the Dictionary:
Printing description of sensor:
{
"created_at" = "2012-10-10T22:19:50.501-07:00";
desc = "<null>";
id = 2;
"latest_value" = "<null>";
name = "AC Vent Temp";
"sensor_type" = temp;
slug = "ac-vent-temp";
"updated_at" = "2013-11-17T15:34:27.495-07:00";
}
If I just need to wrap all of this in a conditional, that's fine. I just haven't been able to figure out what that conditional is. Back in the Objective-C world I would compare against [NSNull null] but that doesn't seem to be working in Swift.
You can use the as? operator, which returns an optional value (nil if the downcast fails)
if let latestValue = sensor["latestValue"] as? String {
cell.detailTextLabel.text = latestValue
}
I tested this example in a swift application
let x: AnyObject = NSNull()
if let y = x as? String {
println("I should never be printed: \(y)")
} else {
println("Yay")
}
and it correctly prints "Yay", whereas
let x: AnyObject = "hello!"
if let y = x as? String {
println(y)
} else {
println("I should never be printed")
}
prints "hello!" as expected.
You could also use is to check for the presence of a null:
if sensor["latestValue"] is NSNull {
// do something with null JSON value here
}
I'm using this combination and it also checks if object is not "null".
func isNotNull(object: AnyObject?) -> Bool {
guard let object = object else { return false }
return isNotNSNull(object) && isNotStringNull(object)
}
func isNotNSNull(object: AnyObject) -> Bool {
object.classForCoder != NSNull.classForCoder()
}
func isNotStringNull(object: AnyObject) -> Bool {
guard let object = object as? String where object.uppercaseString == "NULL" else {
return true
}
return false
}
It's not that pretty as extension but work as charm :)
NSNull is a class like any other. Thus you can use is or as to test an AnyObject reference against it.
Thus, here in one of my apps I have an NSArray where every entry is either a Card or NSNull (because you can't put nil in an NSArray). I fetch the NSArray as an Array and cycle through it, switching on which kind of object I get:
for card:AnyObject in arr {
switch card { // how to test for different possible types
case let card as NSNull:
// do one thing
case let card as Card:
// do a different thing
default:
fatalError("unexpected object in card array") // should never happen!
}
}
That is not identical to your scenario, but it is from a working app converted to Swift, and illustrates the full general technique.
my solution for now:
func isNull(someObject: AnyObject?) -> Bool {
guard let someObject = someObject else {
return true
}
return (someObject is NSNull)
}
tests look good so far...
I had a very similar problem and solved it with casting to the correct type of the original NSDictionary value. If your service returns a mixed type JSON object like this
{"id":2, "name":"AC Vent Temp", ...}
you'll have to fetch it's values like that.
var id:int = sensor.valueForKey("id") as Int;
var name:String? = sensor.valueForKey("name") as String;
This did solve my problem. See BAD_INSTRUCTION within swift closure

Resources