Long break, status update & demo

So, another long break in blogging. This, of course, is due to having too much work and spending the remaining energy somewhere else (like running).

So I thought I’d write a couple of posts on what I have been doing in between the previous post in January:

  • Evaluating the Data Structures and Algorithms course programming assignments. This has been faster this year, since my automated tool that both unit tests all the submissions and now also checks the source code that it doesn’t use things students are not supposed to use, and does use things they are supposed to use. Later I’ll make a post about the latest and future improvements to this tool. I still use too much time on this, due to lacking teaching resources, so the only ways to improve the situation are to add more automation and forget about giving detailed feedback to students.
  • Launching and supervising six BSc student projects. This started in January and will continue to end of April or May, depending on individual project schedules. Groups of 4-6 students show the skills and knowledge they (hopefully) acquired during their BSc studies and design and/or implement a software for an external (or “external”) customer. Sometimes they continue development of already existing software. I have weekly status meetings with them and guide them through the project, and participate in official steering group meetings.
  • Preparing and conducting our MSc program applicant interviews. This took most of the February, almost full day.
  • In March, the Programming 4 course started. The course focuses on GUI design, usability and user experience. I am assisting my colleague in this course. Students design and implement a GUI app, often the first time in their lives. Up to this point, they have usually implemented only small command line apps with relatively small number of functions or classes. This week is the last week of the course.
  • Supervising some BSc and MSc thesis students and
  • Preparing courses for teaching in Fall.

So, lots of work and thus no time for blogging, considering my priorities.

Anyways, I thought that in addition to listing the jobs above, I could also show some demonstrations I prepared for the Programming 4 course. The one described below in this post is a very simple and small demonstration showing how to use a Publish/Subscribe pattern to update GUI views when a data object (a model) in software changes state.

The old school way to do this is the Observer design pattern, which has it’s pros and cons — easy to use but has its negative consequences too. Classes related to that in Java have been deprecated though still usable.

Instead of Observer, Java recommends using other techniques, like the Publish/Subscribe pattern, using Java Flow Publisher and Subscriber interfaces and classes.

So, I implemented a simple demo app in Java and Swing, WallClockApp (GitHub link to source). The app uses the Publish/Subsctibe pattern and looks like this:

You can add several timezones using the plus button, and see the time in these different timezones. The red circle and cross button in each timezone can be used to remove a timezone. Obviously, the times advance with each clock, as seconds tick forward.

The app follows the also common Model-View-Controller architecture in GUI apps. In the app the Model (class ClockTickSource) is an object that provides the time signal, once a second. The View objects (class WallClock) are the different time zone views (three visible in the screenshot above). The app object (class WallClockApp) acts as the Controller, creating the model, and adding clock Views for user selected timezones. The Views basically just show the time in their corresponding time zones.

Where does the Publish/Subscribe then fits into this? Well, the Model needs a way to tell the views when a second has passed. So it has to know about the views that need the time tick signal. When the Model’s timer ticks, once a second, it then needs to notify the views so that they can update the time visible in their timezones.

This has been implemented here using the Java Publish/Subscribe pattern. The model is the publisher, that publishes the current time in one second intervals. The current time is published as the Unix time, milliseconds since Jan 1st, 1970, in UTC time.

Views, the Subscribers, subscribe to the Publisher as they are created:

