All work

Case study

2Study

Study smarter. Plan automatically.

2Study is a native study planner for university students. It organizes courses, exams, assignments and study sessions, turns the hours a student has into a day-by-day plan, and adapts it when a session is missed.

  • Available now on the App Store
  • Version 1.7 · iPhone and iPad
  • 2.0 submitted to Apple, not released
  • Android in development
2Study Home on iPad in landscape: today’s plan with a Start button, a 22 percent progress ring and upcoming deadlines on the left; course progress rings and friends’ weekly scores on the right.
iPhone Home Screen in dark mode with two 2Study widgets, a month calendar with today’s plan and a this-week strip, and a focus timer running in the Dynamic Island.
Screenshots from the current App Store listing (version 1.7): Home on iPad, and Home Screen widgets with a focus session in the Dynamic Island.
Role
Product, design and engineeringiOS, Android, backend, testing and release
Platforms
iPhone and iPad, iOS 17 or laterAndroid version in development
Languages
English, Hebrew and ArabicFull right‑to‑left layouts
Released
20 July 2026iPad support in 1.7, 1 August 2026

Students add their courses, exams and assignments and say how much time they have. 2Study builds the plan, tracks progress against it and keeps it realistic as deadlines approach.

It is two native apps on one shared Firebase backend: Swift and SwiftUI on iOS, Kotlin and Jetpack Compose on Android. Version 1.7 is on the App Store for iPhone and iPad. Version 2.0, which grows 2Study from exam planning to the whole semester, has been submitted to Apple for iPhone and iPad and is in App Review or waiting for it. It isn’t released yet. The Android app, including its 2.0 work, is in development and has not been publicly released.

It is localized in English, Hebrew and Arabic with full right-to-left layouts, and it follows the conventions of Israeli universities and colleges, such as Moed A, B and C exam sittings.

A semester means several courses, each with its own exams and assignments, and only so many free hours a day. Turning that into a realistic schedule is work in itself.

Then the schedule has to keep working when the day changes.

What a plan has to cope with

  • A missed session
  • A sick day
  • A short day
  • An exam day
  • Moed A, B or C

In 1.7, missed work moves to the next day or the whole plan adapts. Named actions for a sick day or a short day, and a lighter exam day, come in 2.0. 2Study is designed around students at Israeli institutions: Moed exam sittings, and Hebrew or Arabic as a first language.

  1. Add courses and deadlines

    Courses, exams and assignments, each with a priority and difficulty, and the Moed sitting the student plans to take.

  2. Set the hours

    Available study time, and how to mix courses: one at a time, a chosen set, or automatic.

  3. Pick a plan

    Three plans, Balanced, Early Finish and High Flexibility, each an ordered checklist for every day.

When a session is missed, the work moves to the next day or the whole plan adapts. Around the plan, the app keeps students going: a focus timer with walk breaks, home-screen widgets, a Live Activity on the Lock Screen, streaks, a weekly review, and private leagues with friends.

Inside the app

2Study is built from the system’s own components, SF Symbols and grouped backgrounds, then adds colour where it carries meaning.

Every course gets one of 20 named colours. Breaks are always yellow, and walk breaks green. Cards use 20-point continuous corners and a soft shadow, and students choose Light, Dark or Auto appearance.

  • Accent, light#FF7A57
  • Accent, dark#7995FF
  • Brand gradient#597AFA → #AD52DE
  • Break#FAD424
  • Walk break#33C773

One look on both platforms

The Android app was planned as standard Material 3. Once the decision was made that both apps should look identical, its controls were rebuilt in Compose to match iOS, from the tab bar and forms to alerts, wheel pickers and blurred glass sheets. Sizes and the type ramp are measured from iOS simulator captures rather than guessed, and Material You dynamic colour is off so the brand stays fixed. The platform integrations stay Android-native: Glance widgets, a foreground-service timer and per-app language settings.

