Apr 24, 2021

My 2021 Swift Student Challenge Submission

In a previous post, I teased the topic of this year's Swift Student Challenge, which was about creating a short interactive experience using a Swift playground, with roughly three minutes of content. Since the submission deadline has passed for some time now, I'll elaborate on what I worked on for this year's challenge.

As a foreword, submissions usually impress with the use of topics like computer graphics, animations, everything around 3D and rendering, or machine learning and augmented reality (AR) with the ARKit. Unfortunately, at the time of writing this and working on the submission, I have close to zero experience whatsoever in any of these topics, since I usually work on things around web applications and services, as you might have noticed.

So I chose a different topic that came to mind much earlier, around the time when I first started studying at university last fall. Due to the restrictions and health risk involved with learning on campus, everything shifted online, which you probably know. This made it very hard to get to know people, outside of Zoom meetings, and the occasional breakout session.

Immediately I was inclined to build something that would make it easier for students to connect, pretty much the old Facebook mission statement, adjusted for a global pandemic. Fortunately, in the end, I teamed up with a good friend of mine, and over the past months we've been building sonata, a platform where you can meet people who enjoy the same music, but that's a story for a different day.

For this challenge I had the idea to use my SwiftUI knowledge to showcase how relatively straightforward it would be for students to build digital experiences for their universities and schools, without compromising on a native experience.

Find Friends

My example application, Find Friends, would allow the user to select their preferences in activities, music, food, and potentially any topic you could think about. I did keep it relatively limited for the submission, but this could of course be extended easily.

Once everything was configured, you would be able to meet people who shared your preferences in any of the selected topics, and you'd get to see their profile with some more details, as well as a prompt to reach out and connect.

While the idea isn't novel or particularly complex, I enjoyed the process of building it, and demonstrating that you don't need a degree in rocket science for a relatively powerful experience, especially in comparison to the awfully bland services the university used a few months ago.

Since the Student Challenge put limitations in place that require all submissions to run completely offline, I couldn't rely on my main skill of building something fancy for the backend, instead, I opted to go with a JSON string of all demo users, who you could meet.

Due to limitations in the playground system, persisting data wasn't really working either, so the final playground ended up being one page, which is neither good nor bad in the end.

/**
 A struct describing a user
 */
public struct User: Decodable, Identifiable {
    // A unique user identifier
    public var id: String

    // The user's first name
    public var name: String

    // A set of unique user preferences
    public var preferences: Set<String>

    // Wheter the user prefers video chat over text messages
    public var prefersVideo: Bool

    // The subject or course a user is studying at the moment
    public var studies: String

    // An optional image for the user
    public var image: UIImage?

    // These keys will be using for encoding to/decoding from JSON
    // Note how the image is not contained, as this will be used
    // from an external location
    private enum CodingKeys: String, CodingKey {
        case id, name, preferences, prefersVideo, studies
    }

    // Creater a user
    public init(
			id: String,
			name: String,
			preferences: Set<String>,
			prefersVideo: Bool,
			studies: String,
			image: UIImage?
			) {
        self.id = id
        self.name = name
        self.preferences = preferences
        self.image = image
        self.studies = studies
        self.prefersVideo = prefersVideo
    }
}

The primary struct used for the playground was the user, a collection of personal details, including a set of preference identifiers. These would be used to compute matches with the current user, simply using set intersection as can be seen below.

/**
 The function to find matching preferences given two sets
 */
func findMatchingPreferences(_ p1: Set<String>, _ p2: Set<String>) -> Set<String>{
    // While this is relatively simple and works by calculating the intersection
    // of two preference sets, this can be evolved throughout the development
    // lifecycle to take more details into account.
    return p1.intersection(p2)
}
/**
 Given the list of users and the current user's preferences,
 find another user with matching preferences.
 */
func findMatch() -> User? {
    // We will calculate a selection of other users with
    // matching preferences first
    let filtered = users.filter { u in
        let match = findMatchingPreferences(u.preferences, preferences)
        return match.count > 0
    }

    // And then select a random user from that list
    return filtered.randomElement()
}

As mentioned in the code above, the matching logic isn't particularly sophisticated, but it does the job quite well, and it's fast enough to run at every rendering step of SwiftUI.

In addition to finding the matches, the profile view also includes a score, helping you to quickly assess the "compatibility" between you and the other person. Once again, quite simple

// Calculate a similarity score, simply the relation of mutual preferences to total preferences
// Format this floating point score as an integer
let similarity = (Float(mutualPreferences.count) / Float(ownPreferences.count)) * 100
let similarityStr = String(format: "%.0f", similarity)

In the end I glued all the logic together with some SwiftUI views and a pretty basic router that would render the onboarding screen if no preferences were selected previously, and the main screen otherwise

/**
 To support multiple views and a flow from selecting preferences
 to finding matches, we will render different views based on the internal state
 */
func buildView() -> AnyView {
    // If the current user specified their preferences
    if let hasPrefs = preferences {
        // we will render the MeetView, which allows to find
        // other users with matching preferences
        return AnyView(
            MeetView(
                users: users,
                preferences: hasPrefs,
                clearPreferences: {
                    self.preferences = nil
                }
            )
        )
    } else {
        // otherwise we will present the onboarding view,
        // which allows to select your preferences.
        // Once updated, we'll invoke the setPreferences function below,
        // which will align the local state to match, triggering a re-render
        // and switching to the upper case
        return AnyView(
            OnboardingView(
                setPreferences: { prefs in
                    self.preferences = prefs
                }
            )
        )
    }
}

Wrapping Up

In the end, there are probably way too many amazing submissions for this to be noticed, but I had fun building it and believe that when it comes to social media, we should start being creative again. And this definitely depends on the community, as each school or university is a different ecosystem in itself, with a different culture, different people, and different needs. And still, I would love to see people using technology to connect in these times, as that seems to be the reality for some time to come, still.