Testing on a device.

As a group we feel it is important to test our app on a physical device. The Xcode development environment allows the software to be tested on a iPhone simulator, however testing on a physical device allows for a more accurate representation to be assessed on how the app will ultimately function. Things such as how easy the user interface is to navigate can be more easily found out if testing on a physical device, due to the nature of the interaction with the software being more representative of the final desired outcome.

In order to get our code built to a physical device, we first had to jump through several administrative hoops, in regards to the Apple developer account system. This involved getting approved through the university, as well as setting up provisioning profiles and getting a specific device approved for application testing. This proved more difficult than expected at first, and we suffered some minor delays in being able to test our application.

When we did manage to get our software on to a physical iPhone, we were faced with additional problems. Our work on setting up the constraints in Xcode had been mainly focused on an iPhone 6-sized canvas. Unfortunately, we had often fallen into the pitfall of using static number based measurements in our laying out of the application elements. This meant that on the specific-sized canvas we were designing for, the application looked as we intended, however on screens any smaller, the elements were arranged incorrectly or sized too large for the display.

In order to fix this, we went through and redid much of our constraints work within our application. Using techniques such as proportional widths and heights, aspect ratios, and pinning elements to each other we achieved a design methodology that responds to whatever size canvas it is set to fill. This allows the application to appear properly and as we had designed it on a range of different devices, from an iPhone 4S to iPads.

As for the other functions of the application, we also initially had issues with sounds not playing, however we also managed to fix this easily, as it was simply due to a file storage issue.

Now that the constraints have been redone, the application works well on a physical phone, and we are happy that its functionality and aesthetics transfer well to different device sizes.

A snapshot of the application running on an iPhone 4S, after constraints have been fixed.

A snapshot of the application running on an iPhone 4S, after constraints have been fixed.

 

Advertisements

Implementing the JSON.

After establishing that we’d be using JSON to store data for our questions, it was time to actually implement this in the code.

The bulk of the code written to implement JSON data storage.

The bulk of the code written to implement JSON data storage.

This is the main section of code that we developed in order to make data storage possible with JSON. As we have discussed elsewhere, due to limitations with the environment in which the app will be used we are opting to store our data locally. This code therefore essentially locates the local JSON file, and parses it as an array of objects of the question class we declared at the start of development of the project.

In the main ViewController for the quiz section of the app, an array is declared and set to the result of the loadQuestions() function above, so that the code in the main body of the app essentially remains the same as it ultimately interfaces with the same array of objects, but more code has been implemented on the back end in creating this array from our JSON file, a snippet of which can be seen here.

Snippet of the JSON file.

Snippet of the JSON file.

File storage, P-lists and JSON.

Having previously discussed here the benefits of separating the data for the app’s questions from the functionality in the code, the question still stood on how to best go about achieving this goal.

Speaking to our tutors, and doing our own research as well, there appeared to be several different options to explore which could provide a suitable solution in the context of our work.

P-lists

P-lists, more formally referred to as property lists, are files used  in OSX and iOS programming to store serialised objects. They are generally utilised to store a user’s settings for an app, however can be leveraged to store various information about applications. In this current context, a property list file could be used to store information about each question contained within our app.

These files are most commonly formatted in either an XML or a binary form, and can be edited in a text editor. Additionally, the Xcode environment has built in support for editing property lists. These files can be viewed in a hierarchical manner and edited in a similar fashion.

An example of viewing a P-list file in Xcode.

An example of viewing a P-list file in Xcode.

JSON

JSON, standing for JavaScript Object Notation, is a commonly used, lightweight data interchange format (JSON.org). The format uses human-readable text to transmit data which is comprised of pairs of attributes and values. Its most common use is to transmit information between servers and web applications, similarly to the XML format. JSON is a format that was originally derived from JavaScript, a scripting language that is well-used on the web. Despite this, the format of JSON itself is language independent, meaning JSON data can be created and interpreted in many varied programming languages.

Usefully, one such programming language with support for the JSON format is Swift. Even more usefully, we have already been given somewhat of a head-start on using JSON in Swift, due to a tutorial workshop that was given which covered some of the basics. Storing our data in the JSON format makes sense at it is somewhat of a standard for data transfer, being somewhat easier to use that XML-based solutions, and after some trial and error I have had more success adapting the currently stored information to this format than to a property list-based structure. The JSON format also, similarly to P-lists, has the benefit of being easily human-readable and understandable in its text-based representation. Unlike the property list format, there is no visual hierarchical editor built into Xcode, but I do not think this is too big a consideration, since part of the point of separating the data from the code to begin with is to make it easily editable by people with no knowledge of the code base of the application – people who would likely not wish to use the Xcode software to make these changes anyway.

Online/Offline

After deciding that our data will be stored in the JSON format, there is still the question of where exactly it will be stored. This format lends itself well to communicating with an external server to fetch data over the internet. This, however, would add an additional layer of complexity to the development process and would come with its own set of problems and limitations. We are unsure at this moment in time whether to go down this road or whether to store our data on the local filesystem with the application, as these methods both have their relative strengths and weaknesses. In either case, however, a JSON-based approach to providing the code with data for the questions is likely to be feasibly achievable, and therefore we have decided to adopt this technology moving forwards. I will post the progress we make with implementing this functionality as it is made.

 

References:

Introducing JSON [online]. JSON.org. Available from: http://json.org [accessed 15 May 2015].

Separating data from function and the Model-View-Controller architecture pattern.

With the basic structure of the application done, and a class made to define the custom question objects we’d be using, the question was raised of how the data would be stored to populate these objects. Each question needs to have stored a sound file, multiple options for users to choose from, and a value to determine which of these is the correct answer. As well as this, a fact needs to be stored relating to each question, however this functionality has not yet been implemented into the application.

So far, the relevant information has simply been stored in the code as an array. This is an array of our custom question objects, and is statically declared in the code before any functionality takes place. This works well for the basic prototype, as it allows the core functionality of the app to work with a small sample of example questions. However, this is not a solution which lends itself well to being easily updated with additional questions in the future. It also doesn’t make understanding this data and where/how it is stored very easy if anyone needs to change the information for any reason.

Both of these issues would be solved by storing the information about all of the questions in the application in a single file, separate from the main body of the code. This  would allow the questions to be updated and changed more easily, since the code of the application itself would not need to be changed at all. The person changing or adding a question could simply open this file, change the text for the relevant question or add a new one, and then drop an accompanying sound file into the right folder within the application directory. This not only makes this process faster to do, but also allows people with no knowledge of the Swift programming language, array declaration, or our custom class structure to add in their own questions or change existing ones without a steep learning curve.

Separating the data from the functionality and appearance of an application in such a way is a common and useful strategy. This way of designing a system is referred to as ‘model-view-controller’ or MVC. As described by Burbeck (1992), “In the MVC paradigm the user input, the modeling of the external world, and the visual feedback to the user are explicitly separated and handled by three types of object, each specialized for its task”. In essence, MVC is made up of a model, which manages the data of the application, a view, which manages graphical/text output, and a controller, which interprets and responds to user inputs and updates the model and view where necessary. In this instance, separating the data for questions into a separate file will create a distinction between the model (this new file) and the controller (the existing ViewController in the Swift code).

To this end, I intend to go about the reworking of the code that has been produced thus far so that these elements can be effectively separated out while still maintaining the intended functionality of the application. This will involve moving the question data to a separate file, and producing some accompanying code in the controller to fetch this data at the right time so that it can be used and displayed in the view. In order for the data to be used within the app, it will likely still be pulled into an array in the code so I expect the implementation of functionality after that point to stay largely the same, however work will need to be done on developing a system to fetch the data and interpret it in the right way. I am not yet certain what form or file-type the data will take, or how exactly the code to manage it will function, but I am certain that completing this task will be beneficial to the general functionality of the application and the ease with which we will be able to continue to work on it.

 

References:

Burbeck, S., 1992. Applications Programming in Smalltalk-80(TM): How to use Model-View-Controller (MVC) [online]. Available from: http://st-www.cs.illinois.edu/users/smarch/st-docs/mvc.html    [Accessed 15 May 2015].

Saving state.

One important criteria we set out for our application was that it should be able to save the question the user is on, and store this information even through closing the app and restarting the device. When the app is reopened, it should then recall the last position when the user starts playing the game again and return them to the relevant question.

To do this, information needs to be stored in a file which is saved to the device’s storage so that it can persist when the software stops running. There are several different formats this data can take, however perhaps the simplest, most common and the one that we have chosen to use here is NSUserDefaults.