public WallClock(final ClockTickSource clock, final TimeZone timeZone) {
   super();
   setLayout( new BorderLayout());
   this.clock = clock;
   this.timeZone = timeZone;
   this.clock.subscribe(this);

On the last line above, you can see the View object subscribing to the clock ticks, calling the clock.subscribe. The Publisher (clock, the Model) then will notify the Subscriber about successful subscription and the View can then request for the clock ticks (Long values; the milliseconds from Jan 1st, 1970 UTC):

public void onSubscribe(final Subscription subscription) {
   this.subscription = subscription;
   subscription.request(Long.MAX_VALUE);
}

The Publisher then (code below) starts the clock ticking using a Timer, but only once, for the first subscriber (since only one timer is needed to run the show; see the start() method on details on how to launch the timer):

public void subscribe(final Subscriber<Long> subscriber) {
  if (publisher == null) {
    publisher = new SubmissionPublisher<Long>(ForkJoinPool.commonPool(), 10);
  }
  publisher.subscribe(subscriber);
  if (publisher.getNumberOfSubscribers() == 1) {
    start();
  }
}

A concrete Java class SubmissionPublisher does the actual work here in the Publisher side.

Then the subscribers (views) handle the published data (Unix timestamps) as they get the notification from the Publisher when onNext is called by the Java Flow framework. The views will then convert the UTC time in milliseconds in the parameter item to their respective timezones and show that to the user:

public void onNext(final Long item) {
   final long currentTime = item;
   Date time = new Date(currentTime);
   ZonedDateTime zonedTime = time.toInstant().atZone(timeZone.toZoneId());		
   timeLabel.setText(DateTimeFormatter.ofPattern("HH:mm:ss", Locale.getDefault()).format(zonedTime));
}

When the view closes (from the red circle/cross button), it needs to unsubscribe from the publisher, using the subscription.cancel():

public void close() {
   subscription.cancel();
   Container parent = getParent();
   parent.remove(this);
   parent.revalidate();
   parent.repaint();
}

You can get the rest of the details from the linked GitHub repository. This was a very small demo, just to show how the pub-sub works with the Java interfaces and classes. If you are interested in the details, head to the GitHub demo, clone it and check it out in action!

In the following posts I’ll show some other demonstrations and also write about the updates to the automatic DSA course testing/checking tool. Have a nice day!

Slowly out of Corona virus

I got the corona virus again, the second time for me. Previous infection was in August 2022, and then it was really bad. I had a terrible headache and very, very sore throat for two weeks. Couldn’t event swallow saliva without much pain, even though I ate the max amount of over-the-counter pain killers.

This time was not as bad but still quite bad. Partly the same symptoms but now it was more like a really bad flu with almost as bad headache and sore throat as last time. It’s almost three weeks since I got the first symptoms and it is still not over.

Anyways, I did have the energy to solve Advent of Code days 10 and 11 during this time, when I wasn’t yet feeling too bad. Just didn’t have the energy to write anything about the solutions here. Also managed to solve part 1 of day 12. Since then, nothing.

I still have cough, sneezing every now and then, a feeling of having a flu and am also very tired. The whole Xmas break, as well as new year, and all the days in-between and after was spent just being sick. Didn’t see any family or the kids and their families during the break, to avoid infecting them.

We had great plans to rest and recuperate after the busy work period from September to December; just rest mentally, go skiing and running, eat good food, go sauna, meet the family, etc. So that we’d be well rested when teaching begins next week.

Instead, we are just tired, consumed by this fucking virus. Hopefully no long covid comes out of this, that’d be too much.

Let’s see if I bother to write about the solved AoC puzzles and/or continue to solve the remaining puzzles later on. Because it seems that I have more student projects to supervise than last year, with larger groups. That seems to be a trend nowadays, each year more students, less resources (teachers and hours) to do the work, actually teaching students stuff. This is so bullshit but hey, what can you do.

The student projects start next week, when I am supposed to contact them all and kick them off. Also, next week I need to start grading the Data structures and algorithms course projects, as well as prepare the final exam for that course. That’s already a lot of work to do, in this shape I currently am. Going to take it easy…

Advent of Code quick update

Solved Day 9 part 1 yesterday, but since then I’ve been caught with much work and haven’t had the opportunity to continue in spare/free time. So, I haven’t had the time nor energy to finish part 2.

This is the final teaching week in two programming courses, when students are getting quite active in making sure their work is either ready and acceptable, or they are too late in the schedule and are desperately asking for help to be able to proceed to meet the deadlines. It’s been quite busy, in Zoom breakout rooms, email and our internal support Q&A system…

So, this is probably going to be the AoC week when I’m going to fall behind. I’ll post updates when something gets done, but otherwise it’s radio silence until there is time for this “project”.

Home network improvements

The new and the old router

Last summer, when we got the new 5G network from the operator, replacing the 4G, they delivered a new router for us (on the right side of the picture).

Having a two storey house with a tin roof and a side building where I work, that little router could not handle the range needed at all. I tried to configure an old Asus router to act as a repeater for the white thing, but that did not help at all.

So, after using the phone network at the side building for a while, the frustration finally grew to intolerable levels and I went to Verkkokauppa and bought that monster on the left.

Since it is also an Asus router, the old Asus Wi-Fi router and the monster are now configured as a mesh. Goodbye networking problems.

Other news: busy at work, grading Data structures and algorithms course projects. That is put on hold from Monday for three weeks, when I spend all my free working time in student admission work. Since I am again the only teacher doing the grading, this will take some time…

The way teaching resources are diminishing each year as the number of students is rising each yer, is a constant source of frustration to me. Last Fall I was teaching 3-4 courses at the same time totalling almost 1000 students, having only two full time teachers and two part time assistants is not fun. AFAIK, next Fall is going to be probably worse. No like, no like at all.

So no resources means everything is delayed. Anyways, the rest of the work time in the coming three weeks, around 5-10 hours per week, are spent in watching over BSc project work groups. Each group has six students, and they are supposed to work on software projects for external clients. During the next three weeks, the first steering group meetings are supposed to accept the project plans for these projects. Lots of guidance giving, reading plans, giving feedback and meetings, that is.

All this is slowed down a bit me having a flu since Tuesday. All face to face meetings were changed to online meetings. Suits me well, don’t have to dress up and move to the campus, taking time from other things. But then I need to make sure I get light walks done on free time. Not too sick for that, luckily.

Fast Index Order in Binary Search Tree

So, you can use a Binary Search Tree (BST) to store and quickly access key-value pairs. Usually the key gives the position of the value element in the tree. You place smaller values in the left side, and larger values in the right side of the tree.

A sample BST unbalanced, having 13 nodes in the tree. Key in the BST is the person’s name in ascending order.

Finding by key value is O(log n) operation. Starting from the root node of the tree, comparing the key, you can choose which subtree to continue searching for the key. If the BST is balanced, you need to only do O(log n) comparisons at most to get to the key-value pair you wish to find. Like searching for Jouni Kappalainen takes four comparisons from the root and it is found.

The tree contents in key order shown in a Java Swing table view. Order is by BST key order.

Note that the numbers in each tree node picture above is the number of children of that specific node. For example, Kevin Bacon has zero children, as Samir Numminen has five, and the root node has 12 children.

How about if you want to treat the BST as an ordered collection, each item in the tree accessible by index, as you would do with an array? Like, “give me the 6th element in the tree (element at index 5), when navigating in-order, from smallest to largest key in the tree”. In the sample tree above, Kappalainen would be in the index 5 (zero based indexing). Kevin Bacon’s index would be zero since it is the first element in-order. Root node would be at index 1 in this sample tree.

The obvious thing to do would be to traverse the tree in-order, counting indices from the lowest left side node (Bacon) with index 0 (zero) and then continue counting from there until you are at the index 5 (Kappalainen).

Sample tree with in-order indices added in blue. Compare this to the GUI table order and you see the indices are correct.

This works, but the time complexity of this operation is O(n). If you have, let’s say a million or more elements in the three, that is a lot of steps. Especially if you need to do this repeatedly. Is there a faster way to do this?

The faster way is to use the child counts of the tree nodes to count the index of the current node (starting from the root), and then use that information to decide if you should proceed to left or right subtree to continue finding the desired index. This makes finding the element with an index O(log n) operation, which is considerably faster than linear in-order traversal with O(n).

For example, consider you wish to find element at the index 5. Since the root node’s left child has zero children, we can then deduce that the root node’s index is zero plus one, equals one. Therefore at the root you already know that the element at index 5 must be at the right subtree of the root. No use navigating the left subtree.

Also, when going to the right child from the root, you can calculate the index of that node, Töllikkö. Since Töllikkö is after the root node, add one to the index of root node, therefore the current index being two (2). Then, since all Töllikkö’s left children are in earlier indices, add the child count of Töllikkö’s left subtree, which is 8, to the current index, having current index of ten (10). And since the left child node must be counted too, not just its children, we add another one (1) to current index, it now being 11. So the index of Töllikkö is 11.

Now we know that since we are finding the index 5, it must be in the left subtree of Töllikkö, so we continue there and ignore Töllikkö’s right subtree.

Moving then to Kantonen, we need to deduct one (1) from the current index, since we moved to earlier indices from Töllikkä, current Index being now 10. This is not yet Kantonen’s index. We need to deduct the number of children in Kantonen’s right child + 1 from the current index, and will get 10 – 6, having 4. That is Kantonen’s index.

Since 4 is smaller than 5, we know we need to proceed to the right subtree of Kantonen to find the index 5. Advancing there, in Numminen, we need to add 1 to the current index since we are going “forward” in the index order to bigger indices, as well as the count of children + 1 (the subtree itself) in Numminen’s left subtree, in so current index (Numminen’s) is now 6.

Since 6 < 5 we know to continue searching the index from Numminen’s left subtree there we go (to Kappalainen), deducing one (1) from current index, it now being 5. Now, earlier, when moving to left (to Kantonen), we also deducted the number of children in Kantonen’s right subtree. But since Kappalainen has no right subtree, we deduct nothing.

Now we see that current index is 5, the same we are searching for. So, we can stop the search and return Kappalainen as the element in the index 5 in the tree.

Using this principle of knowing the count of children in each tree node, we can directly proceed to the correct subtree, either left or right, and therefore replacing the linear in-order search with faster logarithmic search. With large data sets, this is considerably faster.

While demonstrating this to students using the course project app TIRA Coders, we saw that loading 1 000 000 coders to the course TIRA Coders app, the O(n) index finding operation (required by the Java Swing table view) made the app quite slow and sluggy. When scrolling, the app kept scrolling long time after I lifted my hand off the mouse wheel. Swing Table view was getting the objects in the visible indices constantly while scrolling and as this is O(n) operation, everything was really, really slow.

When replaced by the O(log n) operation described above, using the app was a breeze even with the same number of one million data elements in the BST; there was no lags in scrolling the table view nor in selecting rows in the table.

So, if you need to access BST elements by the index, consider using the algorithm described above. Obviously, for this to work, you need to update the child counts in parent nodes while adding nodes to the BST. If your BST supports removing nodes and balancing the BST, you need to update the child counts also in those situations.

(I won’t share any code yet, since the students are currently implementing this (optional) feature with the given pseudocode in their BST programming task.)

Blasts from the past

Two blasts from the past. Sorry for the long post but hey, this is a frigging blog, not some social media service for attention spans of mere seconds.

First “blast”

When teaching (exercise support sessions) over Zoom, we course teachers have a private chat in Slack to coordinate who goes to which breakout room to help students asking for help. When there’s no acute problems to address, we occasionally chat about random topics.

Yesterday I was participating from the bed using my laptop since I’ve got the flu but was well enough to participate somewhat.

I have had this idea to write the Data structures and Algorithms course materials into a book format. Not to be sold in book stores, but just shared to the students as a single PDF / eBook file.

I remembered doing exactly that, very very long time ago in a discontinued course, and happened to mention this to the other teachers in Slack; that I couldn’t find the book in my computer archives.

“Hold my beer”, said one of the teachers, and voilá, he uploaded that course pdf “book” from 1999 to the Slack channel! As it happens, he was at that time or just before, a student on that course and had archived the material! He is a lot better than me in archiving, apparently, so thanks JLa for that! He also participated in teaching (also) this course, as far as I can remember.

A snippet from the course material for Programming environments course.

The course, Programming environments, was originally a Mac programming course, by our Department Mac guru who left us a long time ago to work in the industry (good for him). Then later, I implemented the course for programming with C in Windows (thanks to Petzold’s book). Even later I also implemented a version to teach Symbian C++ programming in Nokia devices.

Anyhows, I liked the idea to provide the material in a “book” format to the students, and even distributed the book in an eBook format for them to read. A total of 45 pages. I wish I’d find the original editable document somewhere from my external backup drives or USB drives, would be fun to have it.

Maybe I’ll do this sometime again for some course. Writing can be fun.

The second “blast”

A thing I did find when browsing through my archives, was a department plan to arrange teaching for the study year 2009-2010.

Especially interesting for me was to check out the list of courses our team (software engineering focused teachers/courses) at the department taught that study year (table below).

I will just list the courses about programming or strongly programming related or strongly technical courses:

Finnish course nameEnglish course name
AlgoritmitAlgorithms
C ohjelmointiProgramming in C
C++C++ programming
Johdatus kääntäjiinIntroduction to compilers
Johdatus ohjelmointiinIntroduction to programming
Johdatus tietorakenteisiinIntroduction to data structures
LogiikkaLogic
Ohjelmistojen testausSoftware testing
Ohjelmointikielten periaatteetPrinciples of programming languages
Ohjelmointityö IProgramming assignment I
Ohjelmointityö IIProgramming assignment II
Ohjelmointityö IIIProgramming assignment III
Ohjelmointityö IVProgramming assignment IV
OHJY/MacOS/iPhoneProgramming environments / Mac/iPhone
OHJY /  WindowsProgramming environments / Windows
OHJY / UnixProgramming environments / Unix
OHJY/ SymbianProgramming environments / Symbian
Olio-ohjelmointiObject oriented programming
Projekti ITerm project for external customers, programming
Projekti IITerm project for external customers, often included programming
Rinnakkainen ohjelmointiParallel / concurrent programming
RTCSReal time computer systems
Tietokantojen perusteetIntroduction to databases
Unix perusteetIntroduction to Unix

In some of those courses, doing actual programming by the students might have been a relatively small part of the course. And some of the courses were not offered every year. Just not to inflate the amount of actual programming done in some of these courses.

But – if you compare these strongly programming focused courses of that time to the list of current programming or strongly technical courses in the study program:

Finnish course nameEnglish course name
Laitteet ja tietoverkotDevices and Data networks
Ohjelmointi 1Programming 1
Ohjelmointi 2Programming 2
Tietorakenteet ja algoritmitData Structures and algorithms
TietokannatDatabases
Ohjelmointi 3Programming 3
Ohjelmointi 4Programming 3
Ohjelmistojen laatu ja testausSoftware Quality and testing
Bachelor projectProgramming project for external customers
Master’s projectSystems development /analysis project for external customers

So at that time around 2010, we had a total of 24 very technical/programming courses in old study program. Currently we have only 10.

There are courses on software engineering and such nowadays too, but they usually do not include learning to construct things and spending a relatively significant amount of time doing that – that is what I mean by a technical course here, and what was done earlier.

The current study program at the MSc level does not have a single course focused on programming. No more than 5-6 years ago, we still had at least one, Embedded software development environments, focusing (mostly) on distributed mobile programming (taught by me and some other teachers). Before that, we (including me) taught, together with Tampere University of Technology, Mobile systems programming course (Symbian, J2ME). All discontinued.

Currently we have nothing about parallel / concurrent programming in the study program. Last week I did one very small demo to at least show an extremely little thing about concurrency in my course on Data structures and algorithms. To let the students at least to be aware that something like this actually exists and could be done. So like 15 minutes of that topic in the whole study program.

Principles of programming languages, logic, compilers — totally gone from the study program in just over ten years.

You know (and I know) that there are as many opinions than there are you-know-what, but in my personal honest opinion, this is not good. Considering the needs of the local software industry.

Of course the technical topics of today would be perhaps different and obviously should be up to date with modern technologies. But if these kinds of topics and the deeply technical perspective on things is missing, there is not even the chance to discuss if they would or should be updated.

Well, maybe this is just another case for this:

Image of an old man yelling at the clouds from the Simpsons
Old man yelling at cloud, again

Multiple Git repository status awareness app

A year ago I started to implement a GUI app that I could use to track the 250-300+ student projects of my Data structures and algorithms course. The motivation for the tool was to answer the question:

How can you manage four courses with nearly 1000 students when you only have two full time teachers and two part-time supportive teachers to manage all teaching, evaluating and supporting the students in their learning in all of these courses?

Me

The plan was, that using this tool, I could more easily be aware of the statuses of the many student projects in this one course. And then when necessary, do something about possible issues.

Like, knowing that a student is left behind (no commits to the project in the last week or two), gives me a possibility to ask if there is anything I as a teacher could do to help the student to proceed in the course.

And if one or several of the unit tests of the various course programming tasks fail, especially if the task was supposed to be finished already, I could go and check the reason(s). Either by asking the student or taking a look at the git repository of the student myself.

The version last year required me to run shell scripts that would automatically retrieve the up to date code of the student from their remote repository using git pull. Output was directed to a log file the app then parsed to view the content. Similarly, tests were executed in batch from shell scripts with Maven (mvn test), result written to another log file. Again, the app would parse that log file to show in a graph how the tests succeeded or failed.

A while ago I decided that I would like to integrate all the functionality within the GUI app and ditch the separate shell scripts. Also, I decided that the information about the commits and test results would be saved into a database. This way, when app launch is now much faster since it does not need to parse the various log files to show content every time the app is launched.

Instead, when launched, the app reads the repository information, git log data and the test results from a database file. App launches much faster with immediately visible data. When I want to update the commit data for all student repositories, I just press the refresh button in the app toolbar and the app starts to pull data for each repository from the remote repository URLs.

Anonymised student repositories with commit and test data.

When I want to update the test data, pressing another button (the hammer button in the toolbar) starts to execute tests that are due, saving the test results in the app database. I can also refresh an individual repo from remote or execute tests for a single project to get a quick update on the status of the student’s project.

Until now I’ve focused on getting the tabular data visible. Yesterday, I implemented a similar commit grid view you can see in a person’s GitHub profile. This color grid shows the commits visualised for all student repositories:

Commit grid visualisation,

In this grid, time proceeds vertically from up towards bottom of the grid view. Each repository is one thin column on the grid. The timeline starts from the date the course started, proceeding down.

The grid ends at the current date in the bottom of the grid. So when the course proceeds the grid gets taller. Since this course displayed here started on September, the grid here is 42 days “tall”. This will give me a very high level visual awareness of level of activity among all the students in the course.

Obviously the usefulness of the tool depends on the fact (?) that students push their commits to the remote repository often, preferably daily or at least weekly. If they do not, we teachers cannot see them and this tool does not show them. As you can see, some of the repositories are in red, meaning there has not been a commit for over 14 days. Orange repositories indicate the most recent commit being over a week old.

Red and orange colors may mean that the student has decided to do most of the course progrmming just before the deadline. A common though not the best choice. So seeing lots of red at this point does not necessarily mean big issues with passing the course. We’ll see…

I’ve been constantly trying to motivate the students to do commits and pushing often. It is beneficial for them too – you get a backup of your work in the remote, and when you need teachers’ assistance, teachers have immediate access to the code from the remote.

Since the first real deadline of the course will be after two weeks, it is time for me to start using the information from the tool:

  • I can easily send email to the student not having recent commits, by clicking on the (redacted) blue text where student name is displayed – that will initiate an email I will send to the student.
  • Clicking on the repository URL (also redacted) will open the browser and take me to the remote repository web page of that student.
  • Another (redacted) blue line will open the local folder in Finder for me to start looking at the student project directory on my machine.

Those repositories in blue are all OK, and (probably) I do not need to be concerned with those. Especially if the scheduled tests also pass. The purpose of the tool is to let me focus on those students that probably need attention to be able to pass the course in time.

Some implementation details:

  • Implemented on macOS using Xcode, Swift and SwiftUI.
  • Uses Core Data as the app database, with five database tables and ten relationships between the tables.
  • Executes git and mvn test commands in child processes using Process class.
  • SwiftUI Table used for tabular content.
  • Commit grid is draw in a SwiftUI view using Canvas and GraphicsContext.
  • Uses the SwiftUI redacted(reason:) function to hide student information for demonstration purposes.
  • Student and repository data is imported from a tab separated text file exported from Moodle. There, student enter their information and the remote repository URL in a questionnaire form. The form data is then exported as a tsv file, which is imported to the app and all student data is written to the database. No need to enter the student data manually.

Decoding and encoding class hierarchies in Swift

A while ago I was implementing (for fun) a chat client to work with a chat server used in teaching in a GUI programming course. The client is implemented with Swift and SwiftUI.

In the GUI programming course, students get the server and a sample console client, both implemented in Java. The goal for the students is to both design and implement a GUI chat application. Learning goals are related to GUI design, usability design and implementation of a GUI in a selected language and GUI framework. The server is accessed over TCP and data format is JSON.

One tricky thing was to figure out how the Swift Codable could handle a class structure with a base class Message, having different types of messages as subclasses. For example, ChatMessage (having the sender, message, timestamp, etc.), a ListChannelsMessage (containing the available channels on the server side to join), et cetera.

The Java console client class diagram, containing the same message classes as the Swift app.

The Swift Codable does not directly and simply bend to this kind of a class hierarchy. I searched the net and found a good basis here, but it didn’t fully show how to do both decoding and encoding with a class structure like I had.

So obviously, for fun, I banged my head against the wall until I got it working. I was supposed to write a blog post about that, but haven’t had the time.

But today someone in Mastodon asked the exact question; how to do something like this. A good motivation for me to actually do something about that! I didn’t want to publish the whole Swift Chat client app, since students are still able to start working on this project, and someone might want to select Swift/SwiftUI as the GUI for their app.

So instead, I extracted the code to show how to use Codable and JSONSerialization together to handle a class hierarchy like this to encode and decode objects to JSON and back. The code is now available as a simple example project in GitHub.

I shared the repository to the Mastodon discussion. What was exciting for me is that one of the core persons in developing the Swift language itself favourited my reply! ?

No more sadness

Just checked out for my Computers and computer networks course that there is no more sadness since the Willab weather service works again!

The API has changed from HTTP to HTTPS, and the JSON data structure returned has also changed a little. So I needed to change the demo apps that get the current weather from the service.

This week I’ve again taught how to use curl from the command line, how to see the HTTP traffic using Wireshark and how the two demo apps do HTTP GET to access the weather data from the service.

Today I was [too many] years old…

…when I learned how to localise strings containing plural values using string dictionaries (.stringsdict) in Xcode.

I am working on a terminology app teachers could use to write basic terminologies about the courses they teach. Then they could share those to the students to aid in learning the course topics. Student can also add new terms to the terminology if they wish on their own devices. And even create their own terms and terminologies. Users can also share the terminologies to others.

Anyways, I wanted to localise this at least to Finnish and English. I was supervising three localisation related student software engineering projects this Spring, so I guess that also motivated me to learn more.

Anyways, I already knew from before how to do simple localisation, but not how to localise singular and plural forms of text using string dictionaries. Finally I decided to take a shot at it, and here is the result, in Finnish first. The localised text is on the left side list, on the second gray text row of each list item:

Screenshot of a app view with localised text in Finnish, the number of elements pluralised.
The list on the left is localised from English to Finnish, using string dictionaries to pluralise the count of elements (gray text on second line of list items). The string dictionary XML is visible in the background in Xcode editor.

Below is the English version. Note that the actual contents of the app has been written in Finnish, so the main text in first line of the list is of course Finnish – only the descriptive text (“Category has 40 terms” or “No terms in this category”) is localised. And pluralised, which was the main goal here.

Screenshot of a app view with localised text in English, the number of elements pluralised.
English localisation of the same view.

This was bit of a fight (as usual), since the documents are there but no single document shows in one place a) how to specify the string dictionary and b) how to use it from code in SwiftUI. I managed to write the XML file using Apple documentation, but couldn’t find a usage sample there.

