Too busy for blogging

The Fall semester was again another hectic run through the courses, without much breaks. Three courses with two full time and two part time teachers helping, having nearly 900 students. No wonder…

Regarding my latest posts and this situation, I did manage to solve Advent of Code up to part 1 of day 10. Part 2 is still WIP, and I do have some ideas about that. But don’t know if and when I return to that, if ever.

Much of the teaching work at Fall was conducted not in prepared teaching sessions, since for some reason, many students nowadays do not seem to want or need to come to lectures or exercise sessions. Though we teachers sat though them all! Well we did cancel some sessions at the very end since nobody was coming, but anyways.

We even had to arrange three classroom sessions for the Fall, since last year (2024, fully online teaching) students demanded at least some face-to-face teaching for 1st year students. They even went to the Dean of the Faculty to make sure this happens.

Almost nobody came. Less than 20 students (out of ~900) from those three courses went to the classroom sessions, if I remember correctly. Not very effective use of teaching resources, IMO. Hopefully those who came found them valuable!

Well, we have video lectures and other support (Web based FAQ tool in Moodle, email), and major part of the teaching happens via these. Maybe we should be happy that the course materials and the support we do provide is enough for those students that decide to continue in the course. Some enrolled to my DSA (Data Structures and Algorithms) course do not, since they never provide the URL to their project repository or quit after first 1-2 programming assignments.

In the FAQ tool in DSA, I published 47 new FAQs in addition to the 33 old FAQs from the previous course implementations. Many of the solutions in the FAQs could have been avoided if carefully reading the course materials, watching the lectures and following the instructions in the programming assignments. Anyways, maybe the FAQs also contained explanations and points that somehow provided additional help to the students.

I also moved DSA course exams to controlled exam environment. Because of AI slop and cheating is on the rise. Some small hiccups there I need to fix, but seems to work overall.

To make preparing exams a bit more efficient, I implemented several console based tools (in Swift) that generate random Exam questions, also with image attachments. I have tools to generate questions about binary search algorithms, hash table and hashing using linear probing, as well as tools that generate questions testing if students understand how nodes are added to Binary Search Trees. And how to conduct breadth-first-search and depth-first-search to graphs.

Whenever I like, I can then generate new questions, to make sure every student has their own unique set of questions for their exams.

Today I refactored a part of my code analysis tool. The tool and the refactored feature helps me to check if the 300-something students in DSA have used solutions in their code that must be avoided or are forbidden (e.g. calling Java hashCode() when they should have implemented their own, or calculating the hash from wrong things). Or to make sure they have used elements that must or should be used (e.g. calling a function from itself, when recursive code is required). Something teachers could do manually, but since we do not have those teachers in the numbers required for manual work, automation comes to help.

The analysis code used Antrl to parse the students’ Java code to search for things that are not OK and must be OK. While it worked nicely in my Swift app, Antlr was very slow in debug builds. It was a bit faster when the app was build in release configuration, but still slow enough to be annoying.

So instead, the new implementation just runs a Java console app in a Subprocess, using the Swift Subprocess library. I implemented the same code analysis tool as a Java console app and gave that to the students, using com.github.javaparser for the source code parsing.

Using the tool, students can check, before deadlines‚ that they have not done anything stupid. Avoiding to get failed grade after the last deadline, as they can use the tool to check and fix any obvious wrong solutions during the course. Preferably before deadlines, as they will get sanctions on using solutions that are not accepted.

I needed to modify the Java app a bit so that the Swift app I use can launch it and use the output to record results to my Swift app’s database:

  • modified the command line parameters to include output format, either “text” or “json”. Default is text, so students can see more understandable output from the tool. Given the “json” parameter from my Swift app to the Java app explicitly, produces JSON formatted output my Swift tool reads from Subprocess’ stdout.
  • Make sure the Java app output to stdout contain only the JSON, containing the issues found in the code, when “json” format was selected. So the Swift app can then rely on the received output to be valid JSON.
  • Fix some issues in the code analysis output at the same time, that lead to invalid JSON. Like having unescaped quotes in the analysis output, or line endings like \r which messed the output not just in JSON but also in text format.

