├── .gitignore ├── .travis.yml ├── Example └── QuizKitSample │ ├── QuizKitSample.xcodeproj │ └── project.pbxproj │ └── QuizKitSample │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── BaseQuestionViewController.h │ ├── BaseQuestionViewController.m │ ├── Default-568h@2x.png │ ├── Default.png │ ├── Default@2x.png │ ├── MultipleChoiceViewController.h │ ├── MultipleChoiceViewController.m │ ├── MultipleChoiceViewController.xib │ ├── OpenQuestionViewController.h │ ├── OpenQuestionViewController.m │ ├── OpenQuestionViewController.xib │ ├── QuizController.h │ ├── QuizKitSample-Info.plist │ ├── QuizKitSample-Prefix.pch │ ├── TrueFalseViewController.h │ ├── TrueFalseViewController.m │ ├── TrueFalseViewController.xib │ ├── ViewController.h │ ├── ViewController.m │ ├── en.lproj │ ├── InfoPlist.strings │ └── ViewController.xib │ ├── main.m │ └── quizzes │ └── programming.plist ├── README.md ├── Source ├── ISMatchingQuestion.h ├── ISMatchingQuestion.m ├── ISMultipleChoiceQuestion+Private.h ├── ISMultipleChoiceQuestion.h ├── ISMultipleChoiceQuestion.m ├── ISMultipleMultipleChoiceQuestion+Private.h ├── ISMultipleMultipleChoiceQuestion.h ├── ISMultipleMultipleChoiceQuestion.m ├── ISOpenQuestion.h ├── ISOpenQuestion.m ├── ISQuestion.h ├── ISQuestion.m ├── ISQuiz.h ├── ISQuiz.m ├── ISQuizKit.h ├── ISQuizParser.h ├── ISQuizParser.m ├── ISSession.h ├── ISSession.m ├── ISTrueFalseQuestion.h └── ISTrueFalseQuestion.m ├── iOS-QuizKit.podspec └── iOS-QuizKit ├── iOS-QuizKit.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ ├── iOS-QuizKit.xcscheme │ └── iOS-QuizKitTests.xcscheme ├── iOS-QuizKit ├── ISAppDelegate.h ├── ISAppDelegate.m ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── en.lproj │ └── InfoPlist.strings ├── iOS-QuizKit-Info.plist ├── iOS-QuizKit-Prefix.pch └── main.m └── iOS-QuizKitTests ├── ISMatchingQuestionTests.m ├── ISMultipleChoiceQuestionTests.m ├── ISOpenQuestionTests.m ├── default_selectable_options.plist ├── en.lproj └── InfoPlist.strings ├── iOS-QuizKitTests-Info.plist ├── iOS_QuizKitMultiChoiceGradeTests.m ├── iOS_QuizKitMultipleMultipleChoiceTests.m ├── iOS_QuizKitQuizParserTests.m ├── iOS_QuizKitTests.m ├── quiz_test.plist ├── quiz_test_matching.plist ├── quiz_test_multi_choice.plist ├── quiz_test_multi_multi_choice.plist ├── quiz_test_multiple_multi_multi_choice.plist ├── quiz_test_open.plist ├── quiz_test_sentence_multi_multi_choice.plist └── quiz_test_sentence_multi_multi_choice_multi_selection.plist /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | 3 | before_install: 4 | - brew update 5 | - if brew outdated | grep -qx xctool; then brew upgrade xctool; fi 6 | 7 | script: 8 | - xctool test -freshInstall -project iOS-QuizKit/iOS-QuizKit.xcodeproj -scheme iOS-QuizKitTests -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Inline Studios 3 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | 6 | 7 | #import 8 | 9 | @class ViewController; 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | @property (strong, nonatomic)UINavigationController* navController; 15 | @property (strong, nonatomic) ViewController *viewController; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Inline Studios 3 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | 6 | #import "AppDelegate.h" 7 | 8 | #import "ViewController.h" 9 | 10 | @implementation AppDelegate 11 | 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 16 | // Override point for customization after application launch. 17 | self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil]; 18 | 19 | self.navController = [[UINavigationController alloc] initWithRootViewController:self.viewController]; 20 | 21 | self.window.rootViewController = self.navController; 22 | [self.window makeKeyAndVisible]; 23 | return YES; 24 | } 25 | 26 | - (void)applicationWillResignActive:(UIApplication *)application 27 | { 28 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 29 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 30 | } 31 | 32 | - (void)applicationDidEnterBackground:(UIApplication *)application 33 | { 34 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 35 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 36 | } 37 | 38 | - (void)applicationWillEnterForeground:(UIApplication *)application 39 | { 40 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 41 | } 42 | 43 | - (void)applicationDidBecomeActive:(UIApplication *)application 44 | { 45 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 46 | } 47 | 48 | - (void)applicationWillTerminate:(UIApplication *)application 49 | { 50 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/BaseQuestionViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Inline Studios 3 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | 6 | #import 7 | #import "ISQuizKit.h" 8 | #import "QuizController.h" 9 | 10 | typedef void(^ISQuestionResponseWasGiven)(ISQuestionResponse* response); 11 | 12 | @interface BaseQuestionViewController : UIViewController 13 | 14 | @property(nonatomic, copy) ISQuestionResponseWasGiven questionResponseWasGiven; 15 | 16 | @property(nonatomic, strong) id controller; 17 | 18 | -(id)initWithController:(id )controller responceGivenBlock:(ISQuestionResponseWasGiven)responceGiven; 19 | 20 | - (void)scoreAndProgressWithResponse:(ISQuestionResponse*)response; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/BaseQuestionViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Inline Studios 3 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | 6 | #import "BaseQuestionViewController.h" 7 | 8 | @interface BaseQuestionViewController () 9 | 10 | @end 11 | 12 | @implementation BaseQuestionViewController 13 | 14 | -(id)initWithController:(id )controller responceGivenBlock:(ISQuestionResponseWasGiven)responceGiven{ 15 | 16 | if (self == [super init]) { 17 | _controller = controller; 18 | _questionResponseWasGiven = responceGiven; 19 | } 20 | 21 | return self; 22 | } 23 | 24 | - (void)scoreAndProgressWithResponse:(ISQuestionResponse*)response { 25 | 26 | if(_questionResponseWasGiven) { 27 | _questionResponseWasGiven(response); 28 | } 29 | 30 | [_controller nextQuestion]; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmeiners/iOS-Quizkit/ea0162b2d64e16bcf80d751a60e1fabc13e79858/Example/QuizKitSample/QuizKitSample/Default-568h@2x.png -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmeiners/iOS-Quizkit/ea0162b2d64e16bcf80d751a60e1fabc13e79858/Example/QuizKitSample/QuizKitSample/Default.png -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinmeiners/iOS-Quizkit/ea0162b2d64e16bcf80d751a60e1fabc13e79858/Example/QuizKitSample/QuizKitSample/Default@2x.png -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/MultipleChoiceViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Inline Studios 3 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | 6 | #import 7 | #import "ISQuizKit.h" 8 | #import "QuizController.h" 9 | #import "BaseQuestionViewController.h" 10 | 11 | @interface MultipleChoiceViewController : BaseQuestionViewController 12 | { 13 | IBOutlet UITextView* _questionTextView; 14 | IBOutlet UIPickerView* _pickerView; 15 | ISMultipleChoiceQuestion* _question; 16 | ISMultipleChoiceResponse* _response; 17 | } 18 | 19 | - (id)initWithMultipleChoiceQuestion:(ISMultipleChoiceQuestion*)question 20 | response:(ISMultipleChoiceOption*)response 21 | controller:(id )controller 22 | responceGiven:(ISQuestionResponseWasGiven)responceGiven; 23 | 24 | - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/MultipleChoiceViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Inline Studios 3 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | 6 | #import "MultipleChoiceViewController.h" 7 | 8 | @interface MultipleChoiceViewController () 9 | 10 | - (void)scoreAndProgress; 11 | 12 | @end 13 | 14 | @implementation MultipleChoiceViewController 15 | 16 | - (id)initWithMultipleChoiceQuestion:(ISMultipleChoiceQuestion*)question 17 | response:(ISMultipleChoiceResponse*)response 18 | controller:(id )controller 19 | responceGiven:(ISQuestionResponseWasGiven)responceGiven 20 | { 21 | if (self = [super initWithController:controller responceGivenBlock:responceGiven]) 22 | { 23 | _question = question; 24 | _response = response; 25 | } 26 | return self; 27 | } 28 | 29 | 30 | - (void)viewDidLoad 31 | { 32 | [super viewDidLoad]; 33 | _questionTextView.text = _question.text; 34 | 35 | UIBarButtonItem *anotherButton = [[UIBarButtonItem alloc] initWithTitle:@"Next" style:UIBarButtonItemStylePlain target:self action:@selector(next:)]; 36 | self.navigationItem.rightBarButtonItem = anotherButton; 37 | } 38 | 39 | - (void)next:(id)sender 40 | { 41 | [self scoreAndProgress]; 42 | } 43 | 44 | - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView 45 | { 46 | return 1; 47 | } 48 | 49 | - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component 50 | { 51 | return _question.options.count; 52 | } 53 | 54 | - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component 55 | { 56 | ISMultipleChoiceOption* option = (_question.options)[row]; 57 | return option.text; 58 | } 59 | 60 | - (void)didReceiveMemoryWarning 61 | { 62 | [super didReceiveMemoryWarning]; 63 | // Dispose of any resources that can be recreated. 64 | } 65 | 66 | - (void)scoreAndProgress { 67 | 68 | if(_response) { 69 | _response.answerIndex = (int)[_pickerView selectedRowInComponent:0]; 70 | } else { 71 | _response = [ISMultipleChoiceResponse responseWithAnswerIndex:(int)[_pickerView selectedRowInComponent:0]]; 72 | } 73 | 74 | [super scoreAndProgressWithResponse:_response]; 75 | } 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/MultipleChoiceViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/OpenQuestionViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Inline Studios 3 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | 6 | #import 7 | #import "BaseQuestionViewController.h" 8 | 9 | @interface OpenQuestionViewController : BaseQuestionViewController 10 | { 11 | ISOpenQuestion* _question; 12 | ISOpenQuestionResponse* _response; 13 | } 14 | @property (weak, nonatomic) IBOutlet UITextView *questionText; 15 | @property (weak, nonatomic) IBOutlet UITextField *responseField; 16 | 17 | - (id)initWithOpenQuestion:(ISOpenQuestion*)question 18 | response:(ISOpenQuestionResponse*)response 19 | controller:(id )controller 20 | responceGiven:(ISQuestionResponseWasGiven)responceGiven; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/OpenQuestionViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Inline Studios 3 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | 6 | #import "OpenQuestionViewController.h" 7 | 8 | @interface OpenQuestionViewController () 9 | 10 | - (void)scoreAndProgress; 11 | 12 | @end 13 | 14 | @implementation OpenQuestionViewController 15 | 16 | - (id)initWithOpenQuestion:(ISOpenQuestion*)question 17 | response:(ISOpenQuestionResponse*)response 18 | controller:(id )controller 19 | responceGiven:(ISQuestionResponseWasGiven)responceGiven 20 | { 21 | 22 | if (self = [super initWithController:controller responceGivenBlock:responceGiven]) 23 | { 24 | _question = question; 25 | _response = response; 26 | } 27 | return self; 28 | } 29 | 30 | - (void)viewDidLoad 31 | { 32 | [super viewDidLoad]; 33 | _questionText.text = _question.text; 34 | _responseField.delegate = self; 35 | 36 | UIBarButtonItem *anotherButton = [[UIBarButtonItem alloc] initWithTitle:@"Next" style:UIBarButtonItemStylePlain target:self action:@selector(next:)]; 37 | self.navigationItem.rightBarButtonItem = anotherButton; 38 | 39 | 40 | // Do any additional setup after loading the view from its nib. 41 | } 42 | 43 | - (void)viewDidAppear:(BOOL)animated 44 | { 45 | [_responseField becomeFirstResponder]; 46 | } 47 | 48 | - (BOOL)textFieldShouldReturn:(UITextField *)textField 49 | { 50 | [textField resignFirstResponder]; 51 | [self scoreAndProgress]; 52 | return true; 53 | } 54 | 55 | - (void)next:(id)sender 56 | { 57 | [self scoreAndProgress]; 58 | } 59 | 60 | - (void)didReceiveMemoryWarning 61 | { 62 | [super didReceiveMemoryWarning]; 63 | // Dispose of any resources that can be recreated. 64 | } 65 | 66 | - (void)scoreAndProgress { 67 | 68 | if(_response) { 69 | _response.response = _responseField.text; 70 | } else { 71 | _response = [ISOpenQuestionResponse responseWithResponse:_responseField.text]; 72 | } 73 | 74 | [super scoreAndProgressWithResponse:_response]; 75 | } 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/QuizController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Inline Studios 3 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | 6 | #import 7 | 8 | @protocol QuizController 9 | - (void)nextQuestion; 10 | - (ISSession*)session; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/QuizKitSample-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.inline-studios.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/QuizKitSample-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'QuizKitSample' target in the 'QuizKitSample' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_4_0 8 | #warning "This project uses features only available in iOS SDK 4.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/TrueFalseViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Inline Studios 3 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | 6 | #import 7 | #import "ISQuizKit.h" 8 | #import "QuizController.h" 9 | #import "BaseQuestionViewController.h" 10 | 11 | @interface TrueFalseViewController : BaseQuestionViewController 12 | { 13 | ISTrueFalseQuestion* _question; 14 | ISTrueFalseResponse* _response; 15 | } 16 | @property (weak, nonatomic) IBOutlet UITextView *questionText; 17 | @property (weak, nonatomic) IBOutlet UISwitch *answerSwitch; 18 | 19 | - (id)initWithTrueFalseQuestion:(ISTrueFalseQuestion*)question 20 | response:(ISTrueFalseResponse*)response 21 | controller:(id )controller 22 | responceGiven:(ISQuestionResponseWasGiven)responceGiven; 23 | 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/TrueFalseViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Inline Studios 3 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | #import "TrueFalseViewController.h" 6 | 7 | @interface TrueFalseViewController () 8 | 9 | - (void)scoreAndProgress; 10 | 11 | @end 12 | 13 | @implementation TrueFalseViewController 14 | 15 | - (id)initWithTrueFalseQuestion:(ISTrueFalseQuestion*)question 16 | response:(ISTrueFalseResponse*)response 17 | controller:(id )controller 18 | responceGiven:(ISQuestionResponseWasGiven)responceGiven 19 | { 20 | 21 | if (self = [super initWithController:controller responceGivenBlock:responceGiven]) 22 | { 23 | _question = question; 24 | _response = response; 25 | } 26 | return self; 27 | } 28 | 29 | - (void)viewDidLoad 30 | { 31 | [super viewDidLoad]; 32 | _questionText.text = _question.text; 33 | 34 | UIBarButtonItem *anotherButton = [[UIBarButtonItem alloc] initWithTitle:@"Next" style:UIBarButtonItemStylePlain target:self action:@selector(next:)]; 35 | self.navigationItem.rightBarButtonItem = anotherButton; 36 | } 37 | 38 | - (void)next:(id)sender 39 | { 40 | [self scoreAndProgress]; 41 | } 42 | 43 | - (void)didReceiveMemoryWarning 44 | { 45 | [super didReceiveMemoryWarning]; 46 | // Dispose of any resources that can be recreated. 47 | } 48 | 49 | - (void)scoreAndProgress { 50 | 51 | if(_response) { 52 | _response.response = _answerSwitch.isOn; 53 | } else { 54 | _response = [ISTrueFalseResponse responseWithResponse:_answerSwitch.isOn]; 55 | } 56 | 57 | [super scoreAndProgressWithResponse:_response]; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/TrueFalseViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1552 5 | 12F45 6 | 5053 7 | 1187.40 8 | 626.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 3733 12 | 13 | 14 | IBNSLayoutConstraint 15 | IBProxyObject 16 | IBUISwitch 17 | IBUITextView 18 | IBUIView 19 | 20 | 21 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 22 | 23 | 24 | PluginDependencyRecalculationVersion 25 | 26 | 27 | 28 | 29 | IBFilesOwner 30 | IBCocoaTouchFramework 31 | 32 | 33 | IBFirstResponder 34 | IBCocoaTouchFramework 35 | 36 | 37 | 38 | 274 39 | 40 | 41 | 42 | 292 43 | {{20, 20}, {280, 126}} 44 | 45 | 46 | 47 | _NS:9 48 | 49 | 1 50 | MSAxIDEAA 51 | 52 | YES 53 | YES 54 | IBCocoaTouchFramework 55 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 56 | 57 | 2 58 | IBCocoaTouchFramework 59 | 60 | 61 | 1 62 | 14 63 | 64 | 65 | HelveticaNeue 66 | 14 67 | 16 68 | 69 | 70 | 71 | 72 | 292 73 | {{107, 200}, {94, 27}} 74 | 75 | 76 | _NS:9 77 | NO 78 | IBCocoaTouchFramework 79 | 0 80 | 0 81 | YES 82 | 83 | 84 | {{0, 64}, {320, 504}} 85 | 86 | 87 | 88 | 89 | 3 90 | MQA 91 | 92 | 2 93 | 94 | 95 | 96 | 97 | NO 98 | 99 | 100 | IBUIScreenMetrics 101 | 102 | YES 103 | 104 | 105 | 106 | 107 | 108 | {320, 568} 109 | {568, 320} 110 | 111 | 112 | IBCocoaTouchFramework 113 | Retina 4-inch Full Screen 114 | 2 115 | 116 | IBCocoaTouchFramework 117 | 118 | 119 | 120 | 121 | 122 | 123 | view 124 | 125 | 126 | 127 | 3 128 | 129 | 130 | 131 | questionText 132 | 133 | 134 | 135 | 44 136 | 137 | 138 | 139 | answerSwitch 140 | 141 | 142 | 143 | 45 144 | 145 | 146 | 147 | 148 | 149 | 0 150 | 151 | 152 | 153 | 154 | 155 | 1 156 | 157 | 158 | 159 | 160 | 9 161 | 0 162 | 163 | 9 164 | 1 165 | 1 166 | 167 | 0.0 168 | 169 | 1000 170 | 171 | 6 172 | 24 173 | 2 174 | NO 175 | 176 | 177 | 178 | 3 179 | 0 180 | 181 | 3 182 | 1 183 | 1 184 | 185 | 200 186 | 187 | 1000 188 | 189 | 3 190 | 9 191 | 3 192 | NO 193 | 194 | 195 | 196 | 5 197 | 0 198 | 199 | 5 200 | 1 201 | 1 202 | 203 | 20 204 | 205 | 1000 206 | 207 | 0 208 | 29 209 | 3 210 | NO 211 | 212 | 213 | 214 | 6 215 | 0 216 | 217 | 6 218 | 1 219 | 1 220 | 221 | 20 222 | 223 | 1000 224 | 225 | 0 226 | 29 227 | 3 228 | NO 229 | 230 | 231 | 232 | 3 233 | 0 234 | 235 | 3 236 | 1 237 | 1 238 | 239 | 20 240 | 241 | 1000 242 | 243 | 0 244 | 29 245 | 3 246 | NO 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | -1 255 | 256 | 257 | File's Owner 258 | 259 | 260 | -2 261 | 262 | 263 | 264 | 265 | 4 266 | 267 | 268 | 269 | 270 | 8 271 | 0 272 | 273 | 0 274 | 1 275 | 1 276 | 277 | 126 278 | 279 | 1000 280 | 281 | 3 282 | 9 283 | 1 284 | NO 285 | 286 | 287 | 288 | 289 | 290 | 5 291 | 292 | 293 | 294 | 295 | 9 296 | 297 | 298 | 299 | 300 | 10 301 | 302 | 303 | 304 | 305 | 11 306 | 307 | 308 | 309 | 310 | 18 311 | 312 | 313 | 314 | 315 | 41 316 | 317 | 318 | 319 | 320 | 42 321 | 322 | 323 | 324 | 325 | 326 | 327 | TrueFalseViewController 328 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 329 | UIResponder 330 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 331 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 340 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 341 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 342 | 343 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 344 | 345 | 346 | 347 | 348 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 349 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 350 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 351 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 352 | 353 | 354 | 355 | 356 | 357 | 45 358 | 359 | 360 | 361 | 362 | BaseQuestionViewController 363 | UIViewController 364 | 365 | IBProjectSource 366 | ./Classes/BaseQuestionViewController.h 367 | 368 | 369 | 370 | NSLayoutConstraint 371 | NSObject 372 | 373 | IBProjectSource 374 | ./Classes/NSLayoutConstraint.h 375 | 376 | 377 | 378 | TrueFalseViewController 379 | BaseQuestionViewController 380 | 381 | UISwitch 382 | UITextView 383 | 384 | 385 | 386 | answerSwitch 387 | UISwitch 388 | 389 | 390 | questionText 391 | UITextView 392 | 393 | 394 | 395 | IBProjectSource 396 | ./Classes/TrueFalseViewController.h 397 | 398 | 399 | 400 | 401 | 0 402 | IBCocoaTouchFramework 403 | YES 404 | 405 | com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS 406 | 407 | 408 | 409 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 410 | 411 | 412 | YES 413 | 3 414 | YES 415 | 3733 416 | 417 | 418 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/ViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Inline Studios 3 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | 6 | 7 | #import 8 | #import "ISQuizKit.h" 9 | #import "QuizController.h" 10 | 11 | @interface ViewController : UIViewController 12 | { 13 | IBOutlet UILabel* _scoreLabel; 14 | ISSession* _session; 15 | int _questionIndex; 16 | ISQuiz* _quiz; 17 | } 18 | 19 | - (IBAction)startQuiz:(id)sender; 20 | - (void)nextQuestion; 21 | @end 22 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/ViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Inline Studios 3 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | 6 | 7 | #import "ViewController.h" 8 | #import "OpenQuestionViewController.h" 9 | #import "TrueFalseViewController.h" 10 | #import "MultipleChoiceViewController.h" 11 | 12 | @interface ViewController () 13 | 14 | @end 15 | 16 | @implementation ViewController 17 | 18 | - (void)viewDidLoad 19 | { 20 | [super viewDidLoad]; 21 | self.navigationController.delegate = self; 22 | _scoreLabel.text = @""; 23 | } 24 | 25 | - (void)didReceiveMemoryWarning 26 | { 27 | [super didReceiveMemoryWarning]; 28 | // Dispose of any resources that can be recreated. 29 | } 30 | 31 | - (IBAction)startQuiz:(id)sender 32 | { 33 | _quiz = [ISQuizParser quizNamed:@"programming.plist"]; 34 | 35 | _scoreLabel.text = @""; 36 | 37 | _session = [[ISSession alloc] init]; 38 | [_session start:_quiz]; 39 | 40 | _questionIndex = 0; 41 | [self nextQuestion]; 42 | } 43 | 44 | - (ISSession*)session 45 | { 46 | return _session; 47 | } 48 | 49 | - (void)nextQuestion 50 | { 51 | if (_questionIndex >= _quiz.questions.count) 52 | { 53 | [_session stop]; 54 | 55 | ISGradingResult* result = [ISQuiz gradeSession:_session quiz:_quiz]; 56 | 57 | 58 | _scoreLabel.text = [NSString stringWithFormat:@"Score %i/%i, Time: %.1fs,", result.points, result.pointsPossible, _session.time]; 59 | [self.navigationController popToRootViewControllerAnimated:true]; 60 | return; 61 | } 62 | 63 | ISQuestion* question = (_quiz.questions)[_questionIndex]; 64 | 65 | int questionIndex = _questionIndex; 66 | 67 | if ([question isKindOfClass:[ISOpenQuestion class]]) 68 | { 69 | 70 | OpenQuestionViewController* controller = [[OpenQuestionViewController alloc] initWithOpenQuestion:(ISOpenQuestion*)question 71 | response:nil 72 | controller:self 73 | responceGiven:^(ISQuestionResponse *response) { 74 | [_session setResponse:response atIndex:questionIndex]; 75 | }]; 76 | 77 | [self.navigationController pushViewController:controller animated:true]; 78 | } 79 | else if ([question isKindOfClass:[ISMultipleChoiceQuestion class]]) 80 | { 81 | MultipleChoiceViewController* controller = [[MultipleChoiceViewController alloc] initWithMultipleChoiceQuestion:(ISMultipleChoiceQuestion*)question 82 | response:NULL 83 | controller:self 84 | responceGiven:^(ISQuestionResponse *response) { 85 | [_session setResponse:response atIndex:questionIndex]; 86 | }]; 87 | [self.navigationController pushViewController:controller animated:true]; 88 | } 89 | else if ([question isKindOfClass:[ISTrueFalseQuestion class]]) 90 | { 91 | TrueFalseViewController* controller = [[TrueFalseViewController alloc] initWithTrueFalseQuestion:(ISTrueFalseQuestion*)question 92 | response:NULL 93 | controller:self 94 | responceGiven:^(ISQuestionResponse *response) { 95 | [_session setResponse:response atIndex:questionIndex]; 96 | }]; 97 | [self.navigationController pushViewController:controller animated:true]; 98 | } 99 | 100 | _questionIndex += 1; 101 | } 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/main.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Inline Studios 3 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | 6 | #import 7 | 8 | #import "AppDelegate.h" 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | @autoreleasepool { 13 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Example/QuizKitSample/QuizKitSample/quizzes/programming.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | questions 6 | 7 | 8 | type 9 | open 10 | answer 11 | NSObject 12 | matchMode 13 | caseSensitive 14 | text 15 | What is the base class of most objects in Foundation Objective-C projects? 16 | scoreValue 17 | 1 18 | 19 | 20 | type 21 | open 22 | answer 23 | gcd 24 | matchMode 25 | exact 26 | text 27 | What is the name of Apple's C threading API for Mac and iOS? 28 | scoreValue 29 | 1 30 | 31 | 32 | type 33 | open 34 | answer 35 | NSOBJECT 36 | matchMode 37 | close 38 | text 39 | What is your 40 | scoreValue 41 | 1 42 | 43 | 44 | type 45 | multipleChoice 46 | options 47 | 48 | 49 | text 50 | C 51 | correct 52 | 1 53 | 54 | 55 | text 56 | C++ 57 | 58 | 59 | text 60 | Java 61 | 62 | 63 | text 64 | Ruby 65 | 66 | 67 | text 68 | Objective-C 69 | correct 70 | 1 71 | 72 | 73 | text 74 | What is your favorite programming language? 75 | scoreValue 76 | 2 77 | 78 | 79 | type 80 | trueFalse 81 | text 82 | Is Xcode slow? 83 | answer 84 | 1 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | iOS-Quizkit 2 | =========== 3 | 4 | A fork of [iOS-Quizkit](https://github.com/narpas/iOS-Quizkit) to further develop the project: 5 | - intending to add unit tests with travis integration 6 | - new question types 7 | - base view controllers for question types 8 | - hopefully a pull request back to the iOS-QuizKit repo or separate cocoa pod 9 | 10 | * Have added scoring to the demo project and will continue to develop as View Controllers are added. 11 | 12 | [![Build Status](https://travis-ci.org/LostStudent/iOS-Quizkit.svg?branch=master)](https://travis-ci.org/LostStudent/iOS-Quizkit) 13 | 14 | iOS-Quizkit is an Objective-C API for creating applications with quiz and test features. 15 | For flexibility, the API focuses completely on the model layer and makes no attempt to offer a UI solution. 16 | 17 | Included is a quick sample app with the library integrated. It provides an example of how the API might be integrated into an application. 18 | 19 | Features: 20 | - Multiple choice questions 21 | - Open response questions with optional Levenshtein distance approximation 22 | - True/False questions 23 | - Full NSCodor support for quizzes, questions, and sessions. 24 | - Load quizzes from user created Plists and JSON. 25 | - Optional User Data on most structures. 26 | - Automatic or custom scoring 27 | - Retroactive session grading 28 | 29 | ```Objective-C 30 | // Load Quiz from user plist 31 | [ISQuizParser quizFromContentsOfPlist:...]; 32 | 33 | // Load Quiz from JSON file 34 | [ISQuizParser quizFromContentsOfJSON:...]; 35 | 36 | // Load Quiz from bundle 37 | [ISQuizParser quizNamed:...]; 38 | 39 | ``` 40 | 41 | ```Objective-C 42 | 43 | // Start a new quiz session 44 | ISSession* session = [ISSession session]; 45 | [session start:quiz]; 46 | 47 | // Submit answers 48 | [session setResponse:[ISOpenQuestionResponse responseWithResponse:...] atIndex:0]; 49 | [session setResponse:[ISTrueFalseQuestionResponse responseWithResponse:...] atIndex:1]; 50 | 51 | [session stop]; 52 | 53 | // Grading 54 | ISGradingResult* result = [quiz gradeSession:session]; 55 | NSLog(@"Score: %i/%i", result.points, result.pointsPossible); 56 | 57 | ``` 58 | -------------------------------------------------------------------------------- /Source/ISMatchingQuestion.h: -------------------------------------------------------------------------------- 1 | // 2 | // ISMatchingQuestion.h 3 | // iOS-QuizKit 4 | // 5 | // Created by Christian French on 17/06/2014. 6 | // Copyright (c) 2014 inline-studios. All rights reserved. 7 | // 8 | 9 | #import "ISQuestion.h" 10 | 11 | @interface ISMatchingResponse : ISQuestionResponse 12 | 13 | @property(nonatomic, strong)NSArray* options; 14 | /* 15 | Submit an answer option for each question option in the order of the question options. 16 | 17 | example 18 | question.options [1,two,3,four] 19 | question.answers [2,one,4,three] 20 | response.options [one,2,three,4] 21 | */ 22 | 23 | + (ISMatchingResponse*)responseWithOptions:(NSArray*)options; 24 | 25 | - (id)initWithCoder:(NSCoder *)aDecoder; 26 | 27 | - (void)encodeWithCoder:(NSCoder *)aCoder; 28 | 29 | @end 30 | 31 | @interface ISMatchingOption : NSObject 32 | @property(nonatomic, strong) NSDictionary* userData; 33 | @property(nonatomic, copy) NSString* text; 34 | @property(nonatomic, strong) ISMatchingOption* matchingOption; 35 | 36 | 37 | - (id)initWithCoder:(NSCoder *)aDecoder; 38 | 39 | - (void)encodeWithCoder:(NSCoder *)aCoder; 40 | 41 | + (ISMatchingOption*)optionWithText:(NSString*)text; 42 | 43 | + (ISMatchingOption*)optionWithText:(NSString*)text matchingOption:(ISMatchingOption*)matchingOption; 44 | 45 | @end 46 | 47 | @interface ISMatchingQuestion : ISQuestion 48 | 49 | @property(nonatomic, strong) NSArray* options; 50 | 51 | @property(nonatomic, strong) NSArray* answers; 52 | 53 | @property(nonatomic, strong) NSArray* randomizedAnswers; 54 | 55 | + (instancetype)questionWithOptions:(NSArray*)options answers:(NSArray*)answers; 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Source/ISMatchingQuestion.m: -------------------------------------------------------------------------------- 1 | // 2 | // ISMatchingQuestion.m 3 | // iOS-QuizKit 4 | // 5 | // Created by Christian French on 17/06/2014. 6 | // Copyright (c) 2014 inline-studios. All rights reserved. 7 | // 8 | 9 | #import "ISMatchingQuestion.h" 10 | #import "ISMultipleChoiceQuestion+Private.h" 11 | 12 | static NSString * const _ISAnswerOptionsKey = @"answerOptions"; 13 | 14 | static NSString * const _ISMatchingOptionsKey = @"MatchingOptions"; 15 | 16 | static NSString * const _ISMatchingAnswersKey = @"MatchingAnswers"; 17 | 18 | static NSString * const _ISMatchingOptionKey = @"MatchingOption"; 19 | 20 | @implementation ISMatchingResponse 21 | 22 | + (ISMatchingResponse*)responseWithOptions:(NSArray*)options { 23 | 24 | ISMatchingResponse* response = [ISMatchingResponse new]; 25 | 26 | response.options = options; 27 | 28 | return response; 29 | } 30 | 31 | - (id)initWithCoder:(NSCoder *)aDecoder 32 | { 33 | if (self = [super initWithCoder:aDecoder]) 34 | { 35 | self.options = [aDecoder decodeObjectForKey:_ISAnswerOptionsKey]; 36 | } 37 | return self; 38 | } 39 | 40 | - (void)encodeWithCoder:(NSCoder *)aCoder 41 | { 42 | [super encodeWithCoder:aCoder]; 43 | [aCoder encodeObject:_options forKey:_ISAnswerOptionsKey]; 44 | } 45 | 46 | @end 47 | 48 | @implementation ISMatchingOption 49 | 50 | - (id)initWithCoder:(NSCoder *)aDecoder 51 | { 52 | if (self = [super init]) 53 | { 54 | self.text = [aDecoder decodeObjectForKey:_ISTextKey]; 55 | self.matchingOption = [aDecoder decodeObjectForKey:_ISMatchingOptionKey]; 56 | } 57 | return self; 58 | } 59 | 60 | - (void)encodeWithCoder:(NSCoder *)aCoder 61 | { 62 | [aCoder encodeObject:_text forKey:_ISTextKey]; 63 | [aCoder encodeObject:_matchingOption forKey:_ISMatchingOptionKey]; 64 | } 65 | 66 | + (ISMatchingOption*)optionWithText:(NSString*)text { 67 | 68 | return [ISMatchingOption optionWithText:text matchingOption:nil]; 69 | } 70 | 71 | + (ISMatchingOption*)optionWithText:(NSString*)text matchingOption:(ISMatchingOption*)matchingOption { 72 | 73 | ISMatchingOption* option = [ISMatchingOption new]; 74 | 75 | option.text = text; 76 | 77 | option.matchingOption = matchingOption; 78 | 79 | return option; 80 | } 81 | 82 | - (NSString*)description 83 | { 84 | return [NSString stringWithFormat:@"<%@: %p, text: %@ >", 85 | NSStringFromClass([self class]), self, _text]; 86 | } 87 | 88 | @end 89 | 90 | @implementation ISMatchingQuestion 91 | 92 | 93 | + (instancetype)questionWithOptions:(NSArray*)options answers:(NSArray*)answers { 94 | 95 | ISMatchingQuestion* question = [[ISMatchingQuestion alloc] init]; 96 | 97 | question.answers = answers; 98 | 99 | question.options = options; 100 | 101 | [question randomizeAnswers]; 102 | 103 | return question; 104 | } 105 | 106 | - (id)initWithCoder:(NSCoder *)aDecoder 107 | { 108 | if (self = [super initWithCoder:aDecoder]) 109 | { 110 | _options = [NSArray arrayWithArray:[aDecoder decodeObjectForKey:_ISMatchingOptionsKey]]; 111 | _answers = [NSArray arrayWithArray:[aDecoder decodeObjectForKey:_ISMatchingAnswersKey]]; 112 | [self randomizeAnswers]; 113 | } 114 | return self; 115 | } 116 | 117 | - (void)encodeWithCoder:(NSCoder *)aCoder 118 | { 119 | [super encodeWithCoder:aCoder]; 120 | [aCoder encodeObject:_options forKey:_ISMatchingOptionsKey]; 121 | [aCoder encodeObject:_answers forKey:_ISMatchingAnswersKey]; 122 | } 123 | 124 | -(void)randomizeAnswers { 125 | 126 | NSMutableArray* options = [NSMutableArray arrayWithArray:_answers]; 127 | 128 | NSMutableArray* randomOptions = [NSMutableArray array]; 129 | 130 | while (options.count > 0) { 131 | 132 | NSInteger randomNumber = arc4random() % options.count; 133 | 134 | id option = options[randomNumber]; 135 | 136 | [randomOptions addObject:option]; 137 | 138 | [options removeObject:option]; 139 | } 140 | 141 | _randomizedAnswers = [NSArray arrayWithArray:randomOptions]; 142 | } 143 | 144 | - (BOOL)responseCorrect:(ISQuestionResponse*)response 145 | { 146 | if (![response isKindOfClass:[ISMatchingResponse class]]) 147 | { 148 | return NO; 149 | } 150 | 151 | ISMatchingResponse* matchingResponse = (ISMatchingResponse*)response; 152 | 153 | __block BOOL correct = YES; 154 | 155 | if(matchingResponse.options.count != _options.count) { 156 | 157 | return NO; 158 | 159 | } 160 | 161 | [_options enumerateObjectsUsingBlock:^(ISMatchingOption* obj, NSUInteger idx, BOOL *stop) { 162 | 163 | if(matchingResponse.options[idx] != obj.matchingOption){ 164 | 165 | correct = NO; 166 | 167 | *stop = YES; 168 | } 169 | }]; 170 | 171 | return correct; 172 | } 173 | 174 | @end -------------------------------------------------------------------------------- /Source/ISMultipleChoiceQuestion+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // ISMultipleChoiceQuestion_ISMultipleChoiceQuestion_Private.h 3 | // iOS-QuizKit 4 | // 5 | // Created by Christian French on 12/09/2014. 6 | // Copyright (c) 2014 inline-studios. All rights reserved. 7 | // 8 | 9 | #import "ISMultipleChoiceQuestion.h" 10 | 11 | @interface ISMultipleChoiceQuestion () 12 | 13 | @property(nonatomic,strong) NSArray* randomizedIndexesMap; 14 | 15 | -(void)randomizeOptions; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Source/ISMultipleChoiceQuestion.h: -------------------------------------------------------------------------------- 1 | /* 2 | By: Justin Meiners 3 | 4 | Copyright (c) 2013 Inline Studios 5 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | 8 | #import 9 | #import "ISQuestion.h" 10 | 11 | @interface ISMultipleChoiceResponse : ISQuestionResponse 12 | @property(nonatomic, assign)int answerIndex; 13 | @property(nonatomic, strong)NSArray* answerIndexes; 14 | 15 | + (ISMultipleChoiceResponse*)responseWithAnswerIndex:(int)answerIndex; 16 | 17 | - (id)init; 18 | - (id)initWithAnswerIndex:(int)answerIndex; 19 | - (id)initWithAnswerIndexes:(NSArray*)answerIndexes; 20 | 21 | - (id)initWithCoder:(NSCoder *)aDecoder; 22 | - (void)encodeWithCoder:(NSCoder *)aCoder; 23 | 24 | - (id)initWithIndex:(int)answerIndex; 25 | - (id)initWithIndexes:(NSArray*)answerIndexes; 26 | 27 | + (ISMultipleChoiceResponse*)responseWithIndex:(int)index; 28 | + (ISMultipleChoiceResponse*)responseWithIndexes:(NSArray*)indexes; 29 | 30 | @end 31 | 32 | @interface ISMultipleChoiceOption : NSObject 33 | @property(nonatomic, strong) NSDictionary* userData; 34 | @property(nonatomic, copy) NSString* text; 35 | @property(nonatomic, assign) BOOL correct; 36 | @property(nonatomic, assign) BOOL preSelected; 37 | 38 | - (id)initWithCoder:(NSCoder *)aDecoder; 39 | - (void)encodeWithCoder:(NSCoder *)aCoder; 40 | 41 | - (id)initWithText:(NSString*)text correct:(BOOL)correct; 42 | 43 | - (id)initWithText:(NSString*)text 44 | correct:(BOOL)correct 45 | userData:(NSDictionary*)userData; 46 | 47 | + (ISMultipleChoiceOption*)optionWithText:(NSString*)text correct:(BOOL)correct; 48 | 49 | @end 50 | 51 | @interface ISMultipleChoiceQuestion : ISQuestion 52 | 53 | @property(nonatomic, strong)NSArray* options; 54 | @property(nonatomic, strong)NSArray* correctOptions; 55 | @property(nonatomic, strong, readonly) NSArray* randomizedOptions; 56 | @property(nonatomic, strong)NSNumber* selectableOptions; 57 | 58 | - (id)initWithCoder:(NSCoder *)aDecoder; 59 | - (void)encodeWithCoder:(NSCoder *)aCoder; 60 | 61 | - (void)addOption:(ISMultipleChoiceOption*)option; 62 | - (void)addOptions:(NSArray*)options; 63 | - (void)removeAllOptions; 64 | 65 | - (BOOL)responseCorrect:(ISQuestionResponse*)response; 66 | 67 | - (BOOL)responseCorrectForRandomizedOptions:(ISQuestionResponse*)response; 68 | 69 | -(NSArray*)calculateCorrectFromOptions:(NSArray*)options; 70 | 71 | @end 72 | 73 | 74 | -------------------------------------------------------------------------------- /Source/ISMultipleChoiceQuestion.m: -------------------------------------------------------------------------------- 1 | /* 2 | By: Justin Meiners 3 | 4 | Copyright (c) 2013 Inline Studios 5 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | 8 | #import "ISMultipleChoiceQuestion.h" 9 | #import "ISMultipleChoiceQuestion+Private.h" 10 | 11 | static NSString * const _ISUserDataKey = @"userData"; 12 | static NSString * const _ISCorrectKey = @"correct"; 13 | static NSString * const _ISOptionsKey = @"options"; 14 | static NSString * const _ISAnswerIndexKey = @"answerIndex"; 15 | static NSString * const _ISAnswerIndexesKey = @"answerIndexes"; 16 | static NSString * const _ISSelectableOptionsKey = @"selectableOptions"; 17 | 18 | @implementation ISMultipleChoiceResponse 19 | 20 | -(int)answerIndex { 21 | 22 | if(_answerIndexes.count > 0) { 23 | 24 | NSNumber* answerIndex = _answerIndexes[0]; 25 | 26 | return answerIndex.intValue; 27 | 28 | } else { 29 | 30 | return -1; 31 | } 32 | } 33 | 34 | + (ISMultipleChoiceResponse*)responseWithAnswerIndex:(int)answerIndex 35 | { 36 | return [[self alloc] initWithAnswerIndexes:@[[NSNumber numberWithInt: answerIndex]]]; 37 | } 38 | 39 | + (ISMultipleChoiceResponse*)responseWithAnswerIndexes:(NSArray*)answerIndexes 40 | { 41 | return [[self alloc] initWithAnswerIndexes:answerIndexes]; 42 | } 43 | 44 | - (id)init 45 | { 46 | if (self = [super init]) 47 | { 48 | _answerIndexes = @[]; 49 | } 50 | return self; 51 | } 52 | 53 | - (id)initWithAnswerIndex:(int)answerIndex 54 | { 55 | if (self = [super init]) 56 | { 57 | _answerIndexes = @[[NSNumber numberWithInt: answerIndex]]; 58 | } 59 | return self; 60 | } 61 | 62 | - (id)initWithAnswerIndexes:(NSArray*)answerIndexes 63 | { 64 | if (self = [super init]) 65 | { 66 | _answerIndexes = answerIndexes; 67 | } 68 | return self; 69 | } 70 | 71 | - (id)initWithCoder:(NSCoder *)aDecoder 72 | { 73 | if (self = [super initWithCoder:aDecoder]) 74 | { 75 | if([aDecoder decodeIntForKey:_ISAnswerIndexKey]) { 76 | 77 | int answerIndex = [aDecoder decodeIntForKey:_ISAnswerIndexKey]; 78 | 79 | self.answerIndexes = @[[NSNumber numberWithInt: answerIndex]]; 80 | 81 | } else { 82 | 83 | self.answerIndexes = [aDecoder decodeObjectForKey:_ISAnswerIndexesKey]; 84 | 85 | } 86 | } 87 | return self; 88 | } 89 | 90 | - (void)encodeWithCoder:(NSCoder *)aCoder 91 | { 92 | [super encodeWithCoder:aCoder]; 93 | [aCoder encodeObject:_answerIndexes forKey:_ISAnswerIndexesKey]; 94 | } 95 | 96 | - (id)initWithIndex:(int)answerIndex 97 | { 98 | if (self = [super init]) 99 | { 100 | _answerIndexes = @[[NSNumber numberWithInt: answerIndex]]; 101 | } 102 | return self; 103 | } 104 | 105 | - (id)initWithIndexes:(NSArray*)answerIndexes 106 | { 107 | if (self = [super init]) 108 | { 109 | _answerIndexes = answerIndexes; 110 | } 111 | return self; 112 | } 113 | 114 | + (ISMultipleChoiceResponse*)responseWithIndex:(int)index 115 | { 116 | return [[ISMultipleChoiceResponse alloc] initWithIndexes: @[[NSNumber numberWithInt: index]]]; 117 | } 118 | 119 | + (ISMultipleChoiceResponse*)responseWithIndexes:(NSArray*)indexes 120 | { 121 | return [[ISMultipleChoiceResponse alloc] initWithIndexes:indexes]; 122 | } 123 | 124 | @end 125 | 126 | @implementation ISMultipleChoiceOption 127 | 128 | - (id)initWithCoder:(NSCoder *)aDecoder 129 | { 130 | if (self = [super init]) 131 | { 132 | self.text = [aDecoder decodeObjectForKey:_ISTextKey]; 133 | self.correct = [aDecoder decodeBoolForKey:_ISCorrectKey]; 134 | self.userData = [aDecoder decodeObjectForKey:_ISUserDataKey]; 135 | } 136 | return self; 137 | } 138 | 139 | - (void)encodeWithCoder:(NSCoder *)aCoder 140 | { 141 | [aCoder encodeObject:_text forKey:_ISTextKey]; 142 | [aCoder encodeBool:_correct forKey:_ISCorrectKey]; 143 | [aCoder encodeObject:_userData forKey:_ISUserDataKey]; 144 | } 145 | 146 | 147 | - (id)initWithText:(NSString*)text correct:(BOOL)correct 148 | { 149 | if (self = [super init]) 150 | { 151 | self.text = text; 152 | self.correct = correct; 153 | self.userData = NULL; 154 | } 155 | return self; 156 | } 157 | 158 | - (id)initWithText:(NSString*)text 159 | correct:(BOOL)correct 160 | userData:(NSDictionary*)userData 161 | { 162 | if (self = [super init]) 163 | { 164 | self.text = text; 165 | self.correct = correct; 166 | self.userData = userData; 167 | } 168 | 169 | return self; 170 | } 171 | 172 | 173 | - (NSString*)description 174 | { 175 | return [NSString stringWithFormat:@"<%@: %p, text: %@ correct: %i>", 176 | NSStringFromClass([self class]), self, _text, _correct]; 177 | } 178 | 179 | 180 | + (ISMultipleChoiceOption*)optionWithText:(NSString*)text correct:(BOOL)correct 181 | { 182 | return [[ISMultipleChoiceOption alloc] initWithText:text correct:correct]; 183 | } 184 | 185 | @end 186 | 187 | @implementation ISMultipleChoiceQuestion 188 | 189 | - (id)initWithCoder:(NSCoder *)aDecoder 190 | { 191 | if (self = [super initWithCoder:aDecoder]) 192 | { 193 | _options = [NSArray arrayWithArray:[aDecoder decodeObjectForKey:_ISOptionsKey]]; 194 | 195 | _correctOptions = [self calculateCorrectFromOptions:_options]; 196 | 197 | if([aDecoder decodeObjectForKey:_ISSelectableOptionsKey]) { 198 | 199 | _selectableOptions = [aDecoder decodeObjectForKey:_ISSelectableOptionsKey]; 200 | } else { 201 | 202 | _selectableOptions = @0; 203 | } 204 | 205 | [self randomizeOptions]; 206 | 207 | } 208 | return self; 209 | } 210 | 211 | - (void)encodeWithCoder:(NSCoder *)aCoder 212 | { 213 | [super encodeWithCoder:aCoder]; 214 | [aCoder encodeObject:_options forKey:_ISOptionsKey]; 215 | [aCoder encodeObject:_selectableOptions forKey:_ISSelectableOptionsKey]; 216 | } 217 | 218 | 219 | - (id)init 220 | { 221 | if (self = [super init]) 222 | { 223 | _options = [NSArray new]; 224 | _correctOptions = [NSArray new]; 225 | _selectableOptions = @0; 226 | } 227 | return self; 228 | } 229 | 230 | 231 | - (NSString*)description 232 | { 233 | return [NSString stringWithFormat:@"<%@: %p, option count: %lu>", 234 | NSStringFromClass([self class]), self, (unsigned long)_options.count]; 235 | } 236 | 237 | - (void)addOption:(ISMultipleChoiceOption*)option 238 | { 239 | _options = [_options arrayByAddingObject:option]; 240 | 241 | _correctOptions = [self calculateCorrectFromOptions:_options]; 242 | 243 | [self randomizeOptions]; 244 | } 245 | 246 | - (void)addOptions:(NSArray*)options 247 | { 248 | _options = [_options arrayByAddingObjectsFromArray:options]; 249 | 250 | _correctOptions = [self calculateCorrectFromOptions:_options]; 251 | 252 | [self randomizeOptions]; 253 | } 254 | 255 | - (void)removeAllOptions 256 | { 257 | _options = @[]; 258 | 259 | _correctOptions = @[]; 260 | } 261 | 262 | -(void)randomizeOptions { 263 | 264 | NSMutableArray* options = [NSMutableArray arrayWithArray:_options]; 265 | 266 | NSMutableArray* randomOptions = [NSMutableArray array]; 267 | 268 | NSMutableArray* randomizedMap = [NSMutableArray array]; 269 | 270 | while (options.count > 0) { 271 | 272 | NSInteger randomNumber = arc4random() % options.count; 273 | 274 | id option = options[randomNumber]; 275 | 276 | [randomOptions addObject:option]; 277 | 278 | [randomizedMap addObject:[NSNumber numberWithInteger:[_options indexOfObject:option]]]; 279 | 280 | [options removeObject:option]; 281 | } 282 | 283 | _randomizedIndexesMap = [NSArray arrayWithArray:randomizedMap]; 284 | 285 | _randomizedOptions = [NSArray arrayWithArray:randomOptions]; 286 | } 287 | 288 | - (BOOL)responseCorrect:(ISQuestionResponse*)response 289 | { 290 | if (![response isKindOfClass:[ISMultipleChoiceResponse class]]) 291 | { 292 | return NO; 293 | } 294 | 295 | ISMultipleChoiceResponse* multipleChoiceResponse = (ISMultipleChoiceResponse*)response; 296 | 297 | if (_selectableOptions.integerValue == 0) { 298 | _selectableOptions = [NSNumber numberWithInteger: _correctOptions.count]; 299 | } 300 | 301 | if(multipleChoiceResponse.answerIndexes.count < _selectableOptions.integerValue || multipleChoiceResponse.answerIndexes.count > _selectableOptions.integerValue ) { 302 | 303 | return NO; 304 | } 305 | 306 | BOOL correct = YES; 307 | 308 | for (NSNumber *indexNumber in multipleChoiceResponse.answerIndexes) { 309 | 310 | int index = indexNumber.intValue; 311 | 312 | if (index < 0 || index > _options.count) 313 | { 314 | correct = NO; 315 | break; 316 | } 317 | 318 | ISMultipleChoiceOption* option = _options[index]; 319 | 320 | if(!option.correct) { 321 | 322 | correct = NO; 323 | break; 324 | } 325 | } 326 | 327 | return correct; 328 | } 329 | 330 | - (BOOL)responseCorrectForRandomizedOptions:(ISQuestionResponse*)response { 331 | 332 | 333 | if (![response isKindOfClass:[ISMultipleChoiceResponse class]]) 334 | { 335 | return NO; 336 | } 337 | 338 | if (_selectableOptions.integerValue == 0) { 339 | _selectableOptions = [NSNumber numberWithInteger: _correctOptions.count]; 340 | } 341 | 342 | ISMultipleChoiceResponse* multipleChoiceResponse = (ISMultipleChoiceResponse*)response; 343 | 344 | if(multipleChoiceResponse.answerIndexes.count < _selectableOptions.integerValue || multipleChoiceResponse.answerIndexes.count > _selectableOptions.integerValue ) { 345 | 346 | return NO; 347 | } 348 | 349 | NSMutableArray* options = [NSMutableArray array]; 350 | 351 | for (NSNumber *indexNumber in multipleChoiceResponse.answerIndexes) { 352 | 353 | NSNumber* origialIndex = _randomizedIndexesMap[indexNumber.integerValue]; 354 | 355 | [options addObject:origialIndex]; 356 | } 357 | 358 | return [self responseCorrect:[ISMultipleChoiceResponse responseWithAnswerIndexes:options]]; 359 | 360 | } 361 | 362 | -(NSArray*)calculateCorrectFromOptions:(NSArray*)options { 363 | 364 | NSMutableArray* array = [NSMutableArray array]; 365 | 366 | for (ISMultipleChoiceOption* option in options) { 367 | if(option.correct) { 368 | 369 | [array addObject:option]; 370 | } 371 | } 372 | 373 | return [NSArray arrayWithArray:array]; 374 | } 375 | 376 | 377 | @end 378 | -------------------------------------------------------------------------------- /Source/ISMultipleMultipleChoiceQuestion+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // ISMultipleMultipleChoiceQuestion.h 3 | // iOS-QuizKit 4 | // 5 | // Created by Christian French on 30/04/2014. 6 | // Copyright (c) 2014 inline-studios. All rights reserved. 7 | // 8 | 9 | #import "ISMultipleChoiceQuestion.h" 10 | 11 | @interface ISMultipleMultipleChoiceQuestion () 12 | 13 | @property(nonatomic, strong) NSArray* questions; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Source/ISMultipleMultipleChoiceQuestion.h: -------------------------------------------------------------------------------- 1 | // 2 | // ISMultipleMultipleChoiceQuestion.h 3 | // iOS-QuizKit 4 | // 5 | // Created by Christian French on 30/04/2014. 6 | // Copyright (c) 2014 inline-studios. All rights reserved. 7 | // 8 | 9 | #import "ISQuestion.h" 10 | #import "ISMultipleChoiceQuestion.h" 11 | 12 | @interface ISMultipleMultipleChoiceQuestion : ISMultipleChoiceQuestion 13 | 14 | 15 | + (instancetype)questionWithQuestions:(NSArray*)questions; 16 | 17 | - (id)initWithQuestions:(NSArray*)questions; 18 | 19 | - (void)addQuestion:(ISMultipleChoiceQuestion*)question; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Source/ISMultipleMultipleChoiceQuestion.m: -------------------------------------------------------------------------------- 1 | // 2 | // ISMultipleMultipleChoiceQuestion.m 3 | // iOS-QuizKit 4 | // 5 | // Created by Christian French on 30/04/2014. 6 | // Copyright (c) 2014 inline-studios. All rights reserved. 7 | // 8 | 9 | #import "ISMultipleMultipleChoiceQuestion.h" 10 | 11 | #import "ISMultipleMultipleChoiceQuestion+Private.h" 12 | 13 | @implementation ISMultipleMultipleChoiceQuestion 14 | 15 | + (instancetype)questionWithQuestions:(NSArray*)questions { 16 | 17 | return [[ISMultipleMultipleChoiceQuestion alloc] initWithQuestions:questions]; 18 | } 19 | 20 | - (id)initWithQuestions:(NSArray*)questions { 21 | 22 | if(self = [super init]) { 23 | 24 | NSMutableArray* options = [NSMutableArray array]; 25 | 26 | for (ISMultipleChoiceQuestion* question in questions) { 27 | 28 | [options addObject:question.options]; 29 | } 30 | 31 | self.options = [NSArray arrayWithArray:options]; 32 | 33 | _questions = questions; 34 | 35 | } 36 | return self; 37 | } 38 | 39 | - (NSArray *)options { 40 | 41 | NSMutableArray* array = [NSMutableArray array]; 42 | 43 | for (ISMultipleChoiceQuestion* question in _questions) { 44 | 45 | [array addObject:question.options]; 46 | } 47 | return [NSArray arrayWithArray:array]; 48 | } 49 | 50 | - (BOOL)responseCorrect:(ISQuestionResponse*)response { 51 | 52 | if (![response isKindOfClass:[ISMultipleChoiceResponse class]]) 53 | { 54 | return NO; 55 | } 56 | 57 | ISMultipleChoiceResponse* multipleChoiceResponse = (ISMultipleChoiceResponse*)response; 58 | 59 | BOOL correct = YES; 60 | 61 | for (ISMultipleChoiceQuestion* question in _questions) { 62 | 63 | NSUInteger idx = [_questions indexOfObject:question]; 64 | 65 | if(idx >= multipleChoiceResponse.answerIndexes.count) { 66 | 67 | return NO; 68 | } 69 | 70 | NSArray* answerIndexes = multipleChoiceResponse.answerIndexes[idx]; 71 | 72 | correct = [question responseCorrect: [ISMultipleChoiceResponse responseWithIndexes:answerIndexes]]; 73 | 74 | if(!correct) { 75 | 76 | break; 77 | } 78 | } 79 | 80 | return correct; 81 | } 82 | 83 | - (void)addQuestion:(ISMultipleChoiceQuestion*)question { 84 | 85 | if(!_questions) { 86 | 87 | _questions = @[]; 88 | } 89 | 90 | _questions = [_questions arrayByAddingObject:question]; 91 | 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /Source/ISOpenQuestion.h: -------------------------------------------------------------------------------- 1 | /* 2 | By: Justin Meiners 3 | 4 | Copyright (c) 2013 Inline Studios 5 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | 8 | #import 9 | #import "ISQuestion.h" 10 | 11 | @interface ISOpenQuestionResponse : ISQuestionResponse 12 | @property(nonatomic, copy)NSString* response; 13 | 14 | + (ISOpenQuestionResponse*)responseWithResponse:(NSString*)response; 15 | 16 | - (id)init; 17 | - (id)initWithResponse:(NSString*)response; 18 | 19 | - (id)initWithCoder:(NSCoder *)aDecoder; 20 | - (void)encodeWithCoder:(NSCoder *)aCoder; 21 | @end 22 | 23 | typedef BOOL (^ISMatchFunc_t)(NSString* answer, NSString* response); 24 | 25 | 26 | typedef enum 27 | { 28 | kISOpenQuestionMatchModeClose = 0, // May ignore exact spelling DEFAULT 29 | kISOpenQuestionMatchModeExact, // Same characters - but not same case 30 | kISOpenQuestionMatchModeCaseSensitive, // Case sensitve Exact match 31 | kISOpenQuestionContainsAll, // check if all the answers are present in the response 32 | kISOpenQuestionMatchModeCustom // code will use a custom match block 33 | 34 | } ISOpenQuestionMatchMode; 35 | 36 | @interface ISOpenQuestion : ISQuestion 37 | { 38 | NSMutableArray* _answers; 39 | } 40 | @property(nonatomic, strong)NSArray* options; 41 | @property(nonatomic, readonly)NSArray* answers; 42 | @property(nonatomic, assign)ISOpenQuestionMatchMode matchMode; 43 | @property(nonatomic, copy)ISMatchFunc_t customMatchFunc; // only works on custom match mode 44 | 45 | - (id)init; 46 | - (id)initWithAnswers:(NSArray*)answers; 47 | - (id)initWithAnswer:(NSString*)answer; 48 | 49 | - (id)initWithCoder:(NSCoder *)aDecoder; 50 | - (void)encodeWithCoder:(NSCoder *)aCoder; 51 | - (BOOL)responseCorrect:(ISQuestionResponse*)response; 52 | 53 | - (void)addAnswer:(NSString*)answer; 54 | - (void)addAnswers:(NSArray*)answers; 55 | - (void)removeAllAnswers; 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Source/ISOpenQuestion.m: -------------------------------------------------------------------------------- 1 | /* 2 | By: Justin Meiners 3 | 4 | Copyright (c) 2013 Inline Studios 5 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | 8 | #import "ISOpenQuestion.h" 9 | 10 | static NSString * const _ISResponseKey = @"response"; 11 | static NSString * const _ISAnswersKey = @"answers"; 12 | static NSString * const _ISMatchModeKey = @"matchMode"; 13 | 14 | @implementation ISOpenQuestionResponse 15 | 16 | + (ISOpenQuestionResponse*)responseWithResponse:(NSString*)response 17 | { 18 | return [[self alloc] initWithResponse:response]; 19 | } 20 | 21 | - (id)init 22 | { 23 | if (self = [super init]) 24 | { 25 | self.response = nil; 26 | } 27 | return self; 28 | } 29 | 30 | 31 | - (id)initWithResponse:(NSString*)response 32 | { 33 | if (self = [super init]) 34 | { 35 | self.response = response; 36 | } 37 | return self; 38 | } 39 | 40 | - (id)initWithCoder:(NSCoder *)aDecoder 41 | { 42 | if (self = [super initWithCoder:aDecoder]) 43 | { 44 | self.response = [aDecoder decodeObjectForKey:_ISResponseKey]; 45 | } 46 | return self; 47 | } 48 | 49 | - (void)encodeWithCoder:(NSCoder *)aCoder 50 | { 51 | [super encodeWithCoder:aCoder]; 52 | [aCoder encodeObject:_response forKey:_ISResponseKey]; 53 | } 54 | 55 | @end 56 | 57 | static const ISMatchFunc_t _ISExactMatchFunc = ^BOOL(NSString *answer, NSString *response) { 58 | return [[answer lowercaseString] isEqualToString:[response lowercaseString]]; 59 | }; 60 | 61 | static const ISMatchFunc_t _ISCaseSensitiveMatchFunc = ^BOOL(NSString *answer, NSString *response) { 62 | return [answer isEqualToString:response]; 63 | }; 64 | 65 | static const ISMatchFunc_t _ISCloseMatchFunc = ^BOOL(NSString *answer, NSString *response) { 66 | 67 | answer = [answer lowercaseString]; 68 | response = [response lowercaseString]; 69 | 70 | NSUInteger sl = [answer length]; 71 | NSUInteger tl = [response length]; 72 | NSUInteger *d = calloc(sizeof(*d), (sl+1) * (tl+1)); 73 | 74 | #define d(i, j) d[((j) * sl) + (i)] 75 | for (NSUInteger i = 0; i <= sl; i++) { 76 | d(i, 0) = i; 77 | } 78 | for (NSUInteger j = 0; j <= tl; j++) { 79 | d(0, j) = j; 80 | } 81 | for (NSUInteger j = 1; j <= tl; j++) { 82 | for (NSUInteger i = 1; i <= sl; i++) { 83 | if ([answer characterAtIndex:i-1] == [response characterAtIndex:j-1]) { 84 | d(i, j) = d(i-1, j-1); 85 | } else { 86 | d(i, j) = MIN(d(i-1, j), MIN(d(i, j-1), d(i-1, j-1))) + 1; 87 | } 88 | } 89 | } 90 | 91 | NSUInteger r = d(sl, tl); 92 | #undef d 93 | 94 | free(d); 95 | 96 | if (r <= 3) 97 | { 98 | return YES; 99 | } 100 | else 101 | { 102 | return NO; 103 | } 104 | }; 105 | 106 | static const ISMatchFunc_t _ISContainsMatchFunc = ^BOOL(NSString *answer, NSString *response) { 107 | return ([response rangeOfString:answer].location != NSNotFound); 108 | }; 109 | 110 | @implementation ISOpenQuestion 111 | 112 | - (id)initWithAnswers:(NSArray*)answers 113 | { 114 | if (self = [super init]) 115 | { 116 | _answers = [[NSMutableArray alloc] init]; 117 | self.matchMode = kISOpenQuestionMatchModeClose; 118 | [self addAnswers:answers]; 119 | } 120 | return self; 121 | } 122 | 123 | - (id)initWithAnswer:(NSString*)answer 124 | { 125 | if (self = [super init]) 126 | { 127 | _answers = [[NSMutableArray alloc] init]; 128 | self.matchMode = kISOpenQuestionMatchModeClose; 129 | [self addAnswer:answer]; 130 | } 131 | return self; 132 | } 133 | 134 | - (id)init 135 | { 136 | if (self = [super init]) 137 | { 138 | _answers = [[NSMutableArray alloc] init]; 139 | self.matchMode = kISOpenQuestionMatchModeClose; 140 | } 141 | return self; 142 | } 143 | 144 | 145 | - (id)initWithCoder:(NSCoder *)aDecoder 146 | { 147 | if (self = [super initWithCoder:aDecoder]) 148 | { 149 | _answers = [[NSMutableArray alloc] init]; 150 | [_answers addObjectsFromArray:[aDecoder decodeObjectForKey:_ISAnswersKey]]; 151 | self.matchMode = [aDecoder decodeIntForKey:_ISMatchModeKey]; 152 | } 153 | return self; 154 | } 155 | 156 | - (void)encodeWithCoder:(NSCoder *)aCoder 157 | { 158 | [super encodeWithCoder:aCoder]; 159 | 160 | [aCoder encodeObject:self.answers forKey:_ISAnswersKey]; 161 | [aCoder encodeInt:self.matchMode forKey:_ISMatchModeKey]; 162 | } 163 | 164 | - (NSString*)description 165 | { 166 | return [NSString stringWithFormat:@"<%@: %p, answers: %@>", 167 | NSStringFromClass([self class]), self, _answers]; 168 | } 169 | 170 | - (void)setCustomMatchFunc:(ISMatchFunc_t)custommatchFunc 171 | { 172 | 173 | if (custommatchFunc) 174 | { 175 | _customMatchFunc = [custommatchFunc copy]; 176 | } 177 | else 178 | { 179 | _customMatchFunc = nil; 180 | } 181 | } 182 | 183 | 184 | - (BOOL)responseCorrect:(ISQuestionResponse*)response 185 | { 186 | if (![response isKindOfClass:[ISOpenQuestionResponse class]]) 187 | { 188 | return NO; 189 | } 190 | 191 | ISOpenQuestionResponse* casted = (ISOpenQuestionResponse*)response; 192 | 193 | ISMatchFunc_t matchFunc = nil; 194 | 195 | if (_matchMode == kISOpenQuestionMatchModeCaseSensitive) 196 | { 197 | matchFunc = _ISCaseSensitiveMatchFunc; 198 | } 199 | else if (_matchMode == kISOpenQuestionMatchModeExact) 200 | { 201 | matchFunc = _ISExactMatchFunc; 202 | } 203 | else if (_matchMode == kISOpenQuestionMatchModeClose) 204 | { 205 | matchFunc = _ISCloseMatchFunc; 206 | } 207 | else if (_matchMode == kISOpenQuestionMatchModeCustom) 208 | { 209 | matchFunc = _customMatchFunc; 210 | } 211 | else if (_matchMode == kISOpenQuestionContainsAll) 212 | { 213 | matchFunc = _ISContainsMatchFunc; 214 | } 215 | else 216 | { 217 | NSLog(@"invalid match mode: %i", _matchMode); 218 | return NO; 219 | } 220 | 221 | if(_matchMode == kISOpenQuestionContainsAll) { 222 | 223 | BOOL correct = YES; 224 | 225 | for (NSString* answer in _answers) 226 | { 227 | if (!matchFunc(answer, casted.response)) 228 | { 229 | return NO; 230 | } 231 | } 232 | 233 | return correct; 234 | 235 | } else { 236 | 237 | for (NSString* answer in _answers) 238 | { 239 | if (matchFunc(answer, casted.response)) 240 | { 241 | return YES; 242 | } 243 | } 244 | 245 | } 246 | 247 | return NO; 248 | } 249 | 250 | - (void)addAnswer:(NSString*)answer 251 | { 252 | [_answers addObject:answer]; 253 | } 254 | 255 | - (void)addAnswers:(NSArray*)answers 256 | { 257 | [_answers addObjectsFromArray:answers]; 258 | } 259 | 260 | - (void)removeAllAnswers 261 | { 262 | [_answers removeAllObjects]; 263 | } 264 | 265 | @end 266 | -------------------------------------------------------------------------------- /Source/ISQuestion.h: -------------------------------------------------------------------------------- 1 | /* 2 | By: Justin Meiners 3 | 4 | Copyright (c) 2013 Inline Studios 5 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | 8 | #import 9 | 10 | extern NSString * const _ISTextKey; 11 | 12 | @interface ISQuestionResponse : NSObject 13 | @property(nonatomic, strong)NSDictionary* userData; 14 | 15 | - (id)initWithCoder:(NSCoder *)aDecoder; 16 | - (void)encodeWithCoder:(NSCoder *)aCoder; 17 | 18 | @end 19 | 20 | @interface ISEmptyQuestionResponse : ISQuestionResponse 21 | 22 | + (ISEmptyQuestionResponse*)emptyResponse; 23 | 24 | @end 25 | 26 | @interface ISQuestion : NSObject 27 | @property(nonatomic, copy)NSString* text; 28 | @property(nonatomic, strong)NSDictionary* userData; 29 | @property(nonatomic, assign)int scoreValue; 30 | @property(nonatomic, strong) NSString* questionType; 31 | @property(nonatomic, strong) NSString* questionSubType; 32 | @property(nonatomic, strong)NSString* supplementaryText; 33 | 34 | 35 | - (id)initWithCoder:(NSCoder *)aDecoder; 36 | - (void)encodeWithCoder:(NSCoder *)aCoder; 37 | 38 | - (BOOL)responseCorrect:(ISQuestionResponse*)response; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Source/ISQuestion.m: -------------------------------------------------------------------------------- 1 | /* 2 | By: Justin Meiners 3 | 4 | Copyright (c) 2013 Inline Studios 5 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | 8 | #import "ISQuestion.h" 9 | 10 | static NSString * const _ISUserDataKey = @"userData"; 11 | NSString * const _ISTextKey = @"text"; 12 | static NSString * const _ISTypeKey = @"type"; 13 | static NSString * const _ISScoreValueKey = @"scoreValue"; 14 | 15 | @implementation ISQuestionResponse 16 | 17 | - (id)initWithCoder:(NSCoder *)aDecoder 18 | { 19 | if (self = [super init]) 20 | { 21 | self.userData = [aDecoder decodeObjectForKey:_ISUserDataKey]; 22 | } 23 | return self; 24 | } 25 | 26 | - (void)encodeWithCoder:(NSCoder *)aCoder 27 | { 28 | [aCoder encodeObject:_userData forKey:_ISUserDataKey]; 29 | } 30 | 31 | @end 32 | 33 | @implementation ISEmptyQuestionResponse 34 | 35 | + (ISEmptyQuestionResponse*)emptyResponse 36 | { 37 | return [[self alloc] init]; 38 | } 39 | 40 | @end 41 | 42 | @implementation ISQuestion 43 | 44 | - (id)init 45 | { 46 | if (self = [super init]) 47 | { 48 | self.text = nil; 49 | self.userData = nil; 50 | self.scoreValue = 1; 51 | } 52 | return self; 53 | } 54 | 55 | 56 | - (id)initWithCoder:(NSCoder *)aDecoder 57 | { 58 | if (self = [super init]) 59 | { 60 | self.text = [aDecoder decodeObjectForKey:_ISTextKey]; 61 | self.userData = [aDecoder decodeObjectForKey:_ISUserDataKey]; 62 | self.scoreValue = [aDecoder decodeIntForKey:_ISScoreValueKey]; 63 | } 64 | return self; 65 | } 66 | 67 | - (void)encodeWithCoder:(NSCoder *)aCoder 68 | { 69 | [aCoder encodeObject:_text forKey:_ISTextKey]; 70 | [aCoder encodeObject:_userData forKey:_ISUserDataKey]; 71 | [aCoder encodeInt:_scoreValue forKey:_ISScoreValueKey]; 72 | } 73 | 74 | 75 | - (BOOL)responseCorrect:(ISQuestionResponse*)response 76 | { 77 | return NO; 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /Source/ISQuiz.h: -------------------------------------------------------------------------------- 1 | /* 2 | By: Justin Meiners 3 | 4 | Copyright (c) 2013 Inline Studios 5 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | 8 | #import 9 | #import "ISQuestion.h" 10 | 11 | @interface ISGradingResult : NSObject 12 | @property(nonatomic, assign)float pointPercentage; 13 | @property(nonatomic, assign)int pointsPossible; 14 | @property(nonatomic, assign)int points; 15 | @property(nonatomic, assign)int questionsCorrect; 16 | @property(nonatomic, assign)int questionsPossible; 17 | @property(nonatomic, assign)float questionPercentage; 18 | @end 19 | 20 | @class ISSession; 21 | 22 | @interface ISQuiz : NSObject 23 | { 24 | NSMutableArray* _questions; 25 | NSTimeInterval _timeLimit; 26 | } 27 | @property(nonatomic, readonly)NSArray* questions; 28 | @property(nonatomic, assign)NSTimeInterval timeLimit; 29 | 30 | + (ISGradingResult*)gradeSession:(ISSession*)session quiz:(ISQuiz*)quiz; 31 | 32 | - (id)init; 33 | - (id)initWithCoder:(NSCoder *)aDecoder; 34 | - (void)encodeWithCoder:(NSCoder *)aCoder; 35 | 36 | - (ISGradingResult*)gradeSession:(ISSession*)session; 37 | 38 | - (void)addQuestion:(ISQuestion*)question; 39 | - (void)removeQuestion:(ISQuestion*)question; 40 | - (void)removeAllQuestions; 41 | 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Source/ISQuiz.m: -------------------------------------------------------------------------------- 1 | /* 2 | By: Justin Meiners 3 | 4 | Copyright (c) 2013 Inline Studios 5 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | 8 | #import "ISQuiz.h" 9 | #import "ISSession.h" 10 | 11 | static NSString * const _ISQuestionsKey = @"questions"; 12 | static NSString * const _ISTimeLimitKey = @"timeLimit"; 13 | 14 | @implementation ISGradingResult 15 | 16 | @end 17 | 18 | 19 | @implementation ISQuiz 20 | 21 | + (ISGradingResult*)gradeSession:(ISSession*)session quiz:(ISQuiz*)quiz 22 | { 23 | int totalPoints = 0; 24 | int totalQuestions = (int)quiz.questions.count; 25 | 26 | int correctQuestions = 0; 27 | int correctPoints = 0; 28 | 29 | for (int i = 0; i < totalQuestions; i ++) 30 | { 31 | ISQuestion* question = (quiz.questions)[i]; 32 | ISQuestionResponse* response = (session.responses)[i]; 33 | 34 | BOOL correct = [question responseCorrect:response]; 35 | 36 | if (correct) 37 | { 38 | correctQuestions++; 39 | correctPoints += question.scoreValue; 40 | } 41 | 42 | totalPoints += question.scoreValue; 43 | } 44 | 45 | ISGradingResult* result = [[ISGradingResult alloc] init]; 46 | result.pointPercentage = (float)correctPoints / (float)totalPoints; 47 | result.points = correctPoints; 48 | result.pointsPossible = totalPoints; 49 | result.questionsCorrect = correctQuestions; 50 | result.questionsPossible = totalQuestions; 51 | result.questionPercentage = (float)correctQuestions / (float)totalQuestions; 52 | return result; 53 | } 54 | 55 | - (id)init 56 | { 57 | if (self = [super init]) 58 | { 59 | _questions = [[NSMutableArray alloc] init]; 60 | self.timeLimit = -1.0; 61 | } 62 | return self; 63 | } 64 | 65 | 66 | - (id)initWithCoder:(NSCoder *)decoder 67 | { 68 | if (self = [super init]) 69 | { 70 | _questions = [[NSMutableArray alloc] init]; 71 | [_questions addObjectsFromArray:[decoder decodeObjectForKey:_ISQuestionsKey]]; 72 | 73 | _timeLimit = [decoder decodeDoubleForKey:_ISTimeLimitKey]; 74 | } 75 | return self; 76 | } 77 | 78 | - (void)encodeWithCoder:(NSCoder *)coder 79 | { 80 | [coder encodeObject:_questions forKey:_ISQuestionsKey]; 81 | [coder encodeDouble:_timeLimit forKey:_ISTimeLimitKey]; 82 | } 83 | 84 | - (ISGradingResult*)gradeSession:(ISSession*)session 85 | { 86 | return [ISQuiz gradeSession:session quiz:self]; 87 | } 88 | 89 | - (NSString*)description 90 | { 91 | return [NSString stringWithFormat:@"<%@: %p, questions: %@>", 92 | NSStringFromClass([self class]), self, _questions]; 93 | } 94 | 95 | - (void)addQuestion:(ISQuestion*)question 96 | { 97 | [_questions addObject:question]; 98 | } 99 | 100 | - (void)removeQuestion:(ISQuestion*)question 101 | { 102 | [_questions removeObject:question]; 103 | } 104 | 105 | - (void)removeAllQuestions 106 | { 107 | [_questions removeAllObjects]; 108 | } 109 | 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /Source/ISQuizKit.h: -------------------------------------------------------------------------------- 1 | /* 2 | By: Justin Meiners 3 | 4 | Copyright (c) 2013 Inline Studios 5 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | 8 | #define IS_QUIZ_KIT_VERSION_1_0 0 9 | 10 | #define IS_QUIZ_KIT_VERSION IS_QUIZ_KIT_VERSION_1_0 11 | 12 | /* 13 | v1.0: 14 | - Initial Release 15 | 16 | */ 17 | 18 | #import "ISQuiz.h" 19 | #import "ISQuizParser.h" 20 | #import "ISOpenQuestion.h" 21 | #import "ISMultipleChoiceQuestion.h" 22 | #import "ISMultipleMultipleChoiceQuestion.h" 23 | #import "ISTrueFalseQuestion.h" 24 | #import "ISSession.h" 25 | #import "ISMatchingQuestion.h" -------------------------------------------------------------------------------- /Source/ISQuizParser.h: -------------------------------------------------------------------------------- 1 | /* 2 | By: Justin Meiners 3 | 4 | Copyright (c) 2013 Inline Studios 5 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | 8 | #import 9 | #import "ISQuiz.h" 10 | #import "ISSession.h" 11 | 12 | 13 | static NSString * const kISQuestionTypeOpen = @"open"; 14 | static NSString * const kISQuestionTypeMultipleChoice = @"multipleChoice"; 15 | static NSString * const kISQuestionTypeMultipleMultipleChoice = @"multipleMultipleChoice"; 16 | static NSString * const kISQuestionTypeTrueFalse = @"trueFalse"; 17 | static NSString * const kISQuestionTypeMultipleMultipleChoiceSentence = @"multipleMultipleChoiceSentence"; 18 | static NSString * const kISQuestionTypeMatching = @"matching"; 19 | 20 | static NSString * const kISTypeKey = @"type"; 21 | static NSString * const kISSubTypeKey = @"subType"; 22 | static NSString * const kISupplementaryTextKey = @"supplementaryText"; 23 | static NSString * const kISQuestionsKey = @"questions"; 24 | static NSString * const kISTextKey = @"text"; 25 | static NSString * const kISPreSelectedKey = @"preSelected"; 26 | static NSString * const kISAnswerKey = @"answer"; 27 | static NSString * const kISAnswersKey = @"answers"; 28 | static NSString * const kISOptionsKey = @"options"; 29 | static NSString * const kISCorrectKey = @"correct"; 30 | static NSString * const kISMatchModeKey = @"matchMode"; 31 | static NSString * const kISScoreValueKey = @"scoreValue"; 32 | static NSString * const kISSelectableOptionsKey = @"selectableOptions"; 33 | 34 | @interface ISQuizParser : NSObject 35 | 36 | // does not do UIImage style caching, just a shorcut for NSBundle 37 | + (ISQuiz*)quizNamed:(NSString*)name; // NSEncoder - unless extension is .plist or .json 38 | 39 | + (ISQuiz*)quizWithContentsOfFile:(NSString*)file; // NSEncoder 40 | + (ISQuiz*)quizFromContentsOfPlist:(NSString*)file; // plist 41 | + (ISQuiz*)quizFromContentsOfJSON:(NSString*)jsonFile; // json 42 | + (ISQuiz*)quizFromDictionary:(NSDictionary*)dictionary; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Source/ISQuizParser.m: -------------------------------------------------------------------------------- 1 | /* 2 | By: Justin Meiners 3 | 4 | Copyright (c) 2013 Inline Studios 5 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | 8 | #import "ISQuizParser.h" 9 | #import "ISOpenQuestion.h" 10 | #import "ISMultipleChoiceQuestion.h" 11 | #import "ISTrueFalseQuestion.h" 12 | #import "ISMultipleMultipleChoiceQuestion.h" 13 | #import "ISMatchingQuestion.h" 14 | @implementation ISQuizParser 15 | 16 | + (ISQuiz*)quizNamed:(NSString*)name 17 | { 18 | NSString* extension = [[name pathExtension] lowercaseString]; 19 | NSBundle *bundle = [NSBundle bundleForClass:[self class]]; 20 | NSString* fullpath = [bundle pathForResource:name ofType:nil]; 21 | 22 | if ([extension isEqualToString:@"plist"]) 23 | { 24 | return [self quizFromContentsOfPlist:fullpath]; 25 | } 26 | else if ([extension isEqualToString:@"json"]) 27 | { 28 | return [self quizFromContentsOfJSON:fullpath]; 29 | } 30 | else 31 | { 32 | return [self quizWithContentsOfFile:fullpath]; 33 | } 34 | } 35 | 36 | + (ISQuiz*)quizWithContentsOfFile:(NSString*)file 37 | { 38 | NSData* data = [NSData dataWithContentsOfFile:file]; 39 | 40 | if (!data) 41 | { 42 | return nil; 43 | } 44 | 45 | ISQuiz* quiz = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 46 | 47 | if (![quiz isKindOfClass:[ISQuiz class]]) 48 | { 49 | return nil; 50 | } 51 | 52 | return quiz; 53 | } 54 | 55 | 56 | + (ISQuiz*)quizFromContentsOfPlist:(NSString*)file 57 | { 58 | NSDictionary* plist = [[NSDictionary alloc] initWithContentsOfFile:file]; 59 | 60 | return [self quizFromDictionary:plist]; 61 | } 62 | 63 | + (ISQuiz*)quizFromContentsOfJSON:(NSString*)jsonFile 64 | { 65 | NSData* data = [NSData dataWithContentsOfFile:jsonFile]; 66 | 67 | if (!data) 68 | { 69 | return nil; 70 | } 71 | 72 | NSError* error = nil; 73 | 74 | NSDictionary* plist = [NSJSONSerialization JSONObjectWithData:data 75 | options:0 76 | error:&error]; 77 | 78 | if (error) 79 | { 80 | NSLog(@"quiz json error: %@", [error description]); 81 | return nil; 82 | } 83 | 84 | return [self quizFromDictionary:plist]; 85 | } 86 | 87 | + (BOOL)verify:(id )object class:(Class)class 88 | { 89 | if (!object) 90 | { 91 | return false; 92 | } 93 | 94 | return [object isKindOfClass:class]; 95 | } 96 | 97 | + (ISQuiz*)quizFromDictionary:(NSDictionary*)dictionary 98 | { 99 | NSArray* questions = dictionary[kISQuestionsKey]; 100 | 101 | if (![self verify:questions class:[NSArray class]]) 102 | { 103 | NSLog(@"missing questions"); 104 | return nil; 105 | } 106 | 107 | ISQuiz* quiz = [[ISQuiz alloc] init]; 108 | 109 | for (NSDictionary* questionDict in questions) 110 | { 111 | NSString* type = questionDict[kISTypeKey]; 112 | 113 | ISQuestion* newQuestion = nil; 114 | 115 | if ([type isEqualToString:kISQuestionTypeOpen]) 116 | { 117 | ISOpenQuestion* question = [[ISOpenQuestion alloc] init]; 118 | 119 | question.options = questionDict[kISOptionsKey]; 120 | 121 | if (questionDict[kISAnswerKey]) 122 | { 123 | [question addAnswer:questionDict[kISAnswerKey]]; 124 | } 125 | else if (questionDict[kISAnswersKey]) 126 | { 127 | [question addAnswers:questionDict[kISAnswersKey]]; 128 | } 129 | else // if contain options parse to answers 130 | { 131 | NSLog(@"missing question answer"); 132 | return nil; 133 | } 134 | 135 | NSString* matchMode = questionDict[kISMatchModeKey]; 136 | 137 | if (matchMode) 138 | { 139 | if ([matchMode isEqualToString:@"close"]) 140 | { 141 | question.matchMode = kISOpenQuestionMatchModeClose; 142 | } 143 | else if ([matchMode isEqualToString:@"exact"]) 144 | { 145 | question.matchMode = kISOpenQuestionMatchModeExact; 146 | } 147 | else if ([matchMode isEqualToString:@"caseSensitive"]) 148 | { 149 | question.matchMode = kISOpenQuestionMatchModeCaseSensitive; 150 | } 151 | else if ([matchMode isEqualToString:@"contains"]) 152 | { 153 | question.matchMode = kISOpenQuestionContainsAll; 154 | } 155 | else 156 | { 157 | NSLog(@"unknown match mode: %@", matchMode); 158 | return nil; 159 | } 160 | } 161 | 162 | newQuestion = question; 163 | } 164 | else if ([type isEqualToString:kISQuestionTypeMultipleChoice]) 165 | { 166 | ISMultipleChoiceQuestion* question = [[ISMultipleChoiceQuestion alloc] init]; 167 | 168 | if(questionDict[kISSelectableOptionsKey]) { 169 | 170 | question.selectableOptions = questionDict[kISSelectableOptionsKey]; 171 | 172 | } 173 | 174 | NSArray* options = questionDict[kISOptionsKey]; 175 | 176 | if (![self verify:options class:[NSArray class]]) 177 | { 178 | NSLog(@"missing multiple choice options"); 179 | return nil; 180 | } 181 | 182 | NSInteger correctCount = 0; 183 | 184 | for (NSDictionary* optionDict in options) 185 | { 186 | ISMultipleChoiceOption* option = [[ISMultipleChoiceOption alloc] init]; 187 | option.text = optionDict[kISTextKey]; 188 | option.preSelected = [optionDict[kISPreSelectedKey] boolValue]; 189 | if (optionDict[kISCorrectKey]) 190 | { 191 | option.correct = [optionDict[kISCorrectKey] boolValue]; 192 | 193 | if(option.correct) { 194 | 195 | correctCount++; 196 | } 197 | } 198 | else 199 | { 200 | option.correct = false; 201 | } 202 | 203 | [question addOption:option]; 204 | } 205 | 206 | if(question.selectableOptions.integerValue == 0) { 207 | 208 | question.selectableOptions = [NSNumber numberWithInteger:correctCount]; 209 | } 210 | 211 | newQuestion = question; 212 | } 213 | else if ([type isEqualToString:kISQuestionTypeMultipleMultipleChoice]) 214 | { 215 | ISMultipleMultipleChoiceQuestion* question = [[ISMultipleMultipleChoiceQuestion alloc] init]; 216 | 217 | if(questionDict[kISSelectableOptionsKey]) { 218 | 219 | question.selectableOptions = questionDict[kISSelectableOptionsKey]; 220 | 221 | } 222 | 223 | question.supplementaryText = questionDict[kISupplementaryTextKey]; 224 | 225 | NSArray* options = questionDict[kISOptionsKey]; 226 | 227 | if (![self verify:options class:[NSArray class]]) 228 | { 229 | NSLog(@"missing multiple choice options"); 230 | return nil; 231 | } 232 | 233 | for (NSArray* section in options) { 234 | 235 | ISMultipleChoiceQuestion* multipleChoiceQuestion = [[ISMultipleChoiceQuestion alloc] init]; 236 | 237 | multipleChoiceQuestion.selectableOptions = question.selectableOptions; 238 | 239 | NSInteger correctCount = 0; 240 | 241 | for (NSDictionary* optionDict in section) 242 | { 243 | ISMultipleChoiceOption* option = [[ISMultipleChoiceOption alloc] init]; 244 | option.text = optionDict[kISTextKey]; 245 | option.preSelected = [optionDict[kISPreSelectedKey] boolValue]; 246 | if (optionDict[kISCorrectKey]) 247 | { 248 | option.correct = [optionDict[kISCorrectKey] boolValue]; 249 | 250 | if(option.correct) { 251 | 252 | correctCount++; 253 | } 254 | } 255 | else 256 | { 257 | option.correct = false; 258 | } 259 | 260 | [multipleChoiceQuestion addOption:option]; 261 | } 262 | 263 | if(question.selectableOptions.integerValue == 0) { 264 | 265 | question.selectableOptions = [NSNumber numberWithInteger:correctCount]; 266 | } 267 | 268 | 269 | [question addQuestion:multipleChoiceQuestion]; 270 | 271 | } 272 | 273 | newQuestion = question; 274 | } 275 | else if ([type isEqualToString:kISQuestionTypeMultipleMultipleChoiceSentence]) 276 | { 277 | ISMultipleMultipleChoiceQuestion* question = [[ISMultipleMultipleChoiceQuestion alloc] init]; 278 | 279 | if(questionDict[kISSelectableOptionsKey]) { 280 | 281 | question.selectableOptions = questionDict[kISSelectableOptionsKey]; 282 | 283 | } 284 | 285 | NSArray* options = questionDict[kISOptionsKey]; 286 | 287 | if (![self verify:options class:[NSArray class]]) 288 | { 289 | NSLog(@"missing multiple choice options"); 290 | return nil; 291 | } 292 | 293 | for (NSDictionary* option in options) { 294 | 295 | NSString* optionText = option[kISTextKey]; 296 | 297 | NSArray* optionWords = [optionText componentsSeparatedByString:@" "]; 298 | 299 | NSString* correctText = option[kISCorrectKey]; 300 | 301 | NSArray* correctWordIndexes = [correctText componentsSeparatedByString:@" "]; 302 | 303 | ISMultipleChoiceQuestion* multipleChoiceQuestion = [[ISMultipleChoiceQuestion alloc] init]; 304 | 305 | //multipleChoiceQuestion.selectableOptions = question.selectableOptions; 306 | 307 | NSInteger correctCount = 0; 308 | 309 | for (NSString* word in optionWords) 310 | { 311 | ISMultipleChoiceOption* option = [[ISMultipleChoiceOption alloc] init]; 312 | 313 | option.text = word; 314 | 315 | NSInteger currentIndex = [optionWords indexOfObjectIdenticalTo:word]; 316 | 317 | NSUInteger index = [correctWordIndexes indexOfObjectPassingTest:^BOOL(NSString* obj, NSUInteger idx, BOOL *stop) { 318 | 319 | NSInteger correctIndex = [obj integerValue]; 320 | 321 | if(currentIndex == correctIndex){ 322 | *stop = YES; 323 | return YES; 324 | } 325 | 326 | return NO; 327 | }]; 328 | 329 | if(index != NSNotFound) { 330 | 331 | option.correct = YES; 332 | 333 | correctCount++; 334 | 335 | } 336 | 337 | [multipleChoiceQuestion addOption:option]; 338 | } 339 | 340 | if(multipleChoiceQuestion.selectableOptions.integerValue == 0) { 341 | 342 | multipleChoiceQuestion.selectableOptions = [NSNumber numberWithInteger:correctCount]; 343 | } 344 | 345 | //for the moment this doesnt do anything, just for consistancy 346 | question.selectableOptions = [NSNumber numberWithInteger:correctCount]; 347 | 348 | [question addQuestion:multipleChoiceQuestion]; 349 | 350 | } 351 | 352 | newQuestion = question; 353 | } 354 | else if ([type isEqualToString:kISQuestionTypeTrueFalse]) 355 | { 356 | ISTrueFalseQuestion* question = [[ISTrueFalseQuestion alloc] init]; 357 | 358 | if (![self verify:questionDict[kISAnswerKey] class:[NSNumber class]]) 359 | { 360 | NSLog(@"missing annswer"); 361 | return nil; 362 | } 363 | 364 | question.answer = [questionDict[kISAnswerKey] boolValue]; 365 | 366 | 367 | newQuestion = question; 368 | } 369 | else if ([type isEqualToString:kISQuestionTypeMatching]) 370 | { 371 | 372 | 373 | NSArray* answers = questionDict[kISAnswersKey]; 374 | 375 | NSMutableArray* questionAnswers = [NSMutableArray array]; 376 | 377 | if (![self verify:answers class:[NSArray class]]) 378 | { 379 | NSLog(@"missing answers"); 380 | return nil; 381 | } 382 | 383 | for (NSDictionary* answer in answers) { 384 | 385 | NSString* optionText = answer[kISTextKey]; 386 | 387 | [questionAnswers addObject:[ISMatchingOption optionWithText:optionText]]; 388 | } 389 | 390 | 391 | 392 | NSArray* options = questionDict[kISOptionsKey]; 393 | 394 | NSMutableArray* questionOptions = [NSMutableArray array]; 395 | 396 | if (![self verify:options class:[NSArray class]]) 397 | { 398 | NSLog(@"missing options"); 399 | return nil; 400 | } 401 | 402 | for (NSDictionary* option in options) { 403 | 404 | NSString* optionText = option[kISTextKey]; 405 | 406 | NSNumber* answerIndex = option[kISCorrectKey]; 407 | 408 | [questionOptions addObject:[ISMatchingOption optionWithText:optionText matchingOption:questionAnswers[answerIndex.integerValue]]]; 409 | } 410 | 411 | ISMatchingQuestion* question = [ISMatchingQuestion questionWithOptions:questionOptions answers:questionAnswers]; 412 | 413 | newQuestion = question; 414 | 415 | } 416 | else 417 | { 418 | NSLog(@"unknown question type: %@", type); 419 | continue; 420 | } 421 | 422 | newQuestion.text = questionDict[kISTextKey]; 423 | 424 | newQuestion.supplementaryText = questionDict[kISupplementaryTextKey]; 425 | 426 | newQuestion.questionType = type; 427 | 428 | newQuestion.questionSubType = questionDict[kISSubTypeKey]; 429 | 430 | if (questionDict[kISScoreValueKey]) 431 | { 432 | newQuestion.scoreValue = [questionDict[kISScoreValueKey] intValue]; 433 | } 434 | 435 | [quiz addQuestion:newQuestion]; 436 | } 437 | 438 | return quiz; 439 | } 440 | 441 | @end 442 | -------------------------------------------------------------------------------- /Source/ISSession.h: -------------------------------------------------------------------------------- 1 | /* 2 | By: Justin Meiners 3 | 4 | Copyright (c) 2013 Inline Studios 5 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | 8 | #import 9 | #import "ISQuiz.h" 10 | 11 | @class ISSession; 12 | 13 | @protocol ISSessionDelegate 14 | 15 | @optional 16 | - (void)sessionStarted:(ISSession*)session; 17 | - (void)sessionStopped:(ISSession*)session; 18 | 19 | - (void)sessionPaused:(ISSession*)session; 20 | - (void)sessionResumed:(ISSession*)session; 21 | 22 | - (BOOL)sessionShouldStopAtTimeLimit:(ISSession*)session; 23 | 24 | @end 25 | 26 | @interface ISSession : NSObject 27 | { 28 | NSDate* _startDate; 29 | NSMutableArray* _responses; 30 | NSTimeInterval _time; 31 | NSTimeInterval _bonusTime; 32 | NSTimer* _sessionTimer; 33 | ISQuiz* _currentQuiz; 34 | } 35 | 36 | @property(nonatomic, strong) NSDate *startDate; 37 | @property(nonatomic, readonly)NSMutableArray* responses; 38 | @property(nonatomic, assign) NSTimeInterval time; 39 | @property(nonatomic, assign) NSTimeInterval bonusTime; 40 | @property(nonatomic, strong)NSDictionary* userData; 41 | @property(nonatomic, strong)NSTimer* sessionTimer; 42 | @property(nonatomic, readonly)BOOL inSession; 43 | @property(nonatomic, strong) ISQuiz* currentQuiz; 44 | @property(nonatomic, assign)id delegate; 45 | 46 | + (ISSession*)session; 47 | 48 | - (id)initWithCoder:(NSCoder *)aDecoder; 49 | - (void)encodeWithCoder:(NSCoder *)aCoder; 50 | 51 | - (id)init; 52 | 53 | - (BOOL)start:(ISQuiz*)quiz; 54 | - (void)stop; 55 | 56 | - (void)setResponse:(ISQuestionResponse*)response 57 | atIndex:(int)index; 58 | 59 | // inserts an empty response 60 | - (void)clearResponseAtIndex:(int)index; 61 | 62 | - (NSTimeInterval)time; 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Source/ISSession.m: -------------------------------------------------------------------------------- 1 | /* 2 | By: Justin Meiners 3 | 4 | Copyright (c) 2013 Inline Studios 5 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | #import "ISSession.h" 8 | 9 | static NSString * const _ISUserDataKey = @"userData"; 10 | static NSString * const _ISResponsesKey = @"responses"; 11 | static NSString * const _ISTimeKey = @"time"; 12 | static NSString * const _ISBonusTimeKey = @"bonusTime"; 13 | 14 | @implementation ISSession 15 | 16 | + (ISSession*)session 17 | { 18 | return [[self alloc] init]; 19 | } 20 | 21 | - (id)initWithCoder:(NSCoder *)aDecoder 22 | { 23 | if (self = [super init]) 24 | { 25 | _responses = [[NSMutableArray alloc] init]; 26 | [_responses addObjectsFromArray:[aDecoder decodeObjectForKey:_ISResponsesKey]]; 27 | self.userData = [aDecoder decodeObjectForKey:_ISUserDataKey]; 28 | _time = [aDecoder decodeDoubleForKey:_ISTimeKey]; 29 | self.bonusTime = [aDecoder decodeDoubleForKey:_ISBonusTimeKey]; 30 | } 31 | return self; 32 | } 33 | 34 | - (void)encodeWithCoder:(NSCoder *)aCoder 35 | { 36 | [aCoder encodeObject:_responses forKey:_ISResponsesKey]; 37 | [aCoder encodeObject:_userData forKey:_ISUserDataKey]; 38 | [aCoder encodeDouble:_time forKey:_ISTimeKey]; 39 | [aCoder encodeDouble:_bonusTime forKey:_ISBonusTimeKey]; 40 | } 41 | 42 | - (id)init 43 | { 44 | if (self = [super init]) 45 | { 46 | _bonusTime = 0.0; 47 | _time = 0.0; 48 | _responses = [[NSMutableArray alloc] init]; 49 | } 50 | return self; 51 | } 52 | 53 | 54 | - (BOOL)start:(ISQuiz*)quiz 55 | { 56 | if (_inSession) 57 | { 58 | NSLog(@"session already in session with quiz: %@", quiz); 59 | return NO; 60 | } 61 | 62 | _currentQuiz = quiz; 63 | 64 | if (_responses.count == 0) 65 | { 66 | for (int i = 0; i < _currentQuiz.questions.count; i ++) 67 | { 68 | ISEmptyQuestionResponse* emptyResponse = [[ISEmptyQuestionResponse alloc] init]; 69 | [_responses addObject:emptyResponse]; 70 | } 71 | 72 | _time = 0.0; 73 | } 74 | else 75 | { 76 | if (_responses.count != quiz.questions.count) 77 | { 78 | NSLog(@"resuming session with incorrect quiz"); 79 | return NO; 80 | } 81 | } 82 | 83 | _sessionTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 84 | target:self 85 | selector:@selector(tick:) 86 | userInfo:nil repeats:YES]; 87 | 88 | self.startDate = [NSDate date]; 89 | 90 | _inSession = true; 91 | 92 | if (_delegate && [_delegate respondsToSelector:@selector(sessionStarted:)]) 93 | { 94 | [_delegate sessionStarted:self]; 95 | } 96 | 97 | return YES; 98 | } 99 | 100 | - (void)tick:(NSTimer*)timer 101 | { 102 | _time = [[NSDate date] timeIntervalSinceDate:_startDate]; 103 | 104 | if (_currentQuiz.timeLimit > 0.0) 105 | { 106 | if (_time > _currentQuiz.timeLimit + _bonusTime) 107 | { 108 | if (_delegate && [_delegate respondsToSelector:@selector(sessionShouldStopAtTimeLimit:)]) 109 | { 110 | if ([_delegate sessionShouldStopAtTimeLimit:self]) 111 | { 112 | [self stop]; 113 | } 114 | } 115 | else 116 | { 117 | [self stop]; 118 | } 119 | } 120 | } 121 | } 122 | 123 | 124 | - (void)stop 125 | { 126 | if (!_inSession) 127 | { 128 | NSLog(@"ISSession finish when not in session"); 129 | return; 130 | } 131 | 132 | [_sessionTimer invalidate]; 133 | _sessionTimer = NULL; 134 | 135 | _time = [[NSDate date] timeIntervalSinceDate:_startDate]; 136 | 137 | _inSession = false; 138 | 139 | _currentQuiz = NULL; 140 | 141 | if (_delegate && [_delegate respondsToSelector:@selector(sessionStopped:)]) 142 | { 143 | [_delegate sessionStopped:self]; 144 | } 145 | } 146 | 147 | - (void)setResponse:(ISQuestionResponse*)response 148 | atIndex:(int)index 149 | { 150 | _responses[index] = response; 151 | } 152 | 153 | - (void)clearResponseAtIndex:(int)index 154 | { 155 | _responses[index] = [ISEmptyQuestionResponse emptyResponse]; 156 | } 157 | 158 | - (NSTimeInterval)time 159 | { 160 | return _time; 161 | } 162 | 163 | @end 164 | -------------------------------------------------------------------------------- /Source/ISTrueFalseQuestion.h: -------------------------------------------------------------------------------- 1 | /* 2 | By: Justin Meiners 3 | 4 | Copyright (c) 2013 Inline Studios 5 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | 8 | #import 9 | #import "ISQuestion.h" 10 | 11 | @interface ISTrueFalseResponse : ISQuestionResponse 12 | 13 | @property(nonatomic, assign)BOOL response; 14 | 15 | + (ISTrueFalseResponse*)responseWithResponse:(BOOL)response; 16 | 17 | - (id)initWithResponse:(BOOL)response; 18 | - (id)init; 19 | 20 | - (id)initWithCoder:(NSCoder *)aDecoder; 21 | - (void)encodeWithCoder:(NSCoder *)aCoder; 22 | 23 | @end 24 | 25 | 26 | @interface ISTrueFalseQuestion : ISQuestion 27 | 28 | @property(nonatomic, assign)BOOL answer; 29 | 30 | - (id)initWithCoder:(NSCoder *)aDecoder; 31 | - (void)encodeWithCoder:(NSCoder *)aCoder; 32 | 33 | - (BOOL)responseCorrect:(ISQuestionResponse*)response; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Source/ISTrueFalseQuestion.m: -------------------------------------------------------------------------------- 1 | /* 2 | By: Justin Meiners 3 | 4 | Copyright (c) 2013 Inline Studios 5 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | 8 | #import "ISTrueFalseQuestion.h" 9 | 10 | static NSString * const _ISAnswerKey = @"answer"; 11 | static NSString * const _ISResponseKey = @"response"; 12 | 13 | @implementation ISTrueFalseResponse 14 | 15 | + (ISTrueFalseResponse*)responseWithResponse:(BOOL)response 16 | { 17 | return [[self alloc] initWithResponse:response]; 18 | } 19 | 20 | - (id)initWithResponse:(BOOL)response 21 | { 22 | if (self = [super init]) 23 | { 24 | self.response = response; 25 | } 26 | return self; 27 | } 28 | 29 | - (id)init 30 | { 31 | if (self = [super init]) 32 | { 33 | } 34 | return self; 35 | } 36 | 37 | - (id)initWithCoder:(NSCoder *)aDecoder 38 | { 39 | if (self = [super initWithCoder:aDecoder]) 40 | { 41 | self.response = [aDecoder decodeBoolForKey:_ISResponseKey]; 42 | } 43 | return self; 44 | } 45 | 46 | - (void)encodeWithCoder:(NSCoder *)aCoder 47 | { 48 | [aCoder encodeBool:_response forKey:_ISAnswerKey]; 49 | } 50 | 51 | 52 | @end 53 | 54 | @implementation ISTrueFalseQuestion 55 | @synthesize answer = _answer; 56 | 57 | - (id)init 58 | { 59 | if (self = [super init]) 60 | { 61 | _answer = true; 62 | } 63 | return self; 64 | } 65 | 66 | 67 | - (id)initWithCoder:(NSCoder *)aDecoder 68 | { 69 | if (self = [super initWithCoder:aDecoder]) 70 | { 71 | self.answer = [aDecoder decodeBoolForKey:_ISAnswerKey]; 72 | } 73 | return self; 74 | } 75 | 76 | - (void)encodeWithCoder:(NSCoder *)aCoder 77 | { 78 | [super encodeWithCoder:aCoder]; 79 | [aCoder encodeBool:_answer forKey:_ISAnswerKey]; 80 | } 81 | 82 | - (BOOL)responseCorrect:(ISQuestionResponse*)response 83 | { 84 | if (![response isKindOfClass:[ISTrueFalseResponse class]]) 85 | { 86 | return NO; 87 | } 88 | 89 | ISTrueFalseResponse* casted = (ISTrueFalseResponse*)response; 90 | 91 | return (casted.response == _answer); 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /iOS-QuizKit.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "iOS-QuizKit" 3 | s.version = "0.0.1" 4 | s.license = 'MIT' 5 | s.summary = "Fork of iOS-QuizKit for development" 6 | s.homepage = "https://github.com/LostStudent/iOS-Quizkit" 7 | s.author = { "Christian French" => "christian.french@kiddicare.com" } 8 | s.platform = :ios, '7.0' 9 | s.source = { :git => "https://github.com/LostStudent/iOS-Quizkit.git"} 10 | s.source_files = 'Source/*.{h,m}' 11 | s.resources = "Assets/*.{xib}" 12 | end 13 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKit.xcodeproj/xcshareddata/xcschemes/iOS-QuizKit.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKit.xcodeproj/xcshareddata/xcschemes/iOS-QuizKitTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKit/ISAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // ISAppDelegate.h 3 | // iOS-QuizKit 4 | // 5 | // Created by Christian French on 02/04/2014. 6 | // Copyright (c) 2014 inline-studios. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ISAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKit/ISAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // ISAppDelegate.m 3 | // iOS-QuizKit 4 | // 5 | // Created by Christian French on 02/04/2014. 6 | // Copyright (c) 2014 inline-studios. All rights reserved. 7 | // 8 | 9 | #import "ISAppDelegate.h" 10 | 11 | @implementation ISAppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 16 | // Override point for customization after application launch. 17 | self.window.backgroundColor = [UIColor whiteColor]; 18 | [self.window makeKeyAndVisible]; 19 | return YES; 20 | } 21 | 22 | - (void)applicationWillResignActive:(UIApplication *)application 23 | { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application 29 | { 30 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 32 | } 33 | 34 | - (void)applicationWillEnterForeground:(UIApplication *)application 35 | { 36 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 37 | } 38 | 39 | - (void)applicationDidBecomeActive:(UIApplication *)application 40 | { 41 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 42 | } 43 | 44 | - (void)applicationWillTerminate:(UIApplication *)application 45 | { 46 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKit/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "ipad", 20 | "size" : "29x29", 21 | "scale" : "1x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "40x40", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "76x76", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "2x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKit/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKit/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKit/iOS-QuizKit-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.inline-studios.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKit/iOS-QuizKit-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_3_0 10 | #warning "This project uses features only available in iOS SDK 3.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKit/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // iOS-QuizKit 4 | // 5 | // Created by Christian French on 02/04/2014. 6 | // Copyright (c) 2014 inline-studios. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "ISAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([ISAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/ISMatchingQuestionTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ISMatchingQuestionTests.m 3 | // iOS-QuizKit 4 | // 5 | // Created by Christian French on 17/06/2014. 6 | // Copyright (c) 2014 inline-studios. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ISMatchingQuestion.h" 11 | @interface ISMatchingQuestionTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ISMatchingQuestionTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testCorrect 30 | { 31 | ISMatchingQuestion* question = [ISMatchingQuestion new]; 32 | 33 | question.text = @"Match the numbers to the words"; 34 | 35 | ISMatchingOption* answer1 = [ISMatchingOption optionWithText:@"1"]; 36 | 37 | ISMatchingOption* answer2 = [ISMatchingOption optionWithText:@"2"]; 38 | 39 | ISMatchingOption* answer3 = [ISMatchingOption optionWithText:@"3"]; 40 | 41 | ISMatchingOption* answer4 = [ISMatchingOption optionWithText:@"4"]; 42 | 43 | ISMatchingOption* option1 = [ISMatchingOption optionWithText:@"One" matchingOption:answer1]; 44 | 45 | ISMatchingOption* option2 = [ISMatchingOption optionWithText:@"Two" matchingOption:answer2]; 46 | 47 | ISMatchingOption* option3 = [ISMatchingOption optionWithText:@"Three" matchingOption:answer3]; 48 | 49 | ISMatchingOption* option4 = [ISMatchingOption optionWithText:@"Four" matchingOption:answer4]; 50 | 51 | question.options = @[option1,option2,option3,option4]; 52 | 53 | question.answers = @[answer1,answer2,answer3,answer4]; 54 | 55 | ISMatchingResponse* response = [ISMatchingResponse responseWithOptions:@[answer1,answer2,answer3,answer4]]; 56 | 57 | BOOL correct = [question responseCorrect:response]; 58 | 59 | XCTAssertTrue(correct, @"answer should be correct"); 60 | } 61 | 62 | - (void)testIncorrect 63 | { 64 | ISMatchingQuestion* question = [ISMatchingQuestion new]; 65 | 66 | question.text = @"Match the numbers to the words"; 67 | 68 | ISMatchingOption* answer1 = [ISMatchingOption optionWithText:@"1"]; 69 | 70 | ISMatchingOption* answer2 = [ISMatchingOption optionWithText:@"2"]; 71 | 72 | ISMatchingOption* answer3 = [ISMatchingOption optionWithText:@"3"]; 73 | 74 | ISMatchingOption* answer4 = [ISMatchingOption optionWithText:@"4"]; 75 | 76 | ISMatchingOption* option1 = [ISMatchingOption optionWithText:@"One" matchingOption:answer1]; 77 | 78 | ISMatchingOption* option2 = [ISMatchingOption optionWithText:@"Two" matchingOption:answer2]; 79 | 80 | ISMatchingOption* option3 = [ISMatchingOption optionWithText:@"Three" matchingOption:answer3]; 81 | 82 | ISMatchingOption* option4 = [ISMatchingOption optionWithText:@"Four" matchingOption:answer4]; 83 | 84 | question.options = @[option1,option2,option3,option4]; 85 | 86 | question.answers = @[answer1,answer2,answer3,answer4]; 87 | 88 | ISMatchingResponse* response = [ISMatchingResponse responseWithOptions:@[answer2,answer1,answer3,answer4]]; 89 | 90 | BOOL correct = [question responseCorrect:response]; 91 | 92 | XCTAssertFalse(correct, @"answer should be incorrect"); 93 | } 94 | 95 | - (void)testIncorrectOptionsNumber 96 | { 97 | ISMatchingQuestion* question = [ISMatchingQuestion new]; 98 | 99 | question.text = @"Match the numbers to the words"; 100 | 101 | ISMatchingOption* answer1 = [ISMatchingOption optionWithText:@"1"]; 102 | 103 | ISMatchingOption* answer2 = [ISMatchingOption optionWithText:@"2"]; 104 | 105 | ISMatchingOption* answer3 = [ISMatchingOption optionWithText:@"3"]; 106 | 107 | ISMatchingOption* answer4 = [ISMatchingOption optionWithText:@"4"]; 108 | 109 | ISMatchingOption* option1 = [ISMatchingOption optionWithText:@"One" matchingOption:answer1]; 110 | 111 | ISMatchingOption* option2 = [ISMatchingOption optionWithText:@"Two" matchingOption:answer2]; 112 | 113 | ISMatchingOption* option3 = [ISMatchingOption optionWithText:@"Three" matchingOption:answer3]; 114 | 115 | ISMatchingOption* option4 = [ISMatchingOption optionWithText:@"Four" matchingOption:answer4]; 116 | 117 | question.options = @[option1,option2,option3,option4]; 118 | 119 | question.answers = @[answer1,answer2,answer3,answer4]; 120 | 121 | ISMatchingResponse* response = [ISMatchingResponse responseWithOptions:@[answer2,answer1,answer3]]; 122 | 123 | BOOL correct = [question responseCorrect:response]; 124 | 125 | XCTAssertFalse(correct, @"answer should be incorrect"); 126 | } 127 | 128 | - (void)testRandomizedCorrect 129 | { 130 | 131 | ISMatchingOption* answer1 = [ISMatchingOption optionWithText:@"1"]; 132 | 133 | ISMatchingOption* answer2 = [ISMatchingOption optionWithText:@"2"]; 134 | 135 | ISMatchingOption* answer3 = [ISMatchingOption optionWithText:@"3"]; 136 | 137 | ISMatchingOption* answer4 = [ISMatchingOption optionWithText:@"4"]; 138 | 139 | ISMatchingOption* option1 = [ISMatchingOption optionWithText:@"One" matchingOption:answer1]; 140 | 141 | ISMatchingOption* option2 = [ISMatchingOption optionWithText:@"Two" matchingOption:answer2]; 142 | 143 | ISMatchingOption* option3 = [ISMatchingOption optionWithText:@"Three" matchingOption:answer3]; 144 | 145 | ISMatchingOption* option4 = [ISMatchingOption optionWithText:@"Four" matchingOption:answer4]; 146 | 147 | ISMatchingQuestion* question = [ISMatchingQuestion questionWithOptions:@[option1,option2,option3,option4] answers:@[answer1,answer2,answer3,answer4]]; 148 | 149 | question.text = @"Match the numbers to the words"; 150 | 151 | NSMutableArray* answers = [NSMutableArray array]; 152 | 153 | NSArray* text = @[@"1",@"2",@"3",@"4"]; 154 | 155 | for (NSInteger i = 0 ; i < 4 ; i++) { 156 | 157 | NSUInteger idx = [question.randomizedAnswers indexOfObjectPassingTest:^BOOL(ISMatchingOption* obj, NSUInteger idx, BOOL *stop) { 158 | 159 | if([obj.text isEqualToString:text[i]]) { 160 | 161 | *stop = YES; 162 | 163 | return YES; 164 | 165 | } 166 | 167 | return NO; 168 | }]; 169 | 170 | [answers addObject: question.randomizedAnswers[idx]]; 171 | 172 | } 173 | 174 | ISMatchingResponse* response = [ISMatchingResponse responseWithOptions:answers]; 175 | 176 | BOOL correct = [question responseCorrect:response]; 177 | 178 | XCTAssertTrue(correct, @"answer should be incorrect"); 179 | } 180 | 181 | - (void)testRandomizedIncorrect 182 | { 183 | 184 | ISMatchingOption* answer1 = [ISMatchingOption optionWithText:@"1"]; 185 | 186 | ISMatchingOption* answer2 = [ISMatchingOption optionWithText:@"2"]; 187 | 188 | ISMatchingOption* answer3 = [ISMatchingOption optionWithText:@"3"]; 189 | 190 | ISMatchingOption* answer4 = [ISMatchingOption optionWithText:@"4"]; 191 | 192 | ISMatchingOption* option1 = [ISMatchingOption optionWithText:@"One" matchingOption:answer1]; 193 | 194 | ISMatchingOption* option2 = [ISMatchingOption optionWithText:@"Two" matchingOption:answer2]; 195 | 196 | ISMatchingOption* option3 = [ISMatchingOption optionWithText:@"Three" matchingOption:answer3]; 197 | 198 | ISMatchingOption* option4 = [ISMatchingOption optionWithText:@"Four" matchingOption:answer4]; 199 | 200 | ISMatchingQuestion* question = [ISMatchingQuestion questionWithOptions:@[option1,option2,option3,option4] answers:@[answer1,answer2,answer3,answer4]]; 201 | 202 | question.text = @"Match the numbers to the words"; 203 | 204 | NSMutableArray* answers = [NSMutableArray array]; 205 | 206 | NSArray* text = @[@"1",@"3",@"2",@"4"]; 207 | 208 | for (NSInteger i = 0 ; i < 4 ; i++) { 209 | 210 | NSUInteger idx = [question.randomizedAnswers indexOfObjectPassingTest:^BOOL(ISMatchingOption* obj, NSUInteger idx, BOOL *stop) { 211 | 212 | if([obj.text isEqualToString:text[i]]) { 213 | 214 | *stop = YES; 215 | 216 | return YES; 217 | 218 | } 219 | 220 | return NO; 221 | }]; 222 | 223 | [answers addObject: question.randomizedAnswers[idx]]; 224 | 225 | } 226 | 227 | ISMatchingResponse* response = [ISMatchingResponse responseWithOptions:answers]; 228 | 229 | BOOL correct = [question responseCorrect:response]; 230 | 231 | XCTAssertFalse(correct, @"answer should be incorrect"); 232 | } 233 | 234 | 235 | @end 236 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/ISMultipleChoiceQuestionTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ISMultipleChoiceQuestionTests.m 3 | // iOS-QuizKit 4 | // 5 | // Created by Christian French on 12/09/2014. 6 | // Copyright (c) 2014 inline-studios. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ISMultipleChoiceQuestion.h" 11 | #import "ISMultipleChoiceQuestion+Private.h" 12 | @interface ISMultipleChoiceQuestionTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation ISMultipleChoiceQuestionTests 17 | 18 | - (void)setUp 19 | { 20 | [super setUp]; 21 | // Put setup code here. This method is called before the invocation of each test method in the class. 22 | } 23 | 24 | - (void)tearDown 25 | { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | [super tearDown]; 28 | } 29 | 30 | - (void)testRandomizeMap 31 | { 32 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 33 | 34 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:YES]; 35 | 36 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 37 | 38 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:YES]; 39 | 40 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:YES]; 41 | 42 | ISMultipleChoiceOption* option5 = [ISMultipleChoiceOption optionWithText:@"Five" correct:NO]; 43 | 44 | ISMultipleChoiceOption* option6 = [ISMultipleChoiceOption optionWithText:@"Six" correct:NO]; 45 | 46 | ISMultipleChoiceOption* option7 = [ISMultipleChoiceOption optionWithText:@"Seven" correct:NO]; 47 | 48 | ISMultipleChoiceOption* option8 = [ISMultipleChoiceOption optionWithText:@"Eight" correct:NO]; 49 | 50 | [question addOptions:@[option1,option2,option3,option4,option5,option6,option7,option8]]; 51 | 52 | NSInteger i = 0; 53 | 54 | for(NSNumber* origialIndex in question.randomizedIndexesMap) { 55 | 56 | XCTAssertTrue((question.randomizedOptions[i] == question.options[origialIndex.intValue]), @"objects should match"); 57 | 58 | i++; 59 | } 60 | 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/ISOpenQuestionTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ISOpenQuestionTests.m 3 | // iOS-QuizKit 4 | // 5 | // Created by Christian French on 23/06/2014. 6 | // Copyright (c) 2014 inline-studios. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ISOpenQuestion.h" 11 | @interface ISOpenQuestionTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ISOpenQuestionTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | 30 | - (void)testCorrect 31 | { 32 | ISOpenQuestion* question = [ISOpenQuestion new]; 33 | 34 | question.text = @"Type number 4"; 35 | 36 | question.matchMode = kISOpenQuestionMatchModeExact; 37 | 38 | [question addAnswer:@"4"]; 39 | 40 | ISOpenQuestionResponse* response = [ISOpenQuestionResponse responseWithResponse:@"4"]; 41 | 42 | BOOL correct = [question responseCorrect:response]; 43 | 44 | XCTAssertTrue(correct, @"answer should be correct"); 45 | } 46 | 47 | - (void)testIncorrect 48 | { 49 | ISOpenQuestion* question = [ISOpenQuestion new]; 50 | 51 | question.text = @"Type number 4"; 52 | 53 | question.matchMode = kISOpenQuestionMatchModeExact; 54 | 55 | [question addAnswer:@"4"]; 56 | 57 | ISOpenQuestionResponse* response = [ISOpenQuestionResponse responseWithResponse:@"four"]; 58 | 59 | BOOL correct = [question responseCorrect:response]; 60 | 61 | XCTAssertFalse(correct, @"answer should be incorrect"); 62 | } 63 | 64 | - (void)testCorrectContainsMatchMode 65 | { 66 | ISOpenQuestion* question = [ISOpenQuestion new]; 67 | 68 | question.text = @"Type number 4 and 5"; 69 | 70 | question.matchMode = kISOpenQuestionContainsAll; 71 | 72 | [question addAnswer:@"4"]; 73 | 74 | [question addAnswer:@"5"]; 75 | 76 | ISOpenQuestionResponse* response = [ISOpenQuestionResponse responseWithResponse:@"4 and 5"]; 77 | 78 | BOOL correct = [question responseCorrect:response]; 79 | 80 | XCTAssertTrue(correct, @"answer should be correct"); 81 | } 82 | 83 | - (void)testHalfIncorrectContainsMatchMode 84 | { 85 | ISOpenQuestion* question = [ISOpenQuestion new]; 86 | 87 | question.text = @"Type number 4 and 5"; 88 | 89 | question.matchMode = kISOpenQuestionContainsAll; 90 | 91 | [question addAnswer:@"4"]; 92 | 93 | [question addAnswer:@"5"]; 94 | 95 | ISOpenQuestionResponse* response = [ISOpenQuestionResponse responseWithResponse:@"4 and five"]; 96 | 97 | BOOL correct = [question responseCorrect:response]; 98 | 99 | XCTAssertFalse(correct, @"answer should be incorrect"); 100 | } 101 | 102 | - (void)testIncorrectContainsMatchMode 103 | { 104 | ISOpenQuestion* question = [ISOpenQuestion new]; 105 | 106 | question.text = @"Type number 4 and 5"; 107 | 108 | question.matchMode = kISOpenQuestionContainsAll; 109 | 110 | [question addAnswer:@"4"]; 111 | 112 | [question addAnswer:@"5"]; 113 | 114 | ISOpenQuestionResponse* response = [ISOpenQuestionResponse responseWithResponse:@"four and five"]; 115 | 116 | BOOL correct = [question responseCorrect:response]; 117 | 118 | XCTAssertFalse(correct, @"answer should be incorrect"); 119 | } 120 | 121 | @end 122 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/default_selectable_options.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | questions 6 | 7 | 8 | options 9 | 10 | 11 | correct 12 | 1 13 | text 14 | one 15 | 16 | 17 | correct 18 | 0 19 | text 20 | two 21 | 22 | 23 | correct 24 | 1 25 | text 26 | three 27 | 28 | 29 | ref 30 | 1 31 | scoreValue 32 | 0 33 | selectableOptions 34 | 0 35 | text 36 | select first and last option 37 | type 38 | multipleChoice 39 | 40 | 41 | options 42 | 43 | 44 | text 45 | This is the first sentence. 46 | correct 47 | 3 48 | 49 | 50 | text 51 | Second sentence this is. 52 | correct 53 | 0 54 | 55 | 56 | text 57 | This is the sentence Third. 58 | correct 59 | 4 60 | 61 | 62 | type 63 | multipleMultipleChoiceSentence 64 | text 65 | select the number word option in each question 66 | scoreValue 67 | 0 68 | selectableOptions 69 | 0 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/iOS-QuizKitTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.inline-studios.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/iOS_QuizKitMultiChoiceGradeTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // iOS_QuizKitMultiChoiceGradeTests.m 3 | // iOS-QuizKit 4 | // 5 | // Created by Christian French on 02/04/2014. 6 | // Copyright (c) 2014 inline-studios. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ISQuizKit.h" 11 | @interface iOS_QuizKitMultiChoiceGradeTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation iOS_QuizKitMultiChoiceGradeTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testCorrect2Options 30 | { 31 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 32 | 33 | question.selectableOptions = @1; 34 | 35 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:YES]; 36 | 37 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:NO]; 38 | 39 | [question addOptions:@[option1,option2]]; 40 | 41 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithAnswerIndex:0]; 42 | 43 | BOOL correct = [question responseCorrect:response]; 44 | 45 | XCTAssertTrue(correct, @"answer should be correct"); 46 | } 47 | 48 | - (void)testIncorrect2Options 49 | { 50 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 51 | 52 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:YES]; 53 | 54 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:NO]; 55 | 56 | [question addOptions:@[option1,option2]]; 57 | 58 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithAnswerIndex:1]; 59 | 60 | BOOL correct = [question responseCorrect:response]; 61 | 62 | XCTAssertFalse(correct, @"answer should be incorrect"); 63 | } 64 | 65 | /** 66 | Test for multiple selection 67 | 68 | **/ 69 | 70 | - (void)testCorrect2SelectedOptions 71 | { 72 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 73 | 74 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:YES]; 75 | 76 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 77 | 78 | [question addOptions:@[option1,option2]]; 79 | 80 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[@0,@1]]; 81 | 82 | BOOL correct = [question responseCorrect:response]; 83 | 84 | XCTAssertTrue(correct, @"answer should be correct"); 85 | } 86 | 87 | - (void)test_TwoCorrectSelected_FourCorrectOptions 88 | { 89 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 90 | 91 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:YES]; 92 | 93 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 94 | 95 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:YES]; 96 | 97 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:YES]; 98 | 99 | [question addOptions:@[option1,option2,option3,option4]]; 100 | 101 | question.selectableOptions = @2; 102 | 103 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[@0,@1]]; 104 | 105 | BOOL correct = [question responseCorrect:response]; 106 | 107 | XCTAssertTrue(correct, @"answer should be correct"); 108 | } 109 | 110 | - (void)test_FirstAndSecondSelectedCorrectOptions_FromFourOptions 111 | { 112 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 113 | 114 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:YES]; 115 | 116 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 117 | 118 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:NO]; 119 | 120 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 121 | 122 | [question addOptions:@[option1,option2,option3,option4]]; 123 | 124 | question.selectableOptions = @2; 125 | 126 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[@0,@1]]; 127 | 128 | BOOL correct = [question responseCorrect:response]; 129 | 130 | XCTAssertTrue(correct, @"answer should be correct"); 131 | } 132 | 133 | - (void)test_FirstAndLastTwoSelectedCorrectOptions_FromFourOptions 134 | { 135 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 136 | 137 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:YES]; 138 | 139 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:NO]; 140 | 141 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:NO]; 142 | 143 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:YES]; 144 | 145 | [question addOptions:@[option1,option2,option3,option4]]; 146 | 147 | question.selectableOptions = @2; 148 | 149 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[@0,@3]]; 150 | 151 | BOOL correct = [question responseCorrect:response]; 152 | 153 | XCTAssertTrue(correct, @"answer should be correct"); 154 | } 155 | 156 | - (void)test_SecondAndThirdTwoSelectedCorrectOptions_FromFourOptions 157 | { 158 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 159 | 160 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 161 | 162 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 163 | 164 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:YES]; 165 | 166 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 167 | 168 | [question addOptions:@[option1,option2,option3,option4]]; 169 | 170 | question.selectableOptions = @2; 171 | 172 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[@1,@2]]; 173 | 174 | BOOL correct = [question responseCorrect:response]; 175 | 176 | XCTAssertTrue(correct, @"answer should be correct"); 177 | } 178 | 179 | - (void)testIncorrectTwoSelectedOptions 180 | { 181 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 182 | 183 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 184 | 185 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 186 | 187 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:YES]; 188 | 189 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 190 | 191 | [question addOptions:@[option1,option2,option3,option4]]; 192 | 193 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[@0,@3]]; 194 | 195 | BOOL correct = [question responseCorrect:response]; 196 | 197 | XCTAssertFalse(correct, @"answer should be incorrect"); 198 | } 199 | 200 | - (void)testOneIncorrectOneCorrectTwoSelectedOptionsFromFour 201 | { 202 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 203 | 204 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 205 | 206 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 207 | 208 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:YES]; 209 | 210 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 211 | 212 | [question addOptions:@[option1,option2,option3,option4]]; 213 | 214 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[@0,@3]]; 215 | 216 | BOOL correct = [question responseCorrect:response]; 217 | 218 | XCTAssertFalse(correct, @"answer should be incorrect"); 219 | } 220 | 221 | -(void)test_OneCorrectSelected_FromTwoCorrectOptions_WithFourPossibleOptions { 222 | 223 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 224 | 225 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 226 | 227 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 228 | 229 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:YES]; 230 | 231 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 232 | 233 | question.selectableOptions = @2; 234 | 235 | [question addOptions:@[option1,option2,option3,option4]]; 236 | 237 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[@1]]; 238 | 239 | BOOL correct = [question responseCorrect:response]; 240 | 241 | XCTAssertFalse(correct, @"answer should be incorrect"); 242 | } 243 | 244 | -(void)test_OneInCorrectSelected_FromTwoCorrectOptions_WithFourPossibleOptions { 245 | 246 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 247 | 248 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 249 | 250 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 251 | 252 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:YES]; 253 | 254 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 255 | 256 | [question addOptions:@[option1,option2,option3,option4]]; 257 | 258 | question.selectableOptions = @2; 259 | 260 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[@0]]; 261 | 262 | BOOL correct = [question responseCorrect:response]; 263 | 264 | XCTAssertFalse(correct, @"answer should be incorrect"); 265 | } 266 | 267 | -(void)test_ThreeSelected_FromTwoCorrectOptions_WithFourPossibleOptions { 268 | 269 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 270 | 271 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 272 | 273 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 274 | 275 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:YES]; 276 | 277 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 278 | 279 | [question addOptions:@[option1,option2,option3,option4]]; 280 | 281 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[@1,@2,@3]]; 282 | 283 | BOOL correct = [question responseCorrect:response]; 284 | 285 | XCTAssertFalse(correct, @"answer should be incorrect"); 286 | } 287 | 288 | /** 289 | Test for randomizing options 290 | 291 | **/ 292 | 293 | -(void)test_OneCorrect_fromRandomizedOptions { 294 | 295 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 296 | 297 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:YES]; 298 | 299 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:NO]; 300 | 301 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:NO]; 302 | 303 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 304 | 305 | [question addOptions:@[option1,option2,option3,option4]]; 306 | 307 | NSMutableArray* indexes = [NSMutableArray array]; 308 | 309 | for (ISMultipleChoiceOption* option in question.randomizedOptions) { 310 | 311 | if([option.text isEqualToString:@"One"]) { 312 | 313 | [indexes addObject:[NSNumber numberWithInt:[question.randomizedOptions indexOfObject: option]]]; 314 | } 315 | } 316 | 317 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:indexes]; 318 | 319 | BOOL correct = [question responseCorrectForRandomizedOptions:response]; 320 | 321 | XCTAssertTrue(correct, @"answer should be correct"); 322 | 323 | } 324 | 325 | -(void)test_TwoCorrect_fromRandomizedOptions { 326 | 327 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 328 | 329 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:YES]; 330 | 331 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:NO]; 332 | 333 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:NO]; 334 | 335 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:YES]; 336 | 337 | [question addOptions:@[option1,option2,option3,option4]]; 338 | 339 | NSMutableArray* indexes = [NSMutableArray array]; 340 | 341 | for (ISMultipleChoiceOption* option in question.randomizedOptions) { 342 | 343 | if([option.text isEqualToString:@"Two"] || [option.text isEqualToString:@"Four"]) { 344 | 345 | [indexes addObject:[NSNumber numberWithInt:[question.randomizedOptions indexOfObject: option]]]; 346 | } 347 | } 348 | 349 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:indexes]; 350 | 351 | BOOL correct = [question responseCorrectForRandomizedOptions:response]; 352 | 353 | XCTAssertFalse(correct, @"answer should be incorrect"); 354 | 355 | } 356 | 357 | -(void)test_OneIncorrect_fromRandomizedOptions { 358 | 359 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 360 | 361 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:YES]; 362 | 363 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:NO]; 364 | 365 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:NO]; 366 | 367 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 368 | 369 | [question addOptions:@[option1,option2,option3,option4]]; 370 | 371 | NSMutableArray* indexes = [NSMutableArray array]; 372 | 373 | for (ISMultipleChoiceOption* option in question.randomizedOptions) { 374 | 375 | if([option.text isEqualToString:@"Two"]) { 376 | 377 | [indexes addObject:[NSNumber numberWithInt:[question.randomizedOptions indexOfObject: option]]]; 378 | } 379 | } 380 | 381 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:indexes]; 382 | 383 | BOOL correct = [question responseCorrectForRandomizedOptions:response]; 384 | 385 | XCTAssertFalse(correct, @"answer correct: %d should be incorrect selections: %@ | options: %@",correct,indexes,question.randomizedOptions); 386 | 387 | } 388 | 389 | -(void)test_TwoIncorrect_fromRandomizedOptions { 390 | 391 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 392 | 393 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:YES]; 394 | 395 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:NO]; 396 | 397 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:YES]; 398 | 399 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 400 | 401 | [question addOptions:@[option1,option2,option3,option4]]; 402 | 403 | NSMutableArray* indexes = [NSMutableArray array]; 404 | 405 | for (ISMultipleChoiceOption* option in question.randomizedOptions) { 406 | 407 | if([option.text isEqualToString:@"Two"] || [option.text isEqualToString:@"Four"]) { 408 | 409 | [indexes addObject:[NSNumber numberWithInt:[question.randomizedOptions indexOfObject: option]]]; 410 | } 411 | } 412 | 413 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:indexes]; 414 | 415 | BOOL correct = [question responseCorrectForRandomizedOptions:response]; 416 | 417 | XCTAssertFalse(correct, @"answer should be incorrect"); 418 | 419 | } 420 | 421 | -(void)test_OneCorrect_OneIncorrect_fromRandomizedOptions { 422 | 423 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 424 | 425 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:YES]; 426 | 427 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:NO]; 428 | 429 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:YES]; 430 | 431 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 432 | 433 | [question addOptions:@[option1,option2,option3,option4]]; 434 | 435 | NSMutableArray* indexes = [NSMutableArray array]; 436 | 437 | for (ISMultipleChoiceOption* option in question.randomizedOptions) { 438 | 439 | if([option.text isEqualToString:@"Three"] || [option.text isEqualToString:@"Four"]) { 440 | 441 | [indexes addObject:[NSNumber numberWithInt:[question.randomizedOptions indexOfObject: option]]]; 442 | } 443 | } 444 | 445 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:indexes]; 446 | 447 | BOOL correct = [question responseCorrectForRandomizedOptions:response]; 448 | 449 | XCTAssertFalse(correct, @"answer should be incorrect"); 450 | 451 | } 452 | 453 | -(void)test_fourCorrect_fromRandomizedOptions { 454 | 455 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 456 | 457 | question.selectableOptions = @4; 458 | 459 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:YES]; 460 | 461 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 462 | 463 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:YES]; 464 | 465 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:YES]; 466 | 467 | ISMultipleChoiceOption* option5 = [ISMultipleChoiceOption optionWithText:@"Five" correct:NO]; 468 | 469 | ISMultipleChoiceOption* option6 = [ISMultipleChoiceOption optionWithText:@"Six" correct:NO]; 470 | 471 | ISMultipleChoiceOption* option7 = [ISMultipleChoiceOption optionWithText:@"Seven" correct:NO]; 472 | 473 | ISMultipleChoiceOption* option8 = [ISMultipleChoiceOption optionWithText:@"Eight" correct:NO]; 474 | 475 | [question addOptions:@[option1,option2,option3,option4,option5,option6,option7,option8]]; 476 | 477 | NSMutableArray* indexes = [NSMutableArray array]; 478 | 479 | for (ISMultipleChoiceOption* option in question.randomizedOptions) { 480 | 481 | if([option.text isEqualToString:@"One"] || [option.text isEqualToString:@"Two"] || [option.text isEqualToString:@"Three"] || [option.text isEqualToString:@"Four"]) { 482 | 483 | [indexes addObject:[NSNumber numberWithInt:[question.randomizedOptions indexOfObject: option]]]; 484 | } 485 | } 486 | 487 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:indexes]; 488 | 489 | BOOL correct = [question responseCorrectForRandomizedOptions:response]; 490 | 491 | XCTAssertTrue(correct, @"answer should be correct"); 492 | 493 | } 494 | 495 | @end 496 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/iOS_QuizKitMultipleMultipleChoiceTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // iOS_QuizKitMultipleMultipleChoiceTests.m 3 | // iOS-QuizKit 4 | // 5 | // Created by Christian French on 30/04/2014. 6 | // Copyright (c) 2014 inline-studios. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ISMultipleChoiceQuestion.h" 11 | #import "ISMultipleMultipleChoiceQuestion.h" 12 | #import "ISMultipleMultipleChoiceQuestion+Private.h" 13 | @interface iOS_QuizKitMultipleMultipleChoiceTests : XCTestCase 14 | 15 | @end 16 | 17 | @implementation iOS_QuizKitMultipleMultipleChoiceTests 18 | 19 | - (void)setUp 20 | { 21 | [super setUp]; 22 | // Put setup code here. This method is called before the invocation of each test method in the class. 23 | } 24 | 25 | - (void)tearDown 26 | { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | [super tearDown]; 29 | } 30 | 31 | -(void)test_AddQuestion { 32 | 33 | ISMultipleChoiceQuestion* multipleChoiceQuestion = [ISMultipleChoiceQuestion new]; 34 | 35 | ISMultipleMultipleChoiceQuestion* multipleMultipleChoiceQuestion = [ISMultipleMultipleChoiceQuestion new]; 36 | 37 | [multipleMultipleChoiceQuestion addQuestion:multipleChoiceQuestion]; 38 | 39 | XCTAssertTrue(multipleMultipleChoiceQuestion.questions.count, @"should ahve 1 question"); 40 | } 41 | 42 | 43 | -(void)test_OneQuestion_OneSelected_correct { 44 | 45 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 46 | 47 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 48 | 49 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 50 | 51 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:NO]; 52 | 53 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 54 | 55 | [question addOptions:@[option1,option2,option3,option4]]; 56 | 57 | ISMultipleMultipleChoiceQuestion* multipleMultipleChoiceQuestion = [ISMultipleMultipleChoiceQuestion questionWithQuestions:@[question]]; 58 | 59 | XCTAssertTrue(multipleMultipleChoiceQuestion.options.count == 1, @"should have one option group"); 60 | 61 | NSArray* options = multipleMultipleChoiceQuestion.options[0]; 62 | 63 | XCTAssertTrue(options.count == 4, @"should have 4 options in first group"); 64 | 65 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[ @[@1] ]]; 66 | 67 | BOOL correct = [multipleMultipleChoiceQuestion responseCorrect:response]; 68 | 69 | XCTAssertTrue(correct, @"answer should be correct"); 70 | } 71 | 72 | -(void)test_OneQuestion_OneSelected_incorrect { 73 | 74 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 75 | 76 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 77 | 78 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 79 | 80 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:NO]; 81 | 82 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 83 | 84 | [question addOptions:@[option1,option2,option3,option4]]; 85 | 86 | ISMultipleMultipleChoiceQuestion* multipleMultipleChoiceQuestion = [ISMultipleMultipleChoiceQuestion questionWithQuestions:@[question]]; 87 | 88 | XCTAssertTrue(multipleMultipleChoiceQuestion.options.count == 1, @"should have one option group"); 89 | 90 | NSArray* options = multipleMultipleChoiceQuestion.options[0]; 91 | 92 | XCTAssertTrue(options.count == 4, @"should have 4 options in first group"); 93 | 94 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[ @[@0] ]]; 95 | 96 | BOOL correct = [multipleMultipleChoiceQuestion responseCorrect:response]; 97 | 98 | XCTAssertFalse(correct, @"answer should be incorrect"); 99 | } 100 | 101 | -(void)test_TwoQuestion_OneSelectedEach_correct { 102 | 103 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 104 | 105 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 106 | 107 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 108 | 109 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:NO]; 110 | 111 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 112 | 113 | [question addOptions:@[option1,option2,option3,option4]]; 114 | 115 | ISMultipleChoiceQuestion* question1 = [ISMultipleChoiceQuestion new]; 116 | 117 | ISMultipleChoiceOption* question1option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 118 | 119 | ISMultipleChoiceOption* question1option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 120 | 121 | ISMultipleChoiceOption* question1option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:NO]; 122 | 123 | ISMultipleChoiceOption* question1option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 124 | 125 | [question1 addOptions:@[question1option1,question1option2,question1option3,question1option4]]; 126 | 127 | ISMultipleMultipleChoiceQuestion* multipleMultipleChoiceQuestion = [ISMultipleMultipleChoiceQuestion questionWithQuestions:@[question,question1]]; 128 | 129 | XCTAssertTrue(multipleMultipleChoiceQuestion.options.count == 2, @"should have 2 option groups"); 130 | 131 | NSArray* options1 = multipleMultipleChoiceQuestion.options[0]; 132 | 133 | NSArray* options2 = multipleMultipleChoiceQuestion.options[0]; 134 | 135 | XCTAssertTrue(options1.count == 4, @"should have 4 options in 1 group"); 136 | 137 | XCTAssertTrue(options2.count == 4, @"should have 4 options in 2 group"); 138 | 139 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[ @[@1] , @[@1] ]]; 140 | 141 | BOOL correct = [multipleMultipleChoiceQuestion responseCorrect:response]; 142 | 143 | XCTAssertTrue(correct, @"answer should be correct"); 144 | } 145 | 146 | -(void)test_TwoQuestion_OneSelectedEach_incorrect { 147 | 148 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 149 | 150 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 151 | 152 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 153 | 154 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:NO]; 155 | 156 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 157 | 158 | [question addOptions:@[option1,option2,option3,option4]]; 159 | 160 | ISMultipleChoiceQuestion* question1 = [ISMultipleChoiceQuestion new]; 161 | 162 | ISMultipleChoiceOption* question1option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 163 | 164 | ISMultipleChoiceOption* question1option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 165 | 166 | ISMultipleChoiceOption* question1option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:NO]; 167 | 168 | ISMultipleChoiceOption* question1option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 169 | 170 | [question1 addOptions:@[question1option1,question1option2,question1option3,question1option4]]; 171 | 172 | ISMultipleMultipleChoiceQuestion* multipleMultipleChoiceQuestion = [ISMultipleMultipleChoiceQuestion questionWithQuestions:@[question,question1]]; 173 | 174 | XCTAssertTrue(multipleMultipleChoiceQuestion.options.count == 2, @"should have 2 option groups"); 175 | 176 | NSArray* options1 = multipleMultipleChoiceQuestion.options[0]; 177 | 178 | NSArray* options2 = multipleMultipleChoiceQuestion.options[0]; 179 | 180 | XCTAssertTrue(options1.count == 4, @"should have 4 options in 1 group"); 181 | 182 | XCTAssertTrue(options2.count == 4, @"should have 4 options in 2 group"); 183 | 184 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[ @[@0] , @[@3] ]]; 185 | 186 | BOOL correct = [multipleMultipleChoiceQuestion responseCorrect:response]; 187 | 188 | XCTAssertFalse(correct, @"answer should be incorrect"); 189 | } 190 | 191 | -(void)test_TwoQuestion_OneSelectedEachOneCorrectOneIncorrect_incorrect { 192 | 193 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 194 | 195 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 196 | 197 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 198 | 199 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:NO]; 200 | 201 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 202 | 203 | [question addOptions:@[option1,option2,option3,option4]]; 204 | 205 | ISMultipleChoiceQuestion* question1 = [ISMultipleChoiceQuestion new]; 206 | 207 | ISMultipleChoiceOption* question1option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 208 | 209 | ISMultipleChoiceOption* question1option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 210 | 211 | ISMultipleChoiceOption* question1option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:NO]; 212 | 213 | [question1 addOptions:@[question1option1,question1option2,question1option3]]; 214 | 215 | ISMultipleMultipleChoiceQuestion* multipleMultipleChoiceQuestion = [ISMultipleMultipleChoiceQuestion questionWithQuestions:@[question,question1]]; 216 | 217 | XCTAssertTrue(multipleMultipleChoiceQuestion.options.count == 2, @"should have 2 option groups"); 218 | 219 | NSArray* options1 = multipleMultipleChoiceQuestion.options[0]; 220 | 221 | NSArray* options2 = multipleMultipleChoiceQuestion.options[1]; 222 | 223 | XCTAssertTrue(options1.count == 4, @"should have 4 options in 1 group"); 224 | 225 | XCTAssertTrue(options2.count == 3, @"should have 4 options in 2 group"); 226 | 227 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[ @[@1] , @[@2] ]]; 228 | 229 | BOOL correct = [multipleMultipleChoiceQuestion responseCorrect:response]; 230 | 231 | XCTAssertFalse(correct, @"answer should be incorrect"); 232 | } 233 | 234 | -(void)test_Incorrect_Number_Answers { 235 | 236 | ISMultipleChoiceQuestion* question = [ISMultipleChoiceQuestion new]; 237 | 238 | question.selectableOptions = @1; 239 | 240 | ISMultipleChoiceOption* option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 241 | 242 | ISMultipleChoiceOption* option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 243 | 244 | ISMultipleChoiceOption* option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:NO]; 245 | 246 | ISMultipleChoiceOption* option4 = [ISMultipleChoiceOption optionWithText:@"Four" correct:NO]; 247 | 248 | [question addOptions:@[option1,option2,option3,option4]]; 249 | 250 | ISMultipleChoiceQuestion* question1 = [ISMultipleChoiceQuestion new]; 251 | 252 | question1.selectableOptions = @1; 253 | 254 | ISMultipleChoiceOption* question1option1 = [ISMultipleChoiceOption optionWithText:@"One" correct:NO]; 255 | 256 | ISMultipleChoiceOption* question1option2 = [ISMultipleChoiceOption optionWithText:@"Two" correct:YES]; 257 | 258 | ISMultipleChoiceOption* question1option3 = [ISMultipleChoiceOption optionWithText:@"Three" correct:NO]; 259 | 260 | [question1 addOptions:@[question1option1,question1option2,question1option3]]; 261 | 262 | ISMultipleMultipleChoiceQuestion* multipleMultipleChoiceQuestion = [ISMultipleMultipleChoiceQuestion questionWithQuestions:@[question,question1]]; 263 | 264 | multipleMultipleChoiceQuestion.selectableOptions = @1; 265 | 266 | XCTAssertTrue(multipleMultipleChoiceQuestion.options.count == 2, @"should have 2 option groups"); 267 | 268 | NSArray* options1 = multipleMultipleChoiceQuestion.options[0]; 269 | 270 | NSArray* options2 = multipleMultipleChoiceQuestion.options[1]; 271 | 272 | XCTAssertTrue(options1.count == 4, @"should have 4 options in 1 group"); 273 | 274 | XCTAssertTrue(options2.count == 3, @"should have 4 options in 2 group"); 275 | 276 | ISMultipleChoiceResponse* response = [ISMultipleChoiceResponse responseWithIndexes:@[ @[@1] ]]; 277 | 278 | BOOL correct = [multipleMultipleChoiceQuestion responseCorrect:response]; 279 | 280 | XCTAssertFalse(correct, @"answer should be incorrect"); 281 | } 282 | 283 | @end 284 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/iOS_QuizKitQuizParserTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // iOS_QuizKitQuizParserTests.m 3 | // iOS-QuizKit 4 | // 5 | // Created by Christian French on 02/05/2014. 6 | // Copyright (c) 2014 inline-studios. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ISQuizParser.h" 11 | #import "ISMultipleMultipleChoiceQuestion.h" 12 | #import "ISMultipleMultipleChoiceQuestion+Private.h" 13 | #import "ISOpenQuestion.h" 14 | @interface iOS_QuizKitQuizParserTests : XCTestCase 15 | 16 | @end 17 | 18 | @implementation iOS_QuizKitQuizParserTests 19 | 20 | - (void)setUp 21 | { 22 | [super setUp]; 23 | // Put setup code here. This method is called before the invocation of each test method in the class. 24 | } 25 | 26 | - (void)tearDown 27 | { 28 | // Put teardown code here. This method is called after the invocation of each test method in the class. 29 | [super tearDown]; 30 | } 31 | 32 | - (void)testBasicParse 33 | { 34 | NSBundle *bundle = [NSBundle bundleForClass:[self class]]; 35 | 36 | NSString* path = [bundle pathForResource:@"quiz_test_multi_multi_choice" ofType:@"plist"]; 37 | 38 | NSDictionary* quizData = [NSDictionary dictionaryWithContentsOfFile:path]; 39 | 40 | ISQuiz* quiz = [ISQuizParser quizFromDictionary:quizData]; 41 | 42 | XCTAssertNotNil(quiz, @"should have a quiz"); 43 | 44 | XCTAssertTrue(quiz.questions.count == 1, @"should have 1 question"); 45 | 46 | XCTAssertTrue([quiz.questions[0] isKindOfClass:[ISMultipleMultipleChoiceQuestion class]], @"should have a MMChoice question"); 47 | 48 | ISMultipleMultipleChoiceQuestion* question = (ISMultipleMultipleChoiceQuestion*)quiz.questions[0]; 49 | 50 | XCTAssertTrue([question.questionType isEqualToString:@"multipleMultipleChoice"], @"type should be multipleMultipleChoice"); 51 | 52 | XCTAssertTrue([question.questionSubType isEqualToString:@"list"], @"type should be multipleMultipleChoice"); 53 | 54 | XCTAssertNotNil(question.supplementaryText, @"supplementaryText not set"); 55 | 56 | 57 | 58 | ISMultipleChoiceQuestion* option1 = (ISMultipleChoiceQuestion*)question.questions[1]; 59 | 60 | ISMultipleChoiceOption* option = option1.options[3]; 61 | 62 | XCTAssertTrue(option.preSelected, @"option should be preselected"); 63 | 64 | XCTAssertTrue(question.questions.count == 3, @"should have 1 options"); 65 | 66 | XCTAssertTrue(question.options.count == 3, @"should have 3 options"); 67 | 68 | } 69 | 70 | //open question deserialization 71 | 72 | - (void)testOpenDeserialization { 73 | 74 | NSBundle *bundle = [NSBundle bundleForClass:[self class]]; 75 | 76 | NSString* path = [bundle pathForResource:@"quiz_test_open" ofType:@"plist"]; 77 | 78 | NSDictionary* quizData = [NSDictionary dictionaryWithContentsOfFile:path]; 79 | 80 | ISQuiz* quiz = [ISQuizParser quizFromDictionary:quizData]; 81 | 82 | XCTAssertNotNil(quiz, @"should have a quiz"); 83 | 84 | XCTAssertTrue(quiz.questions.count == 4, @"should have 4 question"); 85 | 86 | XCTAssertTrue([quiz.questions[3] isKindOfClass:[ISOpenQuestion class]], @"should have a Open question"); 87 | 88 | ISOpenQuestion* question = (ISOpenQuestion*)quiz.questions[3]; 89 | 90 | XCTAssertTrue([question.questionType isEqualToString:@"open"], @"type should be open"); 91 | 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/quiz_test.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | questions 6 | 7 | 8 | type 9 | open 10 | answer 11 | NSObject 12 | matchMode 13 | caseSensitive 14 | text 15 | What is the base class of most objects in Foundation Objective-C projects? 16 | scoreValue 17 | 1 18 | 19 | 20 | type 21 | open 22 | answer 23 | gcd 24 | matchMode 25 | exact 26 | text 27 | What is the name of Apple's C threading API for Mac and iOS? 28 | scoreValue 29 | 1 30 | 31 | 32 | type 33 | open 34 | answer 35 | NSOBJECT 36 | matchMode 37 | close 38 | text 39 | What is your 40 | scoreValue 41 | 1 42 | 43 | 44 | type 45 | multipleChoice 46 | options 47 | 48 | 49 | text 50 | C 51 | correct 52 | 1 53 | 54 | 55 | text 56 | C++ 57 | 58 | 59 | text 60 | Java 61 | 62 | 63 | text 64 | Ruby 65 | 66 | 67 | text 68 | Objective-C 69 | correct 70 | 1 71 | 72 | 73 | text 74 | What is your favorite programming language? 75 | selectableOptions 76 | 1 77 | scoreValue 78 | 2 79 | 80 | 81 | type 82 | trueFalse 83 | text 84 | Is Xcode slow? 85 | answer 86 | 1 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/quiz_test_matching.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | questions 6 | 7 | 8 | options 9 | 10 | 11 | text 12 | One 13 | correct 14 | 0 15 | 16 | 17 | text 18 | Two 19 | correct 20 | 1 21 | 22 | 23 | text 24 | Three 25 | correct 26 | 2 27 | 28 | 29 | answers 30 | 31 | 32 | text 33 | 1 34 | 35 | 36 | text 37 | 2 38 | 39 | 40 | text 41 | 3 42 | 43 | 44 | type 45 | matching 46 | subType 47 | list 48 | supplementaryText 49 | some extra text. 50 | text 51 | Match the options to the answers. 52 | scoreValue 53 | 3 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/quiz_test_multi_choice.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | questions 6 | 7 | 8 | type 9 | multipleChoice 10 | options 11 | 12 | 13 | text 14 | One 15 | correct 16 | 1 17 | 18 | 19 | text 20 | Two 21 | correct 22 | 1 23 | 24 | 25 | text 26 | Three 27 | 28 | 29 | text 30 | Four 31 | 32 | 33 | text 34 | select first 2 options 35 | scoreValue 36 | 2 37 | selectableOptions 38 | 2 39 | 40 | 41 | type 42 | multipleChoice 43 | options 44 | 45 | 46 | text 47 | One 48 | correct 49 | 1 50 | 51 | 52 | text 53 | Two 54 | correct 55 | 1 56 | 57 | 58 | text 59 | Three 60 | correct 61 | 1 62 | 63 | 64 | text 65 | Four 66 | 67 | 68 | text 69 | select last 3 options 70 | scoreValue 71 | 3 72 | selectableOptions 73 | 3 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/quiz_test_multi_multi_choice.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | questions 6 | 7 | 8 | options 9 | 10 | 11 | 12 | text 13 | One 14 | correct 15 | 1 16 | 17 | 18 | text 19 | Two 20 | 21 | 22 | text 23 | Three 24 | 25 | 26 | text 27 | Four 28 | 29 | 30 | 31 | 32 | text 33 | One 34 | correct 35 | 1 36 | 37 | 38 | text 39 | Two 40 | 41 | 42 | text 43 | Three 44 | 45 | 46 | preSelected 47 | 1 48 | text 49 | Four 50 | 51 | 52 | 53 | 54 | text 55 | One 56 | correct 57 | 1 58 | 59 | 60 | text 61 | Two 62 | 63 | 64 | text 65 | Three 66 | 67 | 68 | text 69 | Four 70 | 71 | 72 | 73 | type 74 | multipleMultipleChoice 75 | subType 76 | list 77 | supplementaryText 78 | some extra text. 79 | text 80 | select first option in each 81 | scoreValue 82 | 3 83 | selectableOptions 84 | 1 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/quiz_test_multiple_multi_multi_choice.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | questions 6 | 7 | 8 | options 9 | 10 | 11 | 12 | text 13 | One 14 | correct 15 | 1 16 | 17 | 18 | text 19 | Two 20 | 21 | 22 | text 23 | Three 24 | 25 | 26 | text 27 | Four 28 | 29 | 30 | 31 | 32 | text 33 | One 34 | correct 35 | 1 36 | 37 | 38 | text 39 | Two 40 | 41 | 42 | text 43 | Three 44 | 45 | 46 | text 47 | Four 48 | 49 | 50 | 51 | 52 | text 53 | One 54 | correct 55 | 1 56 | 57 | 58 | text 59 | Two 60 | 61 | 62 | text 63 | Three 64 | 65 | 66 | text 67 | Four 68 | 69 | 70 | 71 | type 72 | multipleMultipleChoice 73 | text 74 | select first option in each 75 | scoreValue 76 | 3 77 | selectableOptions 78 | 1 79 | 80 | 81 | options 82 | 83 | 84 | 85 | text 86 | One 87 | correct 88 | 1 89 | 90 | 91 | text 92 | Two 93 | correct 94 | 1 95 | 96 | 97 | text 98 | Three 99 | 100 | 101 | text 102 | Four 103 | 104 | 105 | 106 | 107 | text 108 | One 109 | correct 110 | 1 111 | 112 | 113 | text 114 | Two 115 | correct 116 | 1 117 | 118 | 119 | text 120 | Three 121 | 122 | 123 | text 124 | Four 125 | 126 | 127 | 128 | 129 | text 130 | One 131 | correct 132 | 1 133 | 134 | 135 | text 136 | Two 137 | correct 138 | 1 139 | 140 | 141 | text 142 | Three 143 | 144 | 145 | text 146 | Four 147 | 148 | 149 | 150 | type 151 | multipleMultipleChoice 152 | text 153 | select first 2 options in each 154 | scoreValue 155 | 6 156 | selectableOptions 157 | 2 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/quiz_test_open.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | questions 6 | 7 | 8 | type 9 | open 10 | answer 11 | NSObject 12 | matchMode 13 | caseSensitive 14 | text 15 | What is the base class of most objects in Foundation Objective-C projects? 16 | scoreValue 17 | 1 18 | 19 | 20 | type 21 | open 22 | answer 23 | gcd 24 | matchMode 25 | exact 26 | text 27 | What is the name of Apple's C threading API for Mac and iOS? 28 | scoreValue 29 | 1 30 | 31 | 32 | type 33 | open 34 | answer 35 | NSOBJECT 36 | matchMode 37 | close 38 | text 39 | What is your 40 | scoreValue 41 | 1 42 | 43 | 44 | type 45 | open 46 | answers 47 | 48 | two 49 | one 50 | 51 | matchMode 52 | contains 53 | text 54 | type the words for 1 and 2 55 | scoreValue 56 | 2 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/quiz_test_sentence_multi_multi_choice.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | questions 6 | 7 | 8 | options 9 | 10 | 11 | text 12 | This is the first sentence. 13 | correct 14 | 3 15 | 16 | 17 | text 18 | Second sentence this is. 19 | correct 20 | 0 21 | 22 | 23 | text 24 | This is the third sentence. 25 | correct 26 | 3 27 | 28 | 29 | type 30 | multipleMultipleChoiceSentence 31 | text 32 | select the number word option in each question 33 | scoreValue 34 | 3 35 | selectableOptions 36 | 1 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /iOS-QuizKit/iOS-QuizKitTests/quiz_test_sentence_multi_multi_choice_multi_selection.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | questions 6 | 7 | 8 | options 9 | 10 | 11 | text 12 | This is the first sentence answer first. 13 | correct 14 | 3 6 15 | 16 | 17 | text 18 | Second sentence and second answer second correct. 19 | correct 20 | 0 3 5 21 | 22 | 23 | text 24 | This is the third sentence, third answer. 25 | correct 26 | 3 6 27 | 28 | 29 | type 30 | multipleMultipleChoiceSentence 31 | text 32 | select the number word option in each question 33 | scoreValue 34 | 3 35 | selectableOptions 36 | 0 37 | 38 | 39 | 40 | 41 | --------------------------------------------------------------------------------