So I searched the internet and browsed Stack overflow. Then it came to my mind that surely I have some sample app or open source app on my machine where string dictionaries are used. So I searched from the directory under which I have all the source code:

find ./ -name "*.stringsdict" -print

And found that the NetNewsWire RSS reader app (highly recommended!) that I cloned from GitHub some time ago uses it. Then I dug into the source code and saw a sample from there. This and some stack overflow discussions helped me to finish the job.

So here is a sample of the string dictionary (Finnish version):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
   <dict>
      <key>term(s) in category</key>
         <dict>
            <key>NSStringLocalizedFormatKey</key>
            <string>%#@terms@</string>
            <key>terms</key>
            <dict>
               <key>NSStringFormatSpecTypeKey</key>
               <string>NSStringPluralRuleType</string>
               <key>NSStringFormatValueTypeKey</key>
               <string>lld</string>
               <key>one</key>
               <string>Kategoriassa on yksi termi</string>
               <key>other</key>
               <string>Kategoriassa on %lld termiä</string>
               <key>zero</key>
               <string>Ei termejä tässä kategoriassa</string>
            </dict>
        </dict>
    </dict>
</plist>

And this is how to use it from code — the actual beef of the whole thing. All the complexity above is to make the code simple, even though you would add support for tens of different languages to the app.

First the actual Text element in SwiftUI view that shows the localised and pluralised text:

   Text(categorySubTitle())
       .foregroundColor(.secondary)

And the function categorySubTitle() that does the formatting:

private func categorySubTitle() -> String {
   let format = NSLocalizedString("term(s) in category", comment: "");
   return String.localizedStringWithFormat(format, category.termCount)
}

Simple — when you finally get the pieces together.

Hopefully I’ll get the app finished by Fall to be used by students and staff who find if useful. As a bonus, this will be a topic for a new demonstration in a GUI programming course I am participating as an assistant teacher next Spring.

There’s one potential obstacle though — seeing the new SwiftData and other new stuff coming out this week from WWDC2023 are soooo tempting….

But if I start working using those betas, I’d have to redo lots of stuff. Though since I am already using Core Data, that shouldn’t take too long. There is also the issue that then the apps (this works on macOS, iOS and iPadOS, sharing data via iCloud) would require macOS 14 Sonoma and iOS 17, and that might leave out many users who cannot upgrade or haven’t upgraded by the time courses begin… And I’d also pay some time to refactor the Java version too, that also needs some attention.

Oh well. But now some pizza ? and red wine ?!