List App Store review submissions for an app. A review submission is a top-level submission record that packages one or more AppStoreVersion items and is what Apple's review queue operates on.
asc review-submissions list --app-id <id> [--state <csv>] [--limit <n>]
| Flag | Required | Description |
|---|---|---|
--app-id |
Yes | App ID whose submissions to list |
--state |
No | Comma-separated states: READY_FOR_REVIEW, WAITING_FOR_REVIEW, IN_REVIEW, UNRESOLVED_ISSUES, CANCELING, COMPLETING, COMPLETE. If omitted, all states are returned |
--limit |
No | Maximum number of submissions to return (forwarded to the ASC API limit param) |
asc review-submissions list --app-id 1234567890 \
--state WAITING_FOR_REVIEW,IN_REVIEW,READY_FOR_REVIEW \
--limit 200 --pretty{
"data" : [
{
"affordances" : {
"listVersions" : "asc versions list --app-id 1234567890"
},
"appId" : "1234567890",
"id" : "sub-1",
"platform" : "IOS",
"state" : "WAITING_FOR_REVIEW"
}
]
}asc review-submissions list --app-id 1234567890 --state UNRESOLVED_ISSUES --limit 200APP_ID=1234567890
# 1. See what's pending in Apple's queue
asc review-submissions list --app-id "$APP_ID" \
--state WAITING_FOR_REVIEW,IN_REVIEW,READY_FOR_REVIEW
# 2. See what needs developer action
asc review-submissions list --app-id "$APP_ID" --state UNRESOLVED_ISSUES
# 3. Drill into the underlying versions
asc versions list --app-id "$APP_ID"ASCCommand/Commands/ReviewSubmissions/
└── ReviewSubmissionsCommand + ReviewSubmissionsList
│
▼
Domain/Submissions/
└── SubmissionRepository (protocol, @Mockable)
│
▼
Infrastructure/Submissions/
└── OpenAPISubmissionRepository ── GET /v1/reviewSubmissions?filter[app]=…&filter[state]=…&limit=…
Dependency flow is unidirectional: ASCCommand → Infrastructure → Domain.
public struct ReviewSubmission: Sendable, Equatable, Identifiable, Codable {
public let id: String
public let appId: String // parent, injected by Infrastructure
public let platform: AppStorePlatform
public let state: ReviewSubmissionState
public let submittedDate: Date?
public var isComplete: Bool { state.isComplete }
public var isPending: Bool { state.isPending }
public var hasIssues: Bool { state.hasIssues }
}Affordances (AffordanceProviding):
listVersions→asc versions list --app-id <appId>
public enum ReviewSubmissionState: String {
case readyForReview // READY_FOR_REVIEW
case waitingForReview // WAITING_FOR_REVIEW
case inReview // IN_REVIEW
case unresolvedIssues // UNRESOLVED_ISSUES — needs developer action
case canceling // CANCELING
case completing // COMPLETING
case complete // COMPLETE
}Semantic booleans:
isComplete—state == .completeisPending—.waitingForReview | .inReview | .canceling | .completinghasIssues—.unresolvedIssues
@Mockable
public protocol SubmissionRepository: Sendable {
func submitVersion(versionId: String) async throws -> ReviewSubmission
func listSubmissions(appId: String, states: [ReviewSubmissionState]?, limit: Int?) async throws -> [ReviewSubmission]
}Sources/
├── Domain/Submissions/
│ ├── ReviewSubmission.swift
│ ├── ReviewSubmissionState.swift
│ └── SubmissionRepository.swift
├── Infrastructure/Submissions/
│ └── OpenAPISubmissionRepository.swift # submitVersion + listSubmissions
└── ASCCommand/Commands/ReviewSubmissions/
├── ReviewSubmissionsCommand.swift # subcommand group
└── ReviewSubmissionsList.swift # `list` subcommand
Tests/
├── DomainTests/Submissions/
│ └── ReviewSubmissionTests.swift
├── InfrastructureTests/Submissions/
│ └── SDKSubmissionRepositoryTests.swift # includes listSubmissions tests
└── ASCCommandTests/Commands/ReviewSubmissions/
└── ReviewSubmissionsListTests.swift
Wiring:
| File | Purpose |
|---|---|
Sources/ASCCommand/ASC.swift |
Registers ReviewSubmissionsCommand.self in the subcommands array |
Sources/ASCCommand/ClientProvider.swift |
makeSubmissionRepository() |
Sources/Infrastructure/Client/ClientFactory.swift |
makeSubmissionRepository(authProvider:) → OpenAPISubmissionRepository |
| Operation | Endpoint | SDK call | Repository method |
|---|---|---|---|
| List submissions | GET /v1/reviewSubmissions?filter[app]=…&filter[state]=…&limit=… |
APIEndpoint.v1.reviewSubmissions.get(parameters:) |
listSubmissions(appId:states:limit:) |
| Submit version | POST /v1/reviewSubmissions + POST /v1/reviewSubmissionItems + PATCH /v1/reviewSubmissions/{id} |
— | submitVersion(versionId:) |
filterApp is required by the ASC API, so --app-id is required on the CLI.
The SDK returns the parent app only inside the response's relationships.app.data.id. Domain models always carry appId, so the infrastructure mapper injects the value from the request parameter rather than reading the relationship.
swift test --filter 'ReviewSubmissionsListTests'
swift test --filter 'SDKSubmissionRepositoryTests'Representative command test (state-based, Chicago-school):
@Test func `listed submissions show id, appId, state, platform, and affordances`() async throws {
let mockRepo = MockSubmissionRepository()
given(mockRepo).listSubmissions(appId: .value("app-42"), states: .any, limit: .any)
.willReturn([
ReviewSubmission(id: "sub-1", appId: "app-42", platform: .iOS, state: .waitingForReview),
])
let cmd = try ReviewSubmissionsList.parse(["--app-id", "app-42", "--pretty"])
let output = try await cmd.execute(repo: mockRepo)
#expect(output == """
{
"data" : [
{
"affordances" : {
"listVersions" : "asc versions list --app-id app-42"
},
"appId" : "app-42",
"id" : "sub-1",
"platform" : "IOS",
"state" : "WAITING_FOR_REVIEW"
}
]
}
""")
}get --submission-id <id>— fetch a single submission with its items. AddgetSubmission(id:)to the repository.cancel --submission-id <id>—PATCH /v1/reviewSubmissions/{id}withcanceled: true. AddcancelSubmission(id:).items list --submission-id <id>— list theReviewSubmissionItems attached to a submission.
Stub:
public func getSubmission(id: String) async throws -> ReviewSubmission {
let req = APIEndpoint.v1.reviewSubmissions.id(id).get(parameters: .init(include: [.app]))
let resp = try await client.request(req)
guard let appId = resp.data.relationships?.app?.data?.id else {
throw APIError.unknown("submission \(id) has no app relationship")
}
return mapListedSubmission(resp.data, appId: appId)
}