Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

import Foundation

/// Assigns a set of app groups to an App ID. The request replaces the App ID's groups with
/// `groupIDs`, so every group the app needs has to be in one request: assigning them one
/// request at a time leaves only the last one standing.
public final class DeveloperServicesAssignAppGroupRequest: DeveloperServicesPlatformRequest {

public struct Response: Decodable, Sendable {}
Expand All @@ -16,27 +19,27 @@ public final class DeveloperServicesAssignAppGroupRequest: DeveloperServicesPlat
public let platform: DeveloperServicesPlatform
public let teamID: DeveloperServicesTeam.ID
public let appIDID: String
public let groupID: DeveloperServicesAppGroup.ID
public let groupIDs: [DeveloperServicesAppGroup.ID]

var subAction: String { return "assignApplicationGroupToAppId" }
var subParameters: [String: Any] {
return [
"teamId": teamID.rawValue,
"appIdId": appIDID,
"applicationGroups": groupID.rawValue
"applicationGroups": groupIDs.map(\.rawValue)
]
}

public init(
platform: DeveloperServicesPlatform,
teamID: DeveloperServicesTeam.ID,
appIDID: String,
groupID: DeveloperServicesAppGroup.ID
groupIDs: [DeveloperServicesAppGroup.ID]
) {
self.platform = platform
self.teamID = teamID
self.appIDID = appIDID
self.groupID = groupID
self.groupIDs = groupIDs
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,52 +33,60 @@ public struct DeveloperServicesAssignAppGroupsOperation: DeveloperServicesOperat
self.client = DeveloperServicesClient(authData: authData)
}

/// Finds the team's group for `groupID`, creating it when there is none.
private func upsertAppGroup(
_ groupID: DeveloperServicesAppGroup.GroupID,
existingGroups: [String: DeveloperServicesAppGroup]
) async throws -> DeveloperServicesAppGroup.GroupID {
) async throws -> DeveloperServicesAppGroup {
let sanitized = ProvisioningIdentifiers.sanitize(groupID: groupID)
let group: DeveloperServicesAppGroup
if let existingGroup = existingGroups[sanitized] {
group = existingGroup
} else {
let groupID = ProvisioningIdentifiers.groupID(fromSanitized: sanitized, context: context)
let name = ProvisioningIdentifiers.groupName(fromSanitized: sanitized)
let request = DeveloperServicesAddAppGroupRequest(
platform: .iOS,
teamID: xcodeAuthData.teamID,
name: name,
groupID: groupID
)
group = try await client.send(request)
return existingGroup
}

_ = try await client.send(
DeveloperServicesAssignAppGroupRequest(
platform: .iOS,
teamID: xcodeAuthData.teamID,
appIDID: appID.id,
groupID: group.id
)
let groupID = ProvisioningIdentifiers.groupID(fromSanitized: sanitized, context: context)
let name = ProvisioningIdentifiers.groupName(fromSanitized: sanitized)
let request = DeveloperServicesAddAppGroupRequest(
platform: .iOS,
teamID: xcodeAuthData.teamID,
name: name,
groupID: groupID
)

return group.groupID
return try await client.send(request)
}

/// Creates any missing groups, then assigns all of them to the App ID in one request.
/// `assignApplicationGroupToAppId` replaces the App ID's groups with the ones in the request,
/// so assigning each group separately (as this once did, concurrently) left the App ID with
/// only one of them, and an app with two groups got a profile that iOS refused to install.
/// Returns the groups' IDs in the order they were given.
public func perform() async throws -> [DeveloperServicesAppGroup.GroupID] {
let existing = try await client.send(DeveloperServicesListAppGroupsRequest(
platform: .iOS, teamID: xcodeAuthData.teamID
))
let sanitized = existing.map { (ProvisioningIdentifiers.sanitize(groupID: $0.groupID), $0) }
let dict = Dictionary(sanitized, uniquingKeysWith: { $1 })
return try await withThrowingTaskGroup(of: DeveloperServicesAppGroup.GroupID.self) { group in
for groupID in groupIDs {
let groups = try await withThrowingTaskGroup(of: (Int, DeveloperServicesAppGroup).self) { group in
for (index, groupID) in groupIDs.enumerated() {
group.addTask {
try await upsertAppGroup(groupID, existingGroups: dict)
(index, try await upsertAppGroup(groupID, existingGroups: dict))
}
}
return try await group.reduce(into: []) { $0.append($1) }
}
.sorted { $0.0 < $1.0 }
.map(\.1)

if !groups.isEmpty {
_ = try await client.send(
DeveloperServicesAssignAppGroupRequest(
platform: .iOS,
teamID: xcodeAuthData.teamID,
appIDID: appID.id,
groupIDs: groups.map(\.id)
)
)
}

return groups.map(\.groupID)
}

}