A workout accountability partner app earns its place after the workout you almost skipped. A friend asking "Did you go?" can help once. A shared feed of checkmarks usually becomes another tab to avoid.

PactBuddy treats workout accountability as a private handoff. You choose a weekly count, invite one buddy, then send one current photo when the workout occurrence is due. The buddy reviews the check-in. The plan has somewhere to go when the week gets messy.

A workout accountability partner app needs one weekly target

"Work out more" is an intention. It gives your buddy nothing clear to recognize and gives you too much room to renegotiate the promise after the fact.

PactBuddy's workout goal asks for a number from one to seven sessions each week. That range is deliberately plain. The app does not pretend to know whether three workouts is right for you. It makes you choose a number you can defend to the person you invited.

case .workout:
    Stepper(value: $workoutsPerWeek, in: 1...7) {
        Text("Work out \(workoutsPerWeek)× each week")
            .ocFont(.body)
    }
    .padding(OCSpacing.lg)
    .hairline(radius: OCRadius.button)

The selected count stays in the goal config as workoutsPerWeek. That matters when a buddy checks the week later. They can see the promise that existed, not just the number of photos that happened to arrive.

case .workout:
    return GoalConfig(workoutsPerWeek: workoutsPerWeek)

If you are choosing the person on the other side of that promise, an accountability buddy should have a small, explicit job. The workout goal works for the same reason: both people know what they agreed to before the first deadline.

The workout proof should take one photo, not a training log

The check-in flow gives a workout one proof path. You can take a photo or choose one from your library, then send it to your buddy. The code does not ask you to record sets, reps, or a full training journal before the check-in can move forward.

if UIImagePickerController.isSourceTypeAvailable(.camera) {
    Button("Take a photo") { showCamera = true }
        .buttonStyle(.ocAccent(.lime, fullWidth: true))
}
PhotosPicker(selection: $selectedItem, matching: .images) {
    Label("Choose from library", systemImage: "photo.on.rectangle")
}
Button("Send to my buddy") { Task { await submitPhoto() } }

PactBuddy stores the submission as the photo method. Before it leaves the check-in flow, the app compresses the image to a JPEG at quality 0.72 when it can. That keeps the proof focused on the question the buddy needs to answer: does this show the workout we agreed to count?

The app uses Apple's PhotosPicker for the library path. The important product choice sits above that API. A workout accountability partner should get enough evidence to review the promise, not a new job as your remote coach.

Your workout accountability partner should review the promise, not manage your training

The buddy has a decision after proof arrives. They do not get a dashboard of every gym visit or a mandate to comment on the workout itself.

The backend client keeps that division explicit:

func submitProof(_ submission: ProofSubmission, userId: UUID) async throws
func verifyProof(incidentId: UUID, confirm: Bool, note: String?) async throws
func vouch(incidentId: UUID) async throws

The owner submits the photo. The buddy confirms or rejects it. A weekly vouch can excuse a miss when the pair agrees that the day should not break the pact. The owner cannot verify their own proof, and the server checks that role boundary as part of the incident flow.

That is a better fit for most friends than a stream of advice. Your buddy needs to be willing to answer one question. They do not need a certification in exercise science.

A missed workout needs a next step

Workout accountability falls apart when the only response to a miss is a red square. The next message becomes awkward, then the whole pact becomes another abandoned fitness plan.

PactBuddy gives the missed occurrence an incident state. The proof path moves through a small set of transitions:

open -> awaiting_verify -> resolved   (buddy confirms)
open -> awaiting_verify -> open       (buddy rejects)
open -> awaiting_verify -> excused    (buddy uses a weekly vouch)

This makes the relationship easier to use on a rough week. A rejected photo sends the incident back to open, where the reminder loop can resume. A confirmed photo resolves it. An excused miss preserves the shared streak without pretending the workout happened.

The broader accountability partner app flow after a miss covers the pager and review loop in more detail. For a workout, the useful test is simple: when you miss, does each person know the next action without starting a new negotiation?

Privacy should survive the gym

A workout photo is still personal. It can show a face, a location, or more of the room than you meant to share. PactBuddy keeps proof inside the pact, and the optional Goal Wrapped card uses a much smaller set of facts: goal labels, completed counts, the period, and an optional anonymous name.

That split gives the public share surface less to leak. A person can share that they kept a workout promise without posting the photo that proved it or naming the buddy who reviewed it.

What this workout accountability partner app does not solve

This is a proof loop, not a coaching product. The current workout flow asks for a photo. It does not tell you which program to follow, judge your form, or make a weak goal realistic.

It also cannot make a distracted buddy pay attention. Choose someone who will answer the check-in, agree on what counts as a workout photo, and say when the goal needs to change. The app can keep the handoff visible. It cannot create the relationship.

Start with a workout you keep postponing

Pick a weekly count you can defend. Decide what a fair photo looks like before either of you needs to argue about it. Then invite one person and let the first missed workout test the agreement.

If the next action is obvious, keep going. If it is not, change the pact before you change the person.