Vous êtes sur la page 1sur 242

OCR A-Level Computer ScienceH446-03/04

Coursework
Documentation
Timetable Application

Habib, Juheb (EJNR)


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Contents

Analysis ............................................................................................................................ 5
Overview ................................................................................................................................ 5
Stakeholders ........................................................................................................................... 7
Computation Thinking ............................................................................................................ 8
Thinking Abstractly ............................................................................................................. 8
Thinking Ahead ................................................................................................................... 8
Thinking Procedurally ......................................................................................................... 8
Thinking Logically ................................................................................................................ 8
Thinking Concurrently ........................................................................................................ 9
Application Features....................................................................................................... 10
Research on similar applications .......................................................................................... 10
schedulebuilder.org .......................................................................................................... 10
Timetable (Android application)....................................................................................... 10
Research on Target Demographic ........................................................................................ 11
Questionnaire ................................................................................................................... 11
Justification of Features.................................................................................................... 13
Summary of Features ........................................................................................................... 25
Problems ........................................................................................................................ 27
Requirements ................................................................................................................. 28
Success Criteria .............................................................................................................. 29
Design ............................................................................................................................ 32
Application Structure ........................................................................................................... 32
Structure Chart ................................................................................................................. 32
Wireframe ............................................................................................................................ 33
User Interface ....................................................................................................................... 33
Overview ........................................................................................................................... 33
Splash Screen .................................................................................................................... 33
Icon ................................................................................................................................... 34
Views................................................................................................................................. 34
Database Solution ................................................................................................................ 44
Development Plan ................................................................................................................ 45

Candidate Number - 4164 1 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Development ................................................................................................................. 47
Cycle 0: Setup ....................................................................................................................... 47
Testing .............................................................................................................................. 48
Evaluation ......................................................................................................................... 49
Cycle 1: The Agenda ............................................................................................................. 50
Outline .............................................................................................................................. 50
Header .............................................................................................................................. 51
Table ................................................................................................................................. 51
Queue ............................................................................................................................... 52
Colour Coding ................................................................................................................... 57
Evaluation ......................................................................................................................... 60
Cycle 2: The Notes ................................................................................................................ 61
Outline .............................................................................................................................. 61
View All Notes................................................................................................................... 61
Add/Edit Note ................................................................................................................... 62
Design and Development ................................................................................................. 63
Evaluation ......................................................................................................................... 90
Cycle 3: Schedule View and Events ...................................................................................... 94
Outline .............................................................................................................................. 94
Schedule ........................................................................................................................... 94
Events ............................................................................................................................... 94
Design and Development ............................................................................................... 100
Evaluation ....................................................................................................................... 141
Cycle 4: Linking up Views ................................................................................................... 148
Outline ............................................................................................................................ 148
Testing ............................................................................................................................ 149
Evaluation ....................................................................................................................... 150
Cycle 5: Agenda II ............................................................................................................... 150
Outline ............................................................................................................................ 150
Development .................................................................................................................. 151
Testing and Evaluation ................................................................................................... 154
Cycle 6: Settings ................................................................................................................. 156
Outline ............................................................................................................................ 156

Candidate Number - 4164 2 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Hour Selection ................................................................................................................ 156
Time Display Format ....................................................................................................... 157
Delete All ........................................................................................................................ 157
Development .................................................................................................................. 157
Evaluation ....................................................................................................................... 167
Cycle 7: Schedule Collisions ............................................................................................... 169
Outline ............................................................................................................................ 169
Generation ...................................................................................................................... 169
Load ................................................................................................................................ 169
Conflict Handling ............................................................................................................ 170
Design and Development ............................................................................................... 171
Evaluation ....................................................................................................................... 177
Evaluation .................................................................................................................... 179
Overview ......................................................................................................................... 179
Success Criteria ............................................................................................................... 180
Maintenance ................................................................................................................... 183
Limitations of My Solution ............................................................................................. 184
Conclusion ...................................................................................................................... 184
Source Code ................................................................................................................. 185
Model ................................................................................................................................. 185
App Delegate .................................................................................................................. 185
Note Model ..................................................................................................................... 186
Settings Model ................................................................................................................ 186
Event Model.................................................................................................................... 187
View Controllers ................................................................................................................. 188
Settings ........................................................................................................................... 188
Agenda ............................................................................................................................ 191
Notes............................................................................................................................... 192
View All Notes................................................................................................................. 194
Notes Cell ........................................................................................................................ 198
Schedule ......................................................................................................................... 199
Cells ................................................................................................................................. 204
Event ............................................................................................................................... 206

Candidate Number - 4164 3 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
OccurrencesTVC.............................................................................................................. 212
Occurrences Cell ............................................................................................................. 216
Colour Picker View Controller ........................................................................................ 217
Classes ................................................................................................................................ 218
Linked List Queue ........................................................................................................... 218
Sorts ................................................................................................................................ 220
Hash Table ...................................................................................................................... 222
Extensions .......................................................................................................................... 223
External Files ...................................................................................................................... 226
Picker Image ................................................................................................................... 226
Colour Picker ................................................................................................................... 229
Color Well ....................................................................................................................... 233
Color Picker Controller ................................................................................................... 235
Hue Picker ....................................................................................................................... 236
Side Menu Table ............................................................................................................. 239
Pod File ........................................................................................................................... 240

GitHub Link - https://github.com/grphine/Timetable.git


(GitHub repository is private. Access can be provided upon request)

Video Demonstration - https://vimeo.com/324957953/e93edba569

Candidate Number - 4164 4 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Analysis
Overview
I intend to create an application to assist students in timetabling. I plan to do this because
many students find it difficult to keep to a schedule and balance their work and personal
time. Some students may create timetables to manage their time, but this is often a slow
and tedious process. Physical copies are also easily lost.
Schools create timetables for students to follow for lessons, however these timetables are
inflexible and cannot reflect the fact peoples’ lives are in constant flux, nor do they continue
past the school day, so their scope is limited.
Making the timetable digital brings with it many benefits. All students keep a mobile device
on them and therefore can always access a timetable on their devices. The timetable can
also be backed up and therefore is not easily lost. Even in the case the user loses their
phone, it would still be possible to access the timetable on another device. The digital
nature also means that if the user wishes to create physical copies, they can do provided
they have access to a printer. They can then print out multiple copies, which means that
should they wish to use a physical timetable, they can do easily without risking losing their
timetable.
With the correct user interface and user experience, creating the timetable can be made to
be a simple process, which ultimately is the goal of the app – to make creating and following
a timetable easy. The digital format also means that the timetable can be edited with little
difficulty and also quickly. This allows the timetable to be flexible unlike rigid, traditional
timetables. The flexibility means the app will be easy to user in many different
circumstances. It is designed to cater to a variety of different lifestyles.
Many people often set alarms as reminders for events, this can prove tedious for anything
that happens only once, like a one-off event, and can lead to a user’s alarms screen to
become cluttered with a multitude of old alarms that are no longer useful. Some people do
not even think to use the alarms on their phones for reminders and only use it to wake up in
the morning.
Alarms can also be very intrusive and annoying when they suddenly go off.
Notifications are a gentler way of setting reminders, especially for non-urgent events. They
can still serve the same benefit as an alarm – working as a reminder – without the
annoyance of sounds suddenly going off.
Combining these two features is an effective way to help a student stay punctual and stick
to deadlines.
While working, the mobile device can be a large distraction. Notifications for social media or
games will often distract from studying, greatly reducing productivity. A lot of time is lost to
checking social media or playing games, and it is difficult to stop. Prevention from being

Candidate Number - 4164 5 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
distracted is more effective than coming with ways to stop once distracted.
Many modern phones already come with a do-not-disturb mode, or something similar.
However, a student would have to set the mode on before studying, and this cannot always
be the case. Even if the studying time is set at a specific time, it can be easy to forget to set
the mode on. Some users also are not even aware that their phone has the capability to
block calls and notifications for a period of time.
The application may be designed to streamline the process. When a user initially creates the
timetable, a do-not-disturb mode may be set to activate, beginning and ending with the
studying period. This makes the mode a “create and forget” sort of feature.
The mode may also include many potential customisations, for example allowing calls
through from specific contacts, or having different times have different levels of notification
blocking.
The problem of intrusive, distracting notifications is unique to electronic devices, and most
of all modern smartphones. Therefore this problem is entirely caused be and solvable by
computational means.
Students often dedicate exercise books or notepads to note taking within lessons, however
not many students carry anything to facilitate quick notes that they cannot commit to
memory. For example after a lesson a friend may tell them what the homework is and when
it is due if they had not paid attention in class. Some people write the information on their
hand as a reminder, however not everyone is comfortable doing this, but also have no other
reliable means of remembering the information.
Mobile applications exist already that can be used for note taking in the sense of quick
reminders to go over again later, however they often focus on that job only. By including it
as a feature in the application I will be creating, the application lends itself to be useful in
any academically related circumstance.
Another feature of the notes is that they can be combined with the alarms so that the notes
can remind the user to go over them again and either delete the note if it is no longer
useful, or commit the note to some form of long-term storage. In this case the integration of
a calendar will be useful, since then notes (for example, the missed homework assignment)
can be added to the calendar as a long term reminder, in the case that the user did not have
the time to set the reminder in the calendar from the start.
The overall problem is that students lack the facility to effectively timetable for studying,
and it is easy without a structure to allow non-work time to consume too much time.
The problem lends itself to mobile devices due to their ubiquity and ease of access, and
further, a digital system can be edited quickly and easily. Creating the timetable, along with
all of its additional features, as a mobile application means it can be easily accessed, and has
the advantage of internet backups and cross-device support. The editable nature means
that the timetable can be created to suit the needs of any user, and even the same user
through different periods in their life.
Only through mobile applications can the flexibility and ease of access required be attained.
This will provide a flexible timetable with a number of additional features with the ultimate
goal to enhance their learning.

Candidate Number - 4164 6 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Stakeholders
The application is designed around a student’s life. One thing common with all students is
that they attend an educational institution. They also have a certain amount of studying that
they are required to do. However, this varies depending on the level of education that they
are in and the subject or subjects that they are studying.
The application is designed to accommodate for students regardless of their level of
academic progression. This means it will have to be highly flexible and able to fit to any
lifestyle.
The application will also have to be intuitive to use since a range of different ages may find
use in the app. However, in any case, the user will most likely be quite capable with mobile
devices and able to navigate familiar interfaces, so making the application familiar may
make it easier to use.
While the main stakeholders may be students, staff at educational institutions may also
have an interest in this application, as it solves a problem common to many classrooms –
distractedness and disorganisation. The application will serve to prevent mobile devices (at
least) from being a distraction in the classroom by containing the do not disturb mode,
while the schedule will also help ensure that the students remember which lessons they
may have, while also being able to keep notes as a reminder of any work due. As teacher
deal with a large problem of ensuring students consistently remember what they need for
class, this application, with its notification and alarm features, may serve to fix that.
The application also has potential to appeal to the general population due to its flexible
foundation, which means anybody should be able to use the application as a timetable,
despite the fact that the application’s focus is on students.
Although the app targets students, it may very well be open for use by anyone. The typical
user, however, will be aged between 16 and 21, and are currently attending an educational
institution (be it secondary school, college, or university). They will be the kind of person
who always has their phone on them, and may already use their phone frequently to aid
their education, however have often faced difficulty in finding an application that suits their
needs. They also may be seeking how to enhance their learning with the technology they
use on a daily basis. Sometimes, they may find that said technology is distracting, and need
a way to eliminate those distractions, while still gaining the benefits that technology may
provide. Finally, and most of all, they wish to organise their life and studying in a beneficial
way.
I will conduct research about my application using my target demographic. I will also select a
portion of people from my target demographic to aid me in developing this software. They
will work as a focus group, and provide feedback on various features, along with being my
usability testers.

Candidate Number - 4164 7 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Computation Thinking
In this section, I will discuss other points (that I have not mentioned so far) regarding why
this problem is solvable using computational methods.

Thinking Abstractly
The application abstracts away the specifics of the user’s lifestyle. They may have multiple
tasks to do while attending maths, for example, however the program will only concern
itself with maths occurring at that time and reminding the user that it appears then.
Abstracting the problem helps to simplify it, reducing the complexity and making the
experience easier on the user. The user will not be overwhelmed with reams of unnecessary
information. They only need to focus on the core essentials – being what the event
occurring is, and when it may occur.

Thinking Ahead
To create a schedule for the user, the user will have to enter all the events they wish to
show up. They may choose to only display events during a certain period of time (in which
case it is also ideal to be able to select the range). Furthermore, details about the event are
required – the name, so the user can tell what the event is; the time, so the application
knows when to display it; any alarms, in case the user wishes to be reminded about it.
Note taking, too, requires data input, those being the note title (to differentiate them) and
the body of the note.
Alarms will require the time and date of occurrence, and possibly whether it repeats or has
a different tone.
Printing will require some form of printer set up. The user will have to define which printer
to connect to before any printing can be done.

Thinking Procedurally
The problem can be broken down into various features (schedule, notes etc.). Each of these
can then further be broken down into their components. For example, the schedule may
consist of six parts, where those parts of creating the framework, creating events, editing
events, deleting events, displaying events, and handling conflicts.
Those parts can then be tackled individually, and once each problem is solved, the whole
solution can be put together to from a working product.

Thinking Logically
There are some decisions the software will have to make. When to fire an alarm is a clear
example, as it cannot fire too early or too late. Regarding the alarms, if the alarm is to sound
repeatedly, then the code will fire in loops given a certain condition (usually being that the
user has not dismissed the alarm).

Candidate Number - 4164 8 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
In a more general sense, decisions will have to be taken whenever there is any validation
within the program, as the program checks whether the data is acceptable or not. Other
possible decision making points often rely on other forms of user input, where the program
must decide the best course of action dependent on the choices the user makes.
For example, the user may be given the choice of two (or more) options, and they will make
a decision. The program will then interpret the decision the user has made by their input,
and will execute any relevant code.
There will likely be many cases where repetition is required. Any case of iteration make use
of it. This includes any situation where an array (or other data structure) is iterated through
– maybe to visit every item. Generating a table, for example, may make use of iteration.
Tableviews in Swift certainly make heavy use of this, as they are easily structured from an
array.

Thinking Concurrently
Concurrent programming in an application of my size does not often occur. It is operating at
a high enough level that concurrency at the machine level is not handled by me, meanwhile
it is not so large that it requires optimisation.
While concurrent programming is unlikely to appear, it is not impossible. Any time delayed
function, for example, may make use of concurrency to carry out other tasks.
Database loads, for example, sometimes take a period of time. In that time, other code may
be executed. Alternatively, the program may be structured so that the database call occurs
early on, with setup code for a view following, allowing the database to load in the
background without a significant wait time added, like if it were called directly at the point it
is needed.

Candidate Number - 4164 9 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Application Features
Research on similar applications
Here I will discuss applications and service that have set out to solve a similar problem to
the one that I am.

schedulebuilder.org
This web-app shows a single week view, with days along the top and hours on the side.
The number of days and hours shown can be edited in settings.
Adding events consists of writing the name of the activity, the times between which it takes
place, with optional selection of the days it recurs and colours changes. The timetable’s
background can be changed.
It is slightly buggy when editing. For multiply recurring events, using the edit all button
clears any changes and puts everything to the time of the one being edited, if they happen
to be at different times.
It features an undo button which is incredibly useful, however it only undoes one step.
A unique URL can be used to save edits made and access the timetable again at a later date.
The timetable proves tedious to fill out and it only represents one week. Someone on a
biweekly schedule would have to create another timetable from scratch for the second
week.

Timetable (Android application)

The application features an intuitive user interface, with a bar that slides across to allow
access to every view quickly.
It also features multiple timetables.

Candidate Number - 4164 10 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The dashboard presents a view to quickly see the next event, along with upcoming events
compressed down for quick viewing.
It may be worth implementing a dashboard feature in my own application.
Although similar applications exist to the one I am planning on creating, I intend to create
another timetable application with a key difference: increased user interaction with the
application.
Most other applications are somewhat offhand, showing the information the user put in,
and only used for one specific task.
I intend to make the user’s interaction with the application more personal, by collected data
from usage and feedback from the user within the application to personalise their
experience. I also hope to create the application to be useful in many situations in an
academic environment. Although its main feature is the timetable, I do not plan on this
being the only feature, nor the sole focus.

Research on Target Demographic


Having completed research on similar applications, I will now conduct research on my target
demographic to understand which features they may wish to use and which may not be as
helpful.
I will create a questionnaire detailing the possible features within the application, asking for
their thoughts on each.
From the results, I can decide which features are most useful/practical.
The following questionnaire will be sent out to my target demographic via social media they
are likely to user (Facebook, Snapchat etc.)
The survey is hosted by Google Forms.

Questionnaire
The following questionnaire will be sent to a sample of my target demographic through
social media like Facebook and Snapchat.

 If you had a timetable for school subjects on your phone, how often would you use
it?
o This is the core idea of the app. This question gauges how potentially useful
the application will be at its most basic.
 How likely are you to timetable non-school related activities?
o The secondary feature of the application is to work as a timetable outside of
academics. This question is to find whether users have an interest in this
feature.
 Would you want to be reminded to do specific activities with an alarm or
notification?

Candidate Number - 4164 11 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
o Some people may find alarms disruptive and therefore may be against this
feature. The question indicated whether this is so.
 How useful would an in-built calendar be?
o Users may already have a preference of the calendar they use and therefore
may not find a calendar within the app itself useful. However, this feature has
potential to expand the application’s utility. Furthermore, responses to this
may show if an “all-in-one” sort of application will be useful.
This is also the first of potential additional features I am adding.
 Would you use the app to take quick notes and reminders?
o Many students may already have a preferred method for taking quick notes,
and therefore this feature may not be useful. However, like the calendar
feature question, a positive response to this indicates that the “all-in-one”
idea of the application will be useful.
 How often would you use the app to schedule studying?
o If responses indicate this may happen often, it will show that the ability to
schedule studying time is an important feature, and therefore more focus
may be given to ensure scheduling studying is made easy, to ensure that
students will commit to studying properly.
 Would the inclusion of a do-not-disturb mode help you study better?
o This feature has potential to be annoying to users, as some may not want
notifications to be blocked, while others may find the feature useful. This
question will reveal what the proportion of each may be.
 What other devices, if any, would you access the timetable on?
o This is to see if access to other devices is a feature many users will use. The
application will continue to be mobile based, however other devices may
need to be included if there is demand for it.
 Would analysis of your studying habits, with the data presented back to you help
you understand your studying better?
o The user may be served well from seeing how they tend to study, especially
since it is unlikely they will be tracking themselves. However, some user may
be uncomfortable with having their data recorded and analysed. All data will
be handled in accordance to the Data Protection Act.
 Would suggestions for timetable edits based on your usage be a useful feature?
o The inclusion of a feedback may greatly enhance the application, however
some users may not want this feature for similar reasons above.
 Do you think a system of goals and ratings will help improve your studying?
o A goals system will help users break down large tasks into smaller ones and
complete the task step by step. The rating will help the application
understand which tasks the user does well at and suggest possible
 Would you like the option to print the timetable out?
o The app is designed to move users away from physical timetable copies,
however if this is a feature important to many users then it will be
included, since ultimately the app is meant to serve the user, and make using

Candidate Number - 4164 12 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
a timetable as easy as possible, regardless of how they intend to finally use
that timetable.
 What other features may be useful within this app?
o An open question for any further suggestions.
Using responses from the questionnaire, I will be able to decide which features are most
important for the application, and what these features may entail.

Justification of Features
The Timetable
The timetable is the core feature of the application. The purpose of the timetable is to help
the user create a schedule that is flexible and easy to follow.

The survey responses


show that most of my
target demographic will
use the application
frequently. There is
clearly demand for the
application.

The response to this


question was more
mixed. However it
seems there is enough
demand for the feature
to timetable anything.

Below I will detail the features the timetable will require:


The timetable will be displayable. The user can view the timetable easily. If the timetable is
too large for the screen, it will need to be scrollable or split into multiple views.
If the timetable is one large sheet, then the whole timetable may be viewed by dragging and
zooming. By zooming completely out, the whole timetable may be viewed. However, if

Candidate Number - 4164 13 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
there are many event or the timetable spans a large number of days, then the events may
be too small to make out if fully zoomed out on an iPhone due to the small screen size. The
user will likely have to zoom in a bit to view events, and drag the timetable through the
screen to see other events. It will not be possible to see many events simultaneously. There
is also the issue of events being physically separated by a large virtual distance, for example
the first and last events of a particular day or week. It may become tedious to continuously
scroll between the two.
The alternative will be to split the days up so that they are multiple views. This way, a single
day can be viewed in its entirety easily. However multiple days cannot be compared easily
this way. This method allows for quickly changing between days, which likely will be quicker
and easier than the scrolling method. There also should be no need for zoom since each
view can be optimised to fit the events for that day efficiently within the space available.
The along with being displayed, the timetable must be easily understood. I large block of
text is not intuitive for the user. The events must be listed in such a way that the user can
immediately grasp their agenda for the period they are looking at a glance, without careful
analysis. However should they wish, the user will be given the facility to look at events more
closely. When the user wants to see the details of a particular events they will be allowed to
see all relevant details to the event. This includes (but is not limited to) the event title, the
time and date of the event, additional descriptions or noted added, and the time and date
of the reminder if there is one.
The concepts used in some calendar applications outline how the timetable could be
created to be intuitive to use.

One of the most prominent features used is the use of different colours to quickly
distinguish between events.

Candidate Number - 4164 14 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
For example, if the user attend three lessons, biology, chemistry and physics, then they may
be able to coordinate each to a unique colour that makes the subject instantly recognisable,
even without seeing the lesson name in the timetable. The user may be given the choice of

the whole range of colours available through an RGB or HSV colour pickers.
However, a potential downside is that users may become overwhelmed with the choice
made available to them and end up choosing colours that do not help them distinguish the
events. For example, they may pick varying gradients of blue for all events, making the
distinguishing factor largely useless. In addition, not all user are familiar with the way
RGB/HSV colour pickers work, and they may become confused and unable to use the
feature at all. This may especially be a problem for younger users.
Alternatively, the user may be given a limited selection of colours to choose from, where
each colour is sufficiently different to the others. This ensures each event has a unique
colour, so long as there are enough colours available for every event. This therefore sheds
light on the problem inherent to this approach. If the user opts to create enough events,
there will not be enough colours to accommodate them, which may have a negative impact
on the “viewing at a glance concept” since its job to make viewing the timetable more easily
will have failed.
A final potential issue is that people who suffer from colour-blindness may have difficulty
distinguishing some of the colours. I do not believe this will have a large impact on their
experience of the application since if they have deuteranomaly, as it will make little
difference whether they pick red or green for the event’s colour. This only becomes an issue
if the user suffers from achromatopsia, in which case the colour differentiation serves little
to no use. A colour-blind mode may be added to the application to help mitigate some of
these problems.
An alternative to colour coordination for instant recognisability is using images. This carries
the same benefits and issues to colour-coordination, however I feel that images require
more processing to discern over bold colours (with the exception of colour-blind people,
where the use of images may be of greater benefit). As with colour-coordination, the
question remains as to whether giving users freedom better than not doing so. If given the
freedom, users may be able to upload images unique to the event. If they have a
woodworking class, a picture of the workshop they will be in may be uploaded, which serves
as a better reminder than anything else could. However, there is the potential for users to
upload inappropriate images, which reflects negatively on the user if it is noticed, and also
on the application itself, undermining its point as an application to enhance education. If

Candidate Number - 4164 15 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
given a selection of images to use, there may not be enough to represent all scenarios.
Further, the images must be obtained somehow. It will require a significant amount of
planning and time to create them from scratch, or a significant amount of money to buy all
potentially relevant images, of which there may be a vast quantity.
Event creation: For the timetable to work specifically to the user, the user must be able to
create, modify and delete events.
To create an event, the “add new event” button must be easily accessible for the user. Some
applications include a large “+” button in the bottom corner, which makes it obvious that
this is to add something new. (This is a feature used in Google Calendar for example.) An
alternative would be to have the add button included in the status bar. This is less obvious
but also less intrusive.
The event creation requires all items to be set out for the user so the user has no difficulty
tailoring the event exactly how they would like.
Not all data input will be displayed on the timetable itself, since all but the essentials will be
cut so the display is readable.
The displayed elements will consist of the name of the event, its colour, and its physical size
will scale to the timetable. Therefore, when the user creates an event, required setting will
be the name, date of event and the start and end times. The user will also need to be able
to change the displayed colour/image of the event.
When the user goes into the detail view, the event can be described in more detail. Firstly,
the name of the event will be displayed at the top, followed by the date and time. The
background can be the colour/image. The user may also want to include extra details
concerning the event, so this must be facilitated for. The user may also wish to add a
reminder for the event, as an alarm or notification before the event. They will be given the
option to enable this feature and set how many reminders, and how long before the event
they would like the event to occur.
Finally, the user will be given the option to save the event and commit it to the timetable.
To summarise, the setting required for event creation are:

 Name
 Date/Time/Occurrence
 Description
 Reminders
 Colour/Image
The timetable will need some way of resolving scheduling conflicts. It is possible that two
events clash (for example, one ends after another begins, but it is still possible to attend
both).
Before an event is finalised, the application will need to check if its times clash with any
other events. If so, it must warn the user if they would like to continue.
The two clashing events will then be placed side by side if the user wishes to continue.
There will need to be a maximum number of events that can exist in the same timeslot. A
good option is two or three simultaneous events.

Candidate Number - 4164 16 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
To modify an event after creation, the user may tap on the event. This will bring them to a
details view, from which they will find an edit button, which will allow them to edit all the
fields.
In this view, a delete button may be provided that allows users to delete an event if it is no
longer relevant. To prevent accidental deletion, a warning popup may be included.
However, this may be annoying if the user has to delete many individual events. An
alternative would be to include an undo function, which can be extended to include the
ability to undo any actions the user accidentally makes.
The criteria for the timetable:

 Create event
o Button must be easily accessible
o All options needed listed
 Name
 Datetime/occurrence
 Description
 Reminders
 Colour/image
o Save the event
o A scheduling conflict resolution
 Modify
o Tap on event, press edit button
o Edit all features available in create
 Delete
o Tap, press delete button
o Deletes event
o Allow for an undo action

The Calendar
The calendar is designed to make setting one-off events more intuitive.

This shows there is a clear


positive response to the inclusion
of a calendar in some way.
Whether this “in-built” function
is an integration with the phone
calendar, or a calendar separate
within the application itself is to
be decided.

Candidate Number - 4164 17 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The application needs to include a calendar or at least integrate with the calendar
preinstalled with the phone. The user will often come across events that do not reoccur
more than once or only repeat a few times. These events would not be suited for the main
timetable since that is designed for events that occur frequently, at the same time every
week/two weeks. However, the timetable may be made to accommodate for this. For
example, if there is a lecture that occurs on only Friday 21st September 2018, when the
timetable is displayed for that week it will also include that lecture in its time slot, and will
not show it for the timetables for the weeks before and after. For this to work, the
timetable will need to know the current date, be able to work around scheduling conflicts (if
two events occur at the same time) and allow the user to view their schedule from before
and in the future. Reoccurring events may be stored in one way so that every time the user
loads a new week, it just loads that data again. However, before displaying, modifications
are made to the base depending on event changes made. This allows the user to view weeks
into the future and weeks past.
Another method is to place one off methods into a standalone calendar, or integrate them
with the in-built mobile calendar. This way, large modifications of the timetable base will
not be required. However, in this case, there is not much point of the user adding one-off
events into the application, since they will essentially just be moved to the calendar.
Combining both approaches increases the usefulness of the application for the user.

Alarm/Notification
For some events, especially one off events, the user may wish to set reminder so their
phone notifies them before the event so that they can prepare.

It is clear that people will


find this feature useful,
even if it disruptive.

There are two ways of setting a reminder: by alarm or by notification.


Alarms would most likely be used for urgent events that cannot be missed, like a lecture to
attend. This is because the alarm will continue to ring until the user turns it off, so they are
definitely made aware of it if they are near their phone and it is not on silent. Meanwhile
notifications may be used for less urgent events, like a reminder to begin studying, since
they are less intrusive and do not require immediate attention.

Candidate Number - 4164 18 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
In either case, the user may wish to attach a short message to inform them of what the
reminder is about. The other settings the user will need is the time for the alert to go off,
and the ability to select between alarm and notification. Furthermore, if alarm is selected,
they may wish to control the volume of the alarm and set a tone for the alarm. They also
may want a snooze feature, in which case they will need the ability to change the snooze
length.

Do-Not-Disturb Mode
Phones often make notification sounds, which can be distracting when trying to
concentrate. It is difficult to study with constant interruptions. There is also the chance the
student will completely stop studying and will not go back after being distracted by the
device.

Respondents largely
seemed to agree
that the feature will
be useful to them.

Removing these sorts of distractions may help increase focus and productivity.
iPhones currently come with a do-not-disturb feature, however the point of the feature
within the application is to make it easy to set, and make it last during periods of studying. It
may even be possible to make it set automatically when a person begins a studying session.

Candidate Number - 4164 19 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Note Taking
The application is designed to be all-purpose for studying. A potentially useful feature to
include would be the ability to take notes while within the application. It is likely that the
user has some other form of note taking application, and this feature is not designed to
replace that.

Most users will find this


feature useful.
One respondent added
the suggestion “maybe,
just on ew’s” – they will
use the note-
taking/reminder feature
just to remind them of
‘extra work’ (homework).

Chances are the user will be within the timetabling application while in an educational
environment and therefore being able to quickly access a notepad of some sort without
having to leave the application itself may prove useful.
The note taking will allow the user to create a note, write down text (using a keyboard,
however handwriting and voice recognition may be added), and then save the note.
There is no limit to the size of the notes, or the number of notes created.
The notes can then be deleted when they are no longer useful. A multi-select feature to
copy or delete many notes may also prove useful.
As with the timetable, the delete feature will come with an undo button.
The user may also wish to add a title to their notes, and use formatting options while writing
the notes.
The left shows Evernote’s note
taking interface. Along the
bottom but above the keyboard
is the formatting options. They
allow the user to italicise,
strikethrough, underline text,
along with many more options.
The interface is intuitive to use
and good for quick note taking.
The note-taking feature of my
application will not be as
complex, but may be inspired by
the design choices made here.

Candidate Number - 4164 20 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The notes can be combined with the notification/alarm system so if the user is writing down
extra work they need to do in this part of the application, they can set a reminder before
the due date so they do not forget to do it.

Feedback/Insights System
The app will collect data as it is used. From the collection, it will be able to graphically
present the data back to the user so they can observe their usage and habits. The
application itself will also analyse the data and make suggestions to the user.

Most of the people


surveyed think that
analysis of their studying
may improve their
studying.
Timetable edits received
a similar positive
response, however a
larger proportion are
against suggested edits.
This may imply that users
do not want their
timetable changed after
it has been created.
The idea of ‘suggested
edits’ will be emphasised
if implemented.

The application may see when the user is often free, and suggest that period of time be
dedicated to studying, especially if it is near an exam period.
The application may also suggest breaks if the user is attempting to dedicate too much time
to academics.
If the user is spending a lot of time studying one subject whilst neglecting another, they may
not notice themselves. However, the application will be able to point this out, for example
by comparing the time spent studying one subject to the time spent studying the other. Or
compare an individual subject to the average spent, and see whether one or many subjects
do not have the right amount of time spent on them.
This feature relies on the user input into the application, and unless it somehow checks, will
have to assume that the user has strictly followed their timetable.
If the user does not input into the application inputs erroneous data, then the suggestions
made will be unhelpful and incorrect.

Candidate Number - 4164 21 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Some subjects (or even areas within a subject) come easier for some people than they do
for others. Not everyone needs to spend the same amount of time on every topic they need
to study. Some topics may have been retained better from lessons, while other topics would
just intuitively make more sense. This also is true in the reverse, where some topics may not
have been taught effectively and therefore need more time spent on them, or just do not
make as much sense to the person.
In either case, every subject cannot be treated as requiring the same amount of time, due to
the variation in subject difficulty per user. It is also impossible for the application to predict
how much time a person needs for any particular subject, since every person is unique in
what they find difficult.
Instead, the users can be given the option to rate how difficult a particular activity is. The
application can then save the data, along with how long the user needs to spend on that
activity, and any other relevant data. This can be used to enhance the suggestions to make
them more relevant by taking the difficulty of the task into account for that individual.

Goals
Along with the difficulty rating system, a system of goals may be put into place. The user can
set a goal, or goals, they wish to achieve, and work within the application to complete it.
They may also set milestones within the main goal, so they have a definite sense of
progression while completing the task.
Goals can be long term or short term, depending on how the user sets them.

Most people surveyed


are not against the
goals system.
There seems to be a
similar response to the
goals as to the other
user-tailored systems.
Overall, the response is
positive.
The point of the goals is to encourage users to complete tasks that they wish to while they
are in the process of completing the task.
The application can analyse which category of goals the user tends to complete, which they
often do not, and which they partially manage. It may also analyse time taken for the
category and get a difficulty rating from the user’s own feedback. Through this, the
application may be able to make suggestions to goals the user can complete, or suggest
modifications to goals the user sets if they are too ambitious or not challenging enough.
For the user to set goals, they need a number of settings.

Candidate Number - 4164 22 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
First, the goal needs a name and possibly a description. The user will also have to set when
the goal is to be completed by. This could be within the same day or over many days, weeks
or even months. The time setting therefore has to be flexible. The user also may wish to set
milestones, which will need their own titles and times to be completed by within the period
the goal is set. When the user comes to a deadline, they may wish to set an extension. This
feature will only appear when the user reaches the deadline however, and not when initially
setting the goal.
The user may also wish to set a difficulty rating to the task. For consistency, the application
will have to provide categories to be completed (like academic, extra-curricular etc.)
These categories may be provided within the application, to ensure consistency, especially
for the data analysis. The user may wish to create their own if it happens that there are
categories they may need that are lacking. The user therefore may be given the option to
add their own categories and reuse them in the future.

Other Devices
The timetable need not necessarily be restricted to only one device to be accessed. If the
user does not have their phone nearby then they can access the timetable with another

device and an internet connection if this feature is implemented.

One respondent made it clear they wished to be able to access the timetable on all devices
they had, however it is unrealistic to develop for such a broad range of different device
types.
I found it surprising that such a small proportion (3.2%) wanted to use the application on
their one mobile device exclusively.
On the other hand, it was not surprising there was demand for a laptop/desktop version of
the application. I had predicted that users might wish to access their timetable on a
computer.
Regarding how the timetable will be displayed on a desktop, it may be simplest to make the
timetable accessible within a web browser.
This circumvents the issue of developing for multiple platforms (namely Windows and

Candidate Number - 4164 23 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
MacOS). With MacOS however, it may be possible to transfer to the iOS application to be
compatible with a Mac without much difficulty.

Printing
Responses show that potential users would like the option to print out the timetable.

This may be done in a way where the timetable is exported, as a PDF for example, which can
then be printed by the user.
This method seems simpler than interfacing the application with printers and allowing the
user to print out that way, since there are issues of how the user connects to the printer
(wired or wireless), differences between printer models and manufacturers, and adding in
printing options for the user if they need it.
That feature alone is worth its own entirely separate application (of which there do exist,
although often proprietary and therefore specific to one brand only).

Candidate Number - 4164 24 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Summary of Features
The application will include the following:

 An agenda home screen


o Emulated to be a base for the application, every feature will likely jump off
here. It will include access to every other view, and will always be returned to
as a ‘home screen’.
o The agenda will display upcoming events for the user, and also indicate the
importance of those events.
 A schedule
o This is the core of the application, and the basis for the organisational
capabilities of the application. While the potential use outside of school time
may be somewhat limited – by user choice – the timetable feature of the
application was met with an overall positive reaction, and appears to be a
useful feature.
o The timetable will include a number of sub-features, like colour coding and
settings alarms before events occur.
 Alarms/Notifications
o This was met with an overwhelmingly positive reaction, and appears to be a
feature that is very welcome. It also may serve to be very useful, especially
for any users that often forget about something.
o The alarms will be standalone, and also attached to events to serve as
reminders before they occur.
 Do-Not-Disturb
o Another feature that seems to be desired by the questionnaire respondents,
the do-not-disturb mode is designed to prevent any other distractions from
the phone, which is why it will be implemented within the application,
despite being a feature that already exists.
o It will consist of a setting to enable or disable, and a time period to be in
place (indefinitely or for a set period of time).
 Notes
o While note taking may have been a more divisive feature, 80% of
respondents showed a positive reaction. Note taking itself is also a very
useful feature when in a hurry and needing to jot something down. As the
application is designed to be all-encompassing, this feature serves to aid that
goal.
o Notes will consist of a title and body of text.
 Goals and Insights
o I have grouped these features as they are closely related. While the response
was mixed, there was little negative reaction, so it appears to be a feature
that may be useful. The reason the response was mixed may be due to those
asked not understanding the features.
o The goals will consist of milestones that the user defines and may work
towards.

Candidate Number - 4164 25 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
o Insights consists of analysis of goal completion and other features of the
application.
The application will not include the following (for the time being):

 The Calendar
o The user has little need for an additional calendar. Furthermore, the only way
to implement one within the application is using Apple’s API for the default
calendar in iOS. There seems to be little point in creating a redundant
feature.
 Cross-device support
o While a feature that many users appeared to want, unfortunately it is not
practical to implement. It would require many aspects to accomplish, among
them being proficiency in development for multiple devices, and an internet
synchronisation solution. This feature would multiply the time to develop a
number of times, while increasing the complexity significantly as the
application is reprogrammed within different environments.
o The application will target only iPhone devices. Although mobile devices
typically have hardware limitations, the application does not carry out any
intensive processes, and is ideal as a mobile application. Apple iPhones share
are owned by 49.37% of smartphone users in the UK (statista.com, May
2018), whilst 90% of 16-24 year olds own a smartphone (ofcom.org, August
2015). This shows that the iPhone is a popular device and the application will
reach a large number of users.
 Printing
o While another potentially useful feature, it suffers from similar issues to
cross-device support, where the feature itself may end up larger than the
application itself. Furthermore, the schedule would have to be organised
around the intention of being able to print it, where this may potentially
detract from the user experience by preventing other approaches.
As mentioned in the stakeholders section, I intend to create a focus group to aid with
application development. I was aiming for roughly three to seven people, and received
confirmation of participation from five.
For data protection reasons, they shall not be named. The group as a whole may be
referenced to, or a member within, without any identifying information.

Candidate Number - 4164 26 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Problems
With all development projects, there is a time cost to be considered. The algorithms for the
project will take time to plan and code, and often they will not work first time, meaning
even more time will be required to fix
Cost –The application will be for iOS devices. This means the user can only use it if they own
an iPhone or iPad.
Availability – Although the iPhone may be a popular device (especially with my target
demographic), it hardly covers all potential users. Although the iPhone is more popular in
the UK and US, worldwide Android enjoys a greater market share by a significant margin. It
is difficult to develop for Android due to software fragmentation and large differences in
hardware capabilities, however it means that a large portion of users have no access to the
application. Furthermore, potential users that prefer desktop/laptop will be completely
alienated, as the application has not been optimised for desktop use.
Although there is the possibility of expanding to provide cross-platform support,
Development for this platform also requires the use of a Mac, which is a somewhat
expensive piece of hardware. Computers capable of running Xcode are also more expensive.
Regarding development, there are potential issues with the timetable itself, for example.
After conducting some brief research, there does not appear to be any framework available
to achieve what I am aiming for, so everything I will likely have to create myself from
scratch.
The Do-Not-Disturb mode may not be viable due to iOS application restrictions and
sandboxing. The OS may prevent the application from enacting system-wide changes like
that. This will require more research.
A similar issue is faced with alarms – they may not be possible due to restrictions in access
to hardware (like the speaker) while the application is not in the foreground.
Goals and insights are together an interesting set of features. It is possible to make an entire
application solely based around those two concepts. For that reason, I believe there is a very
good chance that the features may prove to be too difficult to implement within this
project. Furthermore, there are potential legal issues surrounding this. Since data is
collected about the user, it must be stored in compliance with the Data Protection Act,
meaning adequate security must be provided, for example. The issue becomes even more
complex considering that a large portion of the users may be minors, meaning that the laws
protecting their data are even more stringent. This must be taken into account before
developing these features.
There are some limitations to the application. While it sets out with the aim to provide
structure to the life of a student, there is only so much that can be accomplished by an
application. The application can show the user when events occur, and remind them to
attend those events, however there is no way of enforcing this.
A user may have a period of time dedicated to homework, but there is no way of ensuring

Candidate Number - 4164 27 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
that the user keeps to this. Although the goals feature exists, it contains no way of checking
whether the information the user may enter about their progress is genuine; this is
impossible.
The application is also reliant on the technological device (in this case, mobile) that it is
installed on. Should the user lose their device for any reason, their organisational tool is also
lost. This problem may be circumvented by utilising cloud storage and synchronising across
devices, however as mentioned, this is impractical for this application. It is, however, a
possible improvement to bear in mind in the future.

Requirements
Software:

 Xcode – Apple’s software development environment, which will be used to program


the application for iOS devices. I will be using Xcode 10.0, as it is the latest version to
have been released.
 MacOS 10.13.6, to run this version of Xcode.
 iPhone emulator – to test the application in a controlled environment during
development. Xcode 10.0 comes bundled with the SDK for iOS 12, meaning I can
target the latest mobile released by Apple.
 Git/Github – version control for the whole project.
 A database solution – to store user data persistently. Xcode uses Core Data, however
alternatives exist, like Google’s Firebase, for example.
Hardware:

 Apple Mac – required to run Xcode. Although Xcode is capable of running on any
device that supports the MacOS requirement, this does not necessarily mean it will
run well. While I am using a MacBook Air, it may struggle to cope with the program
once it begins to become quite large.
 A certain amount of storage is also necessary. While I am unsure as to how large the
entire project may reach, a few hundred megabytes may be excessive. Dedicating
200MB to the source files seems reasonable. Of course, this is not a hard rule and
can be expanded if necessary (although this is unlikely).
 Apple iPhone – the application targets this device. There may be software version
requirements too, however that is decided when compiling in Xcode. Xcode defaults
to compiling for the latest software version available at the time of its last update,
however compilation for previous version can be done.

Candidate Number - 4164 28 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Success Criteria

Feature Subfeature Criteria


1. Agenda 1. Display The agenda must show upcoming events for the user on a
given day.
The events should be colour coded to indicate their
importance.
Events could also be pushed up to the top of the agenda if
they are important.
The agenda could show any relevant extra information the
user may need to know for that day.

2. Timetable 1. Display The user must be able to create events.


The user must be able to view upcoming events
(up to 24 for a day)
(up to 168 for a week).
2. Interaction The user must be able to create a new event from the
timetable screen.
The user must be able to edit an event that exists within
the timetable.
The user must be able to delete an event in the timetable.

3. Events 1. Display The event must display a title and colour within the table.
The title must be contained within the box, and the box
must have a fixed size (unless it is designed to be variable).
2. Extra The user must be able to add extra information to an
Information event.
3. Reoccurrence An event must be repeatable. The event appear at any slot
within the week. This includes only a single slot, or
multiple.
Conflicts handling must be included to ensure that events
do not overlap.

4. Alarms 1. Function The user must be able to control when an alarm sounds.
and
Notifications
An alarm must sound at the correct time. Sounding
includes any method of attracting the user’s attention. By
default, this is by making a noise.
Alarms that sound must be silenced. The user must have
control over alarm rings.
2. Standalone The user should be able to add alarms that are not
attached to an event or otherwise.

Candidate Number - 4164 29 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
3. Attached The user must be able to attach alarms to an event. The
alarm sounds in relation to the event occurrence, as
decided by the user.

5. Do Not 1. Silent mode A system-wide silent mode must be enacted. This prevents
Disturb any sounds or notification from the device.
2. Control The mode must be enabled and disabled at the user’s
discretion. They could also control the times between
which the silent mode is in place. This could be
implemented to occur automatically with no further input

6. Notes 1. Function A note must be creatable, editable, and removable by the


user. Creation requires a title and note text. Editing must
be applicable to both fields also. Deleting a note removes
it entirely. This could be reversible.
The user could format the text within their note, including,
but not limited to, typing in bold, italicised, or underlined
text.
The user must have no limit to the number of notes they
can create.
2. View The user must be able to view all the notes they have
created (that have not been deleted). This includes
viewing all the notes are collected together, and selective
viewing of one note at a time.

7. Goals 1. Function The user must be able to create as few as one or as many
goals as they like.
The user must have no limit to the number of notes they
can create.
A goal must take a title, explanation, and time by which it
is to be completed.
A goal must track the amount of progress a user makes
towards completing the goal.
It must then display the amount the goal has been
completed to the user.
2. Milestones A goal could be broken into milestones. This will depend
on whether the user chooses to do so.

8. Insights 1. Collection Data must be collected on goal completion, application


usage, alarm dismissals, events attended, events made,
notes made, and any other statistic the user may be
interested in.
2. Presentation Data must be presented in an understandable way. Data
cannot be presented in the raw format, and must be
formatted in some way.

Candidate Number - 4164 30 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
9.General 1. Intuitive use The application must be usable by, at the very least,
anyone within my target demographic. Instruction on how
to use the application will not be necessary. The
application must be structured in such a way that its
function and their usage are immediately recognisable and
understandable.
2. Data storage The application must retain data the user inputs. On
application close, data entered about events, alarms, goals
or notes cannot be lost.
This data must then be presented again once the
application is opened.

Focus group: There was a slight feeling of anticipation within the air as I described my
project in detail (using my success criteria as a reference). They seem to be quite excited
with the prospect of the application beginning development.

Candidate Number - 4164 31 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Design
Application Structure
Structure Chart

This chart shows the potential structure of the application. The arrows show the direction in
which views are traversed. A route can only be travelled in the reverse direction if the user
has already travelled forwards through it (as if undoing the travel). This represent ‘going
back’ in the application. A route that has not be immediately visited forwards cannot be
travelled down backwards.
It is also worth noting that every view will link back to the Agenda, allowing travel from any
view to those immediately linked by the Agenda. This action works like a reversal of all
routes travelled through, carried out in a single step.
The chart demonstrates how navigation through views will work and the views necessary for
the application. While this may change during development, the underlying structure will
not.
The chart also demonstrates how the application as a whole can be broken into smaller
parts. Development will be focused on each part of the whole, where here, each part
represents a view within the application.

Candidate Number - 4164 32 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Wireframe
I have used the structure chart to create a wireframe for the application:

User Interface
Overview
The user interface design as a whole is integral to a user’s interaction with the application.
The design affects every aspect of the user’s interaction, and therefore it is important that
the design is and experience provided to the user by the application is intuitive and pleasing
to the eye.
The application is education focused, and therefore I plan to use clear, contrasting colours
and themes to make interaction with the application as simple as possible.
This will enhance the readability of the application. It is less likely the user will have to spend
time searching for a feature if it is obvious and stands out.
The default iOS design consists of white backgrounds with black text and blue items. This
colour scheme is ideal for what I am going for due to the stark contrast of colours. I will be
incorporating this within the majority of my application.

Splash Screen
When the user opens the application they will be greeted with a “splash” screen – a static
image displayed while the application loads.
The splash will set the tone for the application, and give the user a sense of “something”
happening while the application loads.

Candidate Number - 4164 33 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
While I have not yet decided on what to open the application with, this is an important part
of the user interface design, along with the application icon.

Icon
The application icon will have to grab the user’s attention so it does not get lost in the
multitude of other applications the user may have. Making it memorable will also help the
user locate the application quickly at a glance. The user can therefore open the application
swiftly.

Views
The application will consist of multiple views the user will interact with (as described in the
structure chart). A mock-up of each view will be created in Xcode.
This serves as:

 A visual demonstration of how the views may appear.


 A way for me to experiment with UI elements in real-time, informing my decision on
which may be best.
 A demonstration of the application UI as a whole. Each view prototype can be linked
together to create a working wireframe, which will directly mimic how the
application will be laid out and navigated by a user.
By prototyping in Xcode, I can see directly what the application experience may be like, and
make any modifications necessary, before any irreversible work has gone into development.

Candidate Number - 4164 34 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Login
Here is the login view. After the user has opened the application and passed the splash
screen, the user will have to log in. In subsequent logins on the same device, the user will
not have to login again unless they log out. This will save the
user time and save them the irritation of repeatedly logging
in.
The login takes an email and password. The structure of these
can be verified with Regular Expressions, saving the
application from repeatedly uploading to the database to
authenticate their credentials. I will implement the regex to
work by checking the input against a set structure format for
the email and password. If both are valid locally, the two will
then be uploaded to the database for authentication. If the
username and password are not correct, the server will return
an error, which will be displayed to the user in the form of a
warning message of their incorrect login attempt.

Before a user can login however, they need to create a user.


This can be done in the registration screen, found by tapping
register in the login screen.
In the registration screen, the user can add their email and
create a password. They may also wish to create a username;
however, this feature will be made available if I include cross-
user interaction.
Once a user has registered, they will be directed back to the
login screen. A verification email will also be sent to their
email so that they can verify their account.

Candidate Number - 4164 35 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Agenda/Dashboard
On a successful login attempt, the user will be placed at the
dashboard/agenda screen. The idea of the agenda is to have
a familiar opening for the user where they can gain an insight
into what they have coming up with a quick glance.
The agenda will have key information at the top, like date or
current goal progression. Below will be the user’s upcoming
events. I may include a priority system to order the events so
that they appear in the most relevant order possible. A
logical ordering system is to have items appear
chronologically. However, if items are given priority flags (set
by the user), items may appear beforehand out of
chronological order.
This may help remind user of important upcoming events,
especially if they are unusual or one-off events.
As can be seen in the prototype design, the items are
ordered in chronological order, with the exception of test
alarm – which is highlighted in red and occurs third chronologically. This item simulates
what an item with greater priority may appear in the agenda.

Candidate Number - 4164 36 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

The agenda screen is also the access point for


multiple other views. Firstly, items in the agenda
can be tapped by the user, which will bring up the
relevant view (whether it is timetable or alarm).
There will also be a tab bar at the bottom, which
will allow the user to navigate to different views.
Tab bars are limited in space however. Although
some views can logically be arranged to lead onto
one another, it is entirely possible that there will be
too many unique views. (Views that have to be
navigated to from the tab bar). It may also be
difficult for a new user to find a view they wish if
they are not aware it is grouped behind another
view.
Therefore, if there are too many views, or too much
grouping (to the point it is no longer logical or
simple) then I may use a navigation drawer.
This works with the user can tap a menu button or
swipe to the right to bring up a navigation menu
above the current view. This allows the user to
navigate to any view, from any view.
The implementation will be more complex since I
will not be able to use Xcode’s native navigation
control. However, the user experience may be far greater due to the greater
simplicity of the design, along with the better access of views.

Timetable
The timetable will be set out such that every event the user may have that week is set out
before them in a schedule. Items can be added or modified, and all are collectively viewed in
the schedule view.
If the user taps on an item, they will be directed to the detail view for the item. There they
can view the item in specific detail. They can also edit any data they wish and delete the
item.
The detail view will consist of all the data the user may need to consider when creating an
event:

Candidate Number - 4164 37 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
 The event title (string)
 Colour picker (directed to a new view/popup)
 Set a reminder (links to another view, created as an
instance of reminder class)
 The start and end times (datetimes)
 Whether the event is repeating (Boolean)
 If it is not repeating, what date the event will occur
(datetime)
 Any extra text information to include for themselves
(string)
 Event appearance colour (hex value)
 A message priority (integer)
I will create an event class, which will include these fields.
Each event is an instance of this class. Repeated events
are the same instance being loaded into the timetable
again. This will save redundant storing of events in
memory or the database.
The timetable will be dynamically generated as the user
views a specific time period, rather than static storing of
all information in the layout.
I will be taking this approach since the user will have a repeating weekly timetable, and the
user will also have the capability to view dates far in advance or long gone. A massive
redundancy will be created if every day’s events are stored statically, since many days or
weeks will structurally look the same, with the only difference in the events being the date
they occur.
The event creation screen will include all of these fields for the user to add the information,
as they require. They will then finish editing, and the item will be committed to the
timetable.

Candidate Number - 4164 38 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Do Not Disturb
The do not disturb mode is set within this view, which can be
accessed from the agenda.
In keeping with the simplistic design approach, the view
contains only three objects that can be interacted with:

 The mode slider – this turns the mode on and off. If


the user turns this off, the times will be greyed-out
(locked from editing by the user). The reason for this
is to prevent the user from setting a time and
forgetting the mode is not no activated.
 The times – there are two times: start and end. The
start time is when the mode begins, while the end
time is when the mode finishes. The start time will
automatically be set to the current time, so the mode
will begin immediately unless the user wishes
otherwise. Given this, it is possible to reduce the do
not disturb mode settings to just an end time, where
it always begins immediately. However, users may appreciate having finer control
over their experience.

Notes
The notes screen is for users to quickly take down notes within the application, since they
will likely be using the application within a school environment.
After navigating to the notes screen, the user will be met
with a list of all their created notes, displaying a title and a
snippet of the note (if it is longer than the length of the
flavour text within the cell).
This allows the user to view their notes quickly and easily.
The title gives them an idea of what the note is about, which
can be ascertained at a glance. Meanwhile the snippet
provides extra information on the note, which the user can
also see quickly, especially compared to the time taken to
open the note and view the full body text themselves.
This feature becomes most useful when the user wishes to
compare the contents of multiple notes quickly, to grasp an
idea of what each note is about, since otherwise they would
have to open the first note, glance at the contents, navigate
out, and begin this process again for each subsequent note.
In addition, if the user were trying to find a specific note but
the title they had set was not reminding them of the
contents, they would have to use the above method to find
the note they were looking for.

Candidate Number - 4164 39 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
This feature saves the user from unnecessary hassle, which thereby enhances the user’s
experience.
Another feature I will include that will improve the ease of the user’s experience is the
option for note organisation. Within the list, the notes can be rearranged. This allows the
user to move notes they deem more important to the top of the list, or the opposite if they
wish.
There is also the possibility to have a sort button, and have the note list sorted by various
methods: alphabetically, chronologically, or by the user’s last used list order (custom). Notes
will be sorted chronologically by default.
Although this feature may be useful, I doubt that many users will need it. The notes are
designed to be small and taken at the moment when another, more involved method
cannot be used i.e. these notes are not meant to be used for class notes, but rather a quick
to-do list or similar. This means sorting by any way other than chronologically, along with
giving the user the option to reorder the notes manually, should be sufficient.
Each note can also be deleted directly from the list by swiping to bring up the delete button
underneath the cell. This saves the user hassle if they wish to delete multiple notes.
An alternative to this approach is to have the user long-tap on an item, and have that item
highlighted. From there they can be given multiple options, like sharing or deleting. The key
difference with the highlighting method is that it allows for multiple notes to be deleted at
once.
Both of these implementations have been used by other applications, so the user will be
familiar with either method.
It is also possible to add a search bar at the top, allowing users to search for a note by the
text contained within the note. This allows the user to find notes without having to
manually check every note, which is especially useful if the user cannot remember the note
contents. This feature also allows user to find every note relevant to a particular keyword,
since the search will show every note containing that keyword. For example, the user can
find all notes relating to “biology”. Essentially, the search also works as a filter of the notes.

Candidate Number - 4164 40 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
When the user taps on a note, they are taken to the
expanded note view.
The note name is displayed at the top. The user can edit
this name and thereby change the displayed title within
the cell. If the user does not create a name themselves,
the application will create a generic name for the note
(for example, Note 1).
The main body of the note is displayed below the title.
This is where the user records their note initially, and can
subsequently modify their note.
I will add a “save” button within this interface. This allows
the user to commit changes they make to the note.
However, if they make a mistake, a “cancel” button will
allow them to discard the changes that have made.
When the user leaves the note without having saved the
changes, a pop-up will inform them that the note is
unsaved and exiting will cancel all changes made.
Other note-taking applications include text-editing tools,
however I do not think that will be necessary here, since my application’s focus is not
centred on the notes. In this case, it is only a useful addition rather than a core feature, and
therefore the functionality may remain simple.

Alarm
Alarms can be set alongside events as a reminder. The alarms also work as entities complete
in and of themselves. They can
be used by the user to set
reminders for anything,
regardless of whether the
reminder is attached to an
event or not. The user could
also use the alarms in the
traditional way; however, the
application was not designed for
this use.
The user can navigate to the
alarms screen from the agenda.
Here, they will be presented
with a list of the alarms created.
Each alarm has a time, a note
about the alarm purpose, and a

Candidate Number - 4164 41 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
slider to turn the alarm on or off.

There is also the button to add a new alarm. This takes the user to the detailed alarm
screen. The user picks the time and date for the alarm to go off in a picker view, and can add
extra information about the alarm. If the user does not fill this in, it will be given a default
text (like “Alarm 1” for example).
After filling in the necessary details, the user can save the alarm or cancel and will be taken
back to the initial alarm screen.
The user may also view the detail alarm screen by tapping “set reminder” in the event
creation screen. When creating an event, the user may wish to set a reminder for the event.
When they do so, the alarm (rather than displaying the current time and date and default
text) will load the date and time of the event, and the event name in the details. This may
save the user some time in setting the alarm (allowing them to just save immediately) in the
case that they want the reminder to go off just as the event begins. There is a potential
issue however. If the events are repeated, then the user will expect that the alarm will go
off before every event. However, since the alarms are individual entities, an alarm will have
to be set for every event. This will cause the view alarms screen to be flooded with alarms
for events that are not happening in the near future.
There are three possible solutions:
1. Separate the reminders for events from the alarm entities. This will allow them to
work by a different system and therefore a method to manage reminders along with
events can be implemented.
This method will require a new system to be implemented to manage these alarms,
and will also make the managing of the alarms by the user more difficult, since they
will not be in the same place.
2. If the user sets the event to be repeated, then the option to set a reminder will be
greyed out and inaccessible. This solution will be very simple to implement, however
it comes at the cost of the user experience, since they are barred from a key function
of the events.
3. The time the alarm plays may dynamically change when the alarm goes off. The
alarm screen displays the alarm for the repeating event as single item, with a note or
symbol indicating it repeats. The alarm stores the interval between the repeated
event (intervals if the repeat is follows and irregular pattern). After the alarm sounds
and the user dismisses the alarm, the alarm will add the interval to the set alarm
time, meaning the alarm goes off for the next event. This method is especially useful
since if the user sets the alarm to go off five minutes prior, this setting of five
minutes prior sounding will be retained. This method may prove complex
programmatically to find the intervals between events, especially if the intervals
follow an irregular sequence before repeating. Intervals may be stored in an array
that can be accessed as a reference for interval points. However, a universal method
of recognising these interval times must be decided. This method will also require

Candidate Number - 4164 42 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
greater integration between events and their reminders, which may throw up
unique challenges.
The user can edit the details of an alarm by tapping it in the list view. They will be taken to
the detail alarm screen, which instead of loading default data will load the details of the
alarm they tapped on. From here, the user can edit the alarm setting, or delete the alarm.
Alarms can also be deleted from the list view, by swiping an alarm allowing a delete button
to show.

Focus Group:
I demonstrated my wireframe to my focus group. Some concerns were raised at the lack of
detail for the Goals and Feedback views, which I acknowledged. Unfortunately, I believe
these two features may be too ambitious for this project. While I do hope to include them,
as they are two main features, the difficulty surrounding tracking, protecting and then
interpreting the data may prove too large. Therefore, for the time being, they are remaining
low on the priority list.

Candidate Number - 4164 43 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Database Solution
An important consideration for the application is how data within it will persist.
There are a many solutions available, all often with their own specific way of
implementation. This means that once I make the decision and begin development around
that solution, it will be incredibly difficult to reverse that decision.
This is therefore a decision to be made as well as possible first time round.
First of all, I hope to avoid any SQL database solutions. Although querying has its
advantages, ensuring the application works with SQL correctly and ensuring the safety of
the database will likely prove too large a task to accomplish. For that reason, I am avoiding it
for now.
There are three main options I can consider. I am considering these due to their popularity
(meaning that there is a large support network should there be any issues), their claimed
‘ease of use’ (for a variety of reasons that we shall see), and their cost, or rather, lack
thereof. Database maintenance is often a problem for developers, but for the solutions I am
considering, this is not an issue. The online solution is free to use under a user threshold
(which at that level of usage allows for monetisation to allow the application to support
itself), whilst the offline solutions require no maintenance and handle themselves.
Clean-up of the database internals is not included in ‘maintenance’, and regardless of what I
choose, will have to be handled by me.
The three options are:

 CoreData
 Firebase
 Realm
CoreData – although not traditionally renowned for its ease of use, it is already included
with Xcode and therefore is very easy to implement. The difficulty comes with usage, and
appears to be very specific in the way that it has to be used, making this a potentially
frustrating storage solution to use. Being the default provided within the development
environment, one can assume that many, if not all, Swift-specific features are supported.
Firebase – created by Google, it is an online JSON tree database that is free to use under a
certain user activity threshold. As it is an online service, it comes with the benefits of data
synchronisation. However, since data leaves the device, it must be protected. This includes
authentication by the user before accessing the application. Firebase handles a large portion
of the setup work required, making installation very simple within the application.
An issue lies with the structure however. JSON based databases are not uncommon,
however they may not work with Swift easily. Swift contains no default method of handling
JSON data. To interpret the data sent by the database, a significant amount of work must be
done in translating the received data to a format Swift understands. The difficulty lies in the
repeated translation steps between the database and application. In addition, the database
cannot be queried, meaning that the entire store may have to be downloaded every session

Candidate Number - 4164 44 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
to load the data necessary. Given the JSON structure, only certain variables and data
structures are supported. This may prove an issue if I use more exotic data types in Swift.
Realm – not as common as the other two, Realm is still a well-known database solution used
in industry. The documentation stipulates that it is a non-SQL database, yet it still supports
queries.
Objects are bespoke to the database, yet integrate with Swift well. Although Realm is cross-
platform – it can be found on many devices, its Swift version has been very well optimised.
This brings with it the great advantage of having much greater freedom with Swift data
types. Realm also supposedly supports some native NSData types in Swift, meaning I have
freedom to use the data types I have available as I wish. Realm also supports data
structures, however their array storage, for example, is a little confusing to understand, and
appears to require specific usage to work.
Although it has an online version (essentially working to synchronise changes made to the
local database to the cloud) this comes at a monthly cost and is not affordable.
Realm, similar to CoreData, acts like a traditional database. With Realm however, this is very
loose. It can be treated strictly as a tabled relational database, however it still provides
flexibility in how it is used by the developer. This allows great freedom for implementation,
meaning it could be suitable for a large variety of projects, including my own.
After considering the advantages and disadvantages of the various database solutions
available, I have decided to go with Realm. The reason for this is that, overall, it appears to
have the most benefits with the fewest drawbacks. Although the online solution may cost
money, that feature is unnecessary within my application. It appears to be incredibly easy to
install (with most of the installation being handled automatically). Related to installation,
implementation also appears to be incredibly simple, requiring the fewest lines of code to
setup of any of the three. The documentation is very clearly written, which is a great
advantage as it eases the burden of understanding the new technology off a developer new
to it. Finally, the flexibility offered is very enticing, especially as I do not yet know the
specifics of application implementation. This means that, as I design how the application will
work specifically, I will note have to design around my database structure. Instead, I can
choose the best approach from an application and development standpoint, and modify my
database around that.

Development Plan
I will work in development cycles, similar to the spiral model.
Each cycle consists of five stages:
1. Planning: the objective of the cycle is decided, which depends on what is required,
and the outcome of previous cycles. The criteria is also outlined, and development
for the cycle ends once the criteria have been met (unless the criteria have been
changed).
2. Design: the algorithms are designed beforehand, in pseudocode or flowcharts where
relevant.
3. Implementation: the designed algorithm is programmed.

Candidate Number - 4164 45 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
4. Testing: the implemented algorithm is then tested to see whether it meets the
criteria.
5. Evaluation: the results of the testing are used to decide whether the algorithm is
successful. If not, the stage at which the algorithm requires revision is found, with
the required revisions made.
It is likely that stages two to five will be repeated frequently within one cycle. Therefore,
these stages (consisting of development as a whole) will be extremely short. This allows for
great flexibility. Algorithms will quickly move from the theoretical stages to the physical, and
their success determined rapidly. The flexibility allows for the criteria to be changed if the
original outline was unrealistic to implement, or if a simpler solution is found.
Each cycle will likely be focused on completing a single view in the application.
After a cycle has been completed, I will test the features of the view against those set out by
the success criteria, and evaluate whether I have met the criteria set out. I will also discuss
with my stakeholders on their thoughts of the development so far. By taking their feedback
into account, along with the test results, I can then plan the next stage of development.
Development will be based around Model-View-Controller (MVC). The model is
independent of the view, and handles the data for the entire application. The view presents
the information to the user. It is the interface that the user will interact with. The data the
user enters is sent to the view controller, which decided what actions to take.
Xcode uses views (graphical) and view controllers (programmatic) to interact with the user,
where the user sees the graphical interface and interacts with that. Therefore, I will not be
strictly following MVC, as Xcode is geared more towards combining the view and the
controller together (as the interface is both the presentation and the point of interaction for
the user). My model will be completely separated however, as the data will be stored in a
universally accessible database.
I will also use version control for my application development. This will help me manage my
application and its development, while allowing me to experiment without risking the
integrity of the project. Should any problems arise or functionality break I can roll back the
changes made, reverting to an earlier copy that worked successfully.
In this regard, I will be using Git (which is integrated within Xcode already), and Github to
backup my source and its version online. This comes with the added benefit that I can
download the source and work on any device.

Candidate Number - 4164 46 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Development
Here development of the application will be detailed, following the plan outlined above.

Cycle 0: Setup
This cycle is for initially setting the project up.
Xcode has a significant advantage over other development environments in that it carries
out a large portion of the work for the developer, allowing them to dedicate more time to
programming, and less time considering the specifics of rendering.
Projects in Xcode can be simply created by opening the program, which allows a new project
file to be created. A few basics, like the project name are required.

(The screens to start a new project)


The projects includes within it a number of files, among them, the important ones are the
main storyboard (containing a view), splash storyboard, a Swift view controller, an assets
file, the app delegate.
Xcode allows very simple construction of the class structure, especially when related to view
controllers. I will be designing my application with consideration to how view controllers
work. The class diagrams, for example, will follow this structure:

Candidate Number - 4164 47 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

This shows how all the project interacts as a whole.


Before the project begins, I will be installing my database. As I will be doing so using the
CocoaPods library dependencies manager (https://cocoapods.org). All dependencies are
specified within one text file, and the manager handles fetching updated source code and
linking it to Xcode.
The manager itself must be installed before the project begins. This is done by using $ sudo
gem install cocoapods within terminal, applying to the project folder.

After this, a Podfile is generated (where the dependencies are placed), along with an
xcworkspace file (which replaces the xcodeproj file created at project start).
Within the Podfile, pod 'RealmSwift' is placed within the application target function. This
allows Realm to be installed within the application.
To finalise the installation, pod install is used in terminal. The manager then handles
pulling all the files required to use Realm into the project.
With all Realm files within the project, the database itself must be instantiated. By doing this
within the app delegate, it can be accessed within every view.

Two lines of code are required: import RealmSwift, followed by let uiRealm = try!
Realm().

Testing
At this point, there is not much that can be tested, besides checking the project compiles
correctly at this stage, which I will carry out immediately.
The compilation was a success, which shows that there are currently no fundamental errors
in the project. It also suggests that the podfile and Realm are working correctly.

Candidate Number - 4164 48 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Evaluation
I have not included a testing stage for this cycle, there is not much in place yet to test. I have
ensured that the application works, by opening it and running it. It is a good sign that the
application does not crash, as that implies that nothing is going wrong. Until I move to
implementation stages however, I cannot say for sure.
Focus Group: I have asked my focus group for their thoughts on the project beginning.
While this was met with excitement, there were a number of questions as to what the
application can currently do. “Compile correctly” was apparently not the answer they were
hoping to hear, however they accepted that was far better than the application being
unable to compile at this stage.

Candidate Number - 4164 49 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Cycle 1: The Agenda


Outline
I will begin by outlining the central aspects of the agenda/dashboard, the first view I will
develop.
The keys aspects of this view are as follows:

 A table showing upcoming events


 A priority queue system to order events
 Colour coding system for upcoming events
 Being taken to the relevant view upon tapping an upcoming event
 A view header, consisting of the date and time, followed by the next goal milestone
Some of these features require other views to exist to implement. These features’
implementation will be postponed until these views have been implemented. I therefore
will focus on the features specific to this particular view.
These features are:

 The view header


o This contains the date and time at the top, so the user is aware of the date
and time from the first view. This data is central to the user’s understanding
and interaction with the application. Below that, the user’s completion
towards their next milestone of their goal is displayed. The goal
implementation requires a goal to exist, however, so for the time being I will
focus on building the framework I believe the feature will require. I will leave
it open for modification later so it is relevant to the implementation I decide
when I create the goals system. The date on the other hand should be
relatively easy to import. The application will copy the system date and time
and display it within the application.
 The table
o The table displays the upcoming events. There will be a store of data of sorts,
which will have events coming up soon, from which the table will display the
next items. This feature depends on the timetable, so again I will create the
framework, and test using dummy data.
The user may be able to scroll through the table, viewing events coming up
later on, which the app may generate procedurally. The user may also have
the ability to delete events they do not care about any longer (for example if
the event has been cancelled at short notice).
 The colour coding
o This is a feature within the table, and works alongside the priority system. It
colours items alternately so the user can quickly distinguish different cells. It
can also colour urgent events in red to make them stand out. The red
colouring (along with any other colouring) depends on the event features
(urgency, for example, or event type, for another example).

Candidate Number - 4164 50 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
 The queue
o Items will appear in chronological order within the table. The exception is
where an upcoming item needs to be pushed to the top since it needs to grab
the attention of the user immediately. A case where this may occur is with an
upcoming alarm (allowing the user to prepare in advance, or cancel the alarm
well ahead of when it goes off).
A priority queue system will be used for this, where items are added to the
queue chronologically with the lowest priority. If an item needs to jump to
the top of the queue (be displayed at the top of the table) it will be assigned
a greater priority).
I expect this feature to follow this structure:

Header
In the UI editor, I will create two labels at the top of the view.
I will then make it so these are editable programmatically. In fact, one will be used to
update the user on the current date. This makes use of the Swift date formatter, where the
text displays a converted string of the current date.
The agenda has been created, with two labels connected to the class “Agenda”. The labels
can now be edited programmatically.

Table
Using the UI editor in Xcode, I have created a basic view with two labels and a table.

Candidate Number - 4164 51 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
I have created an “Agenda” class, which contains IB Outlets for the two labels, named
“dateLabel” (for the date and time output) and “goalsLabel” (for the goals output). These
labels are currently a framework and
will be expanded upon or modified at
a later date.
The Swift view controller (Agenda)
controls the view shown on the left
of the image.
The user will interact with the view
shown, which will send data to the
controller. The view also contains
cells. These have their own controller
class, however are accessible by the
Agenda.

I have added two labels to the cells within the table view.
The table displays correctly within the view.

Queue
Overview
I will be using a priority queue. A queue works on a first-in-first-out basis, where the first
item added to the queue is the first to exit. The exception to this is where priority queues
are used. In this case, if an item has a greater priority than another does, it will be pushed
ahead in the queue, regardless of when any previous items were added to the queue.
One way to implement a priority queue is to have separate queues for each priority level.
This method is inefficient where there are many priorities. I will likely need a maximum of
three priority levels, so this drawback is not an issue.
My three priority levels will be:
1. The lowest priority. Every item appearing is automatically assigned this priority.
2. The next priority level. A priority of 2 allows an item to appear before any item with
a priority level of 1. Only once all level 2 priority items have left the queue will any
level 1 priority items appear. A priority of 2 is for items the user may set as being
important (a meeting for example) which they wish to appear early. Items with
priority 1 may also be dynamically assigned a priority of 2 once a set period of time
before the item’s occurrence has been reached. This will push an item of important
from its chronological place to the top of the queue for the user to see.

Candidate Number - 4164 52 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
3. Priority levels of 3 are an extension of priority levels of 2, however they are reserved
for items of great importance (like an upcoming alarm for example).
The user will need to see upcoming events for that day at least. They will either be able to
see all events coming up (by scrolling the table), or only have the relevant events showing.
There are three types of queue I can use, linear, linked list, and circular. A linear queue will
lend itself to the feature where the use can scroll and view all upcoming events, however
space will have to be made to accommodate every event for the day, regardless of how
busy it may be.
A circular queue on the other hand can be repopulated after events have passed by
removing them. Not every event for the day will be stored in the queue, however this
approach means there are no size limits. This comes at the cost of the user’s ability to scroll
all events, since the table will be made to only display a limited number of events
(corresponding to the queue size). As items are removed from the table (the event has
passed), new items will be added from the store of all events for the day. This is done
dynamically, unlike with a linear queue, where all events will have to be converted
immediately. There are potentially some time savings to be made, since the application will
have to download less from the database where the timetable data is stored.
I believe a linked list queue will lend itself to my specific problem the best. It works on the
same principle as a linear queue, however the size of the queue can change dynamically.
This is especially useful where it is unknown how many items need to be in the queue.
Since each schedule will be unique to the user, this approach allows them the most
flexibility. Furthermore, this method allows every item for the day to be displayed in the
agenda as needed. This method is programmatically more complex than the other two
methods. This method may be better for creating a priority queue based on a heap
however.
I will use either a circular queue, or a linked list queue. A linear queue is too restricted for
my use.

I believe a typical day will likely have five to ten events occurring, whereas a busy day may
have up to twenty. These numbers are estimates based on my own day, so these numbers
may be subject to change.
I will have to make the table to display the upcoming five items at least. This seems like a
reasonable number, and will allow the user to look ahead at events in the near future
without being overloaded with less relevant events from later on in the day. If they wish to
see these later events, they can open the timetable to view them directly (although if I use a
linked list queue, the table size restriction is not an issue). The agenda table is, however,
designed to give the user a quick glance at their upcoming schedule; the implication is that
these upcoming events will be limited to the near future.

Design
Below is a flow chart detailing how items are added or removed from the queue.
The queue is created by defining an array of set length. Two pointers are included, the front
pointer for the first item in the queue, and the rear pointer to point to the next free space.

Candidate Number - 4164 53 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
When an item is added, the item is placed in the position the rear pointer is currently at,
with the rear pointer then incrementing.
When an item is removed, the item at the position of the front pointer is output, and the
front pointer then incremented.
Circular queues work slightly differently, where they can wrap around themselves, meaning
the queue can continue to increment without reaching an end. This is done by finding
whether a pointer has reached the end of the array, and if so, it will be set to point back to
the beginning of the array.

Note: the function that allows the


pointer to wrap around back to the
beginning of the array relies on the fact
that the array begins at position 0.
Where the array begins at 1, the
function will ask whether the pointer =
array length.

Candidate Number - 4164 54 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
For a linked list queue, pointers are not used. Instead, nodes with heads and directions to
the next node are used.

To add an item (enqueue), first, a node is created (by creating an instance of the node class).
The program then checks if there is an item in the queue. If there is, the program moves
onwards until it reaches the end of the queue. Once it reaches the end, the node is added.
To remove an item (dequeue), a check is carried to see if there are any items to remove. If
so, a check is made to see if there is only one item. Where there is only one item, the head
is set to nil. If there are more items, the head is set to the next item in the queue.
In a linked list, the nodes connect between each other, one following another. I will create a
class for the node, which the linked list class will call.
Below is an outline of the class diagram.

I will test the queue in playgrounds, and begin with setting up the linked list’s node class.

Candidate Number - 4164 55 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Programming and Testing

Explanation: The liked list data structure, LinkedListNode is


created to take a generic data type “T”. The class is also public,
so it can be accessed anywhere.

The data stored by the node is set to “T”. “T” is undefined


public class LinkedListNode<T> { //Node class.
Generic datatype "T" beforehand, since I do not know what data I will store in the
var data: T queue (or queues if I use multiple, where the data stored may
var next: LinkedListNode?
public init(data: T){ even be different). A “placeholder” of sorts allows me
self.data = data
}
flexibility.
}
The node also has a variable “next” which is a reference to the
next node. This is optionally unwrapped since the node may
not have another follow it.

The initialisation sets up the initial value of the node, which


will be “T”, stored in the variable “data”.

The Queue class is set up, with a generic placeholder again.

public class Queue<T> { The node class is referred to as Node throughout using a
typealias Node = LinkedListNode<T>
typealias, to make writing and reading the code easier.

var head: Node! The variables are set up. A queue needs a head, a pointer to
public var isEmpty: Bool { return head == nil }
var first: Node? { return head }
the first item (set as the head), and a pointer to the last
var last: Node? { item.
if var node = self.head {
while case let next? = node.next {
The last item will begin as the head. If there is an item, then
node = next the pointer points to this item. It does this by moving to the
}
return node next node and checking the node after. If that node’s value
} else { is not nil (i.e. there is a node following) the rear pointer
return nil
} moves to the next item, until it reaches the end.
}
There is also a public Boolean to check if the queue is
empty.

func enqueue(key: T) { The function takes data (T) and creates a node with
let nextItem = Node(data: key) this data. It then finds the last node in the queue,
if let lastNode = last {
lastNode.next = nextItem and sets the next item after that as the node just
} else {
head = nextItem
created.
}
} However, if there are no items, then the node
created is set as the head.

func dequeue() -> T? {


if self.head?.data == nil { return nil }
if let nextItem = self.head?.next { The dequeue function returns the data “T”.
head = nextItem
} else { The function checks the items in the queue. If the
head = nil
} queue is empty (no head), the return is nil.
return head?.data
Candidate Number - 4164 56 Juheb Habib
} The head is set as the next item in the queue
OCR A-Level Computer Science Coursework Documentation
Timetable Application

I tested the queue by enqueueing items and then


dequeuing them.

The expected output was “a”, “b”, “c”, “nil”.

The actual output is “b”, “c”, “nil”, “nil”.

The issue appears to be with the dequeue function. I


have set the return as the data of the node after
moving the node, rather than moving the node after
returning the data.

func dequeue() -> T? {


if self.head?.data == nil { return nil }
let out = head?.data I changed the function so that the
if let nextItem = self.head?.next {
head = nextItem data is stored in a variable, and then
} else {
head = nil
returned at the end, with the node
} moved as usual.
return out
}

I changed the datatype for the queue to


“Any” instead of string. This allows me to
put any datatype into the queue. I have
input strings, and integers and an array.

The dequeue function also outputs


correctly.

This is sufficient evidence that the queue


works correctly.

Evaluation
The queue was fairly simple to program, with the only issue being the error I made when
returning data.
The queue is now ready to be implemented.

Colour Coding
Overview
The three priority levels will use colour coding to distinguish them. Level 1 priorities will
have a neutral tone, since they will be the most frequently occurring. To make individual

Candidate Number - 4164 57 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
cells stand out, I make it so the cells alternate between two neutral colours (light blue and
white in the UI example).
Cells will have a default colouring, of which priority 1 items will be assigned by default.
Items of higher priority’s colour will take precedence to the default.
Items of priority two will receive a colour to make them stand out. Green may be a good
choice since it will contrast the default colouring.
An alternative would be to use orange (since priority 3 will use red/orange to signify its
urgency, and priority 2 will be a step down from that). Using orange may make it difficult to
distinguish between priorities 2 and 3, however it is a logical choice due to priority 3’s
colour.
The text is necessary since its colour will have to be modified to make it stand out against
the background. Since the background is variable, the text will have to be also.
I have decided to go with the user-selected colour for normal events (default priority),
yellow for important items, and red for urgent items. Default priority items will display text
of the colour selected, while the background will be a faded variation of the same colour.
This way, it does not matter what colour the user selects, as it will be displayed regardless.
For the other two events, I have taken cues from warning signs. When
there is a yellow background, black text is used to make it stand out,
while on a red background, white text is used. The text will always be
readable in this way, and also convey a sense of emergency to the user.
In addition, given how I intend to implement the default colours, while a user may select the
colours I have used for higher priority events, the events will not look the same in the
agenda, since the text will be coloured differently.

Design
There is much around the events that has not been setup yet. I do not know exactly how the
priority will be stored or accessed, however I can create a general function that will setup
the cell.
When configureCell is called, the event and all its data is passed through as “event”.
The event’s name is retrieved, and titleLabel.text is set as the name.
The time of the event’s occurrence is also retrieved, and the time until the event is to occur
is calculated, and set as timeLabel.text.
Finally, the colours are established. Swift has a tool named “switch” which allows for testing
various cases of a variable. Upon retrieving the priority, the priority is tested against the
possible cases (default, important, and urgent). I will also create a default case for when a
mistake happens. Errors are catered for in this way.

Candidate Number - 4164 58 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

switch event.priority {

case “default”:

titleLabel.textColour = event.colour
timeLabel.textColour = event.colour
cell.backgroundColour = event.colour withAlpha(0.2)

case “important”:

titleLabel.textColour = black
timeLabel.textColour = black
cell.backgroundColour = yellow

case “urgent”:

titleLabel.textColour = white
timeLabel.textColour = white
cell.backgroundColour = red

default:

titleLabel.textColour = black
timeLabel.textColour = black
cell.backgroundColour = white

The code is a little repetitive within the switch statement. It can be cleaned up by using a
function to generate the colours.

func setColour(text: colour, back: colour){

titleLabel.textColour = text
timeLabel.textColour = text
cell.backgroundColour = back

The function takes two colours as parameters, “text” and “back”. The former is used to set
the colour of the text (title and time) which will always be the same in all cases. The latter
sets the background colour of the cell.
Swift uses a feature named UIColour, which has default colours like red, white, yellow and
black, along with custom colours by specifying the red, green and blue values (along with
alpha [opacity]). These values are CGFloat values, which can be treated as normal floats if
hardcoding the values.
The new colour setting code will look like the following:

Candidate Number - 4164 59 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

switch event.priority {

...

case “important”:

setColour(text: UIColor.black, event: UIColor.yellow)

...

So far, this is just a base that is not ready to be implemented, since there are too many
variables that are still theoretical. The plan has been created here, but real implementation
will happen after I have completed the events.
This feature is unfinished as it relies on another feature being completed. Whilst I could
attempt a solution right now, there I is a very high chance I will have to change many details.
Therefore, for the time being, I shall leave this until the events have been completed. By
doing so, I have a better framework to base on, and can test using real data too.

Evaluation
In this cycle, I have established and setup the core of the application. However, these
features, while creating a baseline, are far from completed. There is much more work
required to bring these to where I wish them to be.
However, I will be putting this work on hold until I have implemented other features in
other views, as the agenda relies heavily on events already having been created and can
therefore work with them.
Focus Group: My focus group were disappointed with the progress on the application so far.
I am coming to realise that the features I intended to include may have been too ambitious,
and I predict this trend to follow. While I wish to limit this as much as possible, I cannot say
for sure. The focus group as a whole were not all too impressed with an unfinished feature.
A couple have studied algorithms, and liked how I had created not only a linked list queue,
but having done so quite efficiently as well.
Overall, a disappointing finish.

Candidate Number - 4164 60 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Cycle 2: The Notes


Outline
The notes are to be used by the user to quickly note down any information in a classroom
setting or otherwise. These notes can be used for very brief class notes, reminders, or
anything the user wishes.
There are two views central to the notes feature – the view to see all notes that have been
created, and the view to create or edit a note.
The latter view could potentially be separated into two distinct views, but I believe a better
approach is to have these two features combined into one view, since they work quite
similarly.

View All Notes


When the user navigates to the view, they will be greeted by a list
of all the notes they have created (demonstrated on the right).
This feature can be easily handled by a tableview, which will be
populated with every note.
Each cell in the tableview has a note title, along with an extract of
the note contents.
The notes will be, by default, be sorted by creation date since they
will stored in that order. (For testing purposes, I will use an array
to populate the notes until I implement a more permanent
storage solution.)
This leaves the possibility of sorting the notes by title or by other
methods. To do this, some method will have to be used to identify
the notes (important when navigating to the note to edit it).
To sort the notes, various sorting algorithms may be used, from quicksort to bubblesort.
The user will also be given the ability to search through notes. The search function will be
accessible from just underneath the navigation bar. The user will type in any keywords they
remember, and the search function will narrow down the notes to any that contain the
keyword in any string of text in the title or body. Just typing in “e” for example, will yield a
very large number of results, as the letter is common to many words. The user will be able
to narrow down the search by using keywords that are more specific.
This view must, at a minimum, display a selection of notes, and navigate to note
editing/modification.
Note display will require all the notes stored to be accessible and loadable into the view.

Candidate Number - 4164 61 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Add/Edit Note
The user will need to be able to add notes, and edit existing notes.
In the previous view, there will be a status bar button with a “+”
icon, which universally means “add new”. Since in “view all notes”
only snippets of a note available, the user will have to tap on the
note cell to view the rest of the note, where they will be taken to
the edit note view with the fields populated, where when adding a
note the fields will be empty.
As shown in the image on the right, the view consists of two parts,
the title editor (made from a text box), and body editor (made
from a text view).
Both make use of fully customisable text, and can support any text
iOS supports, so if the user wishes, emojis may be used. The user
will set a memorable note title so they may locate the note again easily. Within the body,
they may type out text as they please. It is designed to be very simplistic, where it offers to
restrictions. However, it also offers no additional support. At the moment, no further
features are provided besides the fundamentals, nor is it meant to offer any further
features. It is not designed to be a robust solution for all notes; rather, it is for quick notes
only to be transferred to a better medium at a later date (handwritten or a dedicated app
for example).
Once the user has finished creating a note or making any edits, they will be able to submit
the changes with a status bar button similar to the “+” button used in the previous view. In
this case, the button may read “submit” or “done”.
Before the changes are committed however, the application will perform a simple validation
check that the boxes are not empty. If they are, the user will be presented with an alert to
inform them that they boxes must have some form of data within them.
Once validation is complete, the user will be automatically returned to the “view all notes”
view.
Alternatively, they will be able to able to navigate back to the previous view due to the
navigation controller adding a back button to the views.
This view, at a minimum, requires that a note can be loaded and edited, or a new note be
created, with the data saved. The view will have to take data from the user (title, body),
create a note, and then upload it to the database.

Candidate Number - 4164 62 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Design and Development
Class diagram:

Testing Table
Test Success Type Description Data Expected Outcome
No. Criteria
1 6.1.1 Function Notes created, edited and deleted. Notes Notes can be created, their contents
modified, and ultimately deleted
permanently.
2 6.1.1 Usability The user has no difficulty carrying out the Notes The user can identify which feature carries
functions necessary to interact with the notes. out which function and successfully interact
with the notes view without prompting.
3 6.1.1 Function Text formatting carried out within view Notes/ Text can be formatted by some method.
String

4 6.1.1 Sanity Uncommon text types are entered. Notes/ While the database may not support any
String string type, incompatible strings are
handled.
5 6.1.2 Function A large number of notes created. Notes The application allows many note’s creation,
and will only prevent the user from adding
more notes if there is a potential memory
issue.
6 2.2 Function All notes are viewed as their collection. This does Notes All notes are viewed, although not
not include deleted notes. necessarily at exactly the same time.

7 2.2 Function A single note is selected from the collection to be Notes The note selected is viewed, without
viewed. interference of any other notes.

Setup
The first stage will be to set up “view all notes”. This requires a few features, namely the
navigation bar and buttons, the search bar, and the notes themselves.

Candidate Number - 4164 63 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The first is easily managed by embedding the view in a navigation controller, whereas the
last is managed by simply using a tableview in the UI editor.
It is the search bar that may cause some issue, however I know it can be implemented
programmatically. The way the search itself works is by using a filter (native to swift) which
can be used to scan through the text in the titles and bodies of notes and reproduce an
array (filteredArray) for the tableview to use to populate the table. The view is reloaded and
the user sees the filtered notes.
There is a difficulty with this method, since if the segue to the view notes code is not
implemented correctly; there will be problems where the wrong note is seen, or even an
error thrown.
Some method must be used to identify notes uniquely.
I have also decided I will implement this feature directly with Realm, since no migration will
be required.
This will mean it takes a little longer to develop, but that is fine since there is no time
requirement to this part of the development.

To begin with, the data structure for the Realm will be set up. The following are required for
a note:

 Title – the title of the note.


 Body – the main text of the note.
 Time – so notes can be sorted chronologically.
Using Realm’s object documentation, the note object will look like the following:
This sets up a Realm object called NoteData, along with the required variables.
class NoteData: Object {

@objc dynamic var title = ""

@objc dynamic var time = NSDate()

@objc dynamic var body = ""

I have also added a tableview controller within the storyboard, and linked a view controller
“ViewAllNotes” to it. I have also added an empty view and linked a view controller “Notes”
to it.
Within Notes, there is a text field and a text view, which are linked to the view controller as
IB Outlets, “titlefield” and “bodyField” respectively.
A constant, newNote is set as an instance of the NoteData object. The variable can be
manipulated, and then submitted to the Realm after relevant validation.
Once the user submits, the text from the view must be saved to the newNote variable.

Candidate Number - 4164 64 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Therefore, newNote.title = titleField.text! and newNote.body = bodyField.text!. The
variables are set as being not optional as the variable required values. Validation will be
carried out to ensure the fields are not empty before values are actually submitted, so this
will not be an issue.
I have realised that the cells of the ViewAllNotes class require a class themselves.
The following is the setup created for the cell class “NotesCell”:

import UIKit
class NotesCell: UITableViewCell {

The note title and some of


@IBOutlet weak var titleLabel: UILabel! its text will be displayed, so
@IBOutlet weak var descriptionLabel: UILabel! the cell contains two text
fields to accomplish this,
override func awakeFromNib() { which are linked here.
super.awakeFromNib()
// Initialization code
}

override func setSelected(_ selected: Bool, animated: Bool) {


super.setSelected(selected, animated: animated)

// Configure the view for the selected state


}
For now, I am using dummy data for
func configureCell(note: [String]) { testing purposes, where notes are
self.titleLabel.text = note[0] stored as a 2D array.
self.descriptionLabel.text = note[1]
The array containing the data about a
}
note is passed into the configure cell
function, which simply sets the
displayed text as the data of the note.
}
This can easily be reconfigured to my
desired data type once I finalise the
structure.

Candidate Number - 4164 65 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
For the ViewAllNotes tableview, I have performed some more setup. Yes, what?
The numberOfRowsInSection function takes the count of my dummy 2D array. It will
therefore display as many cells as there are notes in the array.
In the cell configuring function, I have set a constant “cell” as
“tableView.dequeueReusableCell(withIdentifier: "notesCell", for: indexPath) as! NotesCell”.

What this does is treat each cell as an instance of NotesCell.


I then retrieve the note I want from the dummy array (they will be in order, so I just go
down iteratively – where the note I am looking at is the current index path row of the table.
The cell is configured by passing the note data to the configure function within the class.
The cell is then returned, displaying the data needed to the view.
I have also set editing as true within the view controller, and added default code to handle
deleted. For the time being however, this will remain in its default state as the deletion
works heavily alongside the data structure (which I have not implemented yet). Once a cell
is deleted by the user, it will no longer be displayed at that time. However, to permanently
remove it, the note must also be deleted from the data store. If this is done without safety
measures, the application will crash, since it is not designed to handle the sudden change
between the number of items the number of rows section says it should, and the number of
items it can display.
In this case, deleting an item without the required safety checks will cause an index out of
range error as the application searched for a note that no longer exists.
For this reason, I have decided to leave it for the time being.
Constraints have been added between the labels and cell borders within view. This means
the view features are held in relation to one-another. While I have been building for an
iPhone 8, the constraints allow cross device support, as it handles the change in screen
aspect ratio (which can sometimes throw items off significantly).

Save Button
To submit a note, the save button must be pressed. This appears in the navigation bar of the
note view.
Once a user is happy with the text they have entered, they may tap the save button, which
will submit the data they have entered. However, before submission, the fields must be
validated to check that there is data within the fields, and that data is not the default text. If
the data is invalid, then the application will show an alert to the user to inform them of this.
The submit button then pushes the new note or the changes made to an existing note to
Realm, provided the data is valid. Finally, the user is returned to the previous view, as there
is no more they need to do with that note.

Candidate Number - 4164 66 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

There is a simple method for validation. All the validation


checks are performed within a single if statement, using
Boolean OR comparisons, where if any of the checks
evaluates to false, the whole statement evaluates to
false.
if (field == empty OR default) OR (body ==
empty OR default){
present alert
} else { submit }

Returning to the previous view can be achieved by


popping the view controller from the navigation
controller stack.
In Swift, this is done by the following:
self.navigationController!.popViewController(animated: true)

This only works where the view is presented modally, as


in that case the view is pushed to a navigation stack.

The final code looks like the following:

Here, an alert is presented if the validation checks return false. Otherwise, the note is added
to the data store (currently a dummy implementation).
The alert is created and set with a default value.
let alertController = UIAlertController(title: "title", message: "message",
preferredStyle: .alert)
let dismissAction = UIAlertAction(title: "Dismiss", style:
UIAlertAction.Style.default, handler: nil)

Candidate Number - 4164 67 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The default values are not presented to the user. They are there to initialise the alert class
and will be overridden. For the time being, I will include a message simply for where the
fields are not correct. I may include other more specific messages depending on other
validation I may carry out.
The dismiss action specified above is added in viewDidLoad:
alertController.addAction(dismissAction)

I have then added the message within the validation and presented the alert.
if (titleField.text == "" || bodyField.text == "") {

alertController.title = "Warning"
alertController.message = "Please leave no empty fields when saving"
self.present(alertController, animated: true, completion: nil)

I will now begin on the Realm implementation.


I have realised an issue – my current implementation with dummy data simply uses the cell
position to update data in the dummy array, however when it comes to Realm, each item
will be identifiable with a primary key. This can be used to load and update data, but
implementation is different depending on whether the item (with its given primary key)
exists within the table already or not, since an items’ primary key cannot be overwritten.
This means that my implementations for whether the user is adding or editing a note will
have to be slightly different.
The structure should look like the following:
update Realm Function{
note.title = textField.text
note.body = bodyField.text
note.time = currentTime (generated by a function call)

if newNote{
note.id = currentTime
add note to Realm

With Realm, items are all added to the table using the same function, realm.add(Object).
However, if the note already exists, then the parameter “update” will be set to true, so that
the existing note is updated rather than added as a new note.
I have been having another issue with the primary key, which is actually more of a general
issue regarding the whole object. In testing, I received an error while trying to add note
objects to the Realm (specifically to see whether my data structure is correct).
Realm supports a great variety of data types, however I have been having difficulty with
Swift’s native types, name NSDate(), which is the way I intended to store times. (It is also
worth mentioning that I will likely have an issue with storing colours for the Agenda, but

Candidate Number - 4164 68 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
that is an issue I will handle later when relevant. For the time being, I will focus on the date
issue at hand.)
First of all, I need to decide how these date/times are working. The primary key will be the
point of creation of the note, down to the second. Since it takes longer than a second to
press save, return to the previous view, press “+”, fill the data, then press save again, this
level or resolution is perfectly acceptable. No more is needed, and less will lead to conflicts.
Each note has a definite primary key. This time taken at this point is also the time since the
last edit of the note. For a new note therefore, the id and time are both the same.
There are a number of ways this time can be generated. Within Swift, there are functions
that can be used to generate the time passed since a fixed point. The two I am considering
are timeSince1970 and timeIntervalSinceNow, where the number of seconds from the fixed
point is returned, whereas the latter takes a value (say, one second), and gives the current
date to the second at that time.
I can drop NSDate as a data type to use, since I have no need to actually manipulate the
date. I simply need to store it for the time being, which can be done by simply converting
the date received directly into a string. Conveniently, within Swift there also exists a class
named DateFormatter(), whereby using the formatter function, a given string (in the correct
format) can be converted into a date useable by Swift. This will be useful for sorting the
notes at a later stage.
Using the practices set out within Realm’s documentation (placing updates within
transactions, for example), the submission looks like the following:

addNoteSegue is simply a check to see whether the note is new or existing. It exists due to
the slight differences in how I have implemented my note segues.
A simple way to improve the efficiency of this is to have the current date/time stored as a
constant once the save button is pressed, and simply update it, rather than generating it
each time.

Candidate Number - 4164 69 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

This example shows how I have used currentDateTime to update the age an id.
To test, multiple notes have been created an saved to the Realm. I called a print of the
database for that object, and it appears that the notes have been saved as objects to the
Realm successfully. (This was done from the Agenda view, proving that it is not affected by
being in the Notes view as a local instance.) This shows that notes save to the Realm
successfully.
I performed three validation checks when attempting to save a note. In each case some data
was missing (title, body, both). The validation check works successfully, as each time the
alert popped up, and no invalid notes were seen to have been saved in the Realm store at
any point.
I tested the usability with members of my focus group. Overall, the interactions indicted
that I would have positive results with my target demographic. I noticed one user
repeatedly cancelled notes, not realising that they were not automatically saved.
I may make the save button stand out more aesthetically so users are aware that it must be
pressed to save a note.

Aesthetics
The notes are currently displayed like this (right) as there has
been no modification to the aesthetics of the prototype.
There are some modifications I can make within Swift to
make the view more pleasant, not only to the eyes, but to
work within too. For example, resigning the keyboard from
the fields when the user has finished typing, or opening it up
automatically are small modifications that can be made to make the user experience better.
To begin with, to be able to work with the text field and text view, the Notes class will
subclass the delegates to these two features.
A simple example function within Swift closes the keyboard when the user taps the ‘return’
button. If the user taps the return button on the title, the keyboard will close so they may
edit the body. This feature will not be implemented with the body since the text there may
be multiline.

Candidate Number - 4164 70 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

The text field looks “very plain”in its default state (according to a member of my focus
group). A way to improve the aesthetics is to have a seamless transition from the title to the
text view.
This can be achieved by setting the background colour to white, removing the borders, and
giving the field a hard, coloured shadow.
Since this only applied to text fields, I have decided to place it within an extension, where I
can reuse it later if need be.

In Swift, these are achieved with the following commands.

borderStyle = .none

layer.backgroundColor = UIColor.white.cgColor

shadowColor = UIColor(red: 245.0/255.0, green: 79.0/255.0, blue:


80.0/255.0, alpha: 1.0).cgColor

shadowOffset = CGSize(width: 0.0, height: 2.0)


shadowOpacity = 1.0
shadowRadius = 0.0

All of these apply to a text field specifically. In Swift, due to


clipping issues, I will also include masksToBounds = false to ensure
that the shadow is not clipped to the field (where it will not be
seen).
In addition, although I mentioned in the flowchart that the tint
colour would be red, I decided to change to a more neutral light
grey.

Candidate Number - 4164 71 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Here, the final aesthetic of the notes


view can be seen, including the
differences between the title and body
text sizes, the separating bar, and the
cancel and save button within the
navigation bar.

I have not found a way to have placeholder text within a text view (like with a text field), so I
have decided to manually implement one. This can apply to the text field too, since
placeholder text is very faint.
The placeholder text works by having a default message displayed, which clears once the
user taps on the field to type in data. My function will work by checking to see if the text in
the field (or view) is default, and if so, remove it and allow the user to begin editing an
empty space. I could leave the default text, but in experimentation, I found it frustrating to
constantly clear the fields if adding many notes.

If the text is equal to the default text (in the case of the text
field, “Title”), then the program will replace it with an empty
string.
The user can then continue editing.

Candidate Number - 4164 72 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Above, the two function are implemented.


In Swift, the code executes within the DidBeginEditing method, which is called as soon as
the user taps the field, to begin editing.
The user is also prevented from saving these values by the validation checks. If the user
does not change the data, an alert will pop up. This prevents potential conflicts caused by
how the program treats the default text.
This has been made as an additional else statement in the validation before the saving:
if (titleField.text == "" || bodyField.text == "") {

alertController.title = "Warning"
alertController.message = "Please leave no empty fields when saving"
self.present(alertController, animated: true, completion: nil)

}
else if (bodyField.text == "Note Description" || titleField.text ==
"Title"){
alertController.title = "Warning"
alertController.message = "Please add your own text or cancel"
self.present(alertController, animated: true, completion: nil)
}
else{
submission...
}

Testing consisted of usability testing, where the fields were tapped to begin editing to see
whether the text disappeared. When it did, that test was passed successfully.
I also tested saving the note with the default text, and the alert successfully shows up.
Focus Group: A member mentioned that having the text disappear as it does was quite neat,
however what if the user wishes to save a note with a title of “Title”. I do not believe that
the user is at all restricted by having a total of three words being prevented from use. I
decide to keep the checks in place.
The same member also mentioned how, while the feature was nice, the delay before typing
could begin was frustrating. When asked whether it was possible to reduce the delay, and
for what reason it was there, I could only respond that it was due to hardware limitations of
the device running the simulator. It was running many applications alongside, and did not

Candidate Number - 4164 73 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
include very powerful hardware, so it was beginning to struggle. There was not much I could
do in that regard.

Displaying All Notes


Since notes can be saved to the Realm, the next logical step would be to then work on
displaying them in the Notes view, however I am not sure how to identify notes from View
All Notes yet. Therefore, I will be working on displaying notes into the View All Notes
tableview, after which I can decide the best way to send the selected note to the Notes
view. The idea could be to pull the relevant note using the primary key defined, which may
be sent through a segue, however since View All Notes will be displaying note titles and
snippets of the notes, it may not be necessary to pull the note again, since it would already
have been loaded. It is entirely possible to send the entire note through, without having to
access the database again, however this may causes issues when updating a modified note.
All in all, I shall be putting that aspect of the program on hold for the time being, and
instead work on displaying the notes in View All Notes, after which I will decide on the best
move forwards.
I have experience with using tableviews based on arrays, and I can imagine the easiest way
to work with the stored notes within View All Notes is to convert the items stored in the
database to an array.
All items can be pulled from the array using uiRealm.objects(NoteData.self), which
returns all the data stored in the NoteData table. However, when printed, the formatting is
as follows:
Results<NoteData> <0x7feebf42efb0> (
[0] NoteData {
id = 2019-03-11 21:38:20 +0000;
title = Note 1;
age = 2019-03-11 21:38:20 +0000;
body = Some contents;
},
[1] NoteData {
id = 2019-03-11 21:38:40 +0000;
title = Note 2;
age = 2019-03-11 21:38:40 +0000;
body = Some more data

Data on new line


;
}
)

To work with this format, it must be converted into an array. This is possible through the use
of a map function with a given closure, since it returns an array. $0 can be used to represent
each item in the collection sequentially, so as the function iterates over the collection, each
item is appended to an array.
I can imagine reusing this function, therefore by creating this as an extension, it can be used
easily across multiple views to operate on the collection type (which according to the Realm
documentation, is a “Results” type).

Candidate Number - 4164 74 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

extention Results {
This pseudocode outlines the basic
func toArray(array) { format for the code I will be writing,
converted = array.map{$0}
however there are a few nuances with
Swift to be aware of.
return converted
The return type for a function must be
}
defined, while also “self” can be used to
} operate on the item without having to
pass parameters. Therefore, I can also operate directly on
Therefore, the Swift implementation may look more like this:

extension Results { The type to return is set as “Any”, as


this function is designed to be general
func toArray() -> [Any] {
use. My current usage is on NoteData
return self.map{$0} objects, however if this were the
}
return, it would cause errors if I were
to attempt to use this function with
} any other object type. “Any” allows
the function to be a general case and
reusable. I have also returned the map results directly, rather than having an extra step of
saving it to a variable and then returning that.
To test, I simply ran the code with a number of notes created. This was compared against
the Realm printout, and matched up well. All notes had successfully been converted into an
array.
With all the notes in the database converted into an array, it can then be used by the
tableview. When the view loads (viewDidLoad or viewWillAppear), the function will be
carried out to store the array into a variable. For now, I will use “allNotes” to represent this
variable. It is worth noting that since the return type is Any, the results must be cast to an
array storing NoteData objects. It may look like the following:
allNotes = uiRealm.objects(NoteData.self).toArray() as! [NoteData]

This can then be used to populate the table. numberOfSections defaults to 1, whereas
numberOfRows will equal allNotes.count (the number of items within allNotes i.e. the
number of notes stored in the database).
The table can then be populated by working through allNotes array sequentially, and
applying the title and body to the cell. Since the notes in the array are still NoteData objects,
they retain all their information.
Firstly, I will have to make changes to the configureCell function, since currently it is based
around a different data structure.
The modifications are simple however. All that is required is modify how the data is
accessed.

Candidate Number - 4164 75 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The current configuring function looks like this:

func configureCell(note: [String]) {


self.titleLabel.text = note[0]
self.descriptionLabel.text = note[1]

Whereas the new one will instead look like this:


func configureCell(note: NoteData) {
self.titleLabel.text = note.title
self.descriptionLabel.text = note.body

The differences stem from passing a NoteData object into the function, as opposed to an
array of strings (each array representing one note, rather than the collection of notes).
Due to the different object passed into the function, the way the data is handled changes.
Array indexes are not accessed like before. Instead, the object states values are directly
accessed.

Selecting and Displaying Notes


Now all notes display within the table, the next step is to allow navigation by tapping on a
note. Xcode provides a good framework for passing data between views through segues. I
could send the whole note through to the next view, or just send the primary key (ID).
Sending the whole note has the advantage of containing the relevant information, which
can directly be used to populate the fields. However, each note is a potentially large item to
pass (although this is not an issue in most cases). The largest issue I perceive is potential
difficulties caused due to a structural change in the code I may have to make in the future.
This may include (but is not limited to) how notes are loaded, saved, or even stored in the
database. By passing the whole note and working on that directly, any changes made may
require a large amount of restructuring in how the notes are used. There is also a possible
issue of updating the note to the database again, however this is solved by simply using the
primary key to modify the note in the database. Of course, if I am already relying on the
primary key to make changes to the note, it makes sense to base the entire process around
using the primary key, firstly to pull the note from the database, and secondly to then
update the note once editing has ended. When the view loads however, a check will have to
be performed to ensure that the database is not searched for a primary key that does not
exist. It can be assumed that the key sent is for a note that definitely exists, however an
error may be thrown if the view attempts to display a note with a primary key set to null (or
whatever default value I may end up sending).
It appears to me that the best approach (and likely the neatest approach) is to send the note
ID across, and then access the note using that in the Notes view. I will have to differentiate

Candidate Number - 4164 76 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
between when a new note is being added and when an existing one is being modified for
displaying a note, but I am sure this will not be too difficult.
My approach will consist of having two variables within the Notes view, “addNoteSegue”
and “noteId”. The first will be a Boolean, which will indicate which segue was used to access
the Notes view. The second is to store the ID of the note if it has been sent.
Design
The process may work like this:
View All Notes Notes

I have given the two segues from View All Notes identifiers, “showNote” and “addNote”.
That way, the two segues and their purposes can be differentiated.

Candidate Number - 4164 77 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Within the perform segue function, I will have the following code:
if segue.identifier == "showNote"{
let destinationVC = segue.destination as! Notes
destinationVC.addNoteSegue = false
destinationVC.noteId = tappedId
}
else if segue.identifier == "addNote"{
let destinationVC = segue.destination as! Notes
destinationVC.addNoteSegue = true
}

Now, the important part is to get the ID of the note tapped.


A possible way of doing this would be to use the “tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath)” function. It allows me to make use of the indexPath of
an item once a user taps on it, which is also the index of the item in the array used to
populate the table (provided the table is generated sequentially, which in my
implementation it is). This means that I can immediately access the item and send its
relevant data across to the next view.
The way I intend to use this is by setting a variable equal to the indexPath.row (the row
number of the item in the table), using that to identify the item in the array, and then
triggering the segue. The segue function has been completed above, so no further
modifications need to be done to send the data across. It may look like this:

func didSelectRowAt... { The first part triggers as soon as a cell is


tapped = indexPath.row tapped. This obtains the index of the note in
performSegue(withIdentifier: "showNote")
allNotes array, which leads to the second part
}
where tappedId is set as the ID of the note at
func prepareForSegue...{ that location in the array.
tappedId = (allNotes[tapped]).id
... This tappedId is then sent to the next view.
}

Testing
The cells were tapped, and the indexPath.row printed. When tapping the third cell, the
printout was 2. This is exactly as expected, as the indexPath is indexed at 0.
The correct note data was then printed in the next view.
While navigating between the two views, an interesting thing happened – the Notes view
appears to move in over View All Notes twice. After some research, I believe the reason to
be that it is being triggered twice, and therefore is segued to twice.
The two triggers would be the segue within the storyboard, and the segue trigger within the
“didSelectRowAt” function. This means, to prevent the segue occurring twice, one of the
triggers must be eliminated. While I may work with an entirely code based segue, it would
require a vast amount of restructuring around the one segue, since my application is heavily
storyboard based. It also helps that the segues within the storyboard show a clear

Candidate Number - 4164 78 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
connection and navigation direction between the different views.
Therefore, I will attempt to fix the issue on the code side. The problem stems from the
segue being called, so therefore this must be removed.
While testing, I found the segue triggers before the indexPath.row printout.
Since the segue triggers and the move happens before almost anything else, the function to
obtain the indexPath.row must happen within the segue trigger parameters. I have found
that using an entire override function for this is unnecessary. Instead, by referring to the
view’s table within the code, the indexPath for a tapped cell can be obtained. It looks like
the following:
self.tableView.indexPathForSelectedRow

If placed within an if let statement, the code can be protected from issues where the user
taps any cells below those generated (i.e. those without indexPaths). Once this is set to a
variable (say, “indexPath”), then the array can be accessed directly by the following:
if let indexPath = self.tableView.indexPathForSelectedRow {
tappedId = (allNotes[indexPath.row] as AnyObject).id
}

Swift requires the note be cast as “AnyObject”, which I am using until I finalise my object
type.
With this, the ID is saved into tappedId and is sent to the next view.

Note Search
An important feature for the user is the ability to search for notes. This is especially useful
should the user have saved many notes, since it will allow the user to easily find the notes.
This comes with the obvious prerequisite that the user remembers some content of their
note. I wish to implement this feature in such a way that the user need only remember a
snippet of either the body or title, and still have relevant results returned. This method may
have two drawbacks: the first is that the user may be returned with a large number of
results, but that may be a product of having similar text in many notes (for example naming
notes “note 1”, “note 2” and so forth, and then searching “note”). Unfortunately, this relies
on the user either using some other way to distinguish notes, or the use of advanced search
features outside the scope of this application. The second potential issue is where the user
cannot quite remember the precise text they used in the note they are searching for, and
therefore will not be returned any relevant notes. Again, this relies on the user’s personal
use of the notes and their own recall. Without the use of an advanced search method, with
more available options, this may be a difficulty for the user. For this application, an
advanced search on top of a secondary feature is too much complexity outside the aim of
the application, and therefore I will only focus on implementing a basic text-matching
search. There is the possibility of including this as a potential improvement in the future;
however, it is hardly a priority.

Candidate Number - 4164 79 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The note search will work using Swift’s inbuilt search bar and the use of the filter function,
which, given parameters, can reduce a collection down to only results containing the
paramters.
All relevant code will be called within the updateSearchResults(for searchController:
UISearchController) function, which is called every time the user makes a change to the
search bar (i.e. it is updated), and works on the search controller embedded within the view.
This means that as the user types in their query, their results are brought in live. The user
can see, in real time, as their more specific query narrows the search field down.
The search works on the basis of search text, where the text typed in is used to match
against the notes in allNotes (the array used to populate the table). In fact, a new array,
“filteredNotes” will be used to populate the table, which will copy directly from allNotes.
The key difference, however, is that only notes that match the text in the search bar will be
contained within filteredNotes, meaning that any that do not match will not be displayed.
Of course, when the search bar is empty, filteredNotes will be set equal to allNotes, so all
notes will be displayed.

func updateSearchResults...{
if search bar is not empty{
text = text in search bar
filteredNotes = allNotes.filter{ note contains(text) }
}
else{
filteredNotes = allNotes
}

To update the table, the reloadData() function must be called on the tableview. This will
show the changes made to the array to the user immediately, as the table is repopulated
from the modified array.
I have decided in my implementation, I can increase the efficiency of the function by using
an if let statement for the search term, along with a check for whether the bar is empty on
the same line.
My Swift implementation will look like the following:
func updateSearchResults(for searchController: UISearchController) {
if let searchText = searchController.searchBar.text, !searchText.isEmpty {
filteredNotes = allNotes.filter { individual in
return (individual.body.contains(searchText) || individual.title.contains(searchText))
}
} else {
filteredNotes = allNotes
}
tableView.reloadData()
}

The filter function works in this case by checking “individual” (every note in the array being
filtered [allNotes]) against the parameters, which in this case checks whether any text in the
note body or title contain the search term (searchTerm).

Candidate Number - 4164 80 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
This issue is coming about due to the way ‘contains’ works. It only checks whether there is
an exact match, which includes case. To remedy this, the search text and the searched text
can all be set to lower case, removing the case issue. I am doing this since it is likely that the
user may not consider case when searching.
The “.lowercased()” function may achieve this easily.
The changes made are as follows:
searchText = searchController.searchBar.text.lowercased()

in the case of the search text, and


(individual.body.contains(searchText.lowercased())

in the case of the body (and repeated for the title too).
There is another consideration to take into account. In the case that filteredNotes is empty,
the numberOfRowsInSection function has to be protected against the lack of cells to display.
The issue is that filtered notes may vary in size quite rapidly, and when suddenly reduced to
0, the function may not be able to handle it.
For this situation, a guard statement will work perfectly. A constant, ‘notes’, will store
filteredNotes as an optional (giving it the possibility to return null). In the case that
filteredNotes is indeed null, the function will return 0, else it will return the length of notes.

guard let notes = filteredNotes as Optional else {


return 0
}
return notes.count

Note Sorting
Being able to reorder a list of items as needed is always a useful feature. In the case of the
notes, having at least a chronological and alphabetical sort would be ideal. The user may
also benefit from their own custom sort (where they may reorder items as they see fit),
however I fear this may be very difficult to implement within the constraints of my table
data structure. Regardless, the first two sorts should still be doable.
Quicksort
I will use a quicksort on the note titles, since I believe the comparisons between strings are
made more easily in the way quicksort handles them.
While a quicksort may include an advanced way of finding an ideal pivot, in my
implementation it will simply work off a random index (in this case, the middle index of the
array). Although having a way to find an ideal pivot is beneficial, there likely will not be
enough notes saved by a user to make a real difference to the sorting and displaying time,
therefore I believe it would be an unnecessary feature.
The quicksort works by comparing every item in the array to the pivot. Larger items are
sorted into one array, and smaller items sorted into another. Values equal to the pivot are

Candidate Number - 4164 81 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
sorted into a third. The quicksort algorithm is then reapplied to the larger and smaller array
recursively, until all items have been sorted, and the final, sorted array returned.
Quicksort algorithms that work in place, but they can be complex to program, and for my
purposes that extra complexity is unnecessary. My algorithm will have a greater space
complexity than one of these (along with a greater time complexity than an algorithm with
an ideal pivot finder), however given the problem size, these are non-issues and possible
improvements to make in the future.
func quickSort(array){
less = []
equal = []
more = []

if array.count > 1{
pivot = array[middle value]

for value in array{


if value > pivot{ more.append(value) }
else if value == pivot{ equal.append(value) }
else if value < pivot{ less.append(value) }
}

return quickSort(less) + equal + quickSort(more)

} else{ return array }

This is a compact, barebones quicksort. It only carries out the basic necessities, but that is all
that is required.
Merge Sort
I will apply a merge sort to the note ages to sort them chronologically. The merge sort works
in two parts – the splitter and the merger. Merge sort works by initially splitting the problem
down to its smallest possible, and then merging the list back together by comparing items in
the sub lists.
My merge sort will work on this basis. The splitting part will be contained within the main
function call. The array is essentially sliced in half by finding the middle index, and creating
two sub arrays, which contain values that appear before the split index and values that
appear after. The split will not necessarily be even as the initial array may have an uneven
number of elements. This main function call, which does the splitting, is recursively called,
so the initial array is split into many sub arrays, all eventually of size one. The exit condition,
therefore, is when the array length is one.
After that, the arrays can begin to merge. This will be done through the use of a second
function. It takes two arrays, and compares every value in them. Values are populated to a
new array (the merged array) in order of size, where the smallest value is copied across
from the arrays by comparing the current values before moving to the next.

Candidate Number - 4164 82 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

func mergeSort(array){
if array.count == 1{ return array }
else{
split = array.count/2
left = mergeSort(array sublist from index first to split)
right = mergeSort(array sublist from index split to last)

return merge(left, right)


}
}

func merge(left, right){

array = []
while left AND right have elements{
if current value in left > in right{
array.append(value in right)
}else if right > left{
array.append(value in left)
}else{
array.append(value in right)
array.append(value in left)
}
move to next value

append any leftover items in either array to array


return array
}

My pseudocode for merging lacks some detail, and therefore cannot be immediately
converted into Swift.
For one, some method must be used to identify the elements in the arrays being compared,
and move after them. I could use a system of pointers, where the pointers mark the indexes
being compared, and move through the list. The pointer will always point at the smallest
item in array that has not yet been added to the merged array.
Here is the merging function once again, but as a flowchart including the pointers:

Candidate Number - 4164 83 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

There is a problem with this, however. The algorithm ends before there is an “index out of
range” error, however this does mean there may be trailing items in the other array if they
are not equal.
To fix this, the algorithm must be amended slightly. The above algorithm finishes once a
pointer exceeds the array length (i.e. it point to an index out of range of the array).
However, this means for certain that the array length is less than the pointer, for at least
one of the arrays. Therefore, all that needs to be done is append all the items in the other
array to the merged array, and then both arrays have been fully merged.
This is quite simple to achieve:

while pointer < array.count{


merged.append(array[pointer])
pointer += 1
}

All that needs to happen is have this addition appear at the end of the merge function, and
have it apply to both arrays (by using the relevant variable names etc.)
Since it uses a while loop, only the one with outstanding elements will trigger, while the
other already does not meet the condition.

Candidate Number - 4164 84 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Finally, after all this, the merged array can be returned.
It was mentioned to me that it made more sense to actually have the most recent item
sorted first, so I made a quick modification where, after the notes have been sorted, to
simply reverse the array. That way, the notes are now sorted in a more logical way.
Implementation
Now, the sorts have been created, the next step is to implement them within the view.
There are various UI elements that may be used to achieve the sort, but the most intuitive
would be to have a segmented controller in the navigation bar, which allows the user to
switch between the two sorts. The default will be chronological (as notes are added that
way), so the default segment with be sorting by time. The segmented control prevents the
user tapping the segment already selected, so they will not be able to unnecessarily resort
the notes.
Each sort works on a different basis. Neither takes a note (or array of notes) and sorts them.
They only take a string or a date, and sorts those. Therefore, some translation must be
achieved between the sorted items and the notes.
A potential way to achieve this is to strip (say in this case) the dates from the notes and
place them directly into an array. These dates are then sorted and returned. Each date will
be considered, and then looked up in the table to find the note that contains that particular
date as an attribute. The notes found like this are then sequentially placed within an array
and therefore appear sorted, even if the notes themselves were not initially.
I will control selection of the sort type with a segment controller.
The segmented controller works with a number of segments, defined with storyboards or
programmatically. My implementation makes use of two segments, as I have two methods
of sorting that the user can switch between.
Each segment is represented by an index (show how segment indexes are accessed).
Whenever the user taps the controller, a function (shown) is called within the view
controller. Within this, if the index selected has changed, then a block of code may be
carried out. As shown below, this code dictates the sort to be applied to the notes.

Candidate Number - 4164 85 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

I programmed the sorts with the specific data type it is handling in mind, which meant I had
to make some changes at the implementation stage to get it working with my system. Since
then, I have decided to change the structure of the sorts, especially since it is unlikely I will
have to reuse these sorts anywhere, which means I can tailor the sorts specifically to the
note object.
Therefore, it may be better now to sort the notes directly, rather than using references as I
did, as the age and title states can be accessed directly.
Focus Group: An important piece of feedback I received was that the order of chronological
note sorting was in the reverse of what they expected. It appeared that the opinion was
shared among the group, although I had not noticed myself.
Taking this into account, I applied Swift’s reverse function on the final sorted array, just so
the output matches what they were expecting.

Candidate Number - 4164 86 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Deleting Notes
Without the ability to remove notes, the user will have the difficulty of handling the build-up
of many useless notes. Deleting notes allows the user the clean up their note space,
removing notes that are no longer relevant or useful.
Within a tableview, there are some options provided for editing the cells, one of which is a
delete method. It removes the item from the table and calls some code, which often would
be whatever may be necessary to remove the item from the array used to populate the
table. In my case, the note will also have to be removed from the database too, since
otherwise, when the tableview is reloaded (to update the changes made), the deleted note
will be pulled from the database once again and repopulated within the table.
In my previous experience with tableviews, I have found that the deleting cells from a table
can cause many issues, so I will approach this problem with caution. I problem I have had
previously, for example, are index out of range errors when reloading the tableview, or
other errors related to different parts of the code being executed at the incorrect time. For
this reason, I will pay keen attention to the structure here, especially since it is very
important that the database be handled correctly. With adequate planning, this should not
be an issue.
1. User swipes cell to delete the note
2. Information is obtained about the cell (the note used to populate it)
3. The note is deleted from the database using its primary key
4. The note is then removed from the tableview array
5. The tableview is reloaded to display the modified set of notes
To begin with, the framework for deleting cells in a tableview must be added to the view
controller. The editingStyle function provides this framework, as it handles all features
relevant to reordering and restructuring the table.
When the table is modified in some way (like swiping, for example), this function is called,
and the editing style used by the user passed into the function. If this editing style is ‘delete’
(the UI style is different), then my code will execute, following the steps outlined above.
To get the note, a variable (tappedId) will hold the ID of the note stored at the
indexPath.row index of the array used to populate the table (filteredNotes). It may look like
this:
tappedId = (allNotes[indexPath.row] as AnyObject).id
The note is then obtained from the Realm by using the retrieve object function, using the
primary key as the identifier, and stored in ‘noteToDelete’.
The note, having been obtained, will then be removed from the Realm. This is done within a
Realm write statement as follows:
try! uiRealm.write {
uiRealm.delete(noteToDelete)
}

Candidate Number - 4164 87 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
After the note has been deleted, it must then be removed from the array. The
indexPath.row can be used to remove the note from the array by simply removing the item
and the index contained in indexPath.row, as follows: filteredNotes.remove(at:
indexPath.row)
Finally, allNotes is repopulated from the modified Realm.
These two steps are necessary, as the user may decide to delete the note after having made
a search, however allNotes must be updated to the change made.
In addition, I have realised that since the delete works by taking the object itself and
removing that, by using the primary key as an identifier, I am simply taking an extra
unnecessary step. Instead, I can set noteToDelete as the note stored in filteredArray
directly, as the entire note object is stored there already. This removes tappedId from being
used, or the function to obtain a note by its primary key, and reduces them to the following:
let noteToDelete = (filteredNotes[indexPath.row]).

While usability testing, users struggled at first to locate the function to delete the notes.
However, having said that, none of the user had to ask for direction and all eventually found
it within a reasonable period of time.
While maybe not the most intuitive part of the application, I believe it may stay as it is and
see no reason to modify how it works.

Displaying Notes
Now all features for View All Notes have been completed, I will focus on the final parts of
the Notes view, namely displaying a note selected, and saving an edited note.
When a user taps the note they wish to view, the primary key for the note will be sent
across. This will then be used to access the note from the Realm, which can then be directly
worked upon. The reason I take this approach is because, while I could send the entire note
across (having access to the data), to update it within the Realm, I will have to retrieve it
regardless. In either case, I will end up retrieving the note from the Realm using the primary
key; therefore, it is simply more efficient to do so from the beginning.
To display a note, the primary key must be sent across so that the note may be retrieved
from the Realm. Since the code for the segue check is already in place, all that needs to be
sent across is the Boolean value so the check can be performed.
With these two items, the note can be displayed. The view controller, having made the
check that the note to be displayed exists, will retrieve the note from the Realm, using the
primary key supplied. The data held within the note can then be displayed to the fields.
The code for the display itself is relatively simple, since it only consists of a Boolean check to
trigger the retrieval and display.

Candidate Number - 4164 88 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

if addNoteSegue == false{
currentNote = realm.object(ofType: NoteData.self, forPrimaryKey: noteId)!

titleField.text = currentNote.title
bodyField.text = currentNote.body
}

Where addNoteSegue and noteId are the sent variables.


The difficulty comes in sending the variables at all.
Firstly, within the Notes view, the two variables must be setup to receive the correct data
type without overriding the data sent. They are instantiated within the view controller class,
but are not assigned any values. The values they will take is only defined by the previous
view.
This is done within the prepare for segue function. If the segue triggered is the “addNote”
segue, then addNoteSegue Boolean variable in Notes is set to true, and the above code
cannot trigger, the view’s fields are left with the default text, and the save will be handled
differently (which I will tackle after this part has been completed).
If the segue triggered is “showNote”, then the note that was tapped is retrieved (by
identifying the indexPath of the cell tapped.
Using the framework for sending variables through a segue, whilst also including the above
conditions, the result is as follows:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

if segue.identifier == "showNote"{
if let indexPath = self.tableView.indexPathForSelectedRow {
tappedId = (allNotes[indexPath.row] as AnyObject).id
}
let destinationVC = segue.destination as! Notes
destinationVC.addNoteSegue = false
destinationVC.noteId = tappedId

}
else if segue.identifier == "addNote"{

let destinationVC = segue.destination as! Notes


destinationVC.addNoteSegue = true
}

Candidate Number - 4164 89 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Evaluation
Test results:

Test Outcome Passed?


Number
1 Tested during development Yes
2 Tested during development Yes
3 Tested during development No
4 Entered and handled with no difficulty. The notes Yes
supports foreign characters and emojis (i.e. Unicode
is perfectly supported)
5 Tested during development Yes
6 Tested during development Yes
7 Tested during development Yes

No action will be taken as a result of test 3’s failure, as I do not intend to implement any
formatting currently.
Altogether, I feel like this cycle went well. The constituent parts to create the entire feature
was longer than I had expected, however I believe my implementation is fairly concise and
offers adequate features.
While there may not be any glaring issues, there are some improvements that could be
made to tweak the feature to be better.
One such issue is the quicksort algorithm I used. The pointer is essentially selected
randomly, meaning that the algorithm may not be efficiently used.
A bigger potential issue may lie in sorting each time, and loading the view. Each time, the
array is regenerated, which may cause issues if there are enough items in the Realm. I was
unable to test this however, as it requires a large number of notes. A way to prevent this is
to store the sorted notes in a separate array, and load that rather than resorting the list,
provided there have been no changes. The issue is in that however, as that adds another
layer of complexity to the application, and another possibility for bugs to be introduced.
Related to view the notes list, my search function is fairly basic. It certainly gets the job
done, and is far better than not having one (to the point it may even be necessary), however
many applications that include notes also make use of an advanced note search, where the
user can filter the notes for better results. Given the sparse amount of details retained
about the notes, along with space constraints and the added complexity, I decided not to
include this. It is a possible improvement that could be made at a later date.
Focus group: after the unfortunate disappointing end to the last cycle, my focus group were
quite pleased to have something to play around with now. I did receive a few comments on
whether “that was [all]” to the application, but I assured them there was much more to
include. Relatedly, I was asked if more would be added to the view, in terms of features and
functionality. A good question, certainly, but not one I could concretely answer. While the

Candidate Number - 4164 90 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
view is completed to the point that it is perfectly usable, there are many more features that
could be included. However, going down that path will detract from the aim of making a
timetable, and given the endless possibilities for making additions, I very well could
accidentally end up making a note taking application with a rushed schedule tacked on. I
therefore informed them that, for the time being, I would not be considering any additions
to the notes feature
Success criteria testing:
6.1 – the notes are creatable, editable and removable. They also contain the minimum
number of features required.
Note deletion is not reversible, but this was not a priority.
No formatting options have been included. The notes only contain the basic necessities.
The user has no limit to the number of notes they can create. The Realm can support a large
number of objects, while the view uses reusable cells so has no display limit, within the
capabilities of the device (which should be more than adequate).
6.2 – All notes can be viewed. As mentioned, there is no limit to the notes that can be
created, nor the number that can be viewed. The only thing preventing the user viewing all
notes at once is screen size.
Notes can be viewed together (View All Notes), and selectively (Note), where they can also
be edited.
To conclude: As the key points of my success criteria have been met, I consider this feature
successfully implemented. While there are additional features that may be included, they
are not necessary and can be left to a later stage.

Candidate Number - 4164 91 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Candidate Number - 4164 92 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Candidate Number - 4164 93 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Cycle 3: Schedule View and Events


Outline
The schedule is the main feature of the application. In this view, the user will be able to view
all events for that week.
The view will show cells for the entire week and will be populated by events that the user
creates. Each event will have its own colour, title, and will slot in at the time it occurs. The
user may then tap on an event to view it in more detail (any extra information, all the times
that it occurs at, any reminders set).

Schedule
After conducting some research, it appears there is no native way to display a ‘schedule’ of
sorts. The closest appears to be calendar, however there is little information on how to
create one, as opposed to integrating with Apple’s provided calendar application.
Therefore, I will have to create this myself. I potential possibility is by using a spreadsheet
and populating cells as events. I do not see any alternatives.
Going by a spreadsheet, the columns would show the days of the week, while the rows will
show the hours. The reason it is ordered this way is that the user will be able to view all
events as they are listed down the hours on any given day easily (as their phone’s default
orientation is portrait).
Unfortunately, I am unable to elaborate any further, as I do not know the best way to
implement this feature without much experimentation to see what methods will produce
the best result.
It is likely a greater portion of the planning stages will be incorporated into the
development.
In any case, the schedule must display the dates, days, times, and events. Furthermore, the
view will include controls for the events, so a button to add new events and a way of
opening the editing screen for views will be included.
The times can be held in an array, as can the days and dates, since they will be loaded and
displayed in a specific order (chronological).
The editing button will have to include variables to hold data about the event that has been
selected so that the edit view may be able to display the event.

Events
Each event will work on the same basis.

Event Creation
The user will create them with four required bits of information: the title, when the event
occurs, its display colour, and whether the event repeats or not.
Once the user has added the parameters they wish, they will tap the create event button,
which will save the event to the Realm, and will then be populated to the Schedule.

Candidate Number - 4164 94 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
If any of the required pieces of information are missing (title, occurrences), then the
application will present the user with an alert informing them that the event they have
attempted to create is invalid and to try again with valid data. These alerts may change
depending on the specific situation.

Event Viewing/Modification
After a user has created an event, they may wish to view the data regarding the event. This
applies especially where the user wishes to see extra data stored about the event, and all
times that the event will occur.
To prevent accidental edits, the event viewing may be locked behind an edit button, where
the fields cannot be interacted with unless the user taps the edit button. This is especially
important where the user may accidentally delete an event. The delete button only appears
when the user is viewing an event they have created (it makes no sense to delete an event
that has not be created yet). The edit button will also change to say save once the user
begins editing.
The edit/save button only saves changes within the view. To commit the changes, the
update event button will be pressed (which replaces create event).

Title and Extra Information


These will be a text field and text view respectively. Each event must have a title, as there
must be some way of distinguishing in within the schedule.
An event does not need to have any extra information if the user does not wish.
Validation will therefore only apply to the title. The check within Swift is simple. It amounts
to testing what the empty parameter of the field evaluates to. If true, then the field is
empty, and this must be rectified by the user.
Regarding each event having its own way of being identified within the table, there may be
conflicts if the user creates two events with the same name. This becomes even more
problematic if the events have the same colour too. However, there are solutions to this.
A check may be performed when the event is submitted. This check applies, whether the
event is new or being modified, as any case where an event may cause a conflict must be
checked. The database is searched for any event with the same name. If there is a match,
then the user is informed. Two problems come from this. Although querying is quite fast in
Realm, there may be limitations. As I do not know the method for querying, I cannot say
what issues, if any, may occur. However, it is helpful to plan in case there are issues. With a
large database, searching may take some time, leaving the user waiting to submit, only to
be returned with an error. The second issue is that the database may mistake an update to
an existing event as a conflict, preventing any updates to events if they retain the same
name. In this case, some way must be made to ensure there is no ambiguity as to whether
an event is meant to be updated, or whether the user is overwriting an existing one by
mistake.
The other method of approaching this problem contains a solution to the second issue
regarding the solution above. The method I will describe here only works due to Realm’s

Candidate Number - 4164 95 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
specific implementation. Here, the title is set as the primary key, and the event to be added
or modified is created as an instance of the Event object. It is then updates to the database.
This way, whether the user creates a new event or modifies an existing one to have the
same title as another event, the change is treated as an update to the second event. This
solves the conflict issue extremely neatly. No further validation is necessary. The drawback
here is that the user may not have expected one of their existing events to suddenly be
overwritten. They will see the change immediately however, as the change is updates to the
schedule.

Repeat Switch
Events come in two flavours, repeating and single. A repeating event is static on the
schedule and never disappears (unless modified by the user). A single event appears only
once, with a predetermined time, day and week of appearance.
This integrates the functionality of a calendar within the schedule.
Whether an event repeats or not is defined by the switch, where changing the setting also
changes how the event occurrence picker displays to the user.
With a repeating event, the user will select all the times for the days that an event will
appear.
For a single event, only a single picker will display, with a time and date selection.

The chart displays how the selection may work with view. Occurrence picker will likely be a
button, and therefore the next view displaying the relevant picker type.

Candidate Number - 4164 96 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Colour Picker
The colour picker allows the user to choose a colour for the event to display. The colour
picker will ideally be of this design:

However, the following is also a possible alternative:

The first format has the advantage that the user can pick any colour they wish. Should many
of their events be associated with shades of brown for any reason, they may pick those
varying shades at their leisure, as all colours that may be displayed by their device are
available to choose from. With the latter example, this is more restricted.

Candidate Number - 4164 97 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The user has a large choice of colours, however the particular one they want may not be
available (like the browns for example). It is unrealistic to provide every possible colour the
user may wish to use, since at that point, the former picker has been created.
This picker makes the selection process incredibly easy however, as the user simply taps the
colour they wish to use.
The ideal picker, in that case, would be to combine the benefits of these two – the picker
contains a gradient, allowing the user the freedom of choosing many shades. The selection
process consists of simply tapping the colour on the gradient they wish to select.
The additional features provided by the first picker example (specific hexadecimal colour
selection and display of HSV and RGB values, along with an eye dropper tool) are
unnecessary for this application.
There exists a potential issue with saving the user’s colour selection. Colours are handled in
Swift with its own UIColour type, a variable which I doubt could be saved to the Realm.
Every colour can be represented with six digit hexadecimal number (each two digits
representing the values for red, green and blue respectively). I can store a hexadecimal
string within the Realm with little difficulty. The only issue may lie in making the conversion,
as it is not natively supported by Swift. I also prefer to user hexadecimal colours over other
types as I have had much experience in the past using hexadecimal colours. It is the colour
representation I am most comfortable with. Unfortunately, Swift does not appear to
support native hexadecimal conversions, so this will likely have to be done manually.
Testing this feature will require a number of steps. Firstly, the hex colour selected will be
entered into an independent colour picker, and the shades compared. They should be
roughly similar, i.e. within the same hue, saturation and blackness range.
The picker can then be tested by recording the hex colour selected, and then navigating
from the view, and printing the data to be updated to the events view. It should match the
hex number recorded.
The colour picker will then be navigated to again to verify that the colour loads correctly
when the user navigates to the view.
Having asked my focus group, there was an even split between the selection and gradient
pickers. I received equally compelling arguments for both, where the former is simpler to
use, and the latter containing a better selection. I then informed them of my intended
solution and was met with unanimous approval. It appears that I was not wrong to attempt
combing the two types into one that has both a great selection of colours, but is also
intuitive to use. The only question is whether it is viable to do so, given that it may be too
difficult to provide a gradient colour.

Occurrences
Two version of occurrences may exist. The former caters to repeating events, the latter to
single events.
I believe the occurrence selection for the latter can easily be done with a picker view, where
the possible selections are the time and the date of occurrence.

Candidate Number - 4164 98 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Repeating events are more complex however. The user may wish to select multiple times
within a single day, multiple times across multiple days, or just a single time in the entire
week.
In any case, this cannot be handled using picker views. As I aim for the application to be as
simple to use as possible, entering the time and day, submitting, and then entering the next
occurrence is impractical. It comes with the user having to work within the constraints of
the application regarding their input, and requires validation application-side. Furthermore,
the method described is incredibly slow and tedious, which is to be avoided.
The only viable method I see is to have all the times the user may wish to use displayed
within a table view. Each time that may be selected is a cell, whilst each day is a section.
The user selects all the times they wish the event to occur. Each selection is highlighted
within the table. Once the user is happy and submits the times, the selected cells are read
off and updated to the event.
This method may lead to a very long table. Should the user select to display all 24 hours, the
table will have a total of 168 cells. Making them too thin impacts usability, and even then,
the absurd number of cells will be difficult for the user to scroll through effectively. This
method is still superior to manual entry, however.
This problem can be alleviated by making the sections collapsible. That way, the user only
sees one (or more, selected at their discretion). The number of cells may still be large, but
not the extreme described previously. However neat this solution may be, it is not
supported by Swift, and therefore unexpected errors may occur, if it is even possible.
The occurrences may be recorded within a 2D array, where the superarray has an index for
every day of the week (therefore 7 by default), whilst each subarray contains the times at
which the event appears. An example may be:
[
[]
[12pm, 1pm, 2pm, 3pm]
[]
[]
[5pm, 6pm]
[]
[9am, 4pm]
]
This represents an event occurring on Tuesday, between 12pm and 3pm, on Friday from
5pm-6pm, and on Sunday at both 9am and 4pm.
The number representation within the array will likely be in 24-hour format, and of integer
type. This will especially be the case where a tableview is used, and the hours selected
indicated by the indexes of the cells selected.
Another variable is required to hold the cell selected temporarily, as it is populated to the
array. The populating function can be completed quite easily with the use of a for loop.

Candidate Number - 4164 99 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
There are a number of tests to be completed on the selector.
The correct times must be updated to the event, meaning that times the user selected and
deselected are to be ignored. Upon submit, the times to be added are printed. This test can
combine with it a second test where, upon navigating back to the previous view and having
the event be updated with the new data, the event can subsequently be printed out to
ensure that the segue has worked correctly.
The final test for the entire event (upon every features’ completion) is to navigate between
the schedule and the event to check that the data has all been stored and then loaded
correctly.

Design and Development


Test plan:
Test Success Type Description Data Expected Outcome
No. Criteria
1 2.1.1 Functional Event will be created and checked that All inputs created to The event will show
it exists in the database. The event create an event. in the database.
must persist past application close.
2 2.1.1 Usability Ease of creating event tested by focus As the user chooses The user can enter
group all data needed and
save an event
without prompting
3 2.1.1 Sanity Strange inputs designed to break the Atypical string The Unicode should
functionality will be entered. characters be saved without
(foreign/emoji any issues
Unicode)
4 2.1.2 Functional Schedule populated and checked that Events All cells will be filled
Boundary all cells can be used to view all successfully
upcoming occurrences that week.
5 2.1.2 Invalid More event occurrences than can be Events The program
displayed will be added. should prevent the
excess from being
created.
6 2.2.1 Functional Event creation can be accessed from Events Navigation is
schedule screen. handled by the
schedule view
7 2.2.2 Functional Existing event in schedule can be Events Event changes
accessed and edited. updated to the
table upon edit
submission
8 2.2.3 Functional An existing event will be deleted from Events The event will no
the table. longer appear in
the schedule or
database.
9 2.2.3 Invalid Event that has not been created yet will Events The application will
be deleted. prevent the
deletion of an
event not in the
table.

Candidate Number - 4164 100 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
10 3.1 Usability Events displayed with title and colour. Events The event displays
Event created and observed. the selected colour
and title
11 3.2 Functional Extra information can be added about Events The event stores
an event. the input data,
which is re-
presented on event
reload
12 3.3.1 Functional Events appear more than once in a Events The schedule
week. Multiple occurrences will be recognisably
assigned to an event. displays an event at
multiple times
13 3.3.2 Functional Conflict handling tested by creating Events The application
overlapping events prevents the
creation of
overlapping events.

I expect to require the following class relations:

Candidate Number - 4164 101 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Schedule Framework
I have found a CocoaPod named SpreadsheetView
(https://cocoapods.org/pods/SpreadsheetView).
While not the first point I would have considered to handle the schedule, the examples
provided appear to me to be a very good way of displaying the schedule.
I quite like the look of the example given. I showed my focus
group, and they agreed that the presentation was very nice, and
most importantly, applicable to my project.
They also quite liked how the spreadsheet was scrollable, which is
an advantage for my implementation, as it means that it does not
matter what the user’s phone screen size may be – the
application will display well regardless, and can still be shown in
full, even if it does not all fit on one screen.
The responses were very much in line with what I had hoped.
They agreed that it would be a good structure to base my
application around. One member voiced his concerns that it
looked “like it’s for kids”, but I assured him that my
implementation would be far more colourful, to his displeasure.
I have installed this using the pods framework. As it has been built for Swift 3, some code
changes had to be made, however Xcode handled all this semi-automatically. I only had to
enable the changes required.
In any case, I will also import the framework for the example into my project. The cell setup,
for example, will be incredibly useful, as I will not have to generate the static columns and
rows myself.
I will lock them however, to ensure they scroll with the user. This is done using:
func frozenColumns(in spreadsheetView: SpreadsheetView) -> Int {
return 1
}

func frozenRows(in spreadsheetView: SpreadsheetView) -> Int {


return 2
}

The days and dates displayed at the top (2 rows) are frozen, along with the column for the
hours (1 column). This allows the user to scroll through, and where they may have left those
rows and columns behind, they are instead still attached to the screen edge and follow the
user, so they can always view them as a reference.
I wish to have the actual dates displayed, so I will add those to the top. This will replace the
hardcoded string already in place.
The first day of the week for a given week must be found. I am unable to do this, however
StackOverflow user Ram’s code (https://stackoverflow.com/questions/46402684/how-to-
get-start-and-end-of-the-week-in-swift) achieves this.

Candidate Number - 4164 102 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
extension Date { //get the date of monday or sunday
var startOfWeek: Date? {
let gregorian = Calendar(identifier: .gregorian)
guard let sunday = gregorian.date(from:
gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self))
else { return nil }
return gregorian.date(byAdding: .day, value: 1, to: sunday)
}

var endOfWeek: Date? {


let gregorian = Calendar(identifier: .gregorian)
guard let sunday = gregorian.date(from:
gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self))
else { return nil }
return gregorian.date(byAdding: .day, value: 7, to: sunday)
}
}
I will be keeping this in extension format so I may use it in any view I wish to load the
current day.
The extension returns the date of the Monday of that week. The following six dates will then
have to be populated to an array, where they can be procedurally displayed to the view as
the cells are populated.
Before the dates can be placed in an array, they will have to be formatted. William Hudson’s
2009 study showed that the d/m/yy format was the most popular numerical format
(http://www.syntagm.co.uk/design/datesstudy.htm).
While this accounted for roughly 15% of the 518 person sample, this is the value I will go
with. The most popular formats were in month/day/year, however this is due to an
American sample, and will be ignored as my application will largely be focused on a UK
demographic, where d/m/y is used. I also will not be using the most popular format for
those that prefer d/m/y (which is d/month/yy), because there is a chance the text will not fit
within the cell. The d/m/yy format is popular, and easily understandable, whilst also not
facing the potential issue that d/month/yy does.
With the date format set, the next step is to retrieve the start date, to be displayed with
Monday. This is done by:
let monday = Date().startOfWeek

This makes use of the startOfWeek function immediately. This is then converted to a string
using:
let mondayString = formatter.string(from: monday!)

and finally appended to the array for the week.


dates.append(mondayString)

A function is then to be created to populate the rest of the date to the array.

Candidate Number - 4164 103 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

In Swift, whole function appears as:


(Note: adding 1 to a date requires use of date(byAdding) function, with a parameters of the
value to add and the date to add to)
formatter.dateFormat = "dd/MM/yyyy"
let monday = Date().startOfWeek
let mondayString = formatter.string(from: monday!)
dates.append(mondayString)

var currentDate = monday


for _ in 0...6{
let nextDate = Calendar.current.date(byAdding: .day, value: 1, to: currentDate!)
let dateString = formatter.string(from: nextDate!)
currentDate = nextDate
dates.append(dateString)
}

To test, I will run the code. I will print monday and mondayString. The two should match in
date, while mondayString should be formatted, and each should be equivalent to the date
of the last Monday.
The array result will be printed, and should appear formatted. The first value should equal
mondayString, while the subsequent values should all be an increment of one to the value
of the first item in the array.

Candidate Number - 4164 104 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
As I having populated the new dates to the array used to populate the cells already, no
further modifications are necessary, as the view already has the necessary code to populate
the dates.

Event Framework
I will begin with repeating event frameworking, as it is the basis of the application. I will
focus on single events afterwards.
The event data model is created in the realm with the following parameters:

 Name - string
 Colour – string/colour type
 Occurrences - array
 Description - string
 Priority - integer
 ID – string

Name – string
Colour – string/colour type
Occurrences – array
Description – string
Priority – integer
ID – string

I will set the ID as the title. The name can be set as anything the user may wish. With that
comes the obvious issue of two events with the same name, and being able to differentiate
them. By setting the ID as the name, and using the ID as the way of pulling and updating
events, the user is restricted to naming only one set of events a particular name. (This only
matches the exact string. For example, “ABc” is not equivalent to “abc”.)
By using the name as the ID, conflict handling is instantly managed. Should the user type the
name of an existing event, the application will treat the event being created as an edit of the
existing event. The user may in fact wish to use the add event button as a way of editing
events (for example, they consider adding a separate event, but decide instead to merge it
with an existing event; all they must do is type the name of the existing event and make the
changes they wish).
With the model created, I will now work on the view and view controller.
The view is created with a title label, a text view for extra information, a switch to decide
whether the event is repeating or not, and a priority picker. The buttons are then setup – a
button to set notifications and alarms, a button to choose the colour, a button to choose the

Candidate Number - 4164 105 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
occurrences, and finally a save button. The back button is automatically created by the
navigation controller. A method of deleting an event will have to be included, likely within
this view (unlike with notes).
These items are then all linked up to the view controller. Each is linked up as an outlet, while
the buttons, picker, and switch also have functions created.
Variables must also be included to hold the data that will be sent about the occurrence
picker and colour picker, however these will remain in the plan for the time being, as my
implementation may end up with a different data structure return.
Variables for the single and repeating events are included. They will be repeatingEvent and
singleEvent, each of which is an instance of their respective data class.

Candidate Number - 4164 106 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

//MARK: Variables
var repeatingEvent = RepeatingEvent()
var singleEvent = SingleEvent()

//Sent from Schedule


var eventName: String!

//Recieved variable, included here as reference


var occurrences: occurrences type
var colour: colour type

@IBOutlet weak var switchLabel: UILabel!


@IBOutlet weak var priorityLabel: UILabel!

@IBOutlet weak var nameLabel: UITextField!


@IBOutlet weak var descriptionLabel: UITextView!

@IBOutlet weak var repeatSwitch: UISwitch!


@IBOutlet weak var priorityPicker: UIPickerView!

@IBOutlet weak var colourPickerButton: UIButton!


@IBOutlet weak var occurenceButton: UIButton!
@IBOutlet weak var reminderButton: UIButton!

@IBOutlet weak var submitButton: UIButton!


@IBOutlet weak var deleteButton: UIButton!

The methods below have been created to handle the display elements of the view.
Currently, a simple description of their use has been included. The methods lay out the
structure for the rest of the view.
@IBAction func occurenceButtonPressed(_ sender: UIButton){
Navigate to occurrences view
}

@IBAction func colourPickerButtonPressed(_ sender: UIButton){


As above
}

@IBAction func submitButtonPressed(_ sender: UIButton) {


Collect data
Create event
Update to Realm
Return to Schedule
}

@IBAction func deleteButtonPressed(_ sender: UIButton) {


Ask for confirmation
Delete event from Realm
Return to Schedule
}

//MARK: Text Setup. The function below carries out the same placeholder functionality
implemented within the Note view. It has simply been adapted for this view
func textViewDidBeginEditing(_ textView: UITextView) {
if (textView.text == "Extra Information"){
textView.text = ""
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if (textField.text == "Event Name"){
textField.text = ""
}
}
}

Candidate Number - 4164 107 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
I will carry over the methodology used when loading notes. The schedule will send over the
primary key of an event so it may be identified, and this then pulls the object from the
database.
The Event view controller will also send data to the Occurrences and Colour Picker views (in
the case of a repeating event being modified). The setup will be similar to how Event loads
data, where if the variable is empty, then a new event is being added, and the view is loaded
empty. Otherwise, the view is loaded with data.
The basic setup of the segues is as follows (for both the Schedule and Event view
controllers):

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

if segue.identifier == "editEventSegue"{
let destinationVC = segue.destination as! EventVC
destinationVC.eventName = name //send event name
}

Data is only sent over when an event is being edited (and therefore the edit segue triggers).
name is the variable to hold the primary key of the event. Whether this will in fact be the
title of the event is to be decided.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

if segue.identifier == "occurenceSegue"{
//send over occurence data
let destinationVC = segue.destination as! OccurencesTVC
destinationVC.occurences = occurences
}
else if segue.identifier == "colourPickerSegue"{
let destinationVC = segue.destination as! ColourPickerViewController
destinationVC.colour = colour
}
}

Of course, this is just framework setup. The data to be sent has not be established yet. A
default value will be set so the program works while the variables are in their prototype
stages.
Again as with notes, the same view will be used when creating a new event or editing an
existing one. There is some complexity added however, as the edit event screen may change
quite a bit depending on the user’s choices.
First of all, when adding a new event, no event identifier can be sent (as there is no event).
This means that the identifier (which will likely be a string), will be sent as empty.
If the event view receives an empty identifier, then it knows that a new event is being
added. It will then carry out the relevant setup to present the correct view to the user.

Candidate Number - 4164 108 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The flow chart below will show the intended plan for setup.

As can be seen, the steps a fairly similar. This makes sense, as adding and editing an event
both work on the same fields. However, the crucial difference is how data loads. In the case
of a new event, there is no data to load; therefore, nothing should load into the fields
besides the placeholder text.
The two views will also have some cosmetic and functional differences. The delete button,
for example, will not show for new events, as there is nothing to delete.

Candidate Number - 4164 109 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
I will convert the flowchart shown above into Swift pseudocode. The fine details will be
ironed out come actual implementation.

if eventName == "" {
interaction = true //enable interaction with all features
deleteButton.isHidden = true //hide the delete button
occurenceButton.text = "Add Occurences (Day/Time)”
submitButton.text = "Create Event"
}
else{
repeatSwitch.isHidden = true //cannot change event from single to repeating
switchLabel.isHidden = true

rightBarButtonItem = editButton //provide edit button for user

repeatingEvent = get object from Realm with key: eventName

nameLabel.text = repeatingEvent.name //set data


descriptionLabel.text = repeatingEvent.desc
occurenceButton.text = "Edit Occurences (Day/Time)"
priorityPicker.selected = repeatingEvent.priority //holds the index to display
submitButton.text = "Update Event"

The basic framework for the view is created. The else statement in fact must be split in two,
as there are two possibilities for when an event is to be displayed: the event is repeating, or
the event occurs once. For either of these cases, the basic display of the view is the same.
The differences lie in the appearance of the event in the schedule, the underlying code, and
how the occurrences picker will work.

Colour Picker
The user can navigate to the colour picker from the event view by tapping the colour picker
button.
The button takes them to the currently empty view.
I have decided to make user of MrMatthias’ Swift Color Picker
(https://github.com/MrMatthias/SwiftColorPicker) to achieve the colour picker.
The installation is relatively simple. The files are imported to the project, which includes a
view controller setup class.
I will simply make my modifications to this class regarding my implementation of the colour
picker.
I have also added some of my own variables.
colour (optional string) stores the colour sent from the last view. In the case of a new event,
no colour can be sent.
As in that case it will be empty, a simple check can be made as to what colour to display.
if colour == nil{
pickerController?.color = UIColor.red
}
else{

Candidate Number - 4164 110 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
rest of setup
}

I have also added a submit button to the view, as the user will have to navigate back to the
previous view.
It will send the colour generated by the picker (currently represented as a string in “colour”),
while popping itself from the navigation stack.
The stack is obtained with let stack = self.navigationController?.viewControllers

The previous view is then found, and cast as the view it is: let previousView =
stack![stack!.count - 2] as! EventVC

The colour selected is sent to the previous. This is accessing the same variable used to send
the colour to the colour picker. previousView.colour = colour
Finally, the view is popped. self.navigationController?.popViewController(animated: true)

This is tested by performing the actions. In my tests, this worked correctly. However, as no
colour is being sent, the colour sent and received is always nil.
With the colour picker installed, the user may select a colour by tapping a colour or dragging
through the colours. The display at the top is very small however, and also does not
represent how events will be displayed within the schedule.
Event display within the schedule is an important consideration to make. The plan is to have
the cell of the event coloured as the colour the user picks. The default text colour is black. If
the user picks a dark colour however, they may have difficulty distinguishing the text from
the rest of the cell. A possibility to interpret the colours exists, where if the values are below
a certain value, the text colour is changed. However, this will require extensive testing to
find the right colour at which to change the text colour, and may not even be possibly to
carry out if the colour to change the text at is different per hue. Furthermore, what I
consider an appropriate value to make the text change may not be the same as any of my
potential users. This is not a viable route to take due to the large uncertainty associated
with the success of the method.
Alternatively, I could display events where the text colour is the colour selected, and the
background a faded version of that same colour. This allows all dark colours, but introduces
issues where the colour is near white. The range provided by this method is greater
(assuming that the unsupported colours or prevented from use), and is far simpler to
implement. Changing the text to black for light colours carries the same issues as the
method described above. Instead, I will display, within the colour picker view, a label, which
will be colours exactly as the cells in events may be.
As the user selects colours, the label will be updated. The text will be the hexadecimal string
(which has the advantage of allowing the user to use a hex string as a reference for a colour
they may wish to select), while the background is the colour, but faded. This way, the user
sees how the event will be displayed immediately. While light colours will not be restricted
from selection, the user will be able to see themselves it is impractical. However, they may
select it if they wish.

Candidate Number - 4164 111 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The label will look like the following (made in Adobe Illustrator):

As can be seen with the last option, the text is very faint. The user is made aware of this as
they can see the example before they submit their selection.
Before this function can be implemented, the UIColor must be converted into hexadecimal
for display. In addditon, UIColor must be converted into a compatible form to save the
Realm, which could be by using a hexadecimal string.
The translation must work in two ways, UIColor to hexadecimal, and hexadecimal to
UIColor.

UIColor takes red, green, blue and alpha values. The values taken are between 0 and 1,
where the greater the number, the greater the saturation of that colour. Hexadecimal
colours are represented by a 6 digit hexadecimal number, where each two subsequent digits
represents red, green and then blue. There is no alpha representation, however I do not
believe this is an issue as I have no intention of using the alpha value.

Candidate Number - 4164 112 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
For the conversion to work, the values of each type need to be convertible into each other.
UIColor values are between 0 and 1, and 2 digit hexadecimal numbers represent between 0
and 255 (256 numbers being representable by two base-sixteen numbers).
This conversion can be done relatively easily mathematically. However, techniques exist
within the framework of Swift where, programmatically, this task can be completed easily.
Swift handles converting to integers well. What is left is the shift from 0-1 to 0-255, which is
easily done with a multiplier of 255 on the decimal value. This returns a value that can be
converted into hexadecimal from there.
let r be a value between 0 and 1

hexadecimal integer = Int(r * 255)

For a specific example, let us give r an actual value.


r = 0.235
r * 255 produces 59.925. Converted into an integer, this returns 59.

59 in hexadecimal is 3B. To conversion will be into a string, which can be done using a
formatter while performing a string conversion.
The formatter applies with the string conversion. The string function takes two arguments,
the format and the number. The number is input as above, using the one line integer
conversion. The number is converted to a hexadecimal string using the format “%X”.
However, this requires padding, in case the value does not have two digits (16/F or less).
This is because every colour is represented by 2 digits in hexadecimal, and any number less
than 16/F must contain padding. This is supported within the format. To do so, two things
are required: the padding, and the digit length required. In my case, I wish the number to
remain unchanged, so the padding will equal nothing, and is therefore represented by 0. As
each number has to be two digits, this is the value to enter.
The format is finalised as “%02X”.

Candidate Number - 4164 113 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
As can be seen above, r is successfully converted into a two digit hex number.
The next step is to combine this for each of red, green and blue.
The function can take multiple parameters, so they can be processed all at once. The only
modification required (besides the additional variables), is a change to the format, where it
must be expanded to accommodate the additional two numbers. This is easily done as the
format is as it is, but subsequently repeated twice within the quotation marks.

var r: Double = 0
var g: Double = 0
var b: Double = 0

String(
format: "%02X%02X%02X",
Int(r * 255),
Int(g * 255),
Int(b * 255)
)

Now, the values of r, g and b have to be obtained from the UIColor object.

As can be seen, UIColor takes CGFloat values, so I will change my variable types from
Doubles to CGFloats. The differences are minor for my use, so that change is
inconsequential.
Imagining that “col” is the UIColor type returned from the colour selection, the values of
red, green, and blue must be extracted.
This can be done using the “getRed” function, which returns CGFloats per colour value
(including alpha). I will pass the variables in as a reference, since they have been prefined.
The alpha value is also returned, which will be stored. This is in case I wish to use it in the
future.
var a: CGFloat = 0

col.getRed(&r, green: &g, blue: &b, alpha: &a)

Candidate Number - 4164 114 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

To finally implement this to operate on UIColor types, I will create an extension, so it may
apply wherever.
This requires some tweaking of the syntax, but the final code is as follows:
extension UIColor {
var toHexString: String {
var r: CGFloat = 0
var g: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0

self.getRed(&r, green: &g, blue: &b, alpha: &a)

return String(
format: "%02X%02X%02X",
Int(r * 255),
Int(g * 255),
Int(b * 255)
)
}
}

Candidate Number - 4164 115 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
For any given UIColor, this should output the corresponding hexadecimal value. I will test
this on default UIColor values, to which the hexadecimal values are known, and therefore
can be compared to the output of my procedure.

Colour Expected Hex Returned Passed?


Value
White FFFFFF Yes

Black 000000 Yes

Red FF0000 Yes

Green 00FF00 Yes

Blue 0000FF Yes

Cyan 00FFFF Yes

Yellow FFFF00 Yes

Magenta FF00FF Yes

The tests have all passed. It can be assumed that if the default colours are being displayed
correctly, then the hex output is correct for all. It is also helpful that the simulator shows the
colour being described.
The conversion must also be carried out in reverse, so a hex colour may be used by Swift.
The hex colour can be stored in Realm as a string, however the views where the colour is
displayed cannot use this, hence the need for the convertor working in reverse.
Unfortunately, the conversion in reverse is simply a case of doing the same steps as before
in reverse, as will be detailed.
To begin with, I will also make this feature an extension to UIColor. That way, the convertor
can be called anywhere, as with the convertor above.

Candidate Number - 4164 116 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
As this operation returns a UIColor, the call syntax is slightly different. Rather than having
the colour and being a method to call (like with UIColor -> Hex [colour.toHexString]), the hex
value is to be passed into the UIColor function and return a UIColor object.
The structure I intend to use is UIColor(hexString), which will be directly treated as a UIColor
object.
For this, the extension syntax is slightly different.
A convenience initialiser allows a custom setup within the class. In this case, it is UIColor,
where I wish to carry out code to return a UIColor object.
All the code will take place within the initialiser.
extension UIColor {
convenience init(hex: String) {
...
}

var toHexString: String {


...
}

}
The hex string passed through is referred to as hex within the function (also, as the function
operates on strings only, this has been specified as the object type of the passed variable).
With the setup complete, I will now plan the algorithm.
The steps are as follows:
1. Convert the string into a hexadecimal number
2. Extract the red, green, and blue values
3. Convert into the right format (CGFloat, 0-1)
4. Populate values to the default function
The first step is conversion. This can be done using a scanner, a very powerful tool available
within Swift to convert string objects into numbers of any sort. While it does not do straight
conversions to CGFloats, this is no matter as the hexadecimal number needs to be split into
its constituents.
Regarding splitting the number, I have decided not to simply split the string into every two
characters. The reason is that this approach does not scale well should I change my data
structures or usage.
By making a number conversion first, and then retrieving the numbers through
mathematical operations will allow for better scalability, as will be seen in the usage below.
Before that however, the number conversion is carried out. A variable (scanner) will be set
as the string (hex) passed into the Scanner function.
let scanner = Scanner(string: hex)

Candidate Number - 4164 117 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The scan location is set as 0, so scanning begins at the start of the string. Furthermore, no
skipping characters need be specified, as the string contains only the relevant digits.

A variable to hold the result of the scan is then initialised. This will be a hexadecimal integer,
however as it is 6 digits (potentially representing up to 16777215), this will be a UInt64
(allowing 64 bit representation of the integer so no data is lost). This is set to zero upon
initialisation.

var rgbValue: UInt64 = 0

The scanner will then operate on the string, scanning for a hexadecimal integer in long
presentation, and, using rgbValue as a reference, will return the string as an integer in
hexadecimal representation.

scanner.scanHexInt64(&rgbValue)

This function should, so far, take any hexadecimal string, and return a hexadecimal integer.
I will test to see that a given hexadecimal number is returned as an integer.

The value is converted to an integer when printed, however otherwise, this is correct.

The extraction of the red, green and blue values can be done quite cleverly using a bitmask
and a bitshift.

First, for each variable, a bitmask is performed. The bitmask is used to cover over the
irrelevant values. In the case of red, for example, only the first two digits are wanted. A
Boolean AND operation is performed against the number FF0000, which removes the green
and blue values. Likewise, the mask is FF00 [00FF00] and FF [0000FF] for green and blue
respectively. This leaves the two digits of the colour to be extracted.
In the case of red and blue, although the colour has been extracted, there is the issue that
the number remains very large. Each representation is on a scale of 0 – 255. Given pure red
(FF0000), with the bitmask performed, the result is FF0000. This is equivalent to 16711680.
This, of course, is not ideal. To reduce the value back down, it can simply be shifted by 16
bits. This shifts the bits into 8-bit representation. The lost bits do not matter, as they have
already been masked to 0. Another way of writing 8-bit binary is 2-digit hexadecimal.
Therefore, the result of applying the shift and mask on the colour results in red being given

Candidate Number - 4164 118 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
as a two digit hexadecimal number. This is precisely what I have been aiming for, as the
remainder of the function is trivial (i.e. divide by 255 and input into the UIColor function).
The shifts on the other two colours are slightly different. Blue only needs to be shifted down
eight places, while green requires no shifting at all as it is already in the smallest bit place.
To test, I will enter the same two digits repeated. The result for each colour should be the
same, as the same two digits are being operated on. This demonstrates that the function is
performing the shifts and masks correctly.

As can be seen, 22 is returned for each value. This is correct, especially considering that 16
in hexadecimal equals 22 in denary (16 + 6).
The final step is to input these values into the UIColor function. The only thing to be aware
of is that it takes an alpha value, which I will have to supply. As I am not using any
transparency with my colours, I will force a permanent value on the alpha component (1).
The call is simply self.init(r, g, b, a), as it is within the extension of the UIColor object. At the
end of all the converting, the final step is essentially the same as performing the conversion
and split separately, and then entering the values into the object manually. This way
however allows just the string of the hex number to be passed in, with a result of a UIColor
object.
self.init(
red: CGFloat(r) / 0xff,
green: CGFloat(g) / 0xff,
blue: CGFloat(b) / 0xff,
alpha: 1
)
Of course, hexadecimal numbers cannot be passed in directly, meaning that the values must
be divided by 255 (represented in hexadecimal here, allowing the division operation to be
on the same types of number.
Here is the final function:
extension UIColor {
convenience init(hex: String) {
let scanner = Scanner(string: hex)
scanner.scanLocation = 0

var rgbValue: UInt64 = 0

scanner.scanHexInt64(&rgbValue)

Candidate Number - 4164 119 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
let r = (rgbValue & 0xff0000) >> 16
let g = (rgbValue & 0xff00) >> 8
let b = rgbValue & 0xff

self.init(
red: CGFloat(r) / 0xff,
green: CGFloat(g) / 0xff,
blue: CGFloat(b) / 0xff,
alpha: 1
)
}
}

To test, the extension will be used to convert the hexadecimal values obtained in the
UIColor -> Hex test to convert back into UIColors. The values should match up.

Colour Input Hex Expected Colour Returned Passed?


Return
White FFFFFF Yes

Black 000000 Yes

Red FF0000 Yes

Green 00FF00 Yes

Blue 0000FF Yes

Cyan 00FFFF Yes

Yellow FFFF00 Yes

Magenta FF00FF Yes

With the conversions complete, the data can now be handled in and between views, and
updated to the database.
The first point I will handle is displaying the colour to the label.
A simple label has been added to the view, with some minor modifications. First of all, it has
been constrained to the colour picker, so it moves with it if the screen size changes. The size
of the label has also been increased, which is because it has to accommodate text of a very
large font size.
The label is referenced by an outlet called label.
Within the function called whenever the colour is changed (pickerController?.onColorChange
= {(color, finished) in ...}), I will place code to push data to the label.

Candidate Number - 4164 120 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
color here represents the colour picked as a UIColor object. The following steps will be taken
every time the colour is changed.
Convert color into hexadecimal and store in “colour”.
Change the label’s text to match the hex string stored in colour.
Change the label’s text colour to the selected colour (color).
Change the label’s background colour as the selected colour, with an alpha component of .2
applied.
As this is applying to the view controller as a whole, each of these needs to reference the
controller class with “self”.
This ends up looking like this:
pickerController?.onColorChange = {(color, finished) in
self.colour = color.toHexString
self.label.text = self.colour
self.label.backgroundColor = color.withAlphaComponent(0.2)
self.label.textColor = color
}

To test that the output was correct, I looked up the hexdecimal value output in a colour
finder, and then visually compared the shades shown. I believe the matches were identical.
With the selector and its display to the user completed, the next step is to handle segues.
This includes incoming and outgoing segues.
The framework for the segue is already in place. All that is required is specificity. The segue
will be sending relevant data back.
In addition, the setup function needs to be finalised, so an existing event’s colour is already
selected.
In the case of sending data back, the function is in fact completed. As the variable “colour” is
set with the hexadecimal string whenever the colour is changed, no further work is needed,
as the string is already sent back to the previous view.
Regarding incoming colours – in the case that no colour is sent (new event), then the label is
set to be the default colour, which is currently set to red. As with when the colour is
changed, the label text and background colours are set (on the same framework, but with
UIColor.red). The text is set to FF0000, the hexadecimal representation of red.
Where a colour is received, pickerController.color is set as UIColor(hex: colour), as this
returns the UIColor value for whatever the colour may be. The same setup on the label then
applies.
With all this in place, colour should be saveable now, as in the user may select a colour,
return to the previous view, and still be able to come back and view the colour change they
made.
This process will be demonstrated in the following test:

Candidate Number - 4164 121 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
A new event is to be added. The colour picker is navigated to, and a colour selected. This is
submitted. The colour picker is then navigated to again, and should display the colour
picked initially. This colour is changed, and the two steps repeated. The new colour should
display now.
The integrity of the colour can by checked by its hexadecimal number. This should remain
unchanged.

Initial colour selected and saved. As it displayed correctly, a second is picked. This also
displayed correctly.
The view is working correctly.
Evaluation: This view had some difficulties to implement, especially when it came to making
the initial decision as to how to make the view.
However, the colour picker Pod found was incredibly useful. It was also incredibly
convenient that it was precisely what I was looking for. The colour picking is incredibly
intuitive – it consists of only sliding the finger to select the colour desired.
I am also happy with how the label turned out, as it shows the user how the event will
appear very effectively.
I also thought that the hexadecimal convertor was very nicely implemented. It makes good
use of some unique functions within Swift, and performs a bit of impressive backend work.
Focus group: They were very impressed with how this view turned out. They spent a not-
insignificant period of time playing around with the picker, because it is quite an interesting
tool to work with. One user commented that if one slides through the colours quickly
enough, a visible “bumping” can be seen with the text on the label.
This is due to the text changing quickly as the hexadecimal is shown. The “bumping” comes
from the text not being monospaced. As the characters change, so does the spacing, and
therefore width of the text on the label, which gives rise to the moving effect.
This issue can be solved by using a monospaced typeface, which I may consider. This does
not heavily detract from the user experience however, and I also feel the font I have already
selected it quite appropriate.

Candidate Number - 4164 122 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
A couple user asked whether a space to enter a hexadecimal number would be included,
and were quite surprised when I told them I had no intention to. The reason for not doing
so, as stated previously, is that the feature is unnecessary.
The colour picker currently in place is more than capable of supplying all the function
needed. A hex input, while a nice addition, simply adds unnecessary features to the
application.
I intend to keep the application as light as possible.

Occurrences
The Occurrences view is important for the user to pick the times at which their event
appears within the Schedule.
As discussed previously, some approaches for this will not work very well. Raw input, for
example, will not be included, nor will multiple picker views.
The best solution I considered was using a tableview with multiple selectable cells. Each cell
appears within a section, representing a day.
The main features of this are:

 The sections (days)


 The times within a section (range of hours that can be selected)
 Highlighting a cell as a selection choice
 Submitting the selection
The overall structure should not be all too difficult. It is, after all, just a large tableview. The
issue lies with the additional features. Finding selected cells for instance, or collapsing the
sections that are unnecessary (while retaining the selection) are two issues I expect will be
difficult to resolve.

Candidate Number - 4164 123 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Setup:
The view controller is created as a manager of a tableview. This populates it with default
tableview controlling methods.
I will need the following variables:

Variable Type Use


hours Array of strings Stores the hours to be
displayed to the sections
occurrences 2D array of integers Stores the days selected. (I
imagine that each sub array
is the hours selected, and
the index of the superarray
is the day)

Constant Type Use


days Array of strings Static, contains the names
of the days of the week in
order

hours will be used to populate each section. For the time being, it will be hardcoded in,
however once the times that can be selected is variable, so will this variable become truly
so.
occurrences will be generated from the selected cells within the sections. Given my
understanding of how indexPath works, each section has an index, and each cell has a
second index within that. That means that a selected cell has two location paths – the
section it resides in, and its own index.
The section pointer indicates the day (i.e. section 0 corresponds to Monday). This is easy to
work with. The difficulty comes with the index of the cell. Say the selected range is 9am to
12pm. The hours 9 and 10 are selected. This is indicated by index 0 and 1. This does not
intuitively line up. While I could work around it, for my own ease of use will introduce a
constant named timeDifference.
This will simply be the difference between the index and the first time (in this case, 9). In
fact, regardless of the first hour of the range, the difference between the hour and the value
of the first index is always going to be equal to the value of the hour, as the first index is
always 0.
A better name for the constant may be startTime. Regardless, what the constant allows is
for me to make sense of the values being worked with. Where the range could change, it is
not helpful when testing to be returned indexes that do not match up with the hours
displayed.
Therefore, within the viewcontroller, I will handle all the hours as their index +
timeDifference. This allows me to work hours that match up with the display.

Candidate Number - 4164 124 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
This is important because it is far easier to make sense of [8, 12, 13, 14, 18] when I know the
hours match up, as opposed to [0, 4, 5, 6, 10]. With the former, I know instantly this
matches up to 8am, 12pm, 1pm, 2pm, and 6pm.
The array for the days is setup as [MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY,
SATURDAY, SUNDAY]. The length of this is used to populate the numberOfSections method.
I have not gone with typing 7 in because I may add a setting to control the number of days
displayed (as I will do with the range of hours).
Similarly, numberOfRows in section will return hours.count.
To enable multiple cell selection, only two lines need be added:
self.tableView.allowsMultipleSelection = true

self.tableView.allowsMultipleSelectionDuringEditing = true

This allows a UI checkmark and the cell to dark when selected. The specifics of this are
defined within the custom cell class.
This is created with the default functions and embedded so the cells are instances of it.
The cell class includes:
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
}
This make the cells reusable (for procedural generation), and ensure they are not generated
pre-selected.
And:
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
self.accessoryType = selected ? .checkmark : .none
}
This controls the selection of cells, where they can be selected and deselected, and upon
selected, display a checkmark.
The cell class also controls an outlet for a label within the cell. This outlet will simply be used
to write text to the label.
As the cells are generated per section by the tableview controller, they will be passed the
hour they are to represent. With all the hours within the hours array, the cell label will equal
the item at the index indexPath.row. This means that cell 0 is given the value at hours[0],
and cell 1 given hours[1] and so forth to the last cell.
This ends most of the setup necessary.
The tableview has been populated with some hours, and cells can be selected and
deselected. I have also included a submit button which has been linked up to the view
controller with an onPress function. This will work much the same way as the submit button
for the Picker View.

Candidate Number - 4164 125 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Priority Picker
The priority picker will allow the user to define the priority of an event, which dictates how
it appears on the agenda screen.
The priorities will be “urgent”, “important”, and “normal”. The pickerview is used so the
user may choose between these choices when creating (or modifying) an event. The
pickerview is ideal, as it only supplies the options, and forces at least one option to be
selected. This is important, as it ensure that I do not need to perform any further validation
on the output of the pickerview, with the obvious requirement that it is working correctly.
The pickerview can be split into multiple pickers and sections within each. I will only need
one picker, and three sections for the three priorities. The sections are represented by an
index value, with the topmost index (the one shown by default unless set otherwise) is 0,
which successive indexes increasing.
The picker size (set within the view controller) will be the length of an array containing the
priorities (as strings). This way, should I wish to add more options, I can do so easily with a
simple addition to the array, without any further changes (besides how the event may
appear in the agenda).
An array of priorities is implemented as a constant:
let priorities = [“Normal”, “Important”, “Urgent”]
The order of priorities has been flipped, so now array element 0 corresponds with the
zeroth element of the picker.
The default option is therefore set as “Normal”. Unless the user makes a change, this will be
selected.
To test, I have opened the view and checked that the options display correctly (they do). I
have then added code in the submit function to print the selected value of the picker. Each
item outputs correctly, and therefore requires no further tweaking. The selected element
can be accessed when an event is being created now.

Event Submission
Event submission is handled when the user taps the submit button. The submit button will
change its appearance whether the event is being edited or added as a new event (however
this will be handled at a later stage).
I will create default alert to handle the case where the data the user enters is invalid. As I
am only using repeating events for the time being, I will not perform any validation on the
event type or anything like that. Instead, validation will simply be performed to check that
the user has input data into the correct fields, and that enough data exists to create an
event.

Candidate Number - 4164 126 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The variables required to create an event are:

 Name
 Colour
 Occurrences
Furthermore, to verify whether the checks have been passed, a variable name valid
(Boolean) will hold whether the tests have passed or not. If valid is equal to true, then the
validation has been a success.
While more stringent validation checks can be performed on the event, checks against these
three perform the least amount necessary to ensure that an event can display correctly, and
can be interacted with.
The validation checks will be to see whether the variables shown are in their default state,
and if any of them are, then the data will be rejected. The user will have to be informed of
the invalid data. This can be done using an alert, which at the very least informs them that
there is some invalid data. To add to that, the messages could be specified depending on
the specific form of invalid data.
let defaultAlert = UIAlertController(title: "Info", message: "", preferredStyle: .alert)

defaultAlert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))

As this is only setup, the message is left empty, and no actions have been added to the alert.
The validation will work like this:

Candidate Number - 4164 127 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
I will perform a very simple Boolean validation check, which in Swift appears like this:
if (occurences == [[], [], [], [], [], [], []]) || (nameLabel.text == "" || nameLabel.text
== "Event Name") || (colour == nil){

valid = false

} else {valid = true}

This simply checks whether the data entered is empty, or in its default state.
The write to Realm statement will then occur within an if statement that checks whether
valid equals true or not. If it is true, then the event is created and submitted to the Realm.
Otherwise, an alert is presented to the user, like so:

if valid == true{
let successAlert = UIAlertController(title: "Info", message: "Schedule Updated", preferredStyle: .alert)
successAlert.addAction(UIAlertAction(title: "Return", style: .default, handler: { action in
self.navigationController?.popViewController(animated: true)
}))
Perform Realm update
self.present(successAlert, animated: true)
}
else{
defaultAlert.title = "Info"
defaultAlert.message = "Some fields may have invalid data \n Please try again"
self.present(defaultAlert, animated: true)
}
As can be seen, if the event is invalid (valid =/= true), the alert is presented with an error
message. Nothing else is done, as the view waits for the (hopefully) changed input.
If the data read is valid, then a different alert is presented, success alert. This contains one
button (labelled “return”) which essentially forces the user to return to the previous view
where they will find their newly created/modified event. The return is done by simply
popping the view from the navigation stack.
To test this, invalid data will be entered (no title, no colour, no occurrences), and the submit
button pressed. The alert should pop up with the message shown above, and no further
actions taken.
To test that the checks pass, valid data will be entered. Where I have written “Perform
Realm update”, the variables holding the valid data will be printed. If the data is printed,
then the checks passed. The printed data will be compared to the entered data.
Furthermore, the application should have moved back to the schedule at this point, even if
events are not being created yet.
For an event to be added to the Realm, its occurrences must be reinterpreted. Realm, to my
surprise, does not support native arrays. Rather, it uses its own custom type. This means I
must adapt my received data to this data model.

Candidate Number - 4164 128 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
To create a 2D array in Realm, a custom array of every subarray must be created, which are
all then appended to the custom superarray. Only then can it be submitted.
The superarray contains seven subarrays, which represent the days of the week. The times
in a given day must be extracted and then pushed to a custom array. Once these custom
arrays have been created, the superarray can be created from these.
Realm arrays cannot even take default types, they only take Realm objects. Therefore, an
object has been created to represent an hour, named Hour for obvious reasons. This object
stores an integer variable, hourItem.

class Hour: Object {


@objc dynamic var hourItem = Int()
}

A day object is then created for Realm’s custom array type. The object Day has a constant
dayItem, which takes a List of Hours.

class Day: Object {


let dayItem = List<Hour>()
}
Finally, a list of dayItems can be added to the event data model.
With the data models created, the population of the arrays can begin. Here is a simple
design of how that may work:
func dayList(subarray){

initialise Day object

for item in subarray{

create hour object

add to day list

return day

To then create an hour, I have created the following design.


hour(val){

init hour object

hour.item = val

return hour

The code for a list for a day is created:

Candidate Number - 4164 129 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
func timesToDay(times: [Int]) -> Day{
let newDay = Day()
for item in times{
let hour = hoursToTime(hour: item)
newDay.dayItem.append(hour)
}
return newDay
}
This uses the following to create hours:
func hoursToTime(hour: Int) -> Hour{
let new = Hour()
new.hourItem = hour
return new
}
Finally, the update within event creation will look like this:
for day in week{
event.week.append(timesToDay(times: day))
}
The 2D array should finally have been created.
I will test this by sending the supermethod various 2D arrays, and check that the output is as
expected.

Input Expected Output Pass?


[[],[],[]] (empty) Empty list Empty Yes
[[1],[2],[3]] Realm type, three As expected Yes
elements each, of
the values
[[1,2,3],[1,2,3],[1,2,3]] Realm type, three As expected Yes
elements each, of
the values
No further action is needed on this feature.
The next step is performing the Realm update. Given how the primary key works with
Realm, new and modified events must be updated slightly differently.
With a new event, the primary key will be defined, whereas with an existing event, the
primary key is known and used to update the event data. Smart processing of the data could
be performed, where only the modified parameters are updated, however this is
unnecessary given how small the event is in size. There is likely little benefit to adding smart
updating systems.

Candidate Number - 4164 130 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

I will separate these two operations out into separate update methods that will be
called within the Realm write statement.

func createEvent(name: String, colour: String, week: [[Int]],


description: String, priority: Int) -> RepeatingEvent{
let event = RepeatingEvent()

event.name = name //add all parameters


event.colour = colour
event.desc = description
event.priority = priority

event.id = name

for day in week{


event.week.append(timesToDay(times: day))
}

return event
}

func modifyEvent(event: RepeatingEvent, name: String, colour: String, week:


[[Int]], description: String, priority: Int) -> RepeatingEvent{

event.name = name

Candidate Number - 4164 131 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
event.colour = colour
event.desc = description
event.priority = priority

event.week.removeAll()

for day in week{


event.week.append(timesToDay(times: day))
}
return event
}

The two functions are then called within the write statement, which adds the event to the
Realm database.
To test, I will be checking whether the events have submitted successfully. This can be done
by printing the entire object database and checking the object is in there correctly.
With new events, the event only needs to appear there. However, in the case of a modified
event, the old data must be compared to the output, which should only match the new data
(or, any data that was modified). If the data modifications made match the output data,
then the test was passed successfully.

Event Deletion
I will include the methods to delete an event here.
The delete itself is rather easy. It simply requires obtaining the object (which is received by
the view on load), and calling the delete function for Realm.
The user will then be directed back to the previous view.
There will be issues if a user attempts to delete an event that has not been created yet.
Therefore, the button will be hidden if the user is adding an event. This prevent the user
from being able to interact with the button and cause issues, and removes the need for
validating whether the event exists in the database or not. By virtue of only appearing when
the user is editing an event, only existing events can be deleted.
I will include the button hiding with a whole host of UI modifications made between when
the user is adding an event or modifying an event. For a new event, the delete button will
be hidden, whereas the submit event button will show “Create Event”. In the case of an
event being edited, the delete button will show, the repeat switch will be hidden, and the
submit event button will read “Update Event”.
This is all handed quite simply within viewDidLoad, where the check is performed as to
whether the event is new or not (eventName == “”, indicates a new event).
The buttons to be changed have their isHidden property set to true, whereas to change
button text, the button setTitle function is passed the string to change the text too.
An extension of this differing views feature is to set editing for editing events.
I will continue this in a new section, as it is important to ensure that the user does not
accidentally delete or modify data they did not intend to. If they are viewing an event, they

Candidate Number - 4164 132 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
will not be able to interact with the elements. They will have to press a specific button to
begin editing, so they will be well aware of what mode the application is in at the time.
However, to delete an event, this code is carried out:
try! uiRealm.write {
uiRealm.delete(self.repeatingEvent)
}
To provide another layer of security (beyond simply beginning edit), the delete will occur
only if the user confirms in an alert.
The alert is created:
let alert = UIAlertController(title: "Warning", message: "This will
permanently delete this event", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel,
handler: nil))
It is then given the delete action. It will appear red to the user, which is a standard for alerts
to warn the user of a dangerous action. This is done by setting the style to destructive (like
how setting it to cancel dismissed the alert).
alert.addAction(UIAlertAction(title: "Delete", style: .destructive,
handler: { action in //Delete event if user confirms
try! uiRealm.write {
uiRealm.delete(self.repeatingEvent)
}
self.navigationController?.popViewController(animated: true)
}))

Finally, the alert is presented:


self.present(alert, animated: true)

This appears to the user like so:

Candidate Number - 4164 133 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
This appears when the user taps the delete event button, and returns them to the previous
view. Back in the Schedule, the event that was just deleted does not appear, nor does it
appear in the Realm store printed out.

Edit Button
As described in the delete feature, I believe it a good idea to add a preventative step for the
user.
The edit button will be a navigation bar item. This will be linked up to a mod check, which on
every other press performs an action.
Contextually, that mean that the user will begin editing, and the elements will be unlocked.
The user, when done, will tap the button again, and it will perform the lock action. The user
may then tap the button again to perform the first action. This can continue indefinitely.
Within the buttonTapped function for the button, the check will be performed using an
incrementing integer. Every time it is a multiple of two (count % 2 == 0), the elements are
locked. Otherwise, the elements are unlocked.
Locking and unlocking will happen within a method within the class. As element locking in
Swift consists of simply setting the element isUserInteractionEnabled state to false, the
element cannot be interacted with.
Furthermore, to inform the user (non-verbally) that interaction is disabled, an alpha
component of 0.5 will be applied to locked elements, as this is universally recognised as
being an indicator of something a user cannot interact with. This is done by setting the alpha
state to 0.5 (and back to 1 when reversing).
The setter function looks like this:
func modifyInteraction(set: Bool){
//nameLabel.isUserInteractionEnabled = set //used as ID, therefore
cannot be changed
repeatSwitch.isUserInteractionEnabled = set
priorityPicker.isUserInteractionEnabled = set
descriptionLabel.isUserInteractionEnabled = set
colourPickerButton.isUserInteractionEnabled = set
occurenceButton.isUserInteractionEnabled = set
reminderButton.isUserInteractionEnabled = set
//submitButton.isUserInteractionEnabled = set //disabled after
user selects done

if set == false{
priorityPicker.alpha = 0.5
priorityLabel.alpha = 0.5
colourPickerButton.alpha = 0.5
occurenceButton.alpha = 0.5
reminderButton.alpha = 0.5
//submitButton.alpha = 0.5
}
else{
priorityPicker.alpha = 1
priorityLabel.alpha = 1
colourPickerButton.alpha = 1
occurenceButton.alpha = 1

Candidate Number - 4164 134 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
reminderButton.alpha = 1
//submitButton.alpha = 1
}

}
The reason for separating it out as a method is because it is so large, however it simply
manages all the elements at once.
The edit button is then created with the modulus check:
override func setEditing(_ editing: Bool, animated: Bool){
super.setEditing(editing, animated: animated)

check += 1 //changes lock state at start

if check % 2 == 0 { //enable interaction on edit button press


modifyInteraction(set: true)
submitButton.isUserInteractionEnabled = true
nameLabel.isUserInteractionEnabled = false
} else { modifyInteraction(set: false) }
}
I have also included modifications for the buttons, as they fall outside the Edit button’s
scope.
To test, the edit button is used, and attempts made to interact with the view elements.
Upon pressing again, the view is unlocked. The colour fading works correctly too, indicating
that the elements cannot be interacted with.
I have realised that the edit button also appears when a new event is being created. To
remove this, I have added the code to add the button, along with the assignment of the
checker variable’s value, within the conditional statement that checks whether the event
exists or not. That way, the button is only generated where the event is being edited, and
requires an edit button.
This also means that a user will not have to “begin editing” for a new event that has no data
yet, as the view elements will be unlocked and interactable.

Event display n
For an event to display, all the event objects saved to the Realm database must be
processed and output to the schedule.
I may take a similar approach to converting the Realm objects into a usable structure as I did
with the notes.
Cells are procedurally populated. Each cell accessed and populated with data. The best way
to do this that I can think of is to have the name of an event found via its occurrence. From
there, the name can be used to access all the data about the event by calling it from the
database.
Each event’s occurrences will be translated from the Realm type back into a 2D array. From
there, the occurrences can be checked if any match the cell position. A collection will hold
all events, along with their occurences. As occurrences already appear as a 2D array, the
collection will either be a 3D array or dictionary of 2D arrays.

Candidate Number - 4164 135 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
I believe a dictionary will be a better approach, as it allows the event name to be included,
which can then be used for event lookup.
To begin with, the Realm data must be converted into the necessary data format. As with
the notes, an array of all objects can be created. Then, the individual hours per event can be
obtained and converted into the necessary 2D array structure. Finally, the array is appended
to the dictionary where the key is the event name. That event has been added, and the next
event can be worked on. This iterative process should convert all the stored items into a
dictionary of occurrences.
func addToDictionary(array){

dict = [String: [[Int]] ] //to return – name keyed to 2D occurrences

for event in array{

weekTimes = [[Int]]

for day in event.week{

dayTimes = []
for hour in day{
dayTimes.append(hour) //array of hours for given day
}

weekTimes.append(dayTimes) //days successively added to week

dict[event.name] = weekTimes

return dict

This code completes the job of working through every event and adding its occurrences to
the converted format, however it has the drawback of containing three nested ‘for’ loops.
The time complexity in the worst case could therefore reach O(n3). However, there is a
known maximum problem size. That is the case where all 24 hours are filled. This means
there are 24 hours, for 7 days for an event, giving 168 as a maximum for append operations.
Therefore, despite the algorithm looking inefficient on the surface (and it certainly would be
for a large problem size), the problem size is capped, preventing the inefficiency of the
algorithm to show through. 168 operations is well within the capabilities of any modern
mobile device, iPhone included. The operations can most likely be completed easily enough.
However, a time complexity issue may come into play where the events must be displayed.
Each cell has its column and row. This can be passed to a function that searches through the
dictionary and returns the item that contains the appropriate values in the occurrences.
For every cell in the schedule, it will have to search through the dictionary until the item is
found.
An interesting quirk shows up here. The worst case scenario for the schedule display could
be one of two cases:

Candidate Number - 4164 136 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
1. The cells are all empty, so the dictionary is fully searched each time. However, as the
cells are empty, that means there are no items in the dictionary, so the search ends
quickly.
2. Each cell is full. The dictionary is searched each time, but the search ends as soon as
the match is found.
Therefore, as the amount of data present scales up, the time taken to search the dictionary
decreases. The time complexity likely follows a bell curve, where the most time is in fact
taken for a partially filled dictionary, as the empty cells are fully traversed.
Given all this however, I have no reason to believe the loading time for the schedule will be
of any issue, as the size of the problem is well within the capabilities of a mobile device, and
the problem size does not scale exponentially, as the function may suggest at first glance.
Regarding populating cells, a method call may be made to search the dictionary for the cell’s
given column and row.
The column and row are accessed by
indexPath.column and indexPath.row respectively.
This will be within the cell generation method,
where the cell is given the parameters.

In the schedule example, a custom Schedule Cell


class is already in use. I have simply changed the
data sent to it. It carries out the rest of the display
for me. I see no reason to modify it, as there is
nothing more I wish to add to it.

To get the name of an event by occurrences, a linear search will be applies to the dictionary.
A binary search cannot be used, as the data needs to be sorted for this to happen. The data
is ordered by event, not time, and therefore cannot be searched by occurrence.
With a linear search, each item is checked to see whether it matches the cell position. If a
match is found, the event name is returned and the loop broken. The name can then be
used to send the event’s name and colour to the cell.
To obtain the event name first, the search is applied:

Candidate Number - 4164 137 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

This algorithm checks through, for every event in the dictionary, the subarray of occurrences
that matches the column of the cell.
Every hour (row) within the column is then checked. If an hour match is found, the event
name is returned via the dictionary key.
While linear searches are an inherently inefficient way to search for data, that does not
mean that the they cannot be optimised. Here, for example, only the relevant column is
searched. That means that, for a given cell, at worst, 24 iterations are performed for a single
event. Assuming that there can only be a maximum of 7 events (due to all spaces being
taken up), the 168 iterations are performed to see that the cell is empty. However, as
discussed previously, this can never be the case, as for the cell to be empty, all spaces
cannot be taken up, so the search cannot perform at its worst.
On a related note, the loop will be given an identifier where, upon the event being found,
the loop will be broken. As the event has been found, no further searching is needed, and
anything more is unnecessary (especially considering it is impossible for another match to
be found).
While accessing the column and row in the data structure, some shifting has to be done, as
the column and row returned by the cell are not going to directly correspond with the
column and cell in the structure. This is due to the schedule including rows and columns for
the hours and date to display.

Candidate Number - 4164 138 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Columns are offset by 1.


Rows are offset by 2.
The hours are stored as an integer representation of the hour, so that means a cell
appearing at 9am will have 9 stored as its hour value. Assuming that 9am is the start hour,
the cell will be in row 2, or row 0 when ignoring the offset. A further offset to accommodate
for the time difference must also be taken into account. This offset will be stored as
startTime, and will simply be the value of the first time to appear.
For comparing rows, the cell row returned is 2. 9 is added to take care of the hour difference
in representation. Then 2 is taken away to account for the information rows above.
The result is the hour being equal to row + startTime – 2.
In Swift, this will appear like this (including the loop break):
func getEventName(dict: [String: [[Int]]], column: Int, row: Int) -> String{

var name = String()

whole: for event in dict{ //get event name at cell position

for hour in event.value[column-1]{

if hour == (row+startTime-2){

name = event.key

break whole //escapes entire loop once value is found

return name

To populate cells, the event name is found by the cell position by the method above. An edit
is made to customise the colour of the cell. Where the cell has a name (an event has been
created with its position), then the event is found in the array of all events.

Event Navigation
At this point, created events cannot be navigated to. A simple way to handle navigation is to
receive the event name by cell position once again.

Candidate Number - 4164 139 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The name can then be sent to the Event view and be used to populate the fields, as it is the
primary key for the event and can be used for quick lookup of the event.
When a cell is tapped, the controller will search through the events to see whether any
correspond with that cell.
The segue is then triggered, and the event loaded.
Within the didSelectItemAt method, the name will be obtained, and then the segue
triggered.
This performs navigation to the next view, and also sends the name over.
Upon testing this, I found that while the correct event is selected and sent, it turns out to be
a little frustrating to always be navigated to the next view whenever an empty cell is tapped.
Furthermore, locked cells, which will never display an event, sends the user to the view also.
I wish to prevent this.
For these two, I will include two conditionals. If the cell tapped has index or row of any of
those that are locked, then the segue will not trigger.
if (indexPath.column >= 1 || indexPath.row >= 2){ trigger segue }

If the column or row is greater than those that are locked (c0, r0 and r1), then the segue is
triggered.
Similarly, if the name found is empty, the segue will not trigger. This does not affect the add
button as it is not controller triggered.
if name != ""{ perform segue }

This checks the if there is no name, and if that validates to false (there is a name), the segue
is triggered.
Upon testing, the empty cells no longer trigger, however I had an issue with the locked cells.
The segue is being triggered strangely, and I cannot pin down the conditions for trigger.
Although I am not sure what the issue is, it appears the OR statement may be the issue.
I have changed it to an AND statement checking the same conditions:
if (indexPath.column > 0 && indexPath.row > 1){ trigger segue }

I am not sure why, but this appears to work properly now. None of the locked cells trigger
the segue.

Feature Test
Before beginning formal testing, I noticed a small bug where the events would not appear
until scrolling through the Schedule.
This is due to the cells immediately shown after navigation not having been generated with
the new data on view appearance. The setup code was contained with viewDidLoad and
reloaded on cell generation (as the view is moved and new reusable cells are created).

Candidate Number - 4164 140 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
However, what this means is that to generate the data on view appearance, the generation
code needed to move to viewWillAppear, where the data is loaded whenever the view is at
the top of the navigation stack.

Test Outcome Passed?


Number
1 Tested during development Yes
2 Tested by focus group. No issues found. Yes
3 Unusual characters are supported by Unicode, and therefore by the application
too.
4 All cells were filled and no performance issues were noticed. Yes
5 Occurrence picker prevented selection outside of display range Yes
6 “+” for creating events from Schedule Yes
7 Existing events accessed by tapping event cell in table Yes
8 Deletes work on existing events Yes
9 New events prevented from delete button access Yes
10 Events display in schedule with colour and title correctly. Cells are uniform Yes
11 Extra information held about events, including text and priority Yes
12 Reoccurrence – events appear multiple times a week, if set by the user Yes
13 No issues are raised by the application if conflicting events are made. No

Evaluation
I have ended this cycle earlier than I may have initially intended. The reason for this is that it
was taking far longer to program than expected. I ended up spending a very significant
amount of time browsing through not only Swift and Realm documentation, but external
API documentation also. Searching for the most appropriate API for various tasks took far
longer than expected. However, this still does not come close to how long it may have taken
to program the features myself. The APIs and Pods have been incredibly useful, but have
been a time-sink nonetheless.
Collapsible occurrences cells, for example, was a feature I intended to implement. I did in
fact find examples of its implementation, and as it was a feature I very much wished to
implement, I spent a great deal of time attempting to understand how it worked so I could
put it in myself.
In the end, I was unable to, and the feature was dropped. The time spent searching for,
experimenting with and attempting to understand the feature was not recovered however.
Although that is one specific example, it carries over for various other features I hoped to
include. This therefore prevented other features from being implemented – the
notifications for example.
While I hope to broach the topic at a later cycle, it will unfortunately have to be placed on
hold as I attempt to include other, more basic, features within this application.
The development has proven to exceed my expectations of the complexity. This is, in fact,
exhibited by my testing. I feel that – since I perform extensive research while planning a

Candidate Number - 4164 141 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
feature – the amount of testing and redevelopment required to finalise it is minimised, as I
can usually expect the precise outcome before implementation. Rarely are my expectation
not met.
While the levels of testing and redevelopment are reduced, even more time is spent on
features to ensure that they are being implemented correctly.
This means that, due to time constraints, an ever-increasing portion of the features I hoped
to include will have to be placed on hold indefinitely, and ultimately not included.
This is an unfortunate product of being too ambitious.
The removal of features has not gone unnoticed. A number of members of my focus group
asked about the notification option, which I had to respond was being placed on hold for
the time being until the most basic features of the application had been included.
The focus did respond positively to features that were in place – especially with respect to
user interface and user experience elements.
I discussed the lack of formal collision detection, and a member suggested maybe using a
hash table to check whether a space has been filled or not. This was a feature I had not
though of implementing, but I believe it will take a significant amount of work to rework my
structure around the addition of this new structural management technique. Furthermore,
these is the issue of storing the hash table, as Realm will no natively support the table
natively. Furthermore, major additions will have to be made to the delete function in
regards to removing its occurrences from the hash table.
While a very good method of conflict management, I believe it will be very difficult to
implement. It is still certainly a feature worth looking into at a later point, especially where I
have no collision detection in place, and no formal conflict management.
Loading events to the Schedule was also picked up as potentially requiring improvement. In
testing, it appeared there is no limit to the number of events a user adds, each of which
could be set to appear in every cell. They may slow the populator down, but then again, it
may not as the loop breaks once an item is found, not all matching items (of which there
should not be any).
In any case, the ‘brute force’ approach of event loading could be improved upon.
Otherwise, most points of the success criteria have been met, meaning that the feature
overall has been implemented fairly successfully, with a few improvements that could be
made.

Candidate Number - 4164 142 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Candidate Number - 4164 143 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Candidate Number - 4164 144 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Candidate Number - 4164 145 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Candidate Number - 4164 146 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Candidate Number - 4164 147 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Cycle 4: Linking up Views


Outline
Now I have a few separate views working. So far, I have been testing them in an isolated
way. This has not been a problem, as the views are not related to each other in any way.
However, to construct the application, the views must all link up.
This could be done using a tab bar controller, however not only does this take up the
already limited space available within the view, it also may not look pleasant once many
separate views have been added (the plan being that the following views would be in place:
agenda, notes, schedule, alarm, settings).
With that, I have decided a clean approach would be to implement a sidebar menu. A
common feature in Android devices, it works where the user always has the option to open
the menu from anywhere within the application, and navigate to any views shown within
the menu. This approach works for me, as it allows as much expansion as is necessary,
without taking up any extra room (besides an open menu button in the navigation bar,
however that is already in place).
The main issue with this, of course, is that menus are not a native feature in iOS devices.
Xcode does not make any provisions for this type of navigation.
I will be using the SideMenu pod (https://cocoapods.org/pods/SideMenu) to achieve this
task, as it contains a variety of customisable options and appears to be simple to implement.
In fact, implementation can be carried out entirely in storyboards.
I will be implementing it with a combination of two methods, in storyboards and within view
controller. The menu will be linked to all the views it is designed to connect to, as per the
structure chart, with other tweaks made in the Agenda view controller (as this is the view
the user is opened up to). All setup will occur in a private function within the class.

The first step of programmatic setup is importing SideMenu. It can then be accessed within
the view.

Candidate Number - 4164 148 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Within the function I have also specified a typealias (m) to help reduce the size of the code a
little.

fileprivate func setupSideMenu() {

typealias m = SideMenuManager

m.default.menuLeftNavigationController =
storyboard!.instantiateViewController(withIdentifier: "LeftMenuNavigationController") as?
UISideMenuNavigationController

m.default.menuAddPanGestureToPresent(toView: self.navigationController!.navigationBar)
m.default.menuAddScreenEdgePanGesturesToPresent(toView:forMenu:)

m.default.menuPushStyle = .popWhenPossible

m.default.menuPresentMode = .viewSlideInOut

I have specified that I wish the menu to only appear on the left side, with:
m.default.menuLeftNavigationController.
I have also used .popWhenPossible as this will allow for the most logical navigation control,
preventing the user from getting lost down many views.
Finally, I have included and aesthetic feature .viewSlideInOut as I believe it is the sleekest
look.

Testing
To test the menu, the views will be navigated.
The following routes will be taken:

 Deep navigation and reversal – agenda -> schedule -> event -> occurrences, and back
again. The expected result is to be able to navigate down, and navigate back up
through the same route.
 Cross navigation, navigation through multiple views and switching to another base
view, and navigating back – agenda -> schedule -> alarms -> schedule, and then using
the return array. This should lead back to the agenda directly. The route travelled
there should not be travelled again in reverse.
The menu appears properly, but opens coloured completely black. I have not applied any
colouring to it, so this is surprising. However, going through the documentation for
solutions, it appears this is known bug with a simple fix provided. By including
“default.menuFadeStatusBar = false” to the menu setup (setupSideMenu()).
I have made the change, and it appears to have worked. The menu now appears as
expected.

Candidate Number - 4164 149 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Evaluation
This cycle has been relatively short, as the focus is on a feature that has already been
completed and only required implementation. This does not make it any less important
however.
Navigation is an integral part of an application, and therefore is very important that it is
done correctly.
Given how the interface is very clean (it contains very few objects), the user will be able to
understand the navigation and how it works with little to no prompting.
Furthermore, the menu system has allowed for logical pathways through the application.
The user can follow one branch, and navigate back up, or, if they switch to another branch
(sometimes repeatedly) they will not get lost through a large stack of views on the
navigation controller. Although this may lead to data loss, by and large the benefits
outweigh the losses, especially since intuitive use is an important feature of my application.
Focus group: the group really liked this feature. All the users were immediately familiar with
how it worked, which was promising, as I had some fears that they would not immediately
grasp the feature without some guidance. The ease of use was praised; however, the
blandness was brought up. Some of the group voiced their concerns that it stood out in the
application (in a negative way) as it contained no colour and a large portion of whitespace.
One of the members suggested putting an image of a kitten in the space so that there was,
at the very least, something there.
It is surprising that a kitten specifically was mentioned, as while looking for menus that I
could implement, I came across another menu, which had a feline theme to it (with kittens
too!)
A very surprising coincidence, but otherwise not notable.
I did mention to the other possible aesthetic features, regarding how the view animates. I
showed the Pod examples, and a few of mine own. I was happy to hear agreement that the
method I chose was, in their opinion, the best looking.
Regarding colouring the menu, no helpful suggestions were made, as all included some form
of complex aesthetics (from images to colour gradients)
I may consider adding the application icon in the menu to fill the space, having taken their
feedback into account.

Cycle 5: Agenda II
Outline
So far, the Agenda has some basic framework, in terms of objectives to achieve and a little
programming. The queue has been programmed. I expect three of those queues will be
initialised to work as a priority queue.
With events having been completed, the Agenda’s features can be implemented.

Candidate Number - 4164 150 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
There are three items I wish to hit within this development cycle:

 Upcoming events
o The agenda is to display events upcoming that day. These events are to be
displayed in order that they are to appear, and after the event has begun
they are not to appear on the agenda any longer.
 Queueing
o Expanding upon displaying events in order, the queue ensures that the order
is maintained. I already have a queue in place, the only question is
implementation. However, some level of complexity is introduced as I intend
to include a system of priority queues.
 Event timing
o The user can see, at a glance, the time at which an event is to occur. The
timer will slowly tick down until it is time for the event to occur.
This cycle tackles the points remaining after the 1st cycle, that could not be included due to
the missing schedule framework.

Development
To begin with, I have made a modification to the cell priority switcher. As the priorities are
defined by the saved pickerview indexes, the switch will be modified to reflect that. Now,
the cases are integers tested against event.priority. Similarly, the cell name is generated by
event.title.
This is all possible as the event is passed by the agenda to the cell.

Queue Implementation
I wish to create a simple priority queue. This will be done by using three instances of the
linked list queue: high, medium, and low priority.
Events are populated to the correct queue based on their priority. I may also include a
mechanism to bump up the priority of events as they approach their occurrence time.
To enqueue, the events for that given day must be obtained. This can be done using a
modification of the toDictionary function used in the schedule. However, the events that
occur on that given day must be extracted.
To begin, the day must be determined. Using a modification of StackOverflow user Ram’s
code (https://stackoverflow.com/questions/46402684/how-to-get-start-and-end-of-the-
week-in-swift), this can be easily achieved. The function returns the current weekday as an
integer (with respect to the seven day week). It is set as Sunday start, whereas I prefer a
Monday start. For that reason, I have included a little clause to return the value less 1. In the
case that it is negative (as Sunday returns 0), return 6.
var weekday = Calendar.current.component(.weekday, from: Date())-2
if weekday == -1 { weekday = 6 }

All saved items are converted into the dictionary format. A second dictionary may then be
created, where the key is the time of occurrence, and the value the event. It essentially

Candidate Number - 4164 151 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
splits the first dictionary up while separating out the occurrences of events for that day. The
reason for not using an array is that the time of occurrence must be known.
With events (or at least, their identifiers), processed into an ordered dictionary, they can
then be populated the queues.
The queues are then used to populate the table with cells.
The steps can be broken up into a number of steps.

Step One – all events to ordered dictionary:


allDict = all events in dictionary

toOrderDict(allDict, weekday){

dictionary = []

for event in allDict{

if event has occurrences on weekday{

for item in occurrences{


dictionary[item] = event’s allDict key (name)
}
}
}
return dictionary
}

Step Two – Populate queue:

for item in orderedDict{


obtain event from Realm

based on event priority, enqueue event name


}

Candidate Number - 4164 152 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
In Swift, the final code is as follows:

func setupView(){
allEvents = uiRealm.objects(RepeatingEvent.self).toArray() as!
[RepeatingEvent]
var weekday = Calendar.current.component(.weekday, from: Date())-2 //get
today's day as a number (week beginning Sunday [-1]), set Monday as 0 index [-1]
if weekday == -1 { weekday = 6 } //due to error on Sundays

let allDict = allEvents.addToDictionary()

orderDict = toOrderDict(dict: allDict, weekday: weekday) //add events


into organised dictionary

if orderDict != orderDictCheck{

prioQueueDef.dequeueAll() //reset the queues to empty, in case an


event was deleted
prioQueueImp.dequeueAll()
prioQueueUrg.dequeueAll()

queueItems(dict: orderDict, startTime: startTime, timeDifference:


timeDifference) //pushes items in ordered dictionary into relevant queue
orderArray = prioQueueUrg.outputArray() + prioQueueImp.outputArray()
+ prioQueueDef.outputArray() //outputs array of items in order they were queued
orderDictCheck = orderDict
}

func toOrderDict(dict: [String: [[Int]]], weekday: Int) -> [Int: String]{

var dictionary = [Int: String]()


for event in dict{ //adds events to an organised dictionary for the day

if event.value[weekday] != []{ //if not empty


for item in event.value[weekday]{
dictionary[item] = event.key
}
}
}

return dictionary
}

Candidate Number - 4164 153 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

func queueItems(dict: [Int: String], startTime: Int, timeDifference: Int){


//pushes items in ordered dictionary into relevant queue

var count = startTime //the first hour that will appear in the
occurrences

for _ in 0...timeDifference{ //the difference between the first hour


and the last

let item = dict[count]

if item != nil{
let event = uiRealm.object(ofType: RepeatingEvent.self,
forPrimaryKey: item)!

switch event.priority{ //queues items based on priority


case 1:
prioQueueImp.enqueue(key: item!)
case 2:
prioQueueUrg.enqueue(key: item!)
default:
prioQueueDef.enqueue(key: item!)
}
}
count += 1
}
}

When running the application (with events populated for today), I noticed that the events
kept repopulating to the table.
It appears that I forgot to include a statement to clear data from the queue.
For the time being, I will use a quick fix to empty the queues. This is done by setting the
head to nil. Node connected are subsequently lost in memory, which has the potential to
cause serious memory leaks. However, I only intend to use this temporarily for testing the
main Agenda features. It has no effect in the Agenda works, and I completely intend to
implement a memory-safe manner of removing all nodes from the queue.

Testing and Evaluation


Testing this feature has thrown great variety of problems, some of the solvable, some of
them not so much.
The queues, for example, keep throwing repeated issues. Ignoring the fact that the dequeue
functions are based on very poor programming practice, the queues themselves do not
appear to play nicely with the view.
The feature has been incredibly buggy, with inconsistent results on each run. Sometimes
extra cells were generated (which I thought had been fixed by the queue clear, but
apparently not), whilst other times the Agenda will not display all the events to appear for a
given day.

Candidate Number - 4164 154 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Two important aspects of the view were the reminders and the display of when the event is
occurring. The former could not be included as the whole feature is not in place, whereas
the latter brought up many implementation issues of its own.
Live tickers in Swift apparently are not used very often. There did not appear to be any
intuitive way of doing so. I experimented a great deal with the timer feature and C#
selectors in order to produce some form of countdown, but no method worked reliably as I
wished.
It was proving far harder to include than expected, especially when considering that I only
wished for by-minute accuracy at best.
While I would have included formal testing, it was impossible to determine exactly what was
happening within the view. For that reason, I have simply described the numerous problems
associated with this feature.
This means that, unfortunately, I believe I will have to remove this feature entirely as it
appears impossible to make work. At the very least, the time expenditure far exceeds the
potential for success per feature, and does not seem reasonable to do so.
I discussed with my stakeholders about my decision to remove the feature due to
impracticality reasons, and together we agree it was for the best. I asked for suggestions on
how to fill the large empty space on the home screen at this point, and was told that a good
method would be to use the application icon as ‘filler’.
As the feature is being removed, all success criteria points have not been met for the
Agenda. Point 1.1.2 did in fact work, however it is not usable without the rest of the view
working as intended.

Candidate Number - 4164 155 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Cycle 6: Settings
Outline
The application is designed to be usable in any circumstances, including where the user may
have a strange schedule. In that case, they will need to be able to edit the times that the
schedule may show.
A default value is provided for the user, so they will not have to enter settings to make any
configurations before being able to use the application.
Realm supports deleting all data stored as an inbuilt function, so I may also provide this
option to the user.
Finally, after all settings changes have been made, the user will save their settings. If they do
not, then none of the changes will save (except delete all, which works independently).
This feature was not initially planned, however I believe it is an invaluable addition to the
application, and provides much more functionality that the user will need. Unfortunately, no
success criteria exists for this feature, so there is no solid framework to test against. Instead,
I will create criteria to test against here.
The features required for this feature are:

 Hour selection – The user will need some way to select the hours between which
they wish their timetable to display.
 Display format – the user may have a preference over whether they see the times
displayed in 24-hour format or 12-hour format.
 Delete All – the user will be given the option to remove all their data from the
application to make a fresh start.

Hour Selection
A possible way of doing this is with two text fields, one to take the starting hours, and the
other to take the finishing hour. This comes with the drawback that the inputs need to be
validated. The format must be defined to the user, as it is impossible to check against every
possible input by any possible user.
A possible way around this is to use a picker view, where the input processing is handled
internally. However, there needs to be a check to ensure that the user does not select a
period that cannot be displayed, like where the starting hour occurs after the finishing hour.
In either case, I believe that the approaches mentioned above are not very intuitive for the
user experience. There is a third possibility, where a slider is used. While Xcode provides a
slider feature, it only supports a single slider. This is important, as a single slider does not
solve my issue. Instead, I will need a sort of dual slider, where the single slider contains two
controllers.
This immediately solves the issues faced. The user selects their range as being between the
two sliders, and can immediately see the distance between the two sliders, to get a sense of
how large a time they are selecting. The two sliders also will not be able to overlap, or if
they do, the times they represent (start/end time) will switch.

Candidate Number - 4164 156 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The slider will also textually display the times selected, so if the user has specific times in
mind, that can also be selected for.

Time Display Format


The time will be handled with integer representation, with possible values between 0 and
24. When it comes to display, the value will be converted into either 12 or 24-hour format
(depending on the user’s selection within settings).
In the case of 24-hour format, “:00” will simply be appended. However, should the value be
less that 10 (i.e. it is in single digits), then “0” will be added to the front.
In the case of 12-hour format, if the value is less than 12, or 24, then “am” is added,
otherwise “pm” is added. In addition, should the value be greater than 12, then 12 is taken
away from the value before the am/pm additions are made.
As the time is stored within the Realm as an integer, this is how every view receives the
time. Therefore, this conversion function will have to be accessible by all views that use the
time.

Delete All
Within Realm, the option to clear all data exists. When the user taps the delete all button in
the settings, this function will be called.
Before that happens however, the user must be informed that the action is irreversible. All
of their data will be lost. The user will be given to cancel the action.
Upon performing the delete, since all data has been deleted, some basic structural data will
have to be reformed. The Schedule view, for example, cannot be opened as there will be no
data about what hours should be displayed.
The Realm delete function does not discriminate, and therefore the settings function will be
deleted. However, after the delete has been called, it will simply be recreated, avoiding any
errors as soon as the user exits the view.

Development
Setup

Candidate Number - 4164 157 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The settings view is created with space for the slider, a label to display the times, a switch,
and the delete all button.
In the navigation bar there will be a save button, defined by creating a right bar button item,
of type save.
These controls are then linked up to the view controller using the relevant outlet and
methods.
A Settings object is created in the Realm. The object is standalone (unlike with other
implementation of Realm stores), where no data is added to the table. Settings is only ever
modified.
class SettingsStore: Object{
@objc dynamic var twentyFour = false
@objc dynamic var lowerBound = 6
@objc dynamic var upperBound = 17

The Realm data model.

I have included default values, which means the application has values to work with,
without the user having to do anything.
The model includes a check for whether the selected format is 12 or 24-hour. It also includes
the range, defined by the start and end times.
At view load, Settings is pulled from the Realm and is used to populate the view with the
data it has stored. That way, the user will always be working with the data they last
selected.
This can be done using a simple primary key call: settings = uiRealm.object(ofType:
SettingsStore.self, forPrimaryKey: "1")!

A change is required for the model to accommodate this. Two lines have been added:
@objc dynamic var id = "1"

override static func primaryKey() -> String? {


return "id"
}

This allows a primary key to be used.


Data is then populated to items in the view; this will be written at a later stage.

Hour Selection
First of all, a double-ended slider must be found.

Candidate Number - 4164 158 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
While searching, I came across SwiftRangeSlider
(https://cocoapods.org/pods/SwiftRangeSlider).

The slider (as shown) performs the ranging function


I am after, and furthermore is customisable within
the storyboard, so I can immediately see how it
appears in the UI. This eases the amount of testing
necessary.
There will need to be two variables to contain the
two values of the slider, being the upper and lower
bounds. I will name them lowerVal and upperVal. As the slider returns a double, they will be
initialised as such.
When the view loads, these two variables will take the value contained within the range
slider (lowerVal = rangeSlider.lowerValue). Every time the slider is modified, the variables
will be updated to keep track. This is because they will be used to update the display label as
they are changed.
The range slider has the following settings that I will be modifying:

 Minimum value – controls the smallest value the slider represents


 Maximum value – as above, but for the largest value. The slider is bounded between
these two values. As I am representing hours of the day, the range will be 0 to 23
(00:00 to 23:00, or 12am to 11pm)
 Minimum Distance – controls the minimum distance between the two knobs. It
prevents the user from selecting a negative range (where the lower knob is
representing a larger value than the greater knob). If set to 1, it also ensures that the
user has selected at least 1 hour to display to the timetable.
 Step value – the value at which the knobs increment. As I am incrementing whole
hours, this will be set to 1.
 Knob size – the default size is very large, so I have reduced this to be more in line
with Xcode’s default slider.

Candidate Number - 4164 159 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
As of yet, I cannot tell whether the selection is working correctly. To test this, I will make use
of the rangeSliderChanged function, which is called every time the knobs are moved. It will
simply print the values of the range slider, which are held in lowerVal and upperVal.
Values are printed whenever the slider values are changed. The values match up roughly
with the position of the knobs, and increment as expected.
As this works, the next step is to output the values held by the slider knobs to a label, so the
user may see the values they are selecting. It is important to note that the
That can be done easily using a label. Instead of printing the results on value change (as was
done for testing purposes), the values are output to a label. The change is made by updating
the text for the label (label.text). The text will be something along the lines of “[lowerVal] to
[upperVal]”, where the actual numbers replace the variables. This is done using the Swift
string escape mechanism.
I have named the label “hoursLabel”. The escapement is done using “\(x)” where ‘x’ is the
variable.
hoursLabel.text = “\(rangeSlider.lowerValue) to \(rangeSlider.upperValue)”

To test this, the view will be loaded and various values selected. The values should
correspond with the rough position of the knob.
The label text matches up with the printed values (as expected), which themselves
correspond with the rough position of the knobs.
The values currently output in their raw state. This can be improved by formatting the
hours, as will be detailed in the next section.

Hour Format
As the values are stored and subsequently displayed as doubles (since this is how the slider
handles the values), the values must be formatted so the user can immediately understand
how the hours work.
As some users have a preference of which time format to use, this must also be taken into
consideration. Below, a flowchart has been created which details the logic of outputting the
correct times for the user. It is called every time the user changes a value on the slider.

Candidate Number - 4164 160 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

There may be more efficient methods of output of formatted time, however since it is such
a simple algorithm with a fixed problem size, the efficiency issues are negligible.
This function will take any given integer (which will be between 0 and 24) and will return the
value as a string to be depicted in the display. It will be the user’s decision as to whether the
value is displayed in either time format.
I will create a function which takes any time and returns the appropriate value. The format
check will be performed on the switch. The reason for this, rather than basing it off settings
stored in the Realm, is that the user may switch between the two until they decide on the
one they prefer. Only after they are happy with everything will they save the changes made,
which will commit the changes to the Realm.
If formatSwitch.isOn evaluates to true, then the user has selected 24 hour, otherwise it is in
the default position, so 12 hour times will be displayed.
The conversion will then happen, using the process above. Any numerical methods (like
subtracting 12) will be carried out before the integer is converted into a string.
In Swift, it will look like this.

Candidate Number - 4164 161 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
func setupTimes(time: Int) -> String{
var string = ""

if formatSwitch.isOn == true{
//generate 24 hour times
if time < 12{
string = "0\(time):00"
}
else{
string = "\(time):00"
}
}
else{
//generate 12 hour times
if time < 12{
if time == 0{
string = "12:00am"
}
else{
string = "\(time):00am"
}
}
else{
if time != 12{
string = "\(time-12):00pm"
}
else{ string = "12:00pm"}
}
}
return string
}

There have been a number of slight changes due to how the implementation must work.
First of all, to add things like “00”, the integer is converted into string and the modifications
made. However, since the time could be any value, the string had to be escaped, which has
been done using ‘\(x)’ where x is the variable.
Furthermore, since the function takes an integer, and the slider returns a double, there has
to be conversion between the two before passing it into the function.
Finally, a function named “setupLabel” has been created to push the text to the label. It uses
similar string escape methods, where the variables being pushed in are the results of the
function to convert the time into its string format.
The function is required as updates may happen when either the switch is tapped, or the
slider changed. The label must be setup again in either case, hence the function.
The output is this for various values:

Candidate Number - 4164 162 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

This function has also been used in the Schedule view (and by extension, the Occurrences
view also), where some modifications have taken place. Since the times are being converted
from a range, the times will be populated to an array. As the function will be embedded
(due to the change in its use), a small change will be made where the function no longer
takes a parameter or returns a value, and instead is an in place array generator, operating
on an array that already exists.
func setupTimes(){
hours = []
if settings.twentyFour == true{
//generate 24 hour times
for x in settings.lowerBound...settings.upperBound{
var string = ""
if x < 12{
string = "0\(x):00"
}
else{
string = "\(x):00"
}
hours.append(string)
}
}
else{
//generate 12 hour times
for x in settings.lowerBound...settings.upperBound{
var string = ""
if x < 12{
if x == 0{
string = "12:00am"
}
else{

Candidate Number - 4164 163 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
string = "\(x):00am"
}
}
else{
if x != 12{
string = "\(x-12):00pm"
}
else{ string = "12:00pm"}
}
hours.append(string)
}
}
}

Testing
This feature is relatively simple, and I only need to check that the output is correct for any
given value, of which there are few enough that I can observe each.
In the testing table below, I will select various values and check the output.

12-Hour Selected 24-Hour Selected


Values selected on Returned Values selected on Returned
slider slider
12am Correctly displayed 00:00 to 11:00 As expected
1am to 11am All times display 12:00 to 23:00 As expected
correctly
12pm Correctly displayed
1pm to 11pm All times display
correctly

Save
Once the user has made all the changes they wish, the settings can then be updated to the
Realm. The changes are not immediately updated because that would lead to too many
writes to the database, which is unnecessary. Therefore, once the user is happy, they may
press the save button in the top right corner (created by defining a right bar button item of
type save; it calls the save function whenever tapped [navigationItem.rightBarButtonItem
= UIBarButtonItem(barButtonSystemItem: .save, target: self, action:
#selector(saveTapped))]).

The button takes all the current values of the slider and switch, converts those values to
those that are saved in the database, and then writes the changes.
The variables taken into account:

 rangeSlider.lowerValue (double)
 rangeSlider.upperValue (double)
 formatSwitch.isOn (bool)

Candidate Number - 4164 164 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
As the slider values are doubles, they have to be converted into integers before they can be
saved. This can be done using Int(x) where x is the double, converting it into an integer
value. formatSwitch requires no formatting.
The following pseudocode saves changes to a settings object, which is then updated to the
Realm.

Realm update {

settings.lowerBound = Int(rangeSlider.lowerValue)
repeated for upperValue

settings.twentyFour = formatSwitch.isOn

The specifics of the update require the above (encased within the braces) to be performed
within a write transaction.
The transaction begins with “try! uiRealm.write {” and ends with
“uiRealm.add(settings, update: true)” (since the settings are being updated, rather than
a new object being added).
To test, I placed a print statement of the object in the Agenda view, and it appears to be
called and printed out correctly, including the most recent changes made. I can conclude
that saving is carried out correctly.

Delete All
Simply connecting a button to the view controller and then calling the delete function when
it is tapped is incredibly risky. The entire Realm is deleted, which has a high chance of
causing issues, especially since Settings uses data from the Realm and updates to it.
Unexpected issues may occur. Furthermore, the Agenda view makes an immediate Realm
call when loaded, as do most other views. Some safety mechanism must be in place, or an
alternative method found.
For that reason, I looked into the Realm documentation regarding deleted. deleteAll()
removes the entire Realm. I wish to keep this intact, so this therefore is not a viable
approach. I wish to keep the store intact, and only delete the data within.
I did experiment with calling deleteAll() on the button press. I also included a catch to
restore the Settings by using the values present within the view to recreate the file. Despite
this, the app continually crashed whenever the view was left (i.e. another view made a call
to the Realm, which did not exist). I rolled back the changes and noted the experiment as
having proved it was likely too much difficulty to catch all the issues to make it work.
A potential method of doing so is to reference all the objects of a type, and then perform an
individual delete on all of them. This prevents the issues of no Realm existing, but it requires
every object to be cleared to be accessed and manually deleted. This will apply to every
object type, bar settings.

Candidate Number - 4164 165 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
However, the method for doing so in Swift is as follows:
let realm = try! Realm()
let allUploadingObjects = realm.objects(Uploading.self)

try! realm.write {
realm.delete(allUploadingObjects)
}

According to GitHub user TimOliver (https://github.com/realm/realm-cocoa/issues/4769).


All the objects of a given type are called referenced by a variable, and then deleted within
the transaction.
This step is simply repeated per object type to delete. The structure should stay intact
however, so it is as if the user has first opened the application and no data has been created
for them yet.
Taking this approach removes the data required. I tested this by populating some data and
calling the delete. No traces remained within the application of the data that had been
deleted, and furthermore, on printout of the database, it appeared to be empty –
suggesting a successful delete.
I performed the delete again to check whether there were any issues deleting an empty
database, however there appeared to be none. As expected, deleting nothing caused no
issues.

Implementation to Views
There are three views that will need to make use of the values in settings. These are the
Schedule, Event, and Occurrences (and by extension, Occurrences cell).
Implementation consists of replacing hardcoded start and end times with the values in the
Settings (obviously the Settings object must be called first).
As I have already based much of the program around the start and end times of the hours to
display (with intermediate hours being procedurally generated), the step of implementation
only consists of replacing the hard integer with the appropriate settings value.
Provided the settings contains a value, it can be assumed that the code carried out
successfully. I will perform a presence check and print the Settings values. They should
match what was set after the most recent edit.
While confirming this, I was also able to check that the views adjusted in size appropriately
to match the settings change, which they have.
I do not believe it is necessary to include any on these views as the Settings will always be
saved with a value. This means that the views will always receive a value regardless, and
validation is unnecessary.

Candidate Number - 4164 166 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Evaluation
The hours formatting went well with few problems, however there may be more efficient
ways of performing the conversion, especially in the case where a collection of times have
to be converted. Furthermore, the function is duplicated across multiple views. Granted, it
had modifications, but it may have been possible to work with a base function and
encapsulate it.
I was quite happy with how the features turned out. The double ended slider, for example,
was a very neat way of managing the user’s selected times, as it automatically bounds the
selectable hours, prevents crossovers, returns a guaranteed data format, and will always
return a value when the user saves. This removes a large portion of validation that would
otherwise be necessary.
Overall, I would say this cycle was quite successful.
Success Criteria:
2.1 – While not designed to meet this success criteria directly, it still addresses some of the
points specified. The timetable, in its default state, would not be able to display all possible
hours (and showing all hours by default would be excessive). The settings help achieve
moderation and this point by giving control to the user as to how much they wish to display.
6.1 – the slider and label help achieve this point by providing a very simple UI to interact
with. The user may not know what the slider is there for when entering the view, however
they almost certainly can deduce its use for a number of reasons: they are in a settings
screen, a range is shown, and the label briefly describes the slider usage. Furthermore, my
user testing showed that members of my target demographic had no difficulty understand
the view’s use.
Meanwhile, I believe this feature meets the criteria set out at the end of my planning
section.
Focus Group: I discussed the results of the 6th cycle with my focus group, and they were
impressed with how I decided to attempt the settings. One member commented that it was
“pretty slick” and that “it’s definitely simple to use”. I was asked whether I had any plans to
add other settings, to which I responded that I had no intention to do so, however I was
open to ideas. The Notes screen was brought up again, and how I could possibly add to that
instead. Alternatively, I could focus on some statistical feedback, although that did come
with the comment that I should do so “because it looks cool”. Overall, however, there was
general agreement that no further settings for the application were necessary.
To Conclude: The settings view, while not planned for initially, is an invaluable addition to
the timetable. It gives the user more degrees of control over the application. Overall, I
believe this was a successful cycle.

Candidate Number - 4164 167 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Candidate Number - 4164 168 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Cycle 7: Schedule Collisions


Outline
This purpose of this cycle will be to address the issue of the Schedule containing no collision
detection.
The best method suggested to me was to make use of a hash table.
A hash table allows very quick look up for a known value. This can be used to handle
collisions, as a lookup can be performed before updating to see whether the space is
occupied.
The table will be used between the Schedule and Occurrence Picker; for the former, cells
will be generated, whereas for the latter it is to check collisions.
This means the features of the hash table are split into three distinct parts: generation, load,
and conflict handling.

Generation
The hash table will initially be created containing spaces for all 24 hours of each of the 7
days. This means there are 168 spaces available.
As I wish this to match up with how the Schedule table is structures, the hash table will in
fact be two dimensional, where week lookup is performed first, followed by day lookup.
The hash table itself can easily be based on the dictionary structure available within Swift.
The dictionaries already contain key value pairs, and therefore are an ideal structure to
implement an hash table on.
The super-dictionary will contain a reference for each day of the week (like 0-6/1-7, for
example). Each key will then contain its own dictionary, from with keys 0 to 23. Each item in
the table will be filled, possible with an empty string, so that comparisons can always be
made between the value and items being considered by the application.
Using an empty string is ideal as no event can have an empty String as a name since that has
been validated against.
On Schedule open, with every event loaded into an array, each occurrence can be looked up
(using a modification of the cell lookup function in place). As each occurrence is registered,
the relevant place is found within the (empty) hash table, and the space updated with the ID
of the event that takes place then.

Load
The Schedule will access the hash table to generate the cells needed. Each cell uses its
column and row number to access the table (with the appropriate level of shift to account
for the title rows/columns). The access immediately returns the event, if any, that is stored
there.
As it is the ID, a Realm call can be performed immediately, which will return the event,
where it can then be used like normal by the cell.

Candidate Number - 4164 169 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Conflict Handling
The hash table implementation is ultimately to prevent occurrences from appearing at once.
The most important feature, arguably, is the check performed as to whether the
occurrences the user is attempting to add are already in use.
In any case, the program will have to ensure it does not return a conflict where the conflict
is the event being modified. Therefore, a simple presence check is not enough; analysis of
the data is required. This may be as basic as a check of whether the data found is the event
being modified or an empty string (or whatever the default may be).
There are two ways of assessing conflicts.
The first is to consider all selected points when the user submits, and then collect all
conflicts, along with the conflicting event. A message is then returned to the user informing
them that these times cannot be selected because they are in use by another event.
This method will likely return very long messages, and it will be difficult to write it compactly
without affecting the ease of reading, and most importantly, understanding, the message
contents.
The other option is to analyse the places taken up in the table beforehand, and then ‘grey-
out’ the cells, preventing them from being selected. While this shows the user immediately
the cells that have been taken up, it does not inform them by which event.
I prefer the second method, as it clear indicates the space in use, without the user having to
try repeatedly to submit an event.
To overcome the issue of the user not knowing which events are in use, I may be able to
remodel the cell, where the text instead displays the name of the event that is using that
time, rather than the time itself.

Candidate Number - 4164 170 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Design and Development
The expected class structure:

This feature only address Success Criteria point 3.3.2 (conflict handling). Otherwise, I will
include my own testing plan unrelated to the Criteria.
Test Type Description Data Expected
No. Outcome
1 Functional An event will be added to the table Events The table takes
to ensure it can take events the event and
stores its
occurrence(s)
2 Functional Test of whether conflicts are Events The Occurrences
handled. A conflict event will be picker will not
added to the Schedule. allow the invalid
event to be added
3 Boundary The table will be stress tested to see Events The table takes
whether it can take an event for the events with
every space available no problem and
displays them as
usual.

Table Generation
Generation works on the function already in place in the view controller. It will be copied to
the hash table class and used within it to generate the data within the table. The table itself
will be hardcoded within the class.
It will look like this:
var table = [
//monday
0: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"",
11:"", 12:"", 13:"", 14:"", 15:"", 16:"", 17:"", 18:"", 19:"", 20:"", 21:"",
22:"", 23:""],
//tuesday

Candidate Number - 4164 171 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
1: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"",
11:"", 12:"", 13:"", 14:"", 15:"", 16:"", 17:"", 18:"", 19:"", 20:"", 21:"",
22:"", 23:""],

2: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"",
11:"", 12:"", 13:"", 14:"", 15:"", 16:"", 17:"", 18:"", 19:"", 20:"", 21:"",
22:"", 23:""],
3: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"",
11:"", 12:"", 13:"", 14:"", 15:"", 16:"", 17:"", 18:"", 19:"", 20:"", 21:"",
22:"", 23:""],
4: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"",
11:"", 12:"", 13:"", 14:"", 15:"", 16:"", 17:"", 18:"", 19:"", 20:"", 21:"",
22:"", 23:""],
5: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"",
11:"", 12:"", 13:"", 14:"", 15:"", 16:"", 17:"", 18:"", 19:"", 20:"", 21:"",
22:"", 23:""],
//sunday
6: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"",
11:"", 12:"", 13:"", 14:"", 15:"", 16:"", 17:"", 18:"", 19:"", 20:"", 21:"",
22:"", 23:""],
]
The generation then works by going through every event and adding its occurrences.
func populateTable(eventsDict: [String: [[Int]]]) -> [Int: [Int: String]]{

for event in eventsDict{


var count = 0
for day in event.value{
for hour in day{
table[count]![hour] = event.key
}
count += 1
}
}

return table
}

For the given events, the output is:


0: [13: "", 10: "", 3: "", 15: "", 18: "", 0: "Event 1", 8: "", 17: "", 12: "", 5:
"", 11: "", 20: "", 22: "", 23: "", 2: "Event 1", 21: "", 14: "", 19: "", 7: "",
9: "", 1: "", 16: "", 6: "", 4: "Event 1"]

Candidate Number - 4164 172 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
While the day and relative positions of the hours are correct, the precise values are not. This
is because I did not take into account the time difference. I will add a modification to pass
that variable through and accommodate for it.
The output now is:
0: [0: "", 9: "", 17: "", 22: "", 10: "Event 1", 23: "", 7: "", 4: "", 14: "", 15:
"", 6: "", 18: "", 8: "Event 1", 13: "", 19: "", 20: "", 5: "", 3: "", 11: "", 21:
"", 1: "", 12: "Event 1", 2: "", 16: ""]

which is as expected (event 1 is at times 8, 10, 12).


I believe the table generation is now complete.

Table Loading
To use the table now to load cells, I will make a small function to the cell generation
function.
Returning the ID of an event is incredibly simply. It consists simply of this:
func idAtPosition(column: Int, row: Int) -> String{
return table[column]![row]!
}

To call this function, the implementation is such:


let text = hashTable.idAtPosition(column: indexPath.column-1, row:
indexPath.row+(settings.lowerBound)-2)

This takes into account the shifting required by the table.


I tested this by viewing the table and attempting navigation (two places where the call is in
place). Both are working as expected.

Conflict Handling
I added the following code to the Occurrences cell generation:
let ID = hashTable.idAtPosition(column: indexPath.section, row:
indexPath.row+settings.lowerBound)

if (ID != "" || ID != name){


cell.isUserInteractionEnabled = false
cell.contentView.alpha = 0.5
let col = uiRealm.object(ofType: RepeatingEvent.self, forPrimaryKey:
name)?.colour
cell.nameLabel.textColor = UIColor(hex: col!)
}
else{ //default action
cell.nameLabel.text = String(describing: hours[indexPath.row])
}

On testing, no effect seemed to have been applied.


The reason appears to be that the same table generated at the start has not been accessed.
Rather, the empty table is in use, and therefore no conflicts are being shown.

Candidate Number - 4164 173 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
I have decided the easiest way to handle this is by using a global variable for the hash table.
I have no concerns of any variable conflicts as the hash table has a unique variable name
within this project, and there is nowhere where another instance of the hash table is being
used.
Should this change, I may modify how the hash table is used.
While now the cells that are not to be selected show, there are also more generated within
other sections, where they should not be. I believe this to be a side effect to using reusable
cells – they carry over the properties they once had unless undone.
For that reason, I will make a modification where, in the else clause, the interaction is
enabled and the colour reset.
Upon testing, this appears to work correctly, however, the event colour does not load. It
returns nil, which is surprising as the call should return the event, and therefore its colour.
Upon further investigation, the reason for the nil return was because I was testing by
opening the Occurrences from a new event, which by definition has no name.
For the handler to work correctly, I have had to consider the two branches: an existing event
has been navigated from, or a new event has been navigated from.
The function can work as it is without having to consider these two, however I wish the
existing event name and colour to show in the case of a new event, and an existing one to

have the hour selection as usual. My initial attempt is making little progress, so I will roll
back the changes, and redesign the feature from the ground up. The basis of the feature is
as follows:

Candidate Number - 4164 174 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

This prevents in-use cells from being selected. The next part is when an event is being
updated, its use within the table must be reconfigured – removing spaces where it was in
place. I have rewritten the hash generator function to be better encapsulated. Views only
call the method and it carries out everything else it must. However, this has meant that the
function may not be as efficient, as it has to generalise to accommodate all possible usage.
Now, whenever the method is called, the table is reset using a template.
It is then re-updated with every item in the database (with the database conversion being
carried out within the class, rather than passed in as a variable).
This does mean that on edit and delete of event, the table does not include now-unused
spaces. Essentially, deleting of event occurrences works correctly.
The next step is to recreate the collision prevention mechanism, as in its current state,
collisions only lead to a rewrite by the most recently edited event, without any information
to the user.

Candidate Number - 4164 175 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The algorithm to decide will be as follows:

I coded this up like this:


(Note the move of empty cell generation to the start, as it is applicable to both.)

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "timesCell", for: indexPath) as! OccurencesCell

let itemInTable = globalTable[indexPath.section]![indexPath.row+settings.lowerBound]

if itemInTable == ""{ //empty position, generate all first


cell.nameLabel.text = String(describing: hours[indexPath.row])
cell.nameLabel.textColor = UIColor.black
cell.isUserInteractionEnabled = true
}
else{ //position is filled
if eventName == itemInTable{ //action to carry out in the case the item matching the event passed
cell.nameLabel.text = String(describing: hours[indexPath.row])
cell.nameLabel.textColor = UIColor.black
cell.isUserInteractionEnabled = true
}

Candidate Number - 4164 176 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
else{ //cell taken up by another event
cell.nameLabel.text = itemInTable
cell.nameLabel.textColor = UIColor(hex: uiRealm.object(ofType: RepeatingEvent.self, forPrimaryKey:
itemInTable)!.colour)
cell.tintColor = UIColor.lightGray
cell.isUserInteractionEnabled = false
}
}

return cell
}

Testing consist of opening the picker and seeing what selections can be made.
Testing of the deletion is carried out by removing occurrences and deleting events.
I have tested the possible use cases – adding a new event with no existing events, modifying
an existing event with no other existing events, adding a new event with existing events,
and modifying an event with existing events.
These will be cases 1 to 4.
In case 1, the entire table was clear, which was as expected. I was able to add an event with
no problems.
In case 2, the event had its selected times highlighted, which were selectable and
changeable. Upon removing some times, I returned to the Schedule to see it had updated
correctly.
(Case 3): I then added another event. This time, the Occurrences showed the name of the
event in palace of the time, and would not allow me to select the time.
In case 4, I then modified an event. I was allowed to edit the times selected, and the times
taken by the other event displayed correctly.
It appears that all is working well. To ensure, I deleted the events, and found the schedule
cleared.
I believe the collision detection feature is complete.

Evaluation
Focus Group: They were quite pleased to hear that I had worked to include this feature to
address the issues present off the end of the Schedule cycle. The application at the time
provided no framework to manage events occurring together.
The focus group carried out extensive usability testing for me, in the spirit of attempting to
break the application. This was, in fact, a good thing, as it appears that six dedicated
individuals were unable to cause any issues. It bodes well for use by a typical user.
That does not mean that the application is without bugs, however the focus group were
unable to dig any up regarding the collision detection, despite trying very hard.

Candidate Number - 4164 177 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The main reason for this, I would say, is that – as with many features of the application – a
significant amount of control is removed from the user. They are unable to test the
application to its limits, as all data input is heavily controlled by the application.
With the Occurrences for example, the user can only select the times they wish. They are
given no freedom to input potentially damaging data (i.e. types that cannot be processed).
This restriction prevents misuse, and also makes the user experience very smooth.

Candidate Number - 4164 178 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Evaluation
Overview
My overall takeaway from developing this application is that it was far more complex than I
had anticipated. This came to be a fundamental issue throughout the entire development
process. My initial plan was far too ambitious for what is realistically possible within the
time period available to develop this application. I had initially intended to include far more
features within this application, however I came to find that I had to simplify the project
significantly to ensure that it could be completed.

While there was no one feature that took an inordinate amount of time, the Schedule and
Events required a large amount of work to complete to an acceptable standard. Even then, I
had not included any collision detection on the first run through. I found that it was possible
to keep thinking of features to add.
They build up and begin to overcomplicate the relevant view controllers. On that note, I
could have structured my solution to better adhere to my architectural plan (Model-View-
Controller).

Of the Success Criteria points I set out, the ones I attempted I broadly met, with the
exception of the Agenda, as I decided to remove that in the end.

All points required for the Schedule and Events were met, which was the most important
feature of the application, as it forms the very core. I was able to include some auxiliary
features within this (the colour picker, for example). Some features were missing however –
the alarms and notifications in particular.

For their implementation, much more work would have been required with integrating the
application with iOS’ notification framework. Furthermore, they had to trigger at a specific
time, requiring analysis of event occurrence. The biggest issue was that they had to repeat
for every event. iOS restricts excessive notifications, which would have killed that particular
aspect of functionality.

Similarly, the Do-Not-Disturb mode could not even be attempted for much the same reason.
iOS sandboxes applications run on the platform. This means they have no access to anything
else on the device, besides what is explicitly given permission by the software. Accessing
hardware of other application must be done through the correct channels. If those channels
do not exist, then the attempted feature cannot be created. This is the case I had with the
Do-Not-Disturb mode.
I wished to affect a system-wide silent mode, however I was restricted as the operating
system provides no access to the hardware or software required to enact it.

Candidate Number - 4164 179 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Success Criteria
With regards to specific implementation, I will discuss the Success Criteria points
specifically:

1. Agenda – Although an attempt at implementation was made, in the end I removed


the feature as it was proving too buggy to include in the final version of the
application.
While it did include some interesting features (the linked list queue for example), it
failed to work correctly as a whole view.
The queue also included some serious issues regarding node deletion. The setup
required that the node objects were not optional (specified by using an exclamation
mark after the variable to represent a node). What this meant is that the node could
not then be cleared, as it had to exist. Thus, the conundrum of deleting arose, as I
was attempting to remove an object that had to exist.
I found no way of solving the issue within Swift’s framework. I also could not simple
use the queue as was, since the nodes remained in memory. This could have led to
memory leaks, and ultimately an application crash.
The Agenda items were also meant to have a timer to show when the event would
occur, however Swift does not easily support a live ticker (in fact, the programming
for a ticker has to be done in Objective-C, as Swift literally did not support its
function). This falls into the same problem of the notifications, where event time
analysis must be performed, and repeatedly so for every event.
Altogether, this feature had to be removed as it raised far too many issues to solve.
2. Timetable – The core of the application has more or less been completed
successfully, without including the use of Single Events. Initially, collision detection
was also not in place, which would have been a serious oversight to end the
application on. The Schedule includes many features that hit points I initially set out
intending to achieve, along with, I thought, impressive ways of handling the
Regarding Single Events, I felt unable to include them as the structure I created for
the timetable was heavily centred around Repeating Events. I felt unable to include
Single Events for that reason.
3. Events – the events themselves contain a number of features that I was quite
pleased with how they turned out. The colour and occurrences pickers in particular,
as each offers a very clean and simple method of carrying out the selection
necessary.
The Events, like the Agenda, are missing a major component – the notifications.
Single Events also do not exist; I have discussed the reasons for this.
To be able to include a feature like Single Events, more emphasis will need to be
placed on the date of the current week, and events that are to occur then only. A
record will have to be kept of past and future events.
The restructure will allow for the user to navigate through past and future versions

Candidate Number - 4164 180 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
of the schedule. This adds a whole new level of functionality to the application, and
brings it more in line with a calendar (which brings into question the necessity of the
feature, however that is beside the point).
By focusing more on specific weeks over a general approach to all, the repeating
events can be restructured to be more dynamic, rather than appearing statically
each and every week. This brings two issues of its own that must be solved – what
input the user may require to enter the event, and how the application understands
the data (along with handling collisions, as a 2D hash table will no longer work).
Events are also restricted to single hour appearances only. A possible improvement
is to handle events stretched over multiple hours, and handling multiple events
happening at once (as it is possible for this to happen). The user is restricted to hour
blocks only, which is an issue where an event may only last, say, thirty minutes.
This would require a fundamental change in how I have approached the problem to
implement, as the events are now far more varied. A smart, dynamic understanding
of events are required, and may require that I create a custom table for it – I do not
believe the Spreadsheet kit will be able to handle the task any longer. This in itself is
quite a major change, and potentially the basis for a new project with different
requirements set out from the start.
4. Alarms and notifications – as discussed, these could not be included due to my
application structure, where to include them would likely require restructuring of
how the entire application works.
As mentioned above, a focus on individual weeks could lead about to this change,
where the times can be taken into account to a greater degree.
The alarms may also be a difficult feature to include at all, for much the same
reasons the Do-Not-Disturb could not be included. iOS restricts external hardware
usage, and this impacts how well the alarms may work. They certainly could not be
implemented in a way similar to how the alarms application already included on the
device works. In fact, I believe that the alarm may not even trigger if the application
is closed, which defeats the point of the alarms entirely.
5. Do-Not-Disturb – as discussed, these could not be included due to iOS application
restrictions. The sandboxing prevents access of other applications/software (so
notifications cannot be blocked), whereas hardware restrictions prevent any form of
silencing.
With many of the feature I decided could not be included, it ultimately comes down
to the fact that the feature exists elsewhere on the device in some form already.
DND modes, for example, have been introduced to iOS (along with other application
usage management), while alarms and calendars are already in existence.
Therefore, the fact remains that the features are not necessarily all that useful as
they emulate features already in use.
6. Notes – These were included and met all Success Criteria points. The basic
necessities are present, namely viewing all notes and creating notes.

Candidate Number - 4164 181 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
The notes also includes additional features, like sorting (alphabetically or
chronologically) and searching.
The search remains in a very basic form, only working through matching the input
string to titles or the contents. No form of advanced search exists (case matching,
word wrapping, search by date etc.)
Furthermore, no formatting to the text has been applied. While there is support for,
say, italicised text within iOS, I cannot say for certain whether the database supports
it. Text formatting is, at the end of the day, an aesthetic feature. This can be
achieved by the application processing the text input using markdown. Markdown
allows text formatting, by encasing the formatted text with identifiers. For example,
“*italics*” would be the string stored in the database and loaded by the application,
however the output to the fields, and therefore what the user sees, is italics. This
requires a number of steps, chiefly string interpretation and processing of the entire
text, and options to allow the user to select the formatting options (most likely
achieved through a segment view). This feature, although a nice addition, is hardly a
core component to the application. Furthermore, the notes are not meant to be
used very extensively, and formatting options are more suited to applications that
focus around a note-taking mechanic. Essentially, the feature is significant overhead
and largely unnecessary. However, it does remain an addition that would improve
the application.
7. Goals and Insights – two features that unfortunately could not be included due to
the overall complexity entailed. Together, they would have carried out many
complex tasks, including data mining and a basic form of machine learning as the
application begins to learn the user’s habits. This of course brings with it problems as
complex as the features themselves. Even a rudimentary form of machine learning is
more than enough to be the an entire project of its own, while data mining bring
potential Data Protection issues, especially where the data could be personally
identifying. Although features that would have been a great addition to the
application, I believe that they may have been outside the possible scope of a project
of this size.
8. Intuitive Use – I structured the application to be as easily accessible by any user, as
the potential user could be of any age and have any experience (even if the majority
of my users are between 16-24).
I have made many of the interactable items selection based. This means the the user
does not have to input any data manually. They simply select what is most
appropriate for them.
This contains two benefits:
a. The user has an easy time of data input
b. No validation is required as the input data cannot change, and in some cases
(the Settings for example), a value is guaranteed to exist.

Candidate Number - 4164 182 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Ultimately, this means that by focusing the application on intuitive use, both the
user and developer benefit.
9. Data Storage – Some Data Protection issues were mitigated by using an offline
database. No security measures need to be taken on my part as the data is protected
by the device. Furthermore, I do not collect any personally identifying information,
so it does not matter if the data is leaked.
A drawback to using an offline database is that the user only has the local copy of
their data and no backups. However, this is, in my opinion, a small issue – especially
since I am not providing cross-device support.

Maintenance
I believe the application requires little real-time maintenance. While of course, feature may
become dated and require updating to keep them relevant, and there are always
improvements that can be made to enhance the application (i.e. it is never finished and
constantly being updated/maintained), I believe this application requires no maintenance to
keep it working on user devices.

The reason for this is that the application makes no use of any external features that may
require maintenance themselves. Had I used Firebase as a database solution, for example, I
may have had to plan for how Firebase use may change, and be aware of the possibility of
the service shutting down. This is due to it being an online service, and potentially under
constant changes itself. With Realm, and many of the other external APIs and Pods used,
these are all offline copies and stored locally, meaning they remain unchanged. As they are
unchanged, no work needs to be done to maintain them.

These features may update in the future, which means I may update them within my
application, which leads to some work to ensure that everything is working as intended
within the application.

One particular long-term potential maintenance issue is the application being supported on
future versions of iOS. Some of the features I have used made some usage of Swift 3. While
this is currently supported, it may not remain so. In that case, the feature may have been
updated, and I will carry out the necessary updating steps. However, if the external feature
is not maintained, then I will either have to update it myself to the latest Swift version, or
find another method of solving the problem it tackled previously.
This updating problem only arises as iOS progresses with Swift, and the language is
improved. This problem is somewhat mitigated by intermediate version of Xcode, as they
support both the latest and the previous generation of Swift. The reason for doing so is to
retain a library of the difference, so that the old version may be updated to the syntax of the
new version. This is handled largely automatically (with changes made only need be
confirmed by the developer), so will likely be a small issue – if at all.

Candidate Number - 4164 183 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Limitations of My Solution
My solution, at its most basic accomplishes the goal I set out to achieve – provide an
application for organisation in a student’s life.
However, it does contain a number of drawbacks. The two largest concerning this goal are
the lack of reminders, and the inflexibility of the hours.
The reminders take a large amount of the thought required about scheduling off the user.
Essentially, it allows the user to ‘create and forget’ about the event. This assumes the user
leaves enough time to react to the reminder, and also sees it/does not ignore it (at which
point it becomes useless).
Meanwhile, the hour inflexibility prevents the user being able to truly tailor the application
around their lifestyle, as not every event lasts exactly an hour and occurring at perfect
round numbers. This is potentially the largest drawback to making the application truly
useful in all circumstances, as many event are prevented from being accurately added.

Conclusion
Overall, this project had many vicissitudes. My take away from it, most of all, is that I vastly
underestimate the complexity of certain computational features. This may be the result of
numerous high quality applications being widely available, which have left me without the
true appreciate these complex features demand.
This caused me to be far too ambitious with my initial project plan, as many of the features I
wished to include could very well have formed projects of their own. This, of course, was
detrimental to the progress of the project, as I spent a number of hours on features I could
not use in the end, whether this was dedicated to researching or programming, the fact
remains that time was not recovered, and was not spent on features that more realistically
could be used to improve the application.
I did learn a great deal throughout the course of this project. The benefits of proper
planning and time management for two, and a better understanding of various
programming techniques through my implementation of them within the application.
My final takeaway is a greater appreciation of the computer systems around us, and to not
underestimate the true complexity of the programming underneath – especially when
attempting to replicate it myself.

Candidate Number - 4164 184 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

Source Code

Model

App Delegate
//
// AppDelegate.swift
// timetable
//
// Created by Juheb on 30/10/2018.
// Copyright © 2018 Juheb. All rights reserved.
//

import UIKit
import RealmSwift

let uiRealm = try! Realm() //instantiate realm

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions


launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}

func applicationWillResignActive(_ application: UIApplication) {


// 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.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics
rendering callbacks. Games should use this method to pause the game.
}

func applicationDidEnterBackground(_ application: UIApplication) {


// 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.
// If your application supports background execution, this method is called instead of
applicationWillTerminate: when the user quits.
}

func applicationWillEnterForeground(_ application: UIApplication) {


// Called as part of the transition from the background to the active state; here you can
undo many of the changes made on entering the background.

Candidate Number - 4164 185 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
}

func applicationDidBecomeActive(_ application: UIApplication) {


// 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.
}

func applicationWillTerminate(_ application: UIApplication) {


// Called when the application is about to terminate. Save data if appropriate. See also
applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application
terminates.

Note Model
//
// Note Model.swift
// timetable
//
// Created by Juheb on 14/03/2019.
// Copyright © 2019 Juheb. All rights reserved.
//

import Foundation
import RealmSwift

class NoteData: Object {

@objc dynamic var id = ""

@objc dynamic var title = ""


@objc dynamic var age = ""
@objc dynamic var body = ""

override static func primaryKey() -> String? {


return "id"
}
}

Settings Model
//
// Settings Model.swift
// timetable
//
// Created by Juheb on 14/03/2019.
// Copyright © 2019 Juheb. All rights reserved.

Candidate Number - 4164 186 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
//

import Foundation
import RealmSwift

class SettingsStore: Object{


@objc dynamic var id = "1"
@objc dynamic var twentyFour = false
@objc dynamic var lowerBound = 6
@objc dynamic var upperBound = 17

override static func primaryKey() -> String? {


return "id"
}

Event Model
//
// Event Model.swift
// timetable
//
// Created by Juheb on 14/03/2019.
// Copyright © 2019 Juheb. All rights reserved.
//

import Foundation
import RealmSwift

class RepeatingEvent: Object{

@objc dynamic var id = ""

@objc dynamic var name = ""


@objc dynamic var colour = ""
let week = List<Day>() //collection of all the occurences of a repeating item in a week
@objc dynamic var desc = ""
@objc dynamic var priority = 0

override static func primaryKey() -> String? {


return "id"
}

class Day: Object { //the column the event appears in


let dayItem = List<Hour>()
}

class Hour: Object { //the row the event appears in


@objc dynamic var hourItem = Int()

Candidate Number - 4164 187 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
}

extension Results {
func toArray() -> [Any] { //put items into an array
return self.map{$0}
}

extension RealmSwift.List {
func toArray() -> [Any] {
return self.map{$0}
}
}

View Controllers

Settings
//
// SettingsViewController.swift
// timetable
//
// Created by Juheb on 03/12/2018.
// Copyright © 2018 Juheb. All rights reserved.
//

import UIKit
import SwiftRangeSlider

class Settings: UIViewController {

@IBOutlet weak var formatSwitch: UISwitch!

@IBOutlet weak var rangeSlider: RangeSlider!

@IBOutlet weak var hoursLabel: UILabel!

var settings = SettingsStore()


var lowerVal = Double()
var upperVal = Double()

override func viewDidLoad() {


super.viewDidLoad()

settings = uiRealm.object(ofType: SettingsStore.self, forPrimaryKey: "1")!

setupSlider(lower: settings.lowerBound, upper: settings.upperBound) //setup slider

navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save,


target: self, action: #selector(saveTapped)) //add save button

Candidate Number - 4164 188 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

lowerVal = rangeSlider.lowerValue
upperVal = rangeSlider.upperValue

if settings.twentyFour == true{
formatSwitch.isOn = true
setupLabel()
}
else {
formatSwitch.isOn = false
setupLabel()
}
// Do any additional setup after loading the view.

@objc func saveTapped(){ //funtion called when save button tapped


//update realm
try! uiRealm.write { //update within a transaction

settings.lowerBound = Int(rangeSlider.lowerValue)
settings.upperBound = Int(rangeSlider.upperValue)
if formatSwitch.isOn == true{
settings.twentyFour = true
} else { settings.twentyFour = false }

uiRealm.add(settings, update: true)


}

@IBAction func rangeSliderChanged(_ sender: RangeSlider) {


lowerVal = rangeSlider.lowerValue
upperVal = rangeSlider.upperValue
setupLabel()
}

@IBAction func formatSwitchPressed(_ sender: UISwitch) {


//update persistent storage
setupLabel()
}

@IBAction func deleteAllButtonPressed(_ sender: UIButton) {


let repeating = uiRealm.objects(RepeatingEvent.self)
let notes = uiRealm.objects(NoteData.self)

//Setup alert
let alert = UIAlertController(title: "Warning", message: "This action is not reversible",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))

Candidate Number - 4164 189 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
alert.addAction(UIAlertAction(title: "Delete", style: .destructive, handler: { action in
//Delete event if user confirms
try! uiRealm.write {
uiRealm.delete(repeating)
uiRealm.delete(notes)
}
}))

self.present(alert, animated: true)

func setupSlider(lower: Int, upper: Int){


rangeSlider.lowerValue = Double(lower)
rangeSlider.upperValue = Double(upper)
}

func setupLabel(){
hoursLabel.text = "\(setupTimes(time: Int(lowerVal))) to \(setupTimes(time:
Int(upperVal)))"
}

func setupTimes(time: Int) -> String{


var string = ""

if formatSwitch.isOn == true{
//generate 24 hour times
if time < 12{
string = "0\(time):00"
}
else{
string = "\(time):00"
}
}
else{
//generate 12 hour times
if time < 12{
if time == 0{
string = "12:00am"
}
else{
string = "\(time):00am"
}
}
else{
if time != 12{
string = "\(time-12):00pm"
}
else{ string = "12:00pm"}
}
}
return string
}

Candidate Number - 4164 190 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
}

Agenda
//
// Agenda.swift
// timetable
//
// Created by Juheb on 30/10/2018.
// Copyright © 2018 Juheb. All rights reserved.
//

import UIKit
import SideMenu

class AgendaViewController: UIViewController{

@IBOutlet weak var dateLabel: UILabel!

override func viewDidLoad() {


super.viewDidLoad()

setupSideMenu() //left menu setup


dateLabel.text = DateFormatter.localizedString(from: Date(), dateStyle: .long, timeStyle:
.short)

override func viewWillAppear(_ animated: Bool) {


super.viewWillAppear(true)

dateLabel.text = DateFormatter.localizedString(from: Date(), dateStyle: .long, timeStyle:


.short)

//MARK: Menu setup


fileprivate func setupSideMenu() {

typealias m = SideMenuManager //typealias SideMenuManager to ease typing

// Define the menus


m.default.menuLeftNavigationController =
storyboard!.instantiateViewController(withIdentifier: "LeftMenuNavigationController") as?
UISideMenuNavigationController

// Enable gestures. The left and/or right menus must be set up above for these to work.
// Note that these continue to work on the Navigation Controller independent of the
View Controller it displays!
m.default.menuAddPanGestureToPresent(toView:
self.navigationController!.navigationBar)

Candidate Number - 4164 191 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
m.default.menuAddScreenEdgePanGesturesToPresent(toView:forMenu:) //gestures for
one menu

m.default.menuFadeStatusBar = false //fix black menu

m.default.menuPushStyle = .popWhenPossible //If a view controller already in the stack


is of the same class as the pushed view controller, the stack is instead popped back to the
existing view controller. This behavior can help users from getting lost in a deep navigation
stack.

m.default.menuPresentMode = .viewSlideInOut //The existing view slides out while the


menu slides in.

Notes
//
// Note.swift
// timetable
//
// Created by Juheb on 12/11/2018.
// Copyright © 2018 Juheb. All rights reserved.
//

import UIKit
import RealmSwift

class Notes: UIViewController, UITextFieldDelegate, UITextViewDelegate,


UINavigationControllerDelegate {

@IBOutlet weak var titleField: UITextField!


@IBOutlet weak var bodyField: UITextView!

var addNoteSegue: Bool! //check whether adding or modifying note


var noteId: String! //ID has to be string or int
var currentNote = NoteData() //instantiate note and write the values to database

let alertController = UIAlertController(title: "title", message: "message", preferredStyle:


.alert)
let dismissAction = UIAlertAction(title: "Dismiss", style: UIAlertAction.Style.default,
handler: nil)

//MARK: Setup
override func viewDidLoad() {
super.viewDidLoad()
alertController.addAction(dismissAction)

if addNoteSegue == false{ //load data if user tapped on cell

Candidate Number - 4164 192 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
currentNote = uiRealm.object(ofType: NoteData.self, forPrimaryKey: noteId)! //get
note by primary key

titleField.text = currentNote.title
bodyField.text = currentNote.body
}

titleField.delegate = self
bodyField.delegate = self

titleField.setBottomBorder() //add border style to title text field

//MARK: Save
@IBAction func saveButtonPressed(_ sender: UIBarButtonItem){
if (titleField.text == "" || bodyField.text == "") { //check if empty

alertController.title = "Warning"
alertController.message = "Please leave no empty fields when saving"
self.present(alertController, animated: true, completion: nil)
//if empty, present warning alert
}
else if (bodyField.text == "Note Description" || titleField.text == "Title"){
alertController.title = "Warning"
alertController.message = "Please add your own text or cancel"
self.present(alertController, animated: true, completion: nil)
//if left to default, present warning alert
}
else{

let currentDateTime = String(describing: Date(timeIntervalSinceNow: 1)) //convert


current date and time into string

if addNoteSegue == true{

try! uiRealm.write { //place all updates within a transaction


currentNote.title = titleField.text!
currentNote.body = bodyField.text!
currentNote.age = currentDateTime
currentNote.id = currentDateTime //write id as primary key for new note, set as
time of initial creation

uiRealm.add(currentNote)
}
}
else{
try! uiRealm.write { //place all updates within a transaction
currentNote.title = titleField.text!
currentNote.body = bodyField.text!
currentNote.age = currentDateTime

uiRealm.add(currentNote, update: true) //updates object


}

Candidate Number - 4164 193 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
}

self.navigationController!.popViewController(animated: true)
}

//MARK: Cancel
@IBAction func cancelButtonPressed(_ sender: UIBarButtonItem){
self.navigationController!.popViewController(animated: true)
}

//MARK: Text Field


func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()

return false
}

func textFieldDidBeginEditing(_ textField: UITextField) { //automatically remove text


if (textField.text == "Title"){
textField.text = ""
}
}

func textViewDidBeginEditing(_ textView: UITextView) { //automatically remove text


if (textView.text == "Note Description"){
textView.text = ""
}
}

View All Notes


//
// NotesTVC.swift
// timetable
//
// Created by Juheb on 12/11/2018.
// Copyright © 2018 Juheb. All rights reserved.
//

import UIKit
import RealmSwift

class ViewAllNotes: UITableViewController, UISearchResultsUpdating {

Candidate Number - 4164 194 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

//MARK: Variables & Constants


var tappedId = String()
var allNotes = [NoteData]()
var filteredNotes = [NoteData]()
let sort = Sorts()
let searchController = UISearchController(searchResultsController: nil)
let formatter = DateFormatter()

//MARK: Setup
override func viewDidLoad() {
super.viewDidLoad()

tableView.rowHeight = 80

searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = false
searchController.dimsBackgroundDuringPresentation = false
tableView.tableHeaderView = searchController.searchBar
//defines the search bar's actions

formatter.dateFormat = "yyyy/MM/dd HH:mm:ss zzzz" //set date format

override func viewWillAppear(_ animated: Bool) {


super.viewWillAppear(true)

allNotes = uiRealm.objects(NoteData.self).toArray() as! [NoteData] //add all note items


to allNotes array
filteredNotes = allNotes

self.tableView.reloadData()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
searchController.dismiss(animated: false, completion: nil)
}

// MARK: - Table view data source


override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -


> Int {
guard let notes = filteredNotes as Optional else {
return 0
}
return notes.count
//displays as many notes as there are in filteredNotes
}

Candidate Number - 4164 195 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) ->
String?{
return "All Notes"
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -


> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "notesCell", for: indexPath) as!
NotesCell

let fullnote = filteredNotes[indexPath.row]


cell.configureCell(note: fullnote) //function configures each cell
return cell
}

// Override to support conditional editing of the table view.


override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -
> Bool {
return true
}

// Override to support editing the table view.


//MARK: Row modification
override func tableView(_ tableView: UITableView, commit editingStyle:
UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath){
if editingStyle == .delete{
let noteToDelete = (filteredNotes[indexPath.row]) //get note object from filtered array
try! uiRealm.write {
uiRealm.delete(noteToDelete)
}
filteredNotes.remove(at: indexPath.row) //in case during search, since stored and
displayed notes are different
allNotes = uiRealm.objects(NoteData.self).toArray() as! [NoteData] //reset data after
deleting note
tableView.deleteRows(at: [indexPath], with: .left)
}

tableView.reloadData() //reload after delete


}

// Override to support rearranging the table view.


override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath:
IndexPath, to: IndexPath) {

// Override to support conditional rearranging of the table view.


override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath)
-> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}

//MARK: Search

Candidate Number - 4164 196 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
func updateSearchResults(for searchController: UISearchController) {
if let searchText = searchController.searchBar.text, !searchText.isEmpty {
filteredNotes = allNotes.filter { notes in
return (notes.body.lowercased().contains(searchText.lowercased()) ||
notes.title.lowercased().contains(searchText.lowercased())) //searches note title and body for
term, irrespective of case of letter
}
} else {
filteredNotes = allNotes
//since the tableview only displayed what has been searched (filtered), if there are no
searches, filtered = all
}
tableView.reloadData()
//reloads tableview with new data given by searching
}

//MARK: Sort
@IBAction func didSelectSort(_ sender: UISegmentedControl) {

var titleArray = [String]()


var ageArray = [Date]()

if sender.selectedSegmentIndex == 0{
// Date

for singleNote in filteredNotes{


let age = formatter.date(from: singleNote.age)
ageArray.append(age!) //convert ages into date types
}

if ageArray.count > 0{ //sort only if there are items in the array


ageArray = sort.mergeSort(ageArray)
ageArray.reverse() //flips sorting so most recent is at top of view

filteredNotes = []
for date in ageArray{
//match item to allnote object name and output item in correct position in filtered
array
let newItem = uiRealm.objects(NoteData.self).filter("age = '\(date)'")
filteredNotes.append(newItem.first!)
}
}

}
else{
// A-Z
for singleNote in filteredNotes{
titleArray.append(singleNote.title)
}
if titleArray.count > 0{
titleArray = sort.quickSort(titleArray)

filteredNotes = []

Candidate Number - 4164 197 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
for name in titleArray{
//match item to allnote object name and output item in correct position in filtered
array
let newItem = uiRealm.objects(NoteData.self).filter("title == '\(name)'")
filteredNotes.append(newItem.first!)
}
}
}
tableView.reloadData() //reload after sort
}

//MARK: Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

if segue.identifier == "showNote"{
if let indexPath = self.tableView.indexPathForSelectedRow { //get indexPath.row
here instead of did select row function
tappedId = (allNotes[indexPath.row] as AnyObject).id
}
let destinationVC = segue.destination as! Notes
destinationVC.addNoteSegue = false
destinationVC.noteId = tappedId

}
else if segue.identifier == "addNote"{

let destinationVC = segue.destination as! Notes


destinationVC.addNoteSegue = true
}

Notes Cell
//
// NotesCell.swift
// timetable
//
// Created by Juheb on 12/11/2018.
// Copyright © 2018 Juheb. All rights reserved.
//

import UIKit

class NotesCell: UITableViewCell {

@IBOutlet weak var titleLabel: UILabel!


@IBOutlet weak var descriptionLabel: UILabel!

override func awakeFromNib() {

Candidate Number - 4164 198 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
super.awakeFromNib()
// Initialization code
}

override func setSelected(_ selected: Bool, animated: Bool) {


super.setSelected(selected, animated: animated)

// Configure the view for the selected state


}

func configureCell(note: NoteData) {


self.titleLabel.text = note.title
self.descriptionLabel.text = note.body
}

Schedule
//
// ViewController.swift
// SpreadsheetView
//
//

import UIKit
import SpreadsheetView

class ScheduleView: UIViewController, SpreadsheetViewDataSource,


SpreadsheetViewDelegate {
@IBOutlet weak var spreadsheetView: SpreadsheetView!

var currentEvent = RepeatingEvent()


var allEvents = [RepeatingEvent]()
var settings = SettingsStore()
var hashTable = HashTable()
var row = 0
var column = 0 //to send row and column data to event view
var allDict = [String: [[Int]]]() //hold each event, its days, and occurences per day
var name = String()
let formatter = DateFormatter()

var dates = [String]()


let days = ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY",
"SATURDAY", "SUNDAY"]
let dayColors = [UIColor(red: 0.918, green: 0.224, blue: 0.153, alpha: 1),
UIColor(red: 0.106, green: 0.541, blue: 0.827, alpha: 1),
UIColor(red: 0.200, green: 0.620, blue: 0.565, alpha: 1),
UIColor(red: 0.953, green: 0.498, blue: 0.098, alpha: 1),
UIColor(red: 0.400, green: 0.584, blue: 0.141, alpha: 1),
UIColor(red: 0.835, green: 0.655, blue: 0.051, alpha: 1),
UIColor(red: 0.153, green: 0.569, blue: 0.835, alpha: 1)]
var hours = [String]()
let evenRowColor = UIColor(red: 0.914, green: 0.914, blue: 0.906, alpha: 1)

Candidate Number - 4164 199 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
let oddRowColor: UIColor = .white

@objc func addTapped(){ //funtion called when add button tapped


name = "" //set to empty to prevent opening last selected cell
performSegue(withIdentifier: "editEventSegue", sender: nil)
}
override func viewDidLoad() {
super.viewDidLoad()

settings = uiRealm.object(ofType: SettingsStore.self, forPrimaryKey: "1")!

setupTimes()

navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add,


target: self, action: #selector(addTapped))

spreadsheetView.dataSource = self
spreadsheetView.delegate = self

spreadsheetView.contentInset = UIEdgeInsets(top: 4, left: 0, bottom: 4, right: 0)


spreadsheetView.intercellSpacing = CGSize(width: 4, height: 1)
spreadsheetView.gridStyle = .none
spreadsheetView.register(DateCell.self, forCellWithReuseIdentifier: String(describing:
DateCell.self))
spreadsheetView.register(TimeTitleCell.self, forCellWithReuseIdentifier:
String(describing: TimeTitleCell.self))
spreadsheetView.register(TimeCell.self, forCellWithReuseIdentifier: String(describing:
TimeCell.self))
spreadsheetView.register(DayTitleCell.self, forCellWithReuseIdentifier:
String(describing: DayTitleCell.self))
spreadsheetView.register(ScheduleCell.self, forCellWithReuseIdentifier:
String(describing: ScheduleCell.self))

hashTable.populateTable(timeDifference: settings.lowerBound)
//load data

//MARK: Populate date headers of timetable


formatter.dateFormat = "dd/MM/yyyy"
let monday = Date().startOfWeek
let mondayString = formatter.string(from: monday!)
dates.append(mondayString)

var currentDate = monday


for _ in 0...6{
let nextDate = Calendar.current.date(byAdding: .day, value: 1, to: currentDate!)
let dateString = formatter.string(from: nextDate!)
currentDate = nextDate
dates.append(dateString)
}

//MARK: View Will Appear

Candidate Number - 4164 200 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
override func viewWillAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setupTimes()
spreadsheetView.flashScrollIndicators()

allEvents = uiRealm.objects(RepeatingEvent.self).toArray() as! [RepeatingEvent]


allDict = allEvents.addToDictionary()

//reload times

self.spreadsheetView.reloadData()
//reload data when view loads
}

// MARK: DataSource
func numberOfColumns(in spreadsheetView: SpreadsheetView) -> Int {
return 1 + days.count
}

func numberOfRows(in spreadsheetView: SpreadsheetView) -> Int {


return 2 + hours.count
}

func spreadsheetView(_ spreadsheetView: SpreadsheetView, widthForColumn column:


Int) -> CGFloat {
if case 0 = column {
return 70
} else {
return 120
}
}

func spreadsheetView(_ spreadsheetView: SpreadsheetView, heightForRow row: Int) ->


CGFloat {
if case 0 = row {
return 24
} else if case 1 = row {
return 32
} else {
return 40
}
}

func frozenColumns(in spreadsheetView: SpreadsheetView) -> Int {


return 1
}

func frozenRows(in spreadsheetView: SpreadsheetView) -> Int {


return 2
}

Candidate Number - 4164 201 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
func spreadsheetView(_ spreadsheetView: SpreadsheetView, cellForItemAt indexPath:
IndexPath) -> Cell? {

//c1-end,r0 - set the date of the columns


if case (1...(dates.count + 1), 0) = (indexPath.column, indexPath.row) {
let cell = spreadsheetView.dequeueReusableCell(withReuseIdentifier:
String(describing: DateCell.self), for: indexPath) as! DateCell
cell.label.text = dates[indexPath.column - 1]
return cell

//c1-end,r1 - set day names of columns


} else if case (1...(days.count + 1), 1) = (indexPath.column, indexPath.row) {
let cell = spreadsheetView.dequeueReusableCell(withReuseIdentifier:
String(describing: DayTitleCell.self), for: indexPath) as! DayTitleCell
cell.label.text = days[indexPath.column - 1]
cell.label.textColor = dayColors[indexPath.column - 1]
return cell

//c0,r1 - set cell name to TIME


} else if case (0, 1) = (indexPath.column, indexPath.row) {
let cell = spreadsheetView.dequeueReusableCell(withReuseIdentifier:
String(describing: TimeTitleCell.self), for: indexPath) as! TimeTitleCell
cell.label.text = "TIME"
return cell

//c0,r2-end - set hours of columns


} else if case (0, 2...(hours.count + 2)) = (indexPath.column, indexPath.row) {
let cell = spreadsheetView.dequeueReusableCell(withReuseIdentifier:
String(describing: TimeCell.self), for: indexPath) as! TimeCell
cell.label.text = hours[indexPath.row - 2]
cell.backgroundColor = indexPath.row % 2 == 0 ? evenRowColor : oddRowColor
return cell

//c1-end,r2-end (i.e. rest of the table) - set all other cells


} else if case (1...(days.count + 1), 2...(hours.count + 2)) = (indexPath.column,
indexPath.row) {
let cell = spreadsheetView.dequeueReusableCell(withReuseIdentifier:
String(describing: ScheduleCell.self), for: indexPath) as! ScheduleCell

let text = hashTable.idAtPosition(column: indexPath.column-1, row:


indexPath.row+(settings.lowerBound)-2)

if text != "" {
cell.label.text = text
var colour = UIColor()

for event in allEvents{ //get colour for event name


if event.name == text{
colour = UIColor(hex: event.colour)
break
}
}

cell.label.textColor = colour

Candidate Number - 4164 202 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
cell.color = colour.withAlphaComponent(0.2)
cell.borders.top = .solid(width: 1, color: colour)
cell.borders.bottom = .solid(width: 1, color: colour)
} else {
cell.label.text = nil
cell.color = indexPath.row % 2 == 0 ? evenRowColor : oddRowColor
cell.borders.top = .none
cell.borders.bottom = .none
}
return cell
}
return nil
}

/// Delegate

//MARK: Segueing to EventVC


func spreadsheetView(_ spreadsheetView: SpreadsheetView, didSelectItemAt indexPath:
IndexPath) {
if (indexPath.column > 0 && indexPath.row > 1){ //prevent locked cells performing
segue
name = hashTable.idAtPosition(column: indexPath.column-1, row:
indexPath.row+(settings.lowerBound)-2)
if name != ""{ //prevent empty cells
performSegue(withIdentifier: "editEventSegue", sender: nil)
}
}
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

if segue.identifier == "editEventSegue"{
let destinationVC = segue.destination as! EventVC
destinationVC.eventName = name //send event name
}

func setupTimes(){
hours = []
if settings.twentyFour == true{
//generate 24 hour times
for x in settings.lowerBound...settings.upperBound{
var string = ""
if x < 12{
string = "0\(x):00"
}
else{
string = "\(x):00"
}
hours.append(string)
}
}
else{

Candidate Number - 4164 203 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
//generate 12 hour times
for x in settings.lowerBound...settings.upperBound{
var string = ""
if x < 12{
if x == 0{
string = "12:00am"
}
else{
string = "\(x):00am"
}
}
else{
if x != 12{
string = "\(x-12):00pm"
}
else{ string = "12:00pm"}
}
hours.append(string)
}
}
}

Cells
//
// Cells.swift
// SpreadsheetView
//
// Created by Kishikawa Katsumi on 5/8/17.
// Copyright © 2017 Kishikawa Katsumi. All rights reserved.
//

import UIKit
import SpreadsheetView

class DateCell: Cell {


let label = UILabel()

override init(frame: CGRect) {


super.init(frame: frame)

label.frame = bounds
label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
label.font = UIFont.boldSystemFont(ofSize: 10)
label.textAlignment = .center

contentView.addSubview(label)
}

required init?(coder aDecoder: NSCoder) {


super.init(coder: aDecoder)

Candidate Number - 4164 204 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
}
}

class DayTitleCell: Cell {


let label = UILabel()

override init(frame: CGRect) {


super.init(frame: frame)

label.frame = bounds
label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
label.font = UIFont.boldSystemFont(ofSize: 14)
label.textAlignment = .center

contentView.addSubview(label)
}

required init?(coder aDecoder: NSCoder) {


super.init(coder: aDecoder)
}
}

class TimeTitleCell: Cell {


let label = UILabel()

override init(frame: CGRect) {


super.init(frame: frame)

label.frame = bounds
label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
label.font = UIFont.boldSystemFont(ofSize: 12)
label.textAlignment = .center

contentView.addSubview(label)
}

required init?(coder aDecoder: NSCoder) {


super.init(coder: aDecoder)
}
}

class TimeCell: Cell {


let label = UILabel()

override init(frame: CGRect) {


super.init(frame: frame)

label.frame = bounds
label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
label.font = UIFont.monospacedDigitSystemFont(ofSize: 12, weight:
UIFont.Weight.medium)
label.textAlignment = .right

Candidate Number - 4164 205 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
contentView.addSubview(label)
}

override var frame: CGRect {


didSet {
label.frame = bounds.insetBy(dx: 6, dy: 0)
}
}

required init?(coder aDecoder: NSCoder) {


super.init(coder: aDecoder)
}
}

class ScheduleCell: Cell {


let label = UILabel()
var color: UIColor = .clear {
didSet {
backgroundView?.backgroundColor = color
}
}

override var frame: CGRect {


didSet {
label.frame = bounds.insetBy(dx: 4, dy: 0)
}
}

override init(frame: CGRect) {


super.init(frame: frame)

backgroundView = UIView()

label.frame = bounds
label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
label.font = UIFont.boldSystemFont(ofSize: 12)
label.textAlignment = .left

contentView.addSubview(label)
}

required init?(coder aDecoder: NSCoder) {


super.init(coder: aDecoder)
}
}

Event
//
// Event.swift
//
//
// Created by Juheb on 11/11/2018.
//

Candidate Number - 4164 206 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

import UIKit

class EventVC: UIViewController, UITextFieldDelegate, UITextViewDelegate,


UIPickerViewDelegate, UIPickerViewDataSource{

//MARK: Variables
var repeatingEvent = RepeatingEvent()
var hashTable = HashTable()
var settings = SettingsStore()
var check = 1 //edit disabled
let priorities = ["Normal", "Important", "Urgent"]

//Sent variables
var eventName: String!
var repeating: Bool!

//Recieved variable
var occurences: [[Int]]?
var colour: String?

@IBOutlet weak var switchLabel: UILabel!


@IBOutlet weak var priorityLabel: UILabel!

@IBOutlet weak var nameLabel: UITextField!


@IBOutlet weak var descriptionLabel: UITextView!

@IBOutlet weak var repeatSwitch: UISwitch!


@IBOutlet weak var priorityPicker: UIPickerView!

@IBOutlet weak var colourPickerButton: UIButton!


@IBOutlet weak var occurenceButton: UIButton!

@IBOutlet weak var submitButton: UIButton!


@IBOutlet weak var deleteButton: UIButton!

override func viewDidLoad() {


super.viewDidLoad()

repeatSwitch.isHidden = true
switchLabel.isHidden = true

repeating = true

nameLabel.delegate = self
descriptionLabel.delegate = self
priorityPicker.delegate = self
priorityPicker.dataSource = self

occurences = [[], [], [], [], [], [], []] //setup occurences

if eventName == "" { //setup in all cases when a new event is being added

Candidate Number - 4164 207 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
//unlock interaction for fields when new event is being added
modifyInteraction(set: true)
submitButton.isUserInteractionEnabled = true
deleteButton.isHidden = true
occurenceButton.setTitle("Add Occurences (Day/Time)", for: .normal)
submitButton.setTitle("Create Event", for: .normal)
}
else{
repeatSwitch.isHidden = true //hide switch since user cannot modify after initial
seleection
switchLabel.isHidden = true

self.navigationItem.rightBarButtonItem = self.editButtonItem //add edit button to


modify data

repeatingEvent = uiRealm.object(ofType: RepeatingEvent.self, forPrimaryKey:


eventName)! //pull data about event

nameLabel.text = repeatingEvent.name //set data


descriptionLabel.text = repeatingEvent.desc
colour = repeatingEvent.colour
occurenceButton.setTitle("Edit Occurences (Day/Time)", for: .normal)
priorityPicker.selectRow(repeatingEvent.priority, inComponent: 0, animated: true)
submitButton.setTitle("Update Event", for: .normal)
occurences = weekToOccurences(event: repeatingEvent)

modifyInteraction(set: false) //disable interaction

}
}

//MARK: Picker view


func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component:


Int) -> Int {
return priorities.count
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent


component: Int) -> String? {
return priorities[row]
}

//MARK: Edit Button


override func setEditing(_ editing: Bool, animated: Bool){
super.setEditing(editing, animated: animated)

check += 1 //changes lock state

Candidate Number - 4164 208 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

if check % 2 == 0 { //enable interaction on edit button press


modifyInteraction(set: true)
submitButton.isUserInteractionEnabled = true
nameLabel.isUserInteractionEnabled = false
} else { modifyInteraction(set: false) } //disable interaction on second press

//MARK: Submit button


@IBAction func submitButtonPressed(_ sender: UIButton) {

let defaultAlert = UIAlertController(title: "Info", message: "", preferredStyle: .alert)


//create a default alert to modify as necessary
defaultAlert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))

var valid = false //check whether all input is valid

if (occurences == [[], [], [], [], [], [], []]) || (nameLabel.text == "" || nameLabel.text ==
"Event Name") || (colour == nil){
valid = false
} else {valid = true}

//MARK: Update schedule


if valid == true{ //only submit if all data is valid
let successAlert = UIAlertController(title: "Info", message: "Schedule Updated",
preferredStyle: .alert)
successAlert.addAction(UIAlertAction(title: "Return", style: .default, handler: { action
in
self.navigationController?.popViewController(animated: true) //return to Schedule
after submitting
}))

try! uiRealm.write { //update within a transaction


//MARK: Create/modify event object
var newEvent = RepeatingEvent()
if (check == 1){ //edit button has not been pressed, therefore new event added
newEvent = createEvent(name: nameLabel.text!, colour: colour!, week:
occurences!, description: descriptionLabel.text, priority:
priorityPicker.selectedRow(inComponent: 0))
}
else{
newEvent = modifyEvent(event: repeatingEvent, name: nameLabel.text!, colour:
colour!, week: occurences!, description: descriptionLabel.text, priority:
priorityPicker.selectedRow(inComponent: 0))
}
uiRealm.add(newEvent, update: true)
}

self.hashTable.populateTable(timeDifference: self.settings.lowerBound)
self.present(successAlert, animated: true)
}
else{
defaultAlert.title = "Info"

Candidate Number - 4164 209 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
defaultAlert.message = "Some fields may have invalid data \n Please try again"

self.present(defaultAlert, animated: true)


}
}

//MARK: Delete Button


@IBAction func deleteButtonPressed(_ sender: UIButton) {
let alert = UIAlertController(title: "Warning", message: "This will permanently delete this
event", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: "Delete", style: .destructive, handler: { action in
//Delete event if user confirms
try! uiRealm.write {
uiRealm.delete(self.repeatingEvent)
}
self.hashTable.populateTable(timeDifference: self.settings.lowerBound)
self.navigationController?.popViewController(animated: true) //return to Schedule
after deleting
}))

self.present(alert, animated: true)


}

//MARK: Add Event and Constituents


func createEvent(name: String, colour: String, week: [[Int]], description: String, priority: Int)
-> RepeatingEvent{
let event = RepeatingEvent()

event.name = name //add all parameters


event.colour = colour
event.desc = description
event.priority = priority

event.id = name

for day in week{ //for every day in the week, append the day to the week
event.week.append(timesToDay(times: day))
}

return event
}

func modifyEvent(event: RepeatingEvent, name: String, colour: String, week: [[Int]],


description: String, priority: Int) -> RepeatingEvent{

event.name = name //modify all parameters


event.colour = colour
event.desc = description
event.priority = priority

event.week.removeAll() //clear week before appending new data

for day in week{ //for every day in the week, append the day to the week

Candidate Number - 4164 210 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
event.week.append(timesToDay(times: day))
}
return event
}

func timesToDay(times: [Int]) -> Day{ //create Day objects


let newDay = Day()
for item in times{
let hour = hoursToTime(hour: item)
newDay.dayItem.append(hour)
}
return newDay
}

func hoursToTime(hour: Int) -> Hour{ //create Hour objects


let new = Hour()
new.hourItem = hour
return new
}

//MARK: Get occurences from RepeatingEvent week array


func weekToOccurences(event: RepeatingEvent) -> [[Int]]{
let week = event.week

var weekOccurences = [[Int]]()


for day in week{
var hourOccurences = [Int]()
for hour in day.dayItem{
hourOccurences.append(hour.hourItem)
}
weekOccurences.append(hourOccurences)
}

return weekOccurences
}

//MARK: Modify Interactability


func modifyInteraction(set: Bool){
repeatSwitch.isUserInteractionEnabled = set
priorityPicker.isUserInteractionEnabled = set
descriptionLabel.isUserInteractionEnabled = set
colourPickerButton.isUserInteractionEnabled = set
occurenceButton.isUserInteractionEnabled = set

if set == false{
priorityPicker.alpha = 0.5
priorityLabel.alpha = 0.5
colourPickerButton.alpha = 0.5
occurenceButton.alpha = 0.5
//submitButton.alpha = 0.5
}
else{
priorityPicker.alpha = 1

Candidate Number - 4164 211 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
priorityLabel.alpha = 1
colourPickerButton.alpha = 1
occurenceButton.alpha = 1
//submitButton.alpha = 1
}

//MARK: Text Setup


func textViewDidBeginEditing(_ textView: UITextView) { //automatically remove text from
text view
if (textView.text == "Extra Information"){
textView.text = ""
}
}
func textFieldDidBeginEditing(_ textField: UITextField) { //automatically remove text from
text field
if (textField.text == "Event Name"){
textField.text = ""
}
}

//MARK: Segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//TODO: Fix segue
if segue.identifier == "occurenceSegue"{
//send over occurence data
let destinationVC = segue.destination as! OccurencesTVC
destinationVC.occurences = occurences
destinationVC.eventName = eventName
}
else if segue.identifier == "colourPickerSegue"{
let destinationVC = segue.destination as! ColourPickerViewController
destinationVC.colour = colour
}

OccurrencesTVC
//
// OccurencesTVC.swift
// timetable
//
// Created by Juheb on 23/11/2018.
// Copyright © 2018 Juheb. All rights reserved.
//

import UIKit

class OccurencesTVC: UITableViewController, UINavigationControllerDelegate {

Candidate Number - 4164 212 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
var hours = [String]()
let days = ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY",
"SATURDAY", "SUNDAY"]
var occurences: [[Int]]?
var startTime = Int()
var settings = SettingsStore()
var eventName: String!

override func viewDidLoad() {


super.viewDidLoad()
settings = uiRealm.object(ofType: SettingsStore.self, forPrimaryKey: "1")!
startTime = settings.lowerBound

self.tableView.allowsMultipleSelection = true
self.tableView.allowsMultipleSelectionDuringEditing = true

let submitButton = UIBarButtonItem(title: "Submit", style: .plain, target: self, action:


#selector(submitButtonPressed))
navigationItem.rightBarButtonItem = submitButton

setupSelection() //display selected cells when view loads

setupTimes()

// MARK: - Table view data source


override func numberOfSections(in tableView: UITableView) -> Int {
return days.count
}

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) ->


String? {
return String(describing: days[section])
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -


> Int {
return hours.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -


> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "timesCell", for: indexPath) as!
OccurencesCell

let itemInTable = globalTable[indexPath.section]![indexPath.row+settings.lowerBound]

if itemInTable == ""{ //empty position, generate all first


cell.nameLabel.text = String(describing: hours[indexPath.row])
cell.nameLabel.textColor = UIColor.black

Candidate Number - 4164 213 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
cell.isUserInteractionEnabled = true
}
else{ //position is filled
if eventName == itemInTable{ //action to carry out in the case the item matching the
event passed
cell.nameLabel.text = String(describing: hours[indexPath.row])
cell.nameLabel.textColor = UIColor.black
cell.isUserInteractionEnabled = true
}
else{ //cell taken up by another event
cell.nameLabel.text = itemInTable
cell.nameLabel.textColor = UIColor(hex: uiRealm.object(ofType:
RepeatingEvent.self, forPrimaryKey: itemInTable)!.colour)
cell.tintColor = UIColor.lightGray
cell.isUserInteractionEnabled = false
}
}

return cell
}

@objc func submitButtonPressed(){

let selected = self.tableView.indexPathsForSelectedRows

if selected == nil{
let alert = UIAlertController(title: "Warning", message: "No times were added \n Are
you sure you want to return?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { action in
self.navigationController?.popViewController(animated: true) //return and send
empty data to EventVC
}))
self.present(alert, animated: true)

//if no dates selected, then submitting event in previous screen deletes it

}
else{

let alert = UIAlertController(title: "Info", message: "Save selected times?",


preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { action in

self.occurences = [[], [], [], [], [], [], []]


for item in selected!{ //loops array and adds hours to their days in occurences
array

self.occurences![item[0]].append(item[1]) //append time indexPath + time


difference

Candidate Number - 4164 214 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
self.submitAndReturn() //return and send empty data to EventVC

}))
self.present(alert, animated: true)
}

//MARK: Setup View


func setupSelection(){
var count = 0
for day in occurences!{
for hour in day{
let path = NSIndexPath(row: hour, section: count)
tableView.selectRow(at: path as IndexPath, animated: false, scrollPosition:
UITableView.ScrollPosition.none)
}
count += 1
}
}

func setupTimes(){
hours = []
if settings.twentyFour == true{
//generate 24 hour times
for x in settings.lowerBound...settings.upperBound{
var string = ""
if x < 12{
string = "0\(x):00"
}
else{
string = "\(x):00"
}
hours.append(string)
}
}
else{
//generate 12 hour times
for x in settings.lowerBound...settings.upperBound{
var string = ""
if x < 12{
if x == 0{
string = "12:00am"
}
else{
string = "\(x):00am"
}
}
else{
if x != 12{
string = "\(x-12):00pm"
}
else{ string = "12:00pm"}
}

Candidate Number - 4164 215 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
hours.append(string)
}
}
}

//MARK: Navigation
func submitAndReturn() { //sends data and returns to previous view
let stack = self.navigationController?.viewControllers
let previousView = stack![stack!.count - 2] as! EventVC
previousView.occurences = occurences
self.navigationController?.popViewController(animated: true)
}

Occurrences Cell
//
// OccurrencesCell.swift
// timetable
//
// Created by Juheb on 23/11/2018.
// Copyright © 2018 Juheb. All rights reserved.
//

import UIKit

class OccurencesCell: UITableViewCell {

@IBOutlet weak var nameLabel : UILabel!

override func awakeFromNib() {


super.awakeFromNib()
// Initialization code
}

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {


super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
}

required init?(coder aDecoder: NSCoder) {


super.init(coder: aDecoder)
}

override func setSelected(_ selected: Bool, animated: Bool) {


super.setSelected(selected, animated: animated)
self.accessoryType = selected ? .checkmark : .none
}

Candidate Number - 4164 216 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Colour Picker View Controller
//
// ColourPickerViewController.swift
// timetable
//
// Created by Juheb on 03/12/2018.
// Copyright © 2018 Juheb. All rights reserved.
//

import UIKit

class ColourPickerViewController: UIViewController {

@IBOutlet var colorWell:ColorWell!


@IBOutlet var colorPicker:ColorPicker!
@IBOutlet var huePicker:HuePicker!
var pickerController:ColorPickerController?
@IBOutlet var label:UILabel!
var colour: String?

override func viewDidLoad() {


super.viewDidLoad()

// The ColorPickerController handels lets you connect a colorWell, a colorPicker and a


huepicker. The ColorPickerController takes care of propagating changes between the
individual components.
pickerController = ColorPickerController(svPickerView: colorPicker, huePickerView:
huePicker, colorWell: colorWell)
//Instead of setting an initial color on all 3 ColorPicker components, the
ColorPickerController lets you set a color which will be propagated to the individual
components.

if colour == nil{
pickerController?.color = UIColor.red
label.text = colour
label.backgroundColor = pickerController?.color!.withAlphaComponent(0.2)
label.textColor = pickerController?.color
}
else {
pickerController?.color = UIColor(hex: colour!)
label.text = colour
label.backgroundColor = pickerController?.color!.withAlphaComponent(0.2)
label.textColor = pickerController?.color
}

/* you shoudln't interact directly with the individual components unless you want to do
customization of the colorPicker itself. You can provide a closure to the pickerController,
which is going to be invoked when the user is changing a color. Notice that you will receive
intermediate color changes. You can use these by coloring the object the User is actually
trying to color, so she/he gets a direct visual feedback on how a color changes the
appearance of an object of interet. The ColorWell aids in this process by showing old and
new color side-by-side.
*/
pickerController?.onColorChange = {(color, finished) in

Candidate Number - 4164 217 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
self.colour = color.toHexString
self.label.text = self.colour
self.label.backgroundColor = color.withAlphaComponent(0.2)
self.label.textColor = color

}
}

override func didReceiveMemoryWarning() {


super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

@IBAction func submitButtonPressed(_ sender: UIButton) {

let stack = self.navigationController?.viewControllers


let previousView = stack![stack!.count - 2] as! EventVC
previousView.colour = colour
self.navigationController?.popViewController(animated: true)
}

Classes
Linked List Queue
//
// Linked List Queue.swift
// timetable
//
// Created by Juheb on 05/11/2018.
// Copyright © 2018 Juheb. All rights reserved.
//

//MARK: LL Queue Class


public class Queue<T> {

typealias Node = LinkedListNode<T>

var head: Node!


public var isEmpty: Bool { return head == nil }
var first: Node? { return head }
var last: Node? {
if var node = self.head { //finds first node in list
while case let next? = node.next {
node = next
}
return node
} else {
return nil
}
}

Candidate Number - 4164 218 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

//MARK: Enqueue
func enqueue(key: T) {
let nextItem = Node(data: key) //item to be added as node
if let lastNode = last {
lastNode.next = nextItem //last node moved to added item
} else {
head = nextItem //unless item is added to empty queue
}
}

//MARK: Dequeue
func dequeue() -> T? {
if self.head?.data == nil { return nil } //cannot dequeue if queue empty

let out = head?.data


if let nextItem = self.head?.next {
head = nextItem //next node set as head
} else {
head = nil
}
return out
}
//MARK: Dequeue All
func dequeueAll(){

head = nil
}

//MARK: Length
func length() -> Int{
var count = 0

if var node = self.head { //begins count if queue not empty


while case let next? = node.next { //+1 every time a node has a next node
node = next
count += 1 //counts nodes until end
}
count += 1 //counts remaining node
}
return count
}

//MARK: Return items


func outputArray() -> [T]{
var array = [T]()

if var node = self.head {

array.append(node.data)
while case let next? = node.next {
node = next
array.append(node.data)

Candidate Number - 4164 219 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
}

}
return array
}
}

//MARK: Node Class


public class LinkedListNode<T> { //Node class for a linked list. Generic datatype "T"
var data: T
var next: LinkedListNode?
public init(data: T){
self.data = data
}
}

Sorts
//
// Sorts.swift
// timetable
//
// Created by Juheb on 19/11/2018.
// Copyright © 2018 Juheb. All rights reserved.
//

import Foundation

class Sorts {

//MARK: Quick Sort a string


func quickSort(_ arr: [String]) -> [String]{

var less = [String]()


var equal = [String]()
var more = [String]()

if arr.count > 1{
let pivot = arr[arr.count/2]

for value in arr{


if value > pivot{
more.append(value)
}
else if value < pivot{
less.append(value)
}
if value == pivot{
equal.append(value)
}
}

Candidate Number - 4164 220 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
return quickSort(less) + equal + quickSort(more)

}
else{
return arr
}
}

//MARK: Merge Sort a date


func mergeSort(_ arr: [Date]) -> [Date]{
var array = arr

if array.count == 1{
return array
}
else{
let split = array.count/2
let leftSide = mergeSort(Array(array[0..<split]))
let rightSide = mergeSort(Array(array[split..<array.count]))

return merge(leftSide, rightSide)


}

func merge(_ a: [Date],_ b: [Date]) -> [Date]{

var array = [Date]()


var indexa = 0
var indexb = 0

while indexa < a.count && indexb < b.count{


if a[indexa] < b[indexb]{
array.append(a[indexa])
indexa += 1
}
else if a[indexa] > b[indexb]{
array.append(b[indexb])
indexb += 1
}
else {
array.append(a[indexa])
indexa += 1
array.append(b[indexb])
indexb += 1
}
}

while indexa < a.count{


array.append(a[indexa])
indexa += 1
}

Candidate Number - 4164 221 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
while indexb < b.count{
array.append(b[indexb])
indexb += 1
}

return array
}

Hash Table
//
// Hash Table.swift
// timetable
//
// Created by Juheb on 12/03/2019.
// Copyright © 2019 Juheb. All rights reserved.
//

import Foundation

var globalTable = [
//monday
0: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"", 11:"", 12:"", 13:"", 14:"", 15:"",
16:"", 17:"", 18:"", 19:"", 20:"", 21:"", 22:"", 23:""],
//tuesday
1: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"", 11:"", 12:"", 13:"", 14:"", 15:"",
16:"", 17:"", 18:"", 19:"", 20:"", 21:"", 22:"", 23:""],

2: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"", 11:"", 12:"", 13:"", 14:"", 15:"",
16:"", 17:"", 18:"", 19:"", 20:"", 21:"", 22:"", 23:""],
3: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"", 11:"", 12:"", 13:"", 14:"", 15:"",
16:"", 17:"", 18:"", 19:"", 20:"", 21:"", 22:"", 23:""],
4: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"", 11:"", 12:"", 13:"", 14:"", 15:"",
16:"", 17:"", 18:"", 19:"", 20:"", 21:"", 22:"", 23:""],
5: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"", 11:"", 12:"", 13:"", 14:"", 15:"",
16:"", 17:"", 18:"", 19:"", 20:"", 21:"", 22:"", 23:""],
//sunday
6: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"", 11:"", 12:"", 13:"", 14:"", 15:"",
16:"", 17:"", 18:"", 19:"", 20:"", 21:"", 22:"", 23:""],
]

class HashTable{

var resetTable = [
//monday
0: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"", 11:"", 12:"", 13:"", 14:"", 15:"",
16:"", 17:"", 18:"", 19:"", 20:"", 21:"", 22:"", 23:""],
//tuesday
1: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"", 11:"", 12:"", 13:"", 14:"", 15:"",
16:"", 17:"", 18:"", 19:"", 20:"", 21:"", 22:"", 23:""],

Candidate Number - 4164 222 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
2: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"", 11:"", 12:"", 13:"", 14:"", 15:"",
16:"", 17:"", 18:"", 19:"", 20:"", 21:"", 22:"", 23:""],
3: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"", 11:"", 12:"", 13:"", 14:"", 15:"",
16:"", 17:"", 18:"", 19:"", 20:"", 21:"", 22:"", 23:""],
4: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"", 11:"", 12:"", 13:"", 14:"", 15:"",
16:"", 17:"", 18:"", 19:"", 20:"", 21:"", 22:"", 23:""],
5: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"", 11:"", 12:"", 13:"", 14:"", 15:"",
16:"", 17:"", 18:"", 19:"", 20:"", 21:"", 22:"", 23:""],
//sunday
6: [0:"", 1:"", 2:"", 3:"", 4:"", 5:"", 6:"", 7:"", 8:"", 9:"", 10:"", 11:"", 12:"", 13:"", 14:"", 15:"",
16:"", 17:"", 18:"", 19:"", 20:"", 21:"", 22:"", 23:""],
]

func populateTable(timeDifference: Int){

let eventsDict = self.eventsToDictionary()

globalTable = resetTable

for event in eventsDict{


var count = 0
for day in event.value{
for hour in day{
globalTable[count]![hour+timeDifference] = event.key
}
count += 1
}
}

func eventsToDictionary() -> [String : [[Int]]]{


return (uiRealm.objects(RepeatingEvent.self).toArray() as!
[RepeatingEvent]).addToDictionary()
}

func idAtPosition(column: Int, row: Int) -> String{


return globalTable[column]![row]!
}

Extensions
//
// UIColor+Hex.swift
// timetable
//
// Created by Juheb on 15/11/2018.
// Copyright © 2018 Juheb. All rights reserved.
//

Candidate Number - 4164 223 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

import Foundation
import UIKit

extension UIColor {
var toHexString: String {
var r: CGFloat = 0
var g: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0

self.getRed(&r, green: &g, blue: &b, alpha: &a) //built-in call that returns values between
0 and 1 for each channel

return String(
format: "%02X%02X%02X", //00 padding on 3 hex digits
Int(r * 0xff),
Int(g * 0xff),
Int(b * 0xff)
)
}

convenience init(hex: String) {


let scanner = Scanner(string: hex)
scanner.scanLocation = 0

var rgbValue: UInt64 = 0

scanner.scanHexInt64(&rgbValue) //convert 6 char string to hex int

let r = (rgbValue & 0xff0000) >> 16 //bitmasked and bitshifted to produce values
let g = (rgbValue & 0xff00) >> 8
let b = rgbValue & 0xff

self.init(
red: CGFloat(r) / 0xff, //divided by hex 255 to turn back into val between 0-1,
compatible with uicolor
green: CGFloat(g) / 0xff,
blue: CGFloat(b) / 0xff, alpha: 1
)
}
}

extension Dictionary where Value: Equatable { //gets the key for input value of dictionary.
may be useless now
func allKeys(forValue val: Value) -> [Key] {
return self.filter { $1 == val }.map { $0.0 }
}
}

extension UITextField{ //gives a bottom border to a text field


func setBottomBorder(){
self.borderStyle = .none

Candidate Number - 4164 224 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
self.layer.backgroundColor = UIColor.white.cgColor

self.layer.masksToBounds = false
self.layer.shadowColor = UIColor(red: 80.0/255.0, green: 80.0/255.0, blue: 80.0/255.0,
alpha: 0.25).cgColor
self.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
self.layer.shadowOpacity = 1.0
self.layer.shadowRadius = 0.0
}
}

extension Date { //get the date of monday or sunday


var startOfWeek: Date? {
let gregorian = Calendar(identifier: .gregorian)
guard let sunday = gregorian.date(from:
gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)) else { return nil
}
return gregorian.date(byAdding: .day, value: 1, to: sunday)
}

var endOfWeek: Date? {


let gregorian = Calendar(identifier: .gregorian)
guard let sunday = gregorian.date(from:
gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)) else { return nil
}
return gregorian.date(byAdding: .day, value: 7, to: sunday)
}
}

extension UITableView {

func setEmptyMessage(_ message: String) {


let messageLabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.size.width,
height: self.bounds.size.height))
messageLabel.text = message
messageLabel.textColor = .black
messageLabel.backgroundColor = .lightGray
messageLabel.numberOfLines = 0
messageLabel.textAlignment = .center
messageLabel.sizeToFit()

self.backgroundView = messageLabel
self.separatorStyle = .none
}

func restore() {
self.backgroundView = nil
self.separatorStyle = .singleLine
}
}

extension Array where Array == [RepeatingEvent]{

Candidate Number - 4164 225 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
func addToDictionary() -> [String: [[Int]]]{ //adds all event names (and corresponding
times) to a dictionary

var eventTimes = [String: [[Int]]]()

for event in self{


var dayHours = [[Int]]()

for day in event.week{


var hours = [Int]()

for hour in day.dayItem{


hours.append(hour.hourItem)
}
dayHours.append(hours)
}
eventTimes[event.name] = dayHours
}

return eventTimes
}
}

External Files

Picker Image
// Created by Matthias Schlemm on 05/03/15.
// Copyright (c) 2015 Sixpolys. All rights reserved.
// Licensed under the MIT License.
// URL: https://github.com/MrMatthias/SwiftColorPicker

import UIKit
import ImageIO

public struct PickerImage {

//FIXME: Colour picker won't open, line 100 exc_bad_instruction

var width: Int


var height: Int
var hue: CGFloat = 0
var alpha: CGFloat = 1.0

let lockQueue = DispatchQueue(label: "PickerImage")


private let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
private let bitmapInfo:CGBitmapInfo = CGBitmapInfo(rawValue:
CGImageAlphaInfo.premultipliedFirst.rawValue)

// MARK: Pixel Data struct

Candidate Number - 4164 226 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

public struct PixelData {


var a:UInt8 = 255
var r:UInt8
var g:UInt8
var b:UInt8
}

var pixelData: [PixelData]

// MARK: Image generation

public func getImage() -> UIImage? {


return self.imageFromARGB32Bitmap(pixels: self.pixelData, width: UInt(self.width),
height: UInt(self.height))
}

private func imageFromARGB32Bitmap(pixels:[PixelData], width:UInt, height:UInt) ->


UIImage? {

let bitsPerComponent:UInt = 8
let bitsPerPixel:UInt = 32

assert(pixels.count == Int(width * height))

var data = pixels // Copy to mutable []


guard let providerRef = CGDataProvider(
data: NSData(bytes: &data, length: data.count * MemoryLayout<PixelData>.size)
) else {
return nil
}

guard let cgim = CGImage(width: Int(width),


height: Int(height),
bitsPerComponent: Int(bitsPerComponent),
bitsPerPixel: Int(bitsPerPixel),
bytesPerRow: Int(width) * MemoryLayout<PixelData>.size,
space: rgbColorSpace,
bitmapInfo: bitmapInfo,
provider: providerRef,
decode: nil,
shouldInterpolate: true,
intent: .defaultIntent) else {
return nil
}

let image = UIImage(cgImage: cgim)


return image
}

// MARK: Size changes

mutating func changeSize(width: Int, height: Int) {


lockQueue.sync() {

Candidate Number - 4164 227 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
self.width = width
self.height = height

let whitePixel = PixelData(a: 255, r: 255, g: 255, b: 255)


self.pixelData = Array<PixelData>(repeating: whitePixel, count: Int(width * height))

self.writeColorData(hue: self.hue, alpha: self.alpha)


}
}

// MARK: Lifecycle

init(width:Int, height:Int) {
self.width = width
self.height = height

let whitePixel = PixelData(a: 255, r: 255, g: 255, b: 255)


self.pixelData = Array<PixelData>(repeating: whitePixel, count: Int(width * height))

self.writeColorData(hue: self.hue, alpha: self.alpha)


}

// MARK: Generating raw image data

public mutating func writeColorData(hue: CGFloat, alpha: CGFloat) {


// lockQueue.sync() {
self.hue = hue
self.alpha = alpha

let saturationSteps = self.width


let brightnessSteps = self.height

let saturationStepSize: CGFloat = 1.0 / CGFloat(saturationSteps)


let brightnessStepSize: CGFloat = 1.0 / CGFloat(brightnessSteps)

var currentBrightnessIndex = 0
while currentBrightnessIndex < brightnessSteps {

var currentSaturationIndex = 0
while currentSaturationIndex < saturationSteps {

let currentSaturation = CGFloat(currentSaturationIndex) * saturationStepSize


let currentBrightness = CGFloat(currentBrightnessIndex) * brightnessStepSize
let color = UIColor(hue: hue,
saturation: currentSaturation,
brightness: currentBrightness,
alpha: alpha)

var red: CGFloat = 0


var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0

Candidate Number - 4164 228 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)

let index = currentBrightnessIndex * width + currentSaturationIndex


self.pixelData[index] = PixelData(a: UInt8(alpha*255.0), r: UInt8(red*255.0), g:
UInt8(green*255.0), b: UInt8(blue*255.0))

currentSaturationIndex += 1
}

currentBrightnessIndex += 1
}
// }
}

Colour Picker
// Created by Matthias Schlemm on 05/03/15.
// Copyright (c) 2015 Sixpolys. All rights reserved.
// Licensed under the MIT License.
// URL: https://github.com/MrMatthias/SwiftColorPicker

import ImageIO
import UIKit

@IBDesignable open class ColorPicker: UIView {

fileprivate var pickerImage1:PickerImage?


fileprivate var pickerImage2:PickerImage?
fileprivate var image:UIImage?
fileprivate var data1Shown = false
fileprivate lazy var opQueue:OperationQueue = { return OperationQueue() }()
fileprivate var lock:NSLock = NSLock()
fileprivate var rerender = false
open var onColorChange:((_ color:UIColor, _ finished:Bool)->Void)? = nil

open var a:CGFloat = 1 {


didSet {
if a < 0 || a > 1 {
a = max(0, min(1, a))
}
}
}

open var h:CGFloat = 0 { // // [0,1]


didSet {
if h > 1 || h < 0 {
h = max(0, min(1, h))
}
renderBitmap()

Candidate Number - 4164 229 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
setNeedsDisplay()
}

}
fileprivate var currentPoint:CGPoint = CGPoint.zero

open func saturationFromCurrentPoint() -> CGFloat {


return currentPoint.x
}

open func brigthnessFromCurrentPoint() -> CGFloat {


return currentPoint.y
}

open var color:UIColor {


set(value) {
var hue:CGFloat = 1
var saturation:CGFloat = 1
var brightness:CGFloat = 1
var alpha:CGFloat = 1
value.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
a = alpha
if hue != h || pickerImage1 == nil {
self.h = hue
}
currentPoint = CGPoint(x: saturation, y: brightness)
self.setNeedsDisplay()
}
get {
return UIColor(hue: h, saturation: saturationFromCurrentPoint(), brightness:
brigthnessFromCurrentPoint(), alpha: a)
}
}

public override init(frame: CGRect) {


super.init(frame: frame)
commonInit()
}

public required init?(coder aDecoder: NSCoder) {


super.init(coder: aDecoder)
commonInit()
}

func commonInit() {
isUserInteractionEnabled = true
clipsToBounds = false
self.addObserver(self, forKeyPath: "bounds",
options: [NSKeyValueObservingOptions.new,
NSKeyValueObservingOptions.initial],
context: nil)
}

Candidate Number - 4164 230 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
deinit {
self.removeObserver(self, forKeyPath: "bounds")
}

open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change:


[NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "bounds" {
if(bounds.isEmpty) {
return
}
if var pImage1 = pickerImage1 {
pImage1.changeSize(width: Int(self.bounds.width), height: Int(self.bounds.height))
}
if var pImage2 = pickerImage2 {
pImage2.changeSize(width: Int(self.bounds.width), height: Int(self.bounds.height))
}
renderBitmap()
self.setNeedsDisplay()
} else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context:
context)
}
}

open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {


let touch = touches.first! as UITouch
handleTouche(touch, ended: false)
}

open override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {


let touch = touches.first! as UITouch
handleTouche(touch, ended: false)
}

open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {


let touch = touches.first! as UITouch
handleTouche(touch, ended: true)
}

fileprivate func handleColorChange(_ color:UIColor, changing:Bool) {


if color !== self.color {
if let handler = onColorChange {
handler(color, !changing)
}
setNeedsDisplay()
}
}

fileprivate func handleTouche(_ touch:UITouch, ended:Bool) {


// set current point
let point = touch.location(in: self)
let x:CGFloat = min(bounds.width, max(0, point.x)) / bounds.width
let y:CGFloat = min(bounds.height, max(0, point.y)) / bounds.height
currentPoint = CGPoint(x: x, y: y)

Candidate Number - 4164 231 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
handleColorChange(pointToColor(currentPoint), changing: !ended)
}

fileprivate func pointToColor(_ point:CGPoint) ->UIColor {


let s:CGFloat = min(1, max(0, point.x))
let b:CGFloat = min(1, max(0, point.y))
return UIColor(hue: h, saturation: s, brightness: b, alpha:a)
}

fileprivate func renderBitmap() {


if self.bounds.isEmpty {
return
}
if !lock.try() {
rerender = true
return
}
rerender = false

if pickerImage1 == nil {
self.pickerImage1 = PickerImage(width: Int(bounds.width), height: Int(bounds.height))
self.pickerImage2 = PickerImage(width: Int(bounds.width), height: Int(bounds.height))
}
#if !TARGET_INTERFACE_BUILDER
opQueue.addOperation { () -> Void in
// Write colors to data array
if self.data1Shown { self.pickerImage2!.writeColorData(hue: self.h, alpha:self.a) }
else { self.pickerImage1!.writeColorData(hue: self.h, alpha:self.a) }

// flip images
self.image = self.data1Shown ? self.pickerImage2!.getImage()! :
self.pickerImage1!.getImage()!
self.data1Shown = !self.data1Shown

// make changes visible


OperationQueue.main.addOperation({ () -> Void in
self.setNeedsDisplay()
self.lock.unlock()
if self.rerender {
self.renderBitmap()
}
})

}
#else
self.pickerImage1!.writeColorData(hue: self.h, alpha:self.a)
self.image = self.pickerImage1!.getImage()!
#endif
}

open override func draw(_ rect: CGRect) {


#if TARGET_INTERFACE_BUILDER
if pickerImage1 == nil {

Candidate Number - 4164 232 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
commonInit()
}
#endif
if let img = image {
img.draw(in: rect)
}

//// Oval Drawing


let ovalPath = UIBezierPath(ovalIn: CGRect(x: currentPoint.x * bounds.width - 5,
y: currentPoint.y * bounds.height - 5,
width: 10,
height: 10))
UIColor.white.setStroke()
ovalPath.lineWidth = 1
ovalPath.stroke()

//// Oval 2 Drawing


let oval2Path = UIBezierPath(ovalIn: CGRect(x: currentPoint.x * bounds.width - 4,
y: currentPoint.y * bounds.height - 4,
width: 8,
height: 8))
UIColor.black.setStroke()
oval2Path.lineWidth = 1
oval2Path.stroke()
}

Color Well
// Created by Matthias Schlemm on 05/03/15.
// Copyright (c) 2015 Sixpolys. All rights reserved.
// Licensed under the MIT License.
// URL: https://github.com/MrMatthias/SwiftColorPicker

import UIKit

@IBDesignable open class ColorWell: UIButton {

@IBInspectable open var color:UIColor = UIColor.cyan {


didSet {
setNeedsDisplay()
}
}

open var previewColor:UIColor? {


didSet {
setNeedsDisplay()
}
}
@IBInspectable open var borderColor:UIColor = UIColor.darkGray {
didSet {
setNeedsDisplay()

Candidate Number - 4164 233 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
}
}

@IBInspectable open var borderWidth:CGFloat = 2 {


didSet{
setNeedsDisplay()
}
}

func commonInit() {
backgroundColor = UIColor.clear
isOpaque = false
}

override init(frame: CGRect) {


super.init(frame: frame)
commonInit()
}

public required init?(coder aDecoder: NSCoder) {


super.init(coder: aDecoder)
commonInit()
}

override open func draw(_ rect: CGRect) {


let ovalPath = UIBezierPath(ovalIn: CGRect(x: 5.5, y: 5.5, width: 35, height: 35))
color.setFill()
ovalPath.fill()

if let col = previewColor {


let ovalRect = CGRect(x: 5.5, y: 5.5, width: 35, height: 35)
let ovalPath = UIBezierPath()
ovalPath.addArc(withCenter: CGPoint(x: ovalRect.midX, y: ovalRect.midY), radius:
ovalRect.width / 2, startAngle: -90 * .pi/180, endAngle: 90 * .pi/180, clockwise: true)
ovalPath.addLine(to: CGPoint(x: ovalRect.midX, y: ovalRect.midY))
ovalPath.close()

col.setFill()
ovalPath.fill()
}

borderColor.setStroke()
ovalPath.lineWidth = borderWidth
ovalPath.stroke()
}

Candidate Number - 4164 234 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
Color Picker Controller
// Created by Matthias Schlemm on 05/03/15.
// Copyright (c) 2015 Sixpolys. All rights reserved.
// Licensed under the MIT License.
// URL: https://github.com/MrMatthias/SwiftColorPicker

import UIKit

open class ColorPickerController: NSObject {

open var onColorChange:((_ color:UIColor, _ finished:Bool)->Void)? = nil

// Hue Picker
open var huePicker:HuePicker

// Color Well
open var colorWell:ColorWell {
didSet {
huePicker.setHueFromColor(colorWell.color)
colorPicker.color = colorWell.color
}
}

// Color Picker
open var colorPicker:ColorPicker

open var color:UIColor? {


set(value) {
colorPicker.color = value!
colorWell.color = value!
huePicker.setHueFromColor(value!)
}
get {
return colorPicker.color
}
}

public init(svPickerView:ColorPicker, huePickerView:HuePicker, colorWell:ColorWell) {


self.huePicker = huePickerView
self.colorPicker = svPickerView
self.colorWell = colorWell
self.colorWell.color = colorPicker.color
self.huePicker.setHueFromColor(colorPicker.color)
super.init()
self.colorPicker.onColorChange = {(color, finished) -> Void in
self.huePicker.setHueFromColor(color)
self.colorWell.previewColor = (finished) ? nil : color
if(finished) {self.colorWell.color = color}
self.onColorChange?(color, finished)
}
self.huePicker.onHueChange = {(hue, finished) -> Void in
self.colorPicker.h = CGFloat(hue)

Candidate Number - 4164 235 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
let color = self.colorPicker.color
self.colorWell.previewColor = (finished) ? nil : color
if(finished) {self.colorWell.color = color}
self.onColorChange?(color, finished)
}
}

Hue Picker
// Created by Matthias Schlemm on 05/03/15.
// Copyright (c) 2015 Sixpolys. All rights reserved.
// Licensed under the MIT License.
// URL: https://github.com/MrMatthias/SwiftColorPicker

import UIKit

@IBDesignable open class HuePicker: UIView {

var _h:CGFloat = 0.1111


open var h:CGFloat { // [0,1]
set(value) {
_h = min(1, max(0, value))
currentPoint = CGPoint(x: CGFloat(_h), y: 0)
setNeedsDisplay()
}
get {
return _h
}
}
var image:UIImage?
fileprivate var data:[UInt8]?
fileprivate var currentPoint = CGPoint.zero
open var handleColor:UIColor = UIColor.black

open var onHueChange:((_ hue:CGFloat, _ finished:Bool) -> Void)?

open func setHueFromColor(_ color:UIColor) {


var h:CGFloat = 0
color.getHue(&h, saturation: nil, brightness: nil, alpha: nil)
self.h = h
}

public override init(frame: CGRect) {


super.init(frame: frame)
isUserInteractionEnabled = true;
}

public required init?(coder aDecoder: NSCoder) {


super.init(coder: aDecoder)
isUserInteractionEnabled = true
}

Candidate Number - 4164 236 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

func renderBitmap() {
if bounds.isEmpty {
return
}

let width = UInt(bounds.width)


let height = UInt(bounds.height)

if data == nil {
data = [UInt8](repeating: UInt8(255), count: Int(width * height) * 4)
}

var p = 0.0
var q = 0.0
var t = 0.0

var i = 0
//_ = 255
var double_v:Double = 0
var double_s:Double = 0
let widthRatio:Double = 360 / Double(bounds.width)
var d = data!
for hi in 0..<Int(bounds.width) {
let double_h:Double = widthRatio * Double(hi) / 60
let sector:Int = Int(floor(double_h))
let f:Double = double_h - Double(sector)
let f1:Double = 1.0 - f
double_v = Double(1)
double_s = Double(1)
p = double_v * (1.0 - double_s) * 255
q = double_v * (1.0 - double_s * f) * 255
t = double_v * ( 1.0 - double_s * f1) * 255
let v255 = double_v * 255
i = hi * 4
switch(sector) {
case 0:
d[i+1] = UInt8(v255)
d[i+2] = UInt8(t)
d[i+3] = UInt8(p)
case 1:
d[i+1] = UInt8(q)
d[i+2] = UInt8(v255)
d[i+3] = UInt8(p)
case 2:
d[i+1] = UInt8(p)
d[i+2] = UInt8(v255)
d[i+3] = UInt8(t)
case 3:
d[i+1] = UInt8(p)
d[i+2] = UInt8(q)
d[i+3] = UInt8(v255)
case 4:
d[i+1] = UInt8(t)

Candidate Number - 4164 237 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
d[i+2] = UInt8(p)
d[i+3] = UInt8(v255)
default:
d[i+1] = UInt8(v255)
d[i+2] = UInt8(p)
d[i+3] = UInt8(q)
}
}
var sourcei = 0
for v in 1..<Int(bounds.height) {
for s in 0..<Int(bounds.width) {
sourcei = s * 4
i = (v * Int(width) * 4) + sourcei
d[i+1] = d[sourcei+1]
d[i+2] = d[sourcei+2]
d[i+3] = d[sourcei+3]
}
}
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue:
CGImageAlphaInfo.premultipliedFirst.rawValue)

let provider = CGDataProvider(data: Data(bytes: d, count: d.count *


MemoryLayout<UInt8>.size) as CFData)
let cgimg = CGImage(width: Int(width), height: Int(height), bitsPerComponent: 8,
bitsPerPixel: 32, bytesPerRow: Int(width) * Int(MemoryLayout<UInt8>.size * 4),
space: colorSpace, bitmapInfo: bitmapInfo, provider: provider!, decode: nil,
shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)

image = UIImage(cgImage: cgimg!)

fileprivate func handleTouch(_ touch:UITouch, finished:Bool) {


let point = touch.location(in: self)
currentPoint = CGPoint(x: max(0, min(bounds.width, point.x)) / bounds.width , y: 0)
_h = currentPoint.x
onHueChange?(h, finished)
setNeedsDisplay()
}

override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {


handleTouch(touches.first! as UITouch, finished: false)
}

override open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {


handleTouch(touches.first! as UITouch, finished: false)
}

override open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {


handleTouch(touches.first! as UITouch, finished: true)
}

Candidate Number - 4164 238 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application

override open func draw(_ rect: CGRect) {


if image == nil {
renderBitmap()
}
if let img = image {
img.draw(in: rect)
}

let handleRect = CGRect(x: bounds.width * currentPoint.x-3, y: 0, width: 6, height:


bounds.height)
drawHueDragHandler(frame: handleRect)
}

func drawHueDragHandler(frame: CGRect) {

//// Polygon Drawing


let polygonPath = UIBezierPath()
polygonPath.move(to: CGPoint(x: frame.minX + 4, y: frame.maxY - 6))
polygonPath.addLine(to: CGPoint(x: frame.minX + 7.46, y: frame.maxY))
polygonPath.addLine(to: CGPoint(x: frame.minX + 0.54, y: frame.maxY))
polygonPath.close()
UIColor.black.setFill()
polygonPath.fill()

//// Polygon 2 Drawing


let polygon2Path = UIBezierPath()
polygon2Path.move(to: CGPoint(x: frame.minX + 4, y: frame.minY + 6))
polygon2Path.addLine(to: CGPoint(x: frame.minX + 7.46, y: frame.minY))
polygon2Path.addLine(to: CGPoint(x: frame.minX + 0.54, y: frame.minY))
polygon2Path.close()
UIColor.white.setFill()
polygon2Path.fill()
}

Side Menu Table


//
// SideMenuTableViewController.swift
// timetable
//
// Created by Juheb on 10/11/2018.
// Copyright © 2018 Juheb. All rights reserved.
//

import Foundation
import SideMenu

Candidate Number - 4164 239 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
class SideMenuTableViewController: UITableViewController {

override func viewWillAppear(_ animated: Bool) {


super.viewWillAppear(animated)

// refresh cell blur effect in case it changed


tableView.reloadData()

/*
// Set up a cool background image for demo purposes
let imageView = UIImageView(image: UIImage(named: "saturn"))
imageView.contentMode = .scaleAspectFit
imageView.backgroundColor = UIColor.black.withAlphaComponent(0.2)
tableView.backgroundView = imageView

*/

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -


> UITableViewCell {
let cell = super.tableView(tableView, cellForRowAt: indexPath) as!
UITableViewVibrantCell

//cell.blurEffectStyle = SideMenuManager.default.menuBlurEffectStyle

return cell
}

Pod File

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'timetable' do
# Comment the next line if you're not using Swift and don't want to use dynamic
frameworks
use_frameworks!

# Pods for timetable

pod 'SpreadsheetView'
pod 'RealmSwift'
pod 'SideMenu'
pod 'SwiftRangeSlider'

target 'timetableTests' do

Candidate Number - 4164 240 Juheb Habib


OCR A-Level Computer Science Coursework Documentation
Timetable Application
inherit! :search_paths
# Pods for testing
end

target 'timetableUITests' do
inherit! :search_paths
# Pods for testing
end

end

Candidate Number - 4164 241 Juheb Habib

Vous aimerez peut-être aussi