NSUserDefaults is a class in Swift which is used to store simple information so that it is persistent. It is limited in what it can store, so it is mainly used for simple data such as integers and strings. Common implementations of NSUserDefaults include saving a user’s birthday, name or other such simple information.

This approach, then, while limited is perfect for what is needed for our app. In order to save which question the user is on, in the way that our code is structured, only a single integer needs to be saved. By saving the integer value of the current question, this number can then be passed into several functions when the app is run to update the text to display the right question, and set the current index of the array of question objects. This will effectively resume the application from the last question, although in effect it simply re-sets the current question when the app loads. This is a simple implementation of persistence, however in this context it s all that is really required.

 

currentQuestion = defaults.integerForKey("currentLevel")

This is essentially the code to load the current question from the user defaults. This code is run when the app loads, so that if a value is stored from a previous session it is loaded into the ‘currentQuestion’ variable, which is used for updating the display and for comparing with the array index.

defaults.setObject(currentQuestion + 1, forKey: "currentLevel")

This snippet of code then sets the next question in the array to the appropriate key in the user defaults. This code is run when a user answers a question correctly, at the same time as updating the view and showing congratulations & facts.

This is a simple solution to the problem, but one that works effectively and more than adequately fits the needs of the project. As a result of incorporating this code into our app, it now saves states between sessions. This is one more requirement fulfilled.

Initial prototype – Class creation and basic functionality.

In order to begin the development process on this project, the first thing that needed to be done was to establish the key functionality of the application at the most basic level, and begin figuring out how to best translate this into the Swift programming language. Following on from our discussion of prototyping strategies, this would enable the creation of an initial prototype which would serve both the purposes of allowing us to test our basic functionality and allowing the basis for an evolutionary prototype which will ultimately be turned into a final piece of software.

We went over our plans for the application, and determined the core functionality of our app to be :

  • Presenting a sound file to the user which can be played
  • Providing multiple options for the user to choose a phrase which relates to the sound
  • Determining whether the option they have selected is the correct answer
  • Moving to a fact/congratulations screen and then the next question after a question has been answered, updating the information on display

In order to more quickly begin prototyping of the application, and due to our current lack of knowledge on the best ways to work with sound in Swift/Xcode, we decided to develop an initial prototype application which focused on the last three points. This piece of software would therefore simply present a set of choices, and then progress through to additional groups of options after the pre-determined ‘correct’ option was chosen.

In order to comply with object-oriented programming practices, it was decided that the best way to structure this functionality was to implement a custom class for our questions. This could then be used, and passed different data, to create instances of question objects which could be stored and used to populate the information displayed on the screen.