This change makes also sure that the Java app the students use is behaves exactly as my tool. No surprises in what the tool reports and does not report to students, compared to the results the teacher sees.

Java app executed in the subprocess executes definitely faster than the Antlr implementation in Swift. Additionally, I managed to strip down tens of lines of code, the Antlr generated Java parser removed, plus the dependency to the Antlr library from the Swift tool project is now gone.

Advent of Code 2025

Advent of Code (AoC) for this year started yesterday. I’ve been too busy with work to write here how my puzzle solving has been going. Or anything else, for that matter. Probably will be for the rest of this year.

Why busy? I’ve been moving Data structures and algorithms course exams from Moodle to Exam. Exam is a controlled environment as Moodle was not (at all). There is a tool to import Moodle exam questions to Exam but that is so limited that I decided to hand write new exam questions for this year.

This has taken time, also learning how the Exam works since I have never used it before. Also I’ve been preparing new demonstrations and visualizations for the course to aid learning. There’s still one visualization demonstration to do, namely how the Dijkstra’s shortest path algorithm works. If I do not find the time for that, I just have to search for existing demonstrations from the internet. Would like to use the familiar graph examples from the lectures and other demo materials though.

Anyhows, I have solved both Day 1 and Day 2 puzzles in AoC, using Swift as last year. If you wish to see how the actual puzzles look like, just head on to the AoC website.

Yesterday’s part 2 was a bit difficult, mainly due to some basic mistakes I made in the beginning, and not starting from scratch when I saw the mistakes I had “fixed” with unnecessarily complex code. Sauna break in the evening helped, teaching and other work in between less so 😅. And starting from scratch was a good idea.

Day 1 implementations were fast, part 1 took 0.000684708 seconds to execute, part 2 took 0.000698 seconds (on MacBook Pro M2, Swift implementation, release build, obviously). Lines of code I needed for the part 1 solution is 18, for part 2 line count is 32.

Day 2 implementation’s Part 1 (17 lines of code) took 0.150482667 seconds to execute, while part 2 (27 lines of code) took 0.437480584 seconds. Here I was using Swift’s .split, .map, stride (a for loop kind of a thing) and chunks(ofCount:) from Swift Algorithms package. Much more straightforward (for me) than yesterday’s part 2.

Now to lunch, then to prepare for a MSc thesis supervision session, then to teach (remotely, as usual) for the rest of the afternoon.

Archiving old course materials

Returning back to this post from two years ago, I am archiving here the course material which was last updated in March 1999. Course was on programming a multiple document interface Windows app with C.

The material, written as a short booklet, instructs the student to implement, step by step, an app to draw simple shapes. The app also uses components as the drawing code is placed in a dynamic library (dll).

The material ends at the point the app can be used to draw simple lines in multiple windows.

Students are supposed to extend the functionality to draw other simple shapes, filled or not, and saving and reading the drawings to/from files.

Unfortunately I think I’ve lost the code and the .doc file for this material. Don’t even have a Windows PC anymore so… Could try out if the code still works, using Windows in virtual machine on my Mac. Would this still be compatible with current Windows API:s, don’t know.

Slippery Weather watchOS app retired

Years ago I developed a watchOS app Slippery Weather (Liukkaat kadut in Finnish) displaying pedestrian slippery weather alerts to users. I sold it at the App Store, and later lowered the price to zero, offering it as a free app.

Slippery weather app on Apple Watch
Apple Watch displaying an alert in a Slippery Weather app widget about slippery weather notification given two hours ago in the city of Oulu.

However, I didn’t have time to maintain it, so I took it off the App Store recently. That was a good decision, since I’ve now learned that the service providing the alerts API has ceased to operate.

The service got the slippery weather condition alerts from the participating cities (road maintenance folks) in Finland. It seems that the cities have switched to using Finnish Meteorological Institute (FMI) weather model based alerts instead.

I do have a prototype watchOS app using Apple Weather service local weather data. The prototype attempts to predict slippery weather for the next 12-24 hours, based on recent and forecasted temperature, precipitation and humidity. But haven’t had time to work on that either. Doesn’t work so reliably that I would want to publish the app.

Learned a lot while developing the app though. Everything lasts for their allotted time, nothing is permanent.

Monitor demo

I read an interesting question about Monitor structure in Swift Forums. The question was how the way it is implemented in Java using synchronized methods compares to Swift concurrency and actors.

Well since this was interesting and I wanted to learn more, there was simply no other option than to pick up the code editors and start coding…

Result is visible here in Codeberg.org.

Otherwise, the last minutes of the week’ s final student supervision session in Zoom are ongoing, and then — Pizza Friday! 🍷🍕

Have a nice weekend!

Vacation is over

Has been for one and a half months already.

I’ve been busy with preparing the Data structures and algorithms (DSA) course. As mentioned earlier, I decided to remake all the programming tasks, which I did.

Partly they were edited from earlier year tasks. Like the Graphs task is the old 2021 Mazes task with a Pac-Man kind of a game. Each task has the actual algorithm and/or data structure to implement, and additionally a task to apply that in some specific problem solving situation. Another programming task applying a data structure is the implementation of linked list and then using it as a snake in a Snakeses game.

I also decided to remake all the recorded lecture videos. There was a couple of errors in the slides and some content needed revisions and better treatment. So I did that too.

All in all my attempt was to make the course easier to pass without cutting or compromising the learning goals. It has become kind of a bottleneck course in the curriculum when the compulsory prerequisite to a later course was cut away. Now students tend to take it later than before, so the number of students passing has been slightly lower than before. Hopefully this helps, well see.

Courses started two weeks ago and the first deadline of DSA is tomorrow at noon. Students are supposed to deliver the URL to the remote repository where their code will be.

I also remade the tool teachers and students can use to check the code does use the building blocks they should use. Like when implementing a recursive binary search, the tool checks that the function actually calls itself. Another example is a data structure’s toString() -method. There, it must use StringBuilder, not String — since it is thousands of times slower with large number of elements in the collection.

Some checks are made to make sure students do not use loops in various cases where they should cope without. So the tool has a rules file listing all these different kinds of rules to follow:

ArrayQueue.clear,mustnot,for
LinkedListImplementation.toString,must,StringBuilder
FastSort.sort,mustnot,Arrays.sort
BinSearch.searchRecursively,must,searchRecursively
BinSearch.searchRecursively,mustnot,while
BinSearch.searchRecursively,mustnot,for

The implementation then uses the com.github.javaparser library to parse the student code and check if the rules are followed. To avoid false warnings (and to some extent trying to fool the tool), the tool first clears comments and literal string values out of the code, and then analyses if the rules are fulfilled.

Otherwise, I’ve been in the usual Fall flu for the past ~25 days. I had to stop running due to that. A week a go I had to visit a doctor due to feeling really odd, like fainting. Nothing was found, except that the white cell levels in blood were a bit elevated. Strange times, strange illnesses.

First run after the break was the on Tuesday this week. I did 5+ km run, and yesterday 4+ km run, totaling a bit over ten km. Hopefully no more sickness this year.

Now I’ll have to rush to a teachers’ meeting. Returning back to this blog later, hopefully sooner.

Vacation!

I usually start my summer vacation from Midsummer. This year that’ll mean that the last workday is today 🥳 I’ll be returning to work in the beginning of August.

So far, I’ve been busy in several fronts, including supervising and evaluating MSc theses and planning and organizing the teaching schedules and resourcing for the Fall. We eventually got one half time student assistant to help us in the courses for the Fall semester.