2Study My Plan on iPad: the August 2026 month calendar on the left, and the day’s generated schedule of tasks, breaks and a long break on the right.
On iPad, added in 1.7, the larger screen puts the month calendar beside the day’s generated plan.
Full-screen focus mode in landscape: a large orange countdown on black, with the task name above it and Deep focus below it.
Full-screen focus mode: turn the iPhone sideways during a session and the timer fills the screen. One task, one number.

Available now in 1.7

On the App Store
  • Automatic study plans

    Three deterministic plans from deadlines, estimated work and available hours: Balanced, Early Finish and High Flexibility.

  • Courses, exams and assignments

    Priority and difficulty for each item, 20 course colours, and Moed A, B and C sittings, where only the planned sitting feeds the plan.

  • Daily checklist and calendar

    My Plan as an ordered list with Week and Month views, manual tasks and breaks, and a flow for unfinished work.

  • Focus timer

    A timer sheet, a full-screen mode and an active-timer bar, walk reminders with 2- or 5-minute walk breaks, and a chime at the end of a session.

  • Widgets and Live Activity

    Today’s Plan and Study Calendar widgets, and a Lock Screen and Dynamic Island timer with an interactive Mark as done button.

  • Shortcuts and Focus

    A Start Focus Session shortcut, and a guided setup that pairs it with an iOS Focus to silence notifications.

  • Reminders

    Local notifications for the start of the day, exams, assignments, falling behind, unfinished tasks and the weekly review.

  • Progress and weekly review

    Progress tracking, streaks, a celebration when the day is done, and a weekly review every Sunday.

  • Academic record

    Completed courses with credits, grade attempts, a chosen grading scale and degree averages.

  • Friends, leagues and achievements

    Friend requests, Study Together invites, weekly leagues across eight tiers, 28 badges, and WhatsApp or email with accepted friends.

  • Accounts and sync

    Email sign-in, offline-first sync, a full restore on a new device, data export and account deletion.

  • iPad

    Since 1.7: every orientation, with layouts for the iPad’s larger size class.

Coming in 2.0

iOS submitted, Android in development

Not released yet

2.0 for iPhone and iPad has been submitted to Apple and is in App Review or waiting for it. The App Store still offers 1.7, and these features may change before release. The Android version is still in development.

12 features, not released yet
  • Dashboard

    Replaces Home: classes, tasks, deadlines, free periods, and daily and semester progress in one place.

  • Weekly timetable

    Recurring classes with one-off changes and cancellations. Classes reach the planner as busy time.

  • Calendar import

    Import a university .ics calendar through a hand-written parser, so no calendar permission is needed. Importing again changes nothing, and exams are never guessed.

  • Attendance

    A prompt after each class, attendance for each course, and catch-up work for missed classes.

  • Course workspace

    Overview, Materials, Community, Attendance and Grades for every course.

  • Course files and sharing

    A private file library for each course, and view-only sharing with friends that can be revoked. Sharing works once the 2.0 security rules are live.

  • Community library

    Shared course files with moderated submissions, reports and an audit log. Publishing goes through a server function, and no library has been published yet.

  • Smart Actions

    15 named actions: 7 that change the plan, such as “Move today’s unfinished work” or “I’m sick today”, each proposed, reviewed, approved and undoable, and 8 read-only questions. No AI and no free text. Comes with 2Study Pro.

  • Institution picker

    33 Israeli institutions, all named in English and Hebrew and 10 also in Arabic, with manual entry for any other. The list isn’t officially verified.

  • Pinned tasks

    Hand edits survive regenerating the plan. An exam day the student can study after loses 4 hours of study time instead of counting as a full day.

  • Classes widget

    Today’s classes on the Home Screen, in small, medium and large sizes, with 2Study Pro.

  • Android

    In development, not on Google Play. All 20 planned 2.0 work packages ported, screens rebuilt to match iOS, Glance widgets, and a push client for friend requests.

Architecture

Two native apps. One backend.

2Study isn’t a cross-platform app. Each platform is written natively, Swift and SwiftUI on iOS and Kotlin and Compose on Android, with no shared UI runtime. They meet at one Firebase project, with the same accounts, documents and security rules, so one account will work on both once the Android app is released.

Client · on the App Store

iOS and iPadOS

  • Swift 6 and SwiftUI, iOS 17+
  • MVVM, repositories, dependency injection
  • Persistent offline cache, newest write wins
  • Widgets fed from an App Group snapshot, and a focus Live Activity
  • Local notifications only
  • App Attest for App Check, in 2.0

Shared backend

Firebase

  • Authentication: one account for both apps
  • Cloud Firestore: the same documents
  • Cloud Storage for photos and files
  • Security rules: the 2.0 rules are tested on the emulators
  • App Check, in 2.0: installed, not enforced
  • Cloud Functions: purchase records live; push and community publishing written

2Work, in development, signs in to the same project and keeps its data in its own namespace.

Client · in development

Android

  • Kotlin and Jetpack Compose, Hilt
  • Layers: ui → domain → data → core
  • Persistent offline cache, newest write wins
  • Glance widgets from a DataStore snapshot, and a foreground-service timer
  • Push client for friend requests
  • Play Integrity for App Check, in 2.0

Both apps read and write the same Firestore documents under one written model contract. The rules and indexes live in the iOS repository, and Android’s real writes can be replayed against them on the emulators. App Check comes with 2.0 and isn’t enforced. Two Cloud Functions that keep purchase records were recorded live in October 2026; the friend-request push and community publishing functions are written.

Inside the iOS app

Pattern
MVVM with repositories and dependency injectionView models are @Observable classes on the main actor
Models
Codable, Sendable value typesEvery synced record carries an id, createdAt and updatedAt
Services
Planner, progress, scoring, grades, focus timer, notifications, sync and storageThe planner is a pure, deterministic struct
Repositories
9 protocols in 1.x, 17 in 2.0Each has a Firestore and an in-memory version; demo or live data is chosen at launch
Extensions
Widgets and the Live ActivityThe widgets read an App Group snapshot; the Live Activity shares its contract with the app

Inside the Android app

Stack
Kotlin 2.2, Jetpack Compose and HiltOne activity with Navigation Compose
Layers
ui → domain → data → coreEach layer imports only the layers to its right; the domain layer has no Android or Firebase imports
Domain
Pure Kotlin, ported from iOSPlanner, scoring, achievements, grades and weekly review, so the iOS tests could be ported to JUnit
Data
Flow-based repositories over Firestore listenersSettings in DataStore reuse the iOS key names
Platform
Glance widgets, a foreground-service timer and scheduled remindersThe timer runs as a foreground service with an ongoing notification, and reminders use AlarmManager and WorkManager

How a change reaches every screen on iOS

  1. Write locally

    A view model calls a repository, and Firestore writes to the on-device cache first, so it works offline.

  2. Sync

    The write syncs when there’s a connection. A network path monitor drives the offline banner.

  3. Merge

    Listeners bring in changes from other devices. Conflicts resolve per document: the newest update wins, with an “Updated from another device” notice.

  4. Widgets

    After each plan change, the app publishes a ready-to-display, pre-translated snapshot for the widgets.

  5. Live Activity

    The focus timer mirrors its state to the Live Activity, which counts down by itself.

On Android, Flow repositories over Firestore listeners do the same job, Glance widgets read a DataStore snapshot, and a foreground service with an ongoing notification takes the Live Activity’s place.

iOS
  • Swift 6 language mode
  • SwiftUI
  • Observation
  • WidgetKit
  • ActivityKit
  • App Intents
  • UserNotifications
  • Network (NWPathMonitor)
  • XCTest
Android
  • Kotlin 2.2
  • KSP
  • Jetpack Compose
  • Material 3 base theme
  • iOS-style Compose controls
  • Hilt
  • Navigation Compose
  • DataStore
  • WorkManager
  • Jetpack Glance
  • Coroutines
  • JUnit 4
Backend
  • Firebase Authentication
  • Cloud Firestore
  • Cloud Storage
  • App Check (2.0)
  • Cloud Functions, Node.js 22 (purchase records live)
  • Cloud Messaging (Android, in development)
Tooling
  • Swift Package Manager
  • Gradle and R8
  • Firebase Emulator Suite
  • Rules tests in Node.js
  • Python string converter
  • Shell test scripts
Versions
  • Firebase iOS SDK 12
  • Firebase Android BOM 34
  • Compose BOM 2025.06
Minimums: iOS 17.0 and Android 8.0 (API 26), targeting API 36
  • English

    Left to right

  • עברית

    Hebrew · right to left

  • العربية

    Arabic · right to left

1.x catalog
905 string keys876 English, 874 Hebrew and 874 Arabic translated
2.0 catalog
1,697 string keys1,665 English, 1,661 Hebrew and 1,661 Arabic
Android
Generated from the iOS catalog1,558 strings and 52 plurals per language
  • Chosen in the app

    The language is picked on the first screen, independent of the device language. The layout direction follows it, and widgets receive their text already translated.

  • Right to left everywhere

    SwiftUI’s direction is set at the root, and UIKit-backed lists, forms and tab bars are forced to follow the app’s language too.

  • Mixed-direction text

    Phone numbers are wrapped in directional isolates, so they read correctly inside right-to-left sentences. The Android app in development isolates names too.

  • On Android

    Strings are generated from the iOS catalog. The app language is set per app and declared in a locale config, Hebrew is packaged as both values-he and values-iw, and Western digits are pinned in resources.

  • An Israeli locale with Western digits Coming in 2.0

    2.0 pins each language to <lang>_IL@numbers=latn, for a 24-hour clock in every language and Western digits in Arabic. It uses the ICU form because Foundation can’t parse the BCP-47 one for English. In 1.7, each language keeps its default formats.

  • Real plural rules Coming in 2.0

    In 2.0, plurals follow the selected language rather than the device’s, so Arabic gets all six of its plural forms and Hebrew its dual.

  1. One planner, two platforms, identical output

    The planner is a pure, deterministic engine. It always offers three plans, weighs urgency with an 85% soft cap, keeps lecture chains together, inserts breaks, adds review days before exams, and regenerates without losing completed work. The Android port has to produce the same plan from the same input, so the 54 iOS test suites that existed at the port were mapped to Android: 32 under the same name, 11 as equivalents, 5 ported, 4 checked in code and 2 not applicable.

  2. Blank days at the start of a plan

    The daily pacing budget was a hard ceiling, so a day whose budget was smaller than one lecture that can’t be split stayed empty, and the error compounded: ten 90-minute lectures with an exam in 20 days left days 1 to 10 blank. The budget is now a target and the day’s capacity is the real limit, fixed in the current codebase on both platforms.

    ReducedCapacityDayTests

  3. A crash hidden in the data shape

    Firestore doesn’t allow nested arrays, and a plan that mixed courses stored its groups as one. Every save raised FIRInvalidArgumentException, an Objective-C exception that Swift’s do/catch can’t catch, so the app crashed. Groups are now saved as an array of maps, and the old shape still loads.

    MixGroupWireTests

  4. Keeping the timetable out of the planner

    Classes and free gaps reach the planner as availability exceptions, prepared before it runs, so the engine never reads the timetable. Later 2.0 work did change the engine: week-by-week plans, repeating homework, and class reviews, which the caller builds from the timetable and passes in. All six original planner test files still pass unedited.

  5. Changing the data model under live users

    Saves overwrite the whole document and reads skip any record that fails to decode, so one new required field would make old data silently disappear. Every new field is optional, no new task type was added that older builds couldn’t decode, and migrations are safe to re-run and never destructive.

  6. Keeping iOS and Android compatible

    A written model contract fixes field names, enum values and timestamps. Tests check that an Android save keeps the fields only iOS writes, after a bug where Android saves erased iOS course fields and task pins, and that IDs both platforms derive match, even under an Arabic locale. Android’s real writes were replayed against the shared rules, most recently in early October 2026.

    android-wire.test.mjs

  7. Writes that looked saved but weren’t

    The offline cache confirms a write on the device, so a server rejection, such as Firestore not being set up or rules not deployed, only shows in the write’s completion. Those completions are now logged, and a guard catches sessions that have no profile document.

  8. Widgets and a Live Activity without logic

    The widget process doesn’t share the app’s language settings, so the app hands it ready-to-display text through an App Group snapshot. The Live Activity counts down with Text(timerInterval:), so it needs no per-second updates. On Android a foreground service takes its place; a launch-time crash loop, where the service could stop before calling startForeground(), was found and fixed.

  9. Planning a safe App Check rollout (2.0)

    The App Check provider is installed before Firebase is configured, and on Android before Hilt creates any client, so the first requests can be attested. Release builds use App Attest and Play Integrity, debug builds a debug provider. Enforcement stays off until store builds without App Check are gone, so no existing user is locked out. The console steps, registering App Attest and Play Integrity and checking Play Integrity’s quota of 10,000 checks a day, are a separate part of the rollout.

  10. Tests that pass, code that’s never used

    A review found five pieces of code that were fully tested but unreachable from any screen. Each one was wired up or deleted.

  11. Matching iOS on Android

    An audit against iOS simulator captures found about 300 visual differences, fixed on the Android 2.0 branch. Along the way, the Android app fixes 34 iOS defects instead of copying them, each recorded in a deviations log. One example: a Free Time Assistant setting that exists on iOS but can’t be reached from any screen.

  • 1,573

    iOS tests

    XCTest, in 111 test classes. The last recorded run, on 4 November 2026, had 0 failures and 1 optional test skipped.

  • 1,824

    Android tests

    1,816 JVM unit tests and 8 instrumented tests, counted from source.

  • 273

    Security-rules tests

    273 passed and 1 was skipped in the last recorded run on the Firebase emulators, on 30 October 2026. The skipped test replays Android’s real writes. That replay was last recorded in early October 2026, when all 166 tests in the run passed.