class Question {
var soundFilePath: String
var options: [String]
var correctAnswer: Int
var fact: String

init(soundFilePath: String, options: [String], correctAnswer: Int, fact: String) {
self.soundFilePath = soundFilePath
self.options = options
self.correctAnswer = correctAnswer
self.fact = fact
}

This is the Question class that I ended up creating. It is quite simple, and it does not have any methods. This is because I intend to use this class as a structure for storing data for each question, but the functionality of the application surrounding the questions and progression will be handled by a main quiz controller, using Swift’s ViewControllers. The class stores five properties – a sound file path (although this is not yet being used), an array of options that make up the potential choices for each question, an integer value which stores which member of the options array is the correct one, and a fact string to store the interesting fact that will be displayed about each question after its completion (although this is also not currently being used).


func updateText(question: Question){
optionOneButton.setTitle(question.options[0], forState: .Normal)
optionTwoButton.setTitle(question.options[1], forState: .Normal)
optionThreeButton.setTitle(question.options[2], forState: .Normal)
optionFourButton.setTitle(question.options[3], forState: .Normal)

In the view controller for the quiz page, this function has been written to update the information shown on the screen when a new question is loaded. When the function is called, a variable object of the Question class, which will be set to the current question to be displayed, is accessed, and four buttons which I have laid out in the view (one for each option) have their title text set to the contents of the options array of the question object. This function will be called whenever the application needs to transition to a new question – so primarily when a question is answered correctly. It is also called upon initial load of the view to set the text for the first question.

 


var questions: [Question] {
return [
Question(soundFilePath: "", options: {
return ["Pillowblade",
"Softknife",
"Softsword",
"Floppystabber"]
}(), correctAnswer: 3, fact:""),
Question(soundFilePath: "", options: {
return ["Longspee",
"Other Stuff",
"Things",
"Choices"]
}(), correctAnswer: 1, fact:""),
Question(soundFilePath: "", options: {
return ["Wrong",
"Wrong",
"Wrong",
"Correct Answer"]
}(), correctAnswer: 4, fact:"")
]
}

I declared in the code an array of three question objects with some example data filled out. By having the code interact with the data by means of an array of questions, the current can easily be accessed and saved as the current index of the array. This allows for an easy method of transitioning between questions, as the previous update method can simply be called on the current index of the array, and the index can be incremented for each question in series.


@IBAction func optionOne(sender: AnyObject) {

       checkSuccess(1)
}

In order for the user to actually select an option, the buttons for each were given a function, via IBAction elements, which runs a function called checkSuccess and passes it the number corresponding to the relevant option. This checkSuccess function essentially simply checks the number passed in with the correct answer integer value stored in the current question object in the array. If the values are equal, the current question index is incremented by one and the next question in the array loaded and displayed (eventually there will be a step in between where a view will be shown to the user displaying a congratulatory message and interesting fact before loading the next question). For now, if the values do not match this is simply printed to the console, however in the final application a message needs to be displayed telling the user of the app to try again.

So, to recap – this initial prototype of our application presents the user with a set of four choices that make up a question in an array, determines which option the user chooses and whether it is correct, and then if it is loads and displays the next question’s options. As far as basic functionality goes in our application, this essentially covers it, minus actually playing the sound, and displaying interesting facts about the answers – both of which will be worked on in the coming days.

Prototyping Strategies

Prototyping is a common practice in many fields, including design, manufacture and software development. A prototype is essentially a very early sample, or model, of a final product which is built in order to test concepts or as a learning experience. By building a prototype, an idea can be tested as a realised, functional system rather than simply on its theoretical merits. This is often the step between formalising and evaluating an idea (Soares and Rebelo 2012).

In software development specifically, prototyping generally takes the form of developing incomplete versions of a software program. Prototypes developed will typically contain a few aspects of the final product being worked towards, but will rarely comprise the complete desired functionality. The final software may also end up completely different from some prototypes.

The benefits of prototyping are several. The process gives an early opportunity for user feedback to be gathered, as key functionality can be tested with users before the rest of an application needs to be developed. A prototype can also be shown to potential clients to make sure the work is proceeding according to specifications. A third benefit is the use of prototyping to allow the developer of the software application to determine whether previously set project goals and timelines are accurate and can be reasonably met.

Nielsen (1994) talks about varying dimensions of prototypes. Essentially software prototypes can be categorised into two distinct categories with different intents. One method, known as a ‘horizontal prototype’ is to showcase a broad section of a whole system. Horizontal prototypes then focus on exploring user interaction with the system rather than deep or detailed functionality. This can be useful in gaining an overview of the interface of a system, and can be shown as a demonstration of the overall scope and aims of a software project. It also allows for estimates into how much work needs to be done to produce the final product, and how long this is likely to take.

The second category of prototype is the ‘vertical’ prototype. This encompasses a different approach from the horizontal prototype, focusing more on a single sub-element or piece of functionality desired for the application. This allows for more specific information to be gleaned, such as network performance, necessary database solutions, and data storage sizes. It also allows one process to be tested thoroughly and worked on to make sure that it, as a standalone piece of functionality, works as intended before it is integrated with the larger software product as a whole and adapted to that.

Their is also a distinction made between throwaway, or rapid, and evolutionary prototyping of software projects. Throwaway prototyping is a process in which quick prototypes are continually developed for various parts of a system which are eventually thrown away and are not integrated into the final software. This allows for these models to be developed quickly, shortly after the initial requirements are given, so that working examples can be worked with from an early stage. This method is in contrast to evolutionary prototyping. The aim here is to build a prototype which is refined and built on over time. This may then form the basis of the finished system once it has been improved and completed.

In this project, I think it will be important to develop a variety of prototyping strategies. Taking from all of these methodologies will hopefully greatly aid in the development of the application. To this end, it will be useful for us to develop a working prototype early on, which can be used in an evolutionary manner. Then, additional features and new implementations can be rapidly prototyped until they are functional before including them in the main evolutionary system. This can then continue to be added to, while being refined and polished in order to ultimately arrive at a complete, final software solution.

References:

Nielsen, J., 1994. Usability Engineering. Elsevier.

Soares, M. and Rebelo, F., 2012. Advances in Usability Evaluation. CRC Press.