We were required to provide at least some face-to-face teaching for 1st year students, since complaints this study year about only online/remote teaching offered. It is “a bit” difficult to organize classroom teaching when the total number of students is 900+ and there are only two full time teachers and two other teachers who have only some hours allocated for these three large courses (plus one smaller) we need to organize. Especially when the remote possibility is a must, not an option, since these courses have Open University students and students from other Finnish universities participating.

Anyways, we again tried to fulfill these requirements with the little resources we’ve got. In some earlier years, we have had two halftime student assistants, last year only one and that’ll be the case for next Fall too. Not even comparing to 15 years ago when we had like ten+ student assistants helping out in various courses. Things really have changed from those days. Let’s see how we cope this time…

Another big thing I’ve been working on is implementing new programming tasks for my Data Structures and Algorithms (DSA) course. The passing ratio of the course is too low. I am again trying to find new ways to make the course more straightforward to pass.

Last year I made explicit paths to pass the course so that the students could take an easier path with less programming to get the grade 1. If someone wanted a higher grade, then they’d do additional programming tasks. And if someone wanted a high grade (4-5/5), then they were also required to participate in one-to-one evaluation discussion with me, talking through the algorithms and data structures they implemented and analyzed in their reports.

Before last year’s changes, most students got a grade 3-5. After this change, most students got the grade 1, then some the grade 2 and minority got grades 3-5. Not sure which change contributed to what extent to this change in the course output.

Unfortunately, the passing rate did not improve regardless of these changes. Most of the students that fail actually either never start doing anything, or they do at most the 1-2 first programming tasks (which actually were based on the previous prerequisite course contents) and then disappeared. Maybe they just decided that the course is too challenging considering their prerequisite knowledge and skills? Or they had more important courses they prioritized?

One reason may be that DSA is no longer a prerequisite for any other course in the curriculum. A couple of years ago it still was a prerequisite and students needed to pass DSA to get into Programming 3 (server side stuff). That was then required to enter Programming 4 (GUI programming) and via that path, getting in the BSc Project course was not possible without passing DSA first in the chain. As the prerequisite DSA ⇒ Programming 3 was removed, it may well be that DSA will be one of those last ones they try to pass before getting their BSc degrees, together with the compulsory Swedish language course… Maybe the hypothesis was that DSA is useless for the courses coming after it. Not that I agree with that…

The issues of plagiarism and using AI to avoid learning and doing things have somewhat increased during the last 2-3 years. These issues are not too widespread yet (?), but still clearly on the rise. I’ve now used the same programming tasks for two years, and it is apparent that some are getting the solutions copied from somewhere.

Luckily there are lots of students that are willing to do the work and walk the path, regardless of the challenges — always very motivating to the teachers to see. It is very rewarding for us teachers to discuss issues with students, walk them through their problems, seeing their joy of finally solving difficult issues and at the same time see them learn and gain skills that are important for their professional development as software developers.

Items from above were the motivation for me to implement new programming tasks for the DSA course. Most of the task learning objectives are the same as before. I decided to drop the first task that was just revisiting the insertion sort algorithm taught already in the prerequisite course. That algorithm is now given to the students, as they are supposed to know it already. So no two weeks of course time is spent on that anymore.

Instead, the first task focuses on correctness, first of the two important learning goals for the DSA course, the other being time complexity (to some extent also memory complexity). They’ll learn to use invariants to ensure code is behaving correctly, including pre- and postconditions, as well as loop and class invariants, implemented with asserts and thrown exceptions.

I’ve brought back the partitioning algorithm task back from 2021 course implementation. The binary search tree task will be a bit easier; the current task is too challenging because the context of applying it makes it too large a task to do. So that’ll be simplified.

Furthermore, all of the tasks where they apply the generic algorithm or data structure they first implement, will change. There will no longer be a single example app using all those algorithms and data structures they implement. Some students did value this concrete example with a GUI; seeing a real world context where all this they are supposed to learn, is actually used and needed. But for many, this context was too large, too much code to digest and understand, they got confused and felt lost. In the next DSA, each task they go through will be in a separate, small and simple(r) component, focusing only on a specific algorithm or data structure and how to apply it and analyze it.

Another of my goals is also to move the course exams from Moodle to controlled computer exam class. It is apparent that the Moodle exam is too easy to do in a way that doesn’t measure the students’ actual knowledge of the topics. Maybe the new setup does this better, if I manage to prepare the new exams in the new environment in time for Fall…

The new set of programming tasks has 11 different tasks, and I’ve currently (re)implemented five of them:

  • Assess and support code correctness of algorithms by using asserts, pre- and postconditions and loop and class invariants.
  • Recursive binary search, comparing time efficiency with linear search.
  • Partitioning an array using a predicate, filtering utilizing the partitioning algorithm.
  • Quicksort, comparing it with insertion sort and partitioning/filtering, when sorting is not actually necessary.
  • Stack, using stack to check XML correctness (parsing code is given).

That leaves six tasks to be finished in August, before the course begins in September:

  • Queue.
  • Linked list.
  • Binary search tree, applying it to count unique words in Project Gutenberg books / frequency of person names in datasets.
  • Hash tables and hash functions, possible application is the same as with binary search tree, to compare time/memory efficiency of these two ?
  • Using the Set in Java (not implementing it).
  • Something about Graphs (breath/depth first searches?), or leave this to theory only, depending on the estimated student workload.

As usual, all tasks will have automated unit tests and also the code is analyzed by using tools that I’ve discussed in some of my previous posts.

To get all this done in time, it’ll be a busy month in August. Especially when I want to avoid having errors with the material given to students. I should reserve time to test and assess the code and the instructions so that I do not have to post too much errata when the course starts…

In the past, I did use the summer time to do these kinds of teaching development work, but since the past 2-3 years, I’ve decided not to sacrifice myself to work. Vacation time is my time, not the employers.

Hopefully you, dear reader, have also the time to relax during your vacation time, whenever that might be. Have a nice Summer! 🌞 (or Winter if you locate at the other side of the globe, don’t actually know if it is dry or rainy season in between 😃).

Course deadlines app re-revisited

In an earlier post, I mentioned an issue that the deadline list rows were not updated properly while looking at the deadline coming and going. And that the solution was to do this:

@Observable
class Deadline: Codable {
// ...
   var viewUpdateNeeded: Bool = false
//...
   var isReached: Bool {
      viewUpdateNeeded = date <= Date.now
      return viewUpdateNeeded
   }

That is, to update an observed value viewUpdateNeeded to generate a change event to the @Observable so that SwiftUI view would get updated properly.

I do not know if the code above always had an issue I just didn’t see. Or did an update to Xcode and Swift/SwiftUI libraries change something. But anyways, what I did notice afterwards was this:

  1. Start the app.
  2. Hide the left side course list view (when the ContentView showed ContentUnavailableView since no course was selected).

In this use case scenario, everything works OK. But if I did this:

  1. Start the app.
  2. Select a course from the course list on the left (ContentView displaying the deadlines of the currently selected course).
  3. Hide the left side course list view.

Then the app would get stuck, freeze totally, at step 3 (not at step 2). Why this happened is that the list row view got updated repeatedly, so that the app used 100% of the CPU.

I saw this happening when testing the app in Instruments and looking at which operation was taking the most of the execution time. Then I placed a breakpoint into that piece of code and repeated the two usage scenarios. That showed me what was happening – app was not frozen but too busy updating the list rows that it became unusable.

The reason was the line viewUpdateNeeded = date <= Date.now “changed” the value of viewUpdateNeeded from false to false repeatedly and SwiftUI then repeatedly rebuild the view. While I was in the impression that this was not supposed to be a change. Only if the value would actually change from false to true or true to false would be a change that would cause view rebuilding.

Either I had misunderstood how SwiftUI works, and my original solution did not work at all, but I just didn’t see that happening until I dit the second use case scenario with the app. Or something changed in how SwiftUI works, and then this became a bug.

Whichever, I do not know. I have no idea why this happened only when hiding the list of courses, since the course deadlines were already visible at step 2 in the second scenario.

Anyways, this change got rid of the bug:

@Observable
class Deadline: Codable {
//...
   var viewUpdateNeeded: Bool = false
//...
   var isReached: Bool {
      if date <= Date.now && !viewUpdateNeeded {
         viewUpdateNeeded = true
      }
      return viewUpdateNeeded
   }

That is, change the value of viewUpdateNeeded only when the condition becomes true.

UI testing is important, folks. Unfortunately I did not implement any automated UI testing for this app, so finding the bug was just by a chance. Regression testing is also important, to check if the app still works after updating the tools, frameworks and libraries used by the app, to catch any changes and possible issues caused by the changes there.

Occurrence of Unicode code points used in Project Gutenberg texts

I think it was when having my morning coffee and loitering the internets, I saw somewhere this discussion:

“Here something that woud be really useful for PG development going forward: A count of the occurrence of unicode code points used in PG texts.”

PG refers to Project Gutenberg, where they publish out-of-copyright books for anyone to read for free, in various languages and formats. Highly recommended, btw, if you didn’t know about them before!

Since I’ve done something like this before, just with words, not Unicode code points, I thought I’d give this a try. Especially when using several books from Project Gutenberg as test material in my Data Structures and Algorithms course. Gotta give back, as you know.

I only know just the basics of Unicode, like: it’s hard and complex, full of rabbit holes to fall into if you are ignorant or assume too much. On the other hand, Swift should be one of the best languages to handle Unicode correctly with the String and Character class.

The tool I made (ducking other responsibilities, again!) is available in GitHub. I commented about the tool in the PG issue discussion, as well as my noobness in correct Unicode processing. Hopefully this and/or the other implementations/data offered to them is usable for the purpose. If you know Unicode better than me, please tell me if there’s something to improve in the tool.

A useful site found while doing this is Unicode Explorer. Worth a bookmark.

As what comes to PG folks’ discussion on “…A recent issue #271 notes that the 2em dash is often used but missing in many typefaces.” — my implementation found that the 2em dash (“⸺”, Unicode symbol with codepoint U+2E3A) occurs in the provided book dataset 8 414 times.

Going to mark the hours of this little job in the category of “contributing to the culture & society” in my university work plan 🙌🏼✌🏼(the hands are codepoints U+1F64C U+1F3FC and U+270C U+1F3FC, the symbol itself plus the Fitzpatrick skin type / color…).

To end this post, here’s some technical details on the implementation. Processing the books is done using a Swift task group, concurrently (some details removed from the actual code):

try await withThrowingTaskGroup(of: [Character: Int].self) { group in

 if let enumerator = fileManager.enumerator(atPath: books) {
  while let file = enumerator.nextObject() as? String {
   if file.hasSuffix(".txt") {
     fileCount += 1
     // Add a task to task group to handle one file
     group.addTask { () -> [Character: Int] in
       var taskCodePoints: [Character: Int] = [:]
       let fullPath = booksDirectory + file
       let fileContents = try String(contentsOf: URL(fileURLWithPath: fullPath), encoding: .utf8)
// ...taskCodePoints contains the result of one file
            return taskCodePoints
         }
      }
   }
   
   // Combine the results from various concurrent tasks totaling the result 
   // from all files:
   for try await partial in group {
      for (key, value) in partial {
         codePointsUsage[key, default: 0] += value
      }
   }
}

The data structure [Character: Int] is a Swift dictionary of key-value pairs. Key (Character) is the unique characters read from the files, and value (Int) is the occurrence count of each character — how many times it was occurring in the books.

Each subtask collects a count of character occurrences from one book, and then those are merged together in the for try await partial in group structure, as the subtasks finish.

Watching from the Mac Activity Monitor, I saw that the app used to have nine threads, processing the books in parallel on my Uni work laptop (MacBook Pro M2). For some reason, my own older MacBook Mini M1 was usually much faster in processing than the M2 MacBook.

Plz halp dedlinz

Apparently I like coding with the Course Deadline Counter app, now renamed to Course Deadlines app. I really, really should stop doing this, and focus on something else more acute and important — that’s why the call for help. I need to be stopped!

[I am aware of this being my own responsibility, thank you so much, so plz do not send halp.]

As a new feature, the app now has a timeline View:

A screenshot of a timeline view. Image has course names, under each deadlines, so that the deadlines are placed on a timescale advancing from left to right. Deadlines have a goal and date time, as well as a symbol.

Where you can view the deadlines of courses currently ongoing. The view does not show courses that haven’t yet begun or courses where the last deadline already passed.

For a student user, viewing all ongoing courses is more important, to be able to view the deadlines of the courses she is currently taking and how they relate between all the work she needs to do.

For a teacher, it is more relevant to show the deadlines to students in a single course. So limiting the view to just that one course is more important, using the drop down list at the top of the view.

Considering the needs and usage patterns of different groups of users is taught in our GUI design and programming course (Programming 4). So obviously I have to be able to do this stuff myself. Consider that done, at least in this case.

For drawing the course and course deadlines, I used the SwiftUI Canvas and for the gradient background, MeshGradient. I should still test the colors with both light and dark modes and find a usable combination of colors and an overlay to mute the colors when and if they are in disagreement with the text.

Drawing on the canvas is just simple drawing operations using the GraphicsContext, plotting the text and symbols on calculated coordinates. For example, if there are no ongoing courses, the code draws only the text “No courses to show”, in the middle of the view:

Canvas { context, size in

   var origin = CGPoint(x: 0, y: 0)
   let text = Text("No courses to show").font(.title).bold()
   let resolved = context.resolve(text)

   if coursesToPlot.isEmpty {
      // Draw info that there is no ongoing courses to show
      var textSize = size
      textSize = resolved.measure(in: size)
      origin.x = size.width / 2 - textSize.width / 2
      origin.y = size.height / 2 - textSize.height / 2
      let rect = CGRect(origin: origin, size: textSize)
      context.draw(resolved, in: rect)
   } else {
// ...

With the Picker, choosing to show all courses or just one, I searched for different solutions from the internet, and chose this one:

@State private var selectedCourse: Course? = nil
// ...
Picker("Show ongoing courses", selection: $selectedCourse) {
   Text("All").tag(Optional<Course>(nil))
   ForEach(deadlines.ongoing, id: \.self) { course in
      Text(course.name).tag(Optional(course))
   }
}

So there is the “show all” option where the course selection is then set to nil. Otherwise, the selection is the selected course. This enables “no selection” or as it is handled in this case, “all are selected”.

I think I got to use the Swift if expression (different from the usual if conditional statement) here the first time, handling the above picker selection:

let coursesToPlot = if let selectedCourse {
   [selectedCourse]
} else {
   deadlines.ongoing.sorted(by: { $0.startDate < $1.startDate } )
}

In plain English: if the selectedCourse is not null, let the coursesToPlot be an array containing only the selectedCourse. Otherwise, get all the ongoing courses from the deadlines, sorted by starting date ascending, and let the coursesToPlot be those courses.

The next time I touch this app should really be in the end of August or beginning of September, when I decide on the deadlines of the courses I am responsible for.

In the other news, I was today awarded as the Distinguished Teacher of the Year (again) by the study program student guild Blanko. Thank you! I am so humbled to receive this appreciation for the work we are doing here.