Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Imagy app icon

Imagy

A photo-first social app for the people who actually matter — no public feed, no ads, no recommendation algorithm.

Platforms: iPhone · iPad · macOS · WidgetKit
Status: Private TestFlight beta; App Store release candidate built with Xcode Cloud.

This is a public showcase repository. The production source code and backend remain private; the screenshots, architecture notes, and selected excerpts here are published for portfolio review.

Imagy across its main iPhone surfaces


What it does

Imagy is built for a small circle of friends rather than an audience.

Open the app and the camera is ready. Take a photo first, then decide whether it belongs in a direct chat, a group, or your seven-day highlights. Photos stay connected to conversations instead of disappearing into an algorithmic feed.

The core product surfaces are:

  • Camera-first sharing — capture immediately and choose recipients afterward
  • Direct and group chats — text, photos, emoji reactions, and quoted photo replies
  • Photo history — a chronological stream and gallery of sent and received moments
  • Seven-day highlights — multi-photo stories with individual captions, organized by person or group
  • Configurable widgets — choose one chat and keep its latest photo on the Home Screen
  • A self-chat — every account gets a private place for photos worth keeping

Imagy on iPad


Why I built it

Most social products optimize for reach, retention, and content volume. That makes them effective media platforms, but poor tools for staying close to a few people.

I wanted a different default:

  • no follower counts or public performance;
  • no advertising or ranking algorithm;
  • no pressure to create polished content;
  • photos should start conversations, not compete for attention;
  • a close group should be understandable at a glance;
  • the camera should feel as immediate as Apple's system camera.

Those constraints made Imagy less like a traditional social network and more like a shared visual memory between friends.


Architecture

flowchart TB
    subgraph Apple["Apple clients"]
        App["SwiftUI app<br/>iPhone · iPad · Mac"]
        Widget["WidgetKit<br/>App Intent chat selection"]
        Cache["Memory + disk media cache<br/>offline content snapshots"]
    end

    subgraph Backend["Convex control plane"]
        Auth["Sign in with Apple<br/>session + rotating refresh tokens"]
        Social["Friends · direct chats · groups<br/>authorization + moderation"]
        Realtime["Reactive messages<br/>history · unread counters"]
        Stories["Seven-day stories<br/>views · reminder deduplication"]
        Push["APNs notifications"]
    end

    subgraph Media["Media data plane"]
        Encrypt["AES-GCM on device"]
        Storage["Convex File Storage<br/>original + thumbnail"]
    end

    Legacy["CloudKit legacy reader<br/>pre-migration photos only"]

    App --> Auth & Social & Realtime & Stories
    App --> Encrypt --> Storage
    Storage --> Cache --> App
    Realtime --> Push
    App -. "read-only migration path" .-> Legacy
    App --> Widget
Loading

Convex owns identity, authorization, social state, chat metadata, story lifecycle, notifications, and storage references. Photo bytes are uploaded directly to file storage through short-lived upload URLs, so they never pass through a Convex function.

See ARCHITECTURE.md for the authentication, media, caching, and migration boundaries.


Tech stack

Layer Technology
Apple clients SwiftUI, Swift 6 concurrency, AVFoundation, PhotosUI, ImageIO
Design Native navigation and controls, Liquid Glass, adaptive light/dark app icon
Realtime backend Convex queries, mutations, actions, scheduled functions, file storage
Authentication Sign in with Apple, custom short-lived session JWTs, rotating refresh tokens, Keychain
Media Client-side AES-GCM, original + thumbnail uploads, memory/disk/network cache
System integration WidgetKit, App Intents, App Groups, APNs, deep links
Testing XCTest, XCUITest, Vitest, convex-test authorization suites
Build and release XcodeGen, Swift Package Manager, Xcode Cloud, TestFlight

The private repository contains roughly 10.5k lines of first-party Swift and 5k lines of TypeScript, excluding generated code and dependencies.


Selected engineering decisions

1. The camera is a warm product surface, not a screen transition

