# FAQ

# Logistics

Organization of the course. Questions about course policies.

# How does this course work?

You team up with other students, pick a project and execute on it! This means choosing requirements, designing a solution, and building a complete application, with unit tests, release notes, and an installer.

You are expected to iterate on a solution through the term, and submit your results dveevery two weeks. Your grade is based on your team's ability to deliver something that effectively meets your user's needs, in a timely fashio.

There are also lectures to introduce technical content and teach you best practices for working as a team. See the schedule for details.

schedule

# Why a team? Can I just work alone?

Software is built by teams, and learning to work within a team - collaborating, helping one another out - is a critical skill for everyone to learn! Working alone is not allowed.

# How do we submit our work?

All your work will be stored in a GitLab project which the course staff can access directly for grading. Make sure your work is complete and documented by the appropriate deadline. To submit for grading, you typically provide a link to your submission through Learn > Dropbox.

# How can I track my grades?

Your grades are tracked in Learn under the Grades menu. Grades are usually available 4-5 days after your submit something.

# How can I learn all of this material?

It's completely manageable! Keep in mind that in this course, everything relates to the project. The course is designed so that the time you might spend on other things in a different course (e.g. midterms) will be spent here reading, tinkering, and working on your project. The lectures are focused, practical and designed to teach you what you specifically need to know at that time, so make sure to attend. There's also reference material available.

# Toolchain

The development tools we use in this course. See required software for details on our technology choices.

# Why are we using this technology stack?

We have specific requirements for our technology stack:

  1. All our development tools need to be supported on Windows, macOS or Linux. This is a firm requirement because we cannot reasonably force anyone to use specific hardware for this course, and students often have a mix of different systems. This is more of a challenge than you might expect since many tools are locked to one specific platform, e.g., you cannot write Swift applications unless you own Apple hardware; C# is typically used for building Windows applications and not Linux applications and so on.
  2. We need to build modern graphical user interfaces. There are only a handful of GUI toolkits that we can use given these constraints. e.g., SwiftUI is a non-starter.
  3. We want you to have the ability to build your choice of desktop or mobile native applications. We strongly prefer native applications over web applications.
  4. We want to use modern, well-designed technologies!

# Can I use my preferred technology stack instead?

Sorry, but no, we cannot support different tech stacks.

It takes a lot of effort to build and support a course like this, and the course materials rely heavily on our technology decisions, i.e., lectures are based on our tech stack; the TAs know how to support you with it; it's what will be discussed on forums. It also happens to be a great tech stack with a lot of real-world applications, so think of this as an opportunity to expand your skills!

# Can I use Vi/Neovim/Emacs/Sublime for development?

You'll find that IntelliJ IDEA (or Android Studio) offer advanced features not available in those other editors, which is why we recommend an IDE. But hey, you do you.

# GitLab

Questions about setting up and using GitLab.

# What should I name my project in GitLab?

See Getting-Started > Creating a GitLab Project for deteails.

# Does it matter who "owns" the project?

No. Just make sure that all team members have full access to the project (Manage > Members in the project settings), and that your TA and instructor have at least Developer access.

# How do issues relate to requirements?

The requirements that you discussed as part of your project proposal should reflect the features in your project.

Issues are a representation of the tasks required to complete the requirements. Each requirements should be fulfilled by one or more issues.

# How should I structure my issues?

In an ideal system, we could model this as an umbrella of issues (work to be done) grouped under a parent issue (requirement). e.g.,

flowchart TB
    Requirement --> Issue1
    Requirement --> Issue2
    Requirement --> Issue3

Unfortunately, GitLab doesn't support issue hierarchies like this. It's recommended that instead you create separate issues and link related issues together (using the Link field in the issue).

flowchart LR
    Issue1 --> Issue2
    Issue2 --> Issue3

e.g. a requirement like "Create a user record" might break into multiple linked issues.

# What information should I add to my issues? (i.e. what should they contain?)

You should decompose your tasks into half-day "chunks" of functionality that is (a) logically related, and (b) testable. The issue should describe what you need to do to implement this functionality.

e.g. the "Create a user record" example above might decompose into two or more related issues.

# Gradle

How to setup and use Gradle. Also see the Gradle notes

# How can I tell Gradle what version of the JDK to use?

Gradle will use the JDK in the class path on your system to execute tasks. If you are using IntelliJ IDEA, you can also specify the version to use in Settings > Build Tools > Gradle.

Often you want to tell Gradle to compile your code with a specific JDK version. You can specify this in your project configuration files. For example, here's how you specify JDK 17.

// add this to your build.gradle.kts
java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(17))
    }
}
// add this to your settings.gradle.kts so that Gradle can download the JDK if needed
plugins {
    id("org.gradle.toolchains.foojay-resolver-convention") version("0.4.0")
}

# How do I create an installer for my program?

For a Compose desktop application, there is a set of Gradle packaging tasks: Gradle > application > Tasks > compose desktop > package*. You probably want to packageDistributionForCurrentOS which will build a regular installer for the OS that you're currently using.

For Android, it's acceptable to build an APK file (Build > Generate APK) instead of a full installer. This file can be drag-dropped onto a running AVD to run the program.

There are also third-party installation tools if you want something more sophisticated. e.g. Conveyor.

# How can I fix an installer that fails?

Here are some common errors to watch out for (all related to the build.gradle.kts configuration file):

  1. You need to specify that you want to include all modules in the installer.
nativeDistributions {
        includeAllModules = true
        targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
        packageName = "cs346-project"
        packageVersion = "1.0.0"

}

  1. The version property defaults to "1.0-SNAPSHOT". You MUST change this to the format "1.0.0" - see Semantic Versioning. You should probably be incrementing this with each software release/demo.
group = "com.example"
   version = "1.0.0"
  1. All libraries don't fully support JDK 20 and later, so you should probably use an older version of the JDK (see required software). Make sure to set in the project settings (File > Project Structure > Project) and the Gradle project settings (IntelliJ > Settings > Build > Gradle)

If your installer fails when you execute it, try checking some of the other related tasks in the Gradle Menu in IntelliJ. e.g. compose desktop > runDistributable. The Gradle tasks in this section represent the subtasks that the installer builds and if you run them in the IDE, you will get more detailed feedback than you would from running the installer manually.

# Kotlin

How to implement specific features?

# How can I locate a user's home directory, or other system information?

If your application writes data (to a file-based database, or even just a file), you need to make sure that the file location is writable. An easy way to accomplish this is to write to the user's home directory, which is always writable for a given user. You can find this type of information by using the System JDK class:

// home directory should always be writable
val homedir = System.getProperty("user.home")

// you can derive other common locations
val docsFolder = "${homedir}/Documents"

If you want something more complex, e.g., the AppData/ directory structure under windows, then you should consider using AppDirs, a multiplatform library that will return this information from the host operating system.

# What classes should I unit test?

Good question. Ideally, you would write unit tests for everything, but practically that's very challenging (e.g., how do you determine if a button is displaying the correct color when rendered?).

One of the reasons why we have ViewModels - models that hold the state for our Views - is that the View itself is difficult to test. By storing its state in a simpler class, we can write unit tests for that class. For example, you can unit test Model changes and make sure that they appear in the ViewModel that is monitoring that Model.

What does this mean for you? If you structure your project using the clean-UI pattern that we discussed in-class, you should be able to write unit tests for any of the "inner layer" classes (i.e., not the external entities like the database, or user interface, but the classes that communicate with them).