The iOS and Android figures are counted from the test sources of the unreleased 2.0 code, November 2026: 3,397 app tests in total. The last recorded Android unit-test run, on 25 October 2026, passed all 1,816 tests. The 1.x code has 205 iOS tests. There is no UI-automation suite yet.

  • Planner tests on both platforms

    Android’s domain layer is pure Kotlin, so the iOS planner tests could be ported to JUnit and hold both engines to the same results.

  • Localization tests

    Checks for Western digits, the 24-hour clock, translations in all 20 feature areas, and plural categories on both platforms.

  • A replay of real writes

    Android records its actual Firestore writes, and a script replays them against the shared security rules on the emulators. The rules have changed since the last recorded replay.

  • Manual sweeps

    The Android app was checked in all three languages, light and dark, at phone and tablet sizes, and compared with 268 iOS simulator captures.

iPhone
iOS 17.0 or later, portraitTurning it sideways during a focus session shows the full-screen timer. Home Screen widgets, and a Live Activity on the Lock Screen and in the Dynamic Island.
iPad
iPadOS 17.0 or later, every orientationLayouts for the larger size class, since version 1.7
Android
Android 8.0 (API 26) or later, targeting API 36In development. Phone layouts match iOS; tablets use a single column for now.
In 2.0
New shell layouts for foldable iPhonesTabs across the top when open, and a side rail when closed
  • 1.7 · iPhone and iPad On the App Store since 1 August 2026, free. 2Study was first released on 20 July 2026. Available now
  • 2.0 · iPhone and iPad Submitted to Apple, in App Review or waiting for it. Not released: the App Store still offers 1.7. It adds the semester features above, and its 1,573 tests had 0 failures and 1 optional test skipped in the last recorded run, on 4 November 2026. Submitted to Apple
  • Android All 20 planned 2.0 work packages are ported, and the screens are rebuilt to match iOS on the Android 2.0 branch. Not publicly available on Google Play. In development
  • 2.0 backend The 2.0 security rules and indexes are tested on the emulators; they also cover the boundary around 2Work’s data. Two Cloud Functions that keep purchase records were recorded live on 23 October 2026. The friend-request push and community publishing functions are written. Partly deployed