Starting an AVCaptureSession only after tapping the Camera tab made the main interaction feel delayed. Imagy keeps the session prepared while the app is active and no conversation is open. Entering a chat stops it to save energy; leaving the chat prepares it again before the user returns to Camera.

Capture and delivery are deliberately separate. A user can take the photo without selecting a recipient, review the result, and then choose one or several chats. When capture starts inside a conversation, the recipient is already known and the extra step disappears.

2. Storage encryption has an explicit, honest boundary

Every new photo receives a random 256-bit key and is AES-GCM encrypted before upload. File storage therefore contains ciphertext, and both original and thumbnail use the same per-photo key.

This is not described as end-to-end encryption: the authorized metadata response currently carries the media key alongside the signed download URL. Full E2EE would wrap the same content key separately for each recipient device. Keeping the blob format independent from key transport makes that upgrade possible without migrating every stored photo.

See snippets/photo-cipher.swift.

3. Stories are a seven-day shared memory, not a daily streak

A story batch can contain up to five photos, each preserving its original aspect ratio and carrying its own caption. The player moves horizontally through one person's photos and vertically between people or groups.

Each photo expires exactly seven days after publication. Sunday at 16:00 local time is only a reminder, not a reset boundary. The backend derives the user's local week safely from their IANA time zone and deduplicates reminder deliveries.

See snippets/story-week.ts.

4. Widgets are configured by relationship, not by global recency

The widget does not show the latest photo from anywhere. Its App Intent lets the user choose a direct chat or group, then displays that conversation's latest image edge-to-edge with only the chat name overlaid.

The conversation entity is compiled into both app and extension. This avoids an OS-beta chronod XPC failure where the configuration picker closed before the extension's entity query could run.

See snippets/widget-configuration.swift.

5. Authorization is tested at the observable boundary

Social features become risky when client filtering is mistaken for access control. Imagy's backend tests create real users, friendships, groups, blocks, stories, and messages, then execute functions under different authenticated identities.

The tests assert that outsiders cannot read a conversation, group stories never leak to non-members, blocked users cannot reopen chats, and removing a friend archives only the remover's view instead of deleting shared history.

See snippets/authorization-boundary.test.ts.

6. A CloudKit migration without breaking old photos

The first media design used one private CloudKit zone and share per conversation. It worked in the happy path but made multi-user recovery dependent on opaque share state, consumed the sender's iCloud quota, and permanently constrained future clients to Apple platforms.

New photos now use Convex File Storage. Existing records retain their CloudKit identifiers, and the client falls back to a read-only legacy loader when no storage ID exists. The migration changed the write path without turning users' old memories into broken placeholders.


Product principles

  • Small is a feature. Imagy is intentionally optimized for people you know, not people you might follow.
  • Photos need context. A reaction or reply should preserve the image that started the conversation.
  • Native behavior earns trust. Camera, gestures, widgets, layout, and platform conventions should feel at home on each Apple device.
  • Privacy claims must match the key path. Encryption at rest is useful, but it is not E2EE until the backend cannot read recipient keys.
  • Migration paths are product features. Replacing infrastructure is only complete when old content remains accessible.

Repository layout

imagy-showcase/
├── assets/
│   ├── app-icon.png
│   ├── imagy-iphone.jpg
│   └── imagy-ipad.jpg
├── snippets/
│   ├── authorization-boundary.test.ts
│   ├── photo-cipher.swift
│   ├── story-week.ts
│   └── widget-configuration.swift
├── ARCHITECTURE.md
├── LICENSE
└── README.md

The production repository additionally contains the universal Swift app, WidgetKit extension, Convex backend, automated tests, design exports, release tooling, and private environment configuration.


Full source

The full production source is private, but I am happy to grant read-only access for technical interviews. Reach out on LinkedIn or by email at armansamary@gmail.com.


License

The selected excerpts in this repository are released under the MIT License. The Imagy application, product design, assets, and full private codebase remain proprietary.

About

Imagy — a photo-first social app for close friends across iPhone, iPad, and Mac. Public portfolio showcase; production source is private.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages