修复上传图片失败问题

This commit is contained in:
zeng 2026-01-27 15:16:20 +08:00
parent 261c75c8b7
commit 54aeb47fd9
2 changed files with 217 additions and 196 deletions

View File

@ -8,12 +8,12 @@
import Cocoa
class APUploadIAPListVC: NSViewController {
@IBOutlet weak var tableView: NSTableView!
@IBOutlet weak var enterBtn: NSButton!
@IBOutlet weak var preserveCurrentPriceBtn: NSButton!
@IBOutlet weak var showApiRateLimitLogsBtn: NSButton!
public var currentApp: App? {
didSet {
setupUI()
@ -24,22 +24,22 @@ class APUploadIAPListVC: NSViewController {
self.tableView.reloadData()
}
}
private var screenshotPaths = [String: String]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.columnAutoresizingStyle = .uniformColumnAutoresizingStyle
self.tableView.selectionHighlightStyle = .none
self.tableView.sizeToFit()
}
func setupUI() {
self.view.window?.title = "批量内购买项目上传 - " + (currentApp?.appName ?? "")
self.tableView.reloadData()
}
func showUploadView() {
// present
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
@ -58,36 +58,36 @@ class APUploadIAPListVC: NSViewController {
self.presentAsSheet(upVC!)
}
}
@IBAction func clickedUploadShotBtn(_ sender: Any) {
showUploadView()
}
@IBAction func clickedSPasswordBtn(_ sender: Any) {
let vc = APASCKeysSettingVC()
presentAsSheet(vc)
}
@IBAction func createIAP(_ sender: Any) {
let list = self.iaps
guard list.count > 0 else {
APHUD.hide(message: "当前 App 无上传的内购商品!", delayTime: 1)
return
}
guard let appid = currentApp?.appId else {
APHUD.hide(message: "当前 App 的 appleid 为空!", delayTime: 1)
return
}
enterBtn.isEnabled = false
APHUD.show(message: "上传中", view: self.view)
let uploadIAPs: ((AppStoreConnectKey) -> Void) = { [weak self] ascKey in
//
self?.updateInAppPurchse(iaps: list, appId: appid, ascKey: ascKey)
}
guard let ascKey = InfoCenter.shared.currentASCKey else {
let vc = APASCKeysSettingVC()
vc.updateCompletion = { password in
@ -98,7 +98,7 @@ class APUploadIAPListVC: NSViewController {
presentAsSheet(vc)
return
}
uploadIAPs(ascKey)
}
@ -106,7 +106,7 @@ class APUploadIAPListVC: NSViewController {
// MARK: -
extension APUploadIAPListVC {
func updateInAppPurchse(iaps: [IAPProduct], appId: String, ascKey: AppStoreConnectKey) {
let showApiRateLimit = showApiRateLimitLogsBtn.state.rawValue == 1
let ascAPI = APASCAPI.init(issuerID: ascKey.issuerID,
@ -114,7 +114,7 @@ extension APUploadIAPListVC {
privateKey: ascKey.privateKey,
showApiRateLimit: showApiRateLimit)
ascAPI.addMessage("密钥信息:\(ascKey.issuerID), \(ascKey.privateKeyID), \(ascKey.privateKey)")
Task {
// 1 app app
guard let apps = await ascAPI.apps() else {
@ -130,7 +130,7 @@ extension APUploadIAPListVC {
APHUD.hide(message: "当前的密钥没有查到App: \(appId),请检查~", delayTime: 2)
return
}
// 2.
let sb = NSStoryboard(name: "APDebugVC", bundle: Bundle(for: self.classForCoder))
let newWC = sb.instantiateController(withIdentifier: "APDebugWC") as? NSWindowController
@ -141,11 +141,11 @@ extension APUploadIAPListVC {
ascAPI.updateMsg = { messages in
logVC?.debugLog = messages.joined(separator: "\n")
}
ascAPI.addMessage("开始处理内购商品,获取现有商品中...")
// 3
let oldIAPs = await ascAPI.fetchInAppPurchasesList(appId: appId)
// 4
for product in iaps {
//
@ -155,15 +155,15 @@ extension APUploadIAPListVC {
await createInAppPurchase(appId: appId, product: product, oldIAPs: oldIAPs, ascAPI: ascAPI)
}
}
self.enterBtn.isEnabled = true
APHUD.hide()
ascAPI.addMessage("完成全部内购商品,可稍后在苹果后台查看!✅✅✅")
}
}
// MARK: -
///
func createInAppPurchase(appId: String, product: IAPProduct, oldIAPs: [ASCInAppPurchaseV2], ascAPI: APASCAPI) async {
ascAPI.addMessage("开始上传内购商品:\(product.productId)\(product.name) ")
@ -172,7 +172,7 @@ extension APUploadIAPListVC {
if let iap = iaps.first {
ascAPI.addMessage("订阅商品已经存在:\(product.productId) ,跳过更新信息...")
return;
ascAPI.addMessage("内购已经存在:\(product.productId) ,开始更新信息中...")
// 0. 使
var product = product
@ -185,8 +185,8 @@ extension APUploadIAPListVC {
ascAPI.addMessage("内购已经存在:\(product.productId) ,更新信息失败!❌ ")
return
}
// 3.
ascAPI.addMessage("开始更新内购本地化版本:\(product.productId)")
let localizations = await ascAPI.fetchInAppPurchasesLocalizations(iapId: iap.id)
@ -208,16 +208,16 @@ extension APUploadIAPListVC {
await createIAPLocalization(iapId: iap.id, localization: localization, product: product, ascAPI: ascAPI)
}
}
// 4.
await createIAPScreenshot(iapId: iap.id, product: product, ascAPI: ascAPI)
// 5.
await updateIAPAvailableTerritories(iapId: iap.id, product: product, ascAPI: ascAPI)
// 2.
await updateIAPPricePoint(iapId: iap.id, product: product, ascAPI: ascAPI)
} else {
// 1.
guard let iap = await ascAPI.createInAppPurchases(appId: appId, product: product) else {
@ -225,48 +225,48 @@ extension APUploadIAPListVC {
ascAPI.addMessage("内购商品:\(product.productId) ,创建失败!❌ ")
return
}
// 3.
for localization in product.localizations {
await createIAPLocalization(iapId: iap.id, localization: localization, product: product, ascAPI: ascAPI)
}
// 4.
await createIAPScreenshot(iapId: iap.id, product: product, ascAPI: ascAPI)
// 5.
await updateIAPAvailableTerritories(iapId: iap.id, product: product, ascAPI: ascAPI)
// 2.
await updateIAPPricePoint(iapId: iap.id, product: product, ascAPI: ascAPI)
}
ascAPI.addMessage("内购商品:\(product.productId)\(product.name) ,上传完成!\n")
}
///
func updateIAPPricePoint(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
guard let schedule = product.priceSchedules else {
ascAPI.addMessage("无价格计划表:\(product.productId) ,请确认!❌ ")
return
}
let baseTerritory = schedule.baseTerritory
let baseCustomerPrice = schedule.baseCustomerPrice.normalizePrice()
ascAPI.addMessage("开始更新价格计划表:\(product.productId)\(baseTerritory)\(baseCustomerPrice) \n")
let points = await ascAPI.fetchPricePoints(iapId: iapId, territory: [baseTerritory])
if let point = points.filter({ $0.attributes?.customerPrice!.normalizePrice() == baseCustomerPrice }).first {
var manualPrices: [Any] = []
var included: [Any] = []
ascAPI.addMessage("开始构建基准国家和自定价格:")
// base Territory
manualPrices.append(["id": "${\(baseTerritory)-\(included.count)}", "type": "inAppPurchasePrices"])
included.append(ascAPI.fetchInAppPurchasePriceSchedule(scheduleId: baseTerritory, pricePointId: point.id, iapId: iapId, index: included.count))
// customerPrice
for pricePoint in schedule.manualPrices {
let territory = pricePoint.territory
@ -279,9 +279,9 @@ extension APUploadIAPListVC {
ascAPI.addMessage("自定价格的内购价格点:\(territory)\(customerPrice) ,未找到此档位!❌ ")
}
}
ascAPI.saveLogs(log: "内购的基准国家和自定价格:\(manualPrices)\(included)")
if (await ascAPI.updateInAppPurchasePricePoint(iapId: iapId, baseTerritoryId: baseTerritory, manualPrices: manualPrices, included: included)) != nil {
//
ascAPI.addMessage("内购价格点:\(baseTerritory)\(baseCustomerPrice) ,更新价格成功!✅ ")
@ -294,8 +294,8 @@ extension APUploadIAPListVC {
ascAPI.addMessage("基准国家的内购价格点:\(baseTerritory)\(baseCustomerPrice) ,未找到此档位!❌ ")
}
}
///
func createIAPLocalization(iapId: String, localization: IAPLocalization, product: IAPProduct, ascAPI: APASCAPI) async {
ascAPI.addMessage("开始更新本地化版本:\(product.productId)\(localization.locale)")
@ -307,7 +307,7 @@ extension APUploadIAPListVC {
ascAPI.addMessage("内购本地化版本:\(localization.locale) ,更新语言失败!❌ ")
}
}
///
func createIAPScreenshot(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
ascAPI.addMessage("开始更新内购商品的送审截图:\(product.productId)\(product.reviewScreenshot)")
@ -316,8 +316,9 @@ extension APUploadIAPListVC {
ascAPI.addMessage("内购商品:\(product.productId) 无送审截图或未上传截图~")
return
}
let imaUrl = URL.init(fileURLWithPath: imgPath)
let uploadFileName = imaUrl.lastPathComponent.isEmpty ? imgName : imaUrl.lastPathComponent
guard let fileMD5 = URL.init(fileURLWithPath: imgPath).fileMD5() else {
ascAPI.addMessage("内购商品截图文件错误:\(imgPath) ,无法生成 md5 值~")
return
@ -332,16 +333,20 @@ extension APUploadIAPListVC {
ascAPI.addMessage("内购商品截图创建失败:\(imgName) ,无法删除旧截图~")
}
}
ascAPI.addMessage("创建新的送审截图:\(product.reviewScreenshot)")
ascAPI.addMessage("创建新的送审截图:\(uploadFileName)")
//
let imaSize = imaUrl.fileSizeInt()
guard let shot = await ascAPI.createInAppPurchasesScreenshot(iapId: iapId, fileName: imgName, fileSize: imaSize) else {
guard imaSize > 0 else {
ascAPI.addMessage("内购商品截图文件错误:\(imgPath) ,文件大小为 0~")
return
}
guard let shot = await ascAPI.createInAppPurchasesScreenshot(iapId: iapId, fileName: uploadFileName, fileSize: imaSize) else {
//
ascAPI.addMessage("内购商品:\(product.productId) ,创建送审截图失败!❌ ")
return
}
//
guard let method = shot.attributes?.uploadOperations?.first?.method,
let url = shot.attributes?.uploadOperations?.first?.url,
@ -350,14 +355,14 @@ extension APUploadIAPListVC {
ascAPI.addMessage("内购商品:\(product.productId) ,创建送审截图失败!苹果参数异常~ ❌ ")
return
}
var request = URLRequest(url: baseURL)
request.httpMethod = method
for header in requestHeaders {
request.headers[header.name ?? ""] = header.value ?? ""
}
ascAPI.addMessage("上传新的送审截图:\(product.reviewScreenshot)")
ascAPI.addMessage("上传新的送审截图:\(uploadFileName)")
//
guard let response = try? await URLSession.shared.upload(for: request, fromFile: imaUrl) else {
ascAPI.addMessage("内购商品:\(product.productId) ,创建送审截图失败!上传图片异常~ ❌ ")
@ -367,8 +372,8 @@ extension APUploadIAPListVC {
ascAPI.addMessage("内购商品:\(product.productId) ,创建送审截图失败!上传图片异常 \(response.1.description)~ ❌ ")
return
}
ascAPI.addMessage("提交新的送审截图:\(product.reviewScreenshot)")
ascAPI.addMessage("提交新的送审截图:\(uploadFileName)")
//
if ((await ascAPI.updateInAppPurchasesScreenshot(iapShotId: shot.id, fileMD5: fileMD5)) != nil) {
ascAPI.addMessage("内购商品:\(product.productId) ,送审截图上传成功!✅ ")
@ -376,7 +381,7 @@ extension APUploadIAPListVC {
ascAPI.addMessage("内购商品:\(product.productId) ,送审截图可能上传失败! ")
}
}
///
func updateIAPAvailableTerritories(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
let inAll = product.territories.availableInAllTerritories
@ -384,7 +389,7 @@ extension APUploadIAPListVC {
let summary = territoryInfo(product: product)
let newTerritory = inNew ? "将来新国家(地区)时自动提供!" : "将来新国家(地区)时不自动提供!"
ascAPI.addMessage("开始更新内购商品的销售国家/地区:\(summary)")
guard !inAll else {
var allTerritories: [[String: String]] = []
if let territories = await ascAPI.territories() {
@ -405,7 +410,7 @@ extension APUploadIAPListVC {
}
return
}
///
var territories: [[String: String]] = []
product.territories.territories?.forEach({ territory in
@ -414,7 +419,7 @@ extension APUploadIAPListVC {
"id": territory.id
])
})
let customerTerritory = product.territories.territories?.map({ $0.id }).joined(separator: ",") ?? ""
if (await ascAPI.updateInAppPurchasesAvailabilityTerritories(iapId: iapId, availableTerritories: territories, availableInNewTerritories: inNew)) != nil {
ascAPI.addMessage("内购商品的销售国家/地区:\(customerTerritory) ,更新成功!✅ ")
@ -422,10 +427,10 @@ extension APUploadIAPListVC {
ascAPI.addMessage("内购商品的销售国家/地区:\(customerTerritory) ,更新失败!❌ ")
}
}
// MARK: -
///
func createRenewSubscription(appId: String, product: IAPProduct, ascAPI: APASCAPI) async {
let groupName = product.groupName
@ -434,46 +439,46 @@ extension APUploadIAPListVC {
var subGroups = await ascAPI.fetchSubscriptionGroups(appId: appId)
// if subGroups.isEmpty {
// }
for subGroup in subGroups {
if subGroup.attributes?.referenceName == groupName {
currentSubGroup = subGroup
}
}
//
if currentSubGroup == nil, let group = await ascAPI.createSubscriptionGroups(appId: appId, groupName: groupName) {
currentSubGroup = group
subGroups.append(group)
for localization in product.localizations {
let _ = await ascAPI.createSubscriptionGroupLocalizations(iapGroupId: group.id, name: localization.name, locale: localization.locale, customAppName: nil)
}
}
//
if let group = currentSubGroup {
if let localization = product.groupLocalization {
let _ = await ascAPI.createSubscriptionGroupLocalizations(iapGroupId: group.id, name: localization.name, locale: localization.locale, customAppName: nil)
}
}
// 2
var subscriptions = [ASCSubscription]()
for subGroup in subGroups {
let subs = await ascAPI.fetchSubscriptionGroupSubscriptions(iapGroupId: subGroup.id)
subscriptions.append(contentsOf: subs)
}
// 3
let subs = subscriptions.filter({ $0.attributes?.productID == product.productId })
if let sub = subs.first {
///
ascAPI.addMessage("订阅商品已经存在:\(product.productId) ,跳过更新信息...")
return;
ascAPI.addMessage("订阅商品已经存在:\(product.productId) ,开始更新信息中...")
// 0. 使
var product = product
@ -486,7 +491,7 @@ extension APUploadIAPListVC {
ascAPI.addMessage("订阅商品已经存在:\(product.productId) ,更新信息失败!❌ ")
return
}
// 2.
ascAPI.addMessage("开始更新订阅商品本地化版本:\(product.productId)")
let localizations = await ascAPI.fetchSubscriptionLocalizations(iapId: iap.id)
@ -508,16 +513,16 @@ extension APUploadIAPListVC {
await createSubscriptionLocalization(iapId: iap.id, localization: localization, product: product, ascAPI: ascAPI)
}
}
// 4.
await createSubscriptionScreenshot(iapId: iap.id, product: product, ascAPI: ascAPI)
// 5.
await updateSubscriptionAvailableTerritories(iapId: iap.id, product: product, ascAPI: ascAPI)
// 3.
await updateSubscriptionPricePoint(iapId: iap.id, product: product, ascAPI: ascAPI)
} else {
// 1.
guard let iapGroupId = currentSubGroup?.id, let iap = await ascAPI.createSubscription(iapGroupId: iapGroupId, product: product) else {
@ -525,7 +530,7 @@ extension APUploadIAPListVC {
ascAPI.addMessage("订阅商品:\(product.productId) ,创建失败!❌ ")
return
}
// 2.
if let localization = product.subscriptionLocalization {
await createSubscriptionLocalization(iapId: iap.id, localization: localization, product: product, ascAPI: ascAPI)
@ -533,41 +538,41 @@ extension APUploadIAPListVC {
// for localization in product.localizations {
// await createSubscriptionLocalization(iapId: iap.id, localization: localization, product: product, ascAPI: ascAPI)
// }
// 4.
await createSubscriptionScreenshot(iapId: iap.id, product: product, ascAPI: ascAPI)
// 5.
await updateSubscriptionAvailableTerritories(iapId: iap.id, product: product, ascAPI: ascAPI)
// 3.
await updateSubscriptionPricePoint(iapId: iap.id, product: product, ascAPI: ascAPI)
}
ascAPI.addMessage("订阅商品:\(product.productId)\(product.name) ,上传完成!\n")
}
///
func updateSubscriptionPricePoint(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
guard let schedule = product.priceSchedules else {
ascAPI.addMessage("无价格计划表:\(product.productId) ,请确认!❌ ")
return
}
let baseTerritory = schedule.baseTerritory
let baseCustomerPrice = schedule.baseCustomerPrice.normalizePrice()
ascAPI.addMessage("开始更新订阅商品价格点,基准国家:\(product.productId)\(baseTerritory)\(baseCustomerPrice) \n")
let isPreservePrice = preserveCurrentPriceBtn.state.rawValue == 1
ascAPI.addMessage("保留自动续期订阅者现有定价:\(isPreservePrice ? "" : "")")
let points = await ascAPI.fetchSubscriptionPricePoints(iapId: iapId, territory: [baseTerritory])
if let point = points.filter({ $0.attributes?.customerPrice!.normalizePrice() == baseCustomerPrice }).first {
ascAPI.addMessage("开始更新自定价格:")
// ,
var customerPriceSchedules = schedule.manualPrices
@ -588,7 +593,7 @@ extension APUploadIAPListVC {
ascAPI.addMessage("自定价格的订阅商品价格点:\(territory)\(customerPrice) ,未找到此档位!❌ ")
}
}
ascAPI.addMessage("开始更新全球均衡价格:")
// API
let allPoints = await ascAPI.fetchSubscriptionPricePointsEqualizations(pointId: point.id, territory: nil)
@ -612,8 +617,8 @@ extension APUploadIAPListVC {
ascAPI.addMessage("基准国家的订阅商品价格点:\(baseTerritory)\(baseCustomerPrice) ,未找到此档位!❌ ")
}
}
///
func createSubscriptionLocalization(iapId: String, localization: IAPLocalization, product: IAPProduct, ascAPI: APASCAPI) async {
ascAPI.addMessage("开始更新订阅商品本地化版本:\(product.productId)\(localization.locale)")
@ -625,7 +630,7 @@ extension APUploadIAPListVC {
ascAPI.addMessage("订阅商品本地化版本:\(localization.locale) ,更新语言失败!❌ ")
}
}
///
func createSubscriptionScreenshot(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
ascAPI.addMessage("开始更新订阅商品的送审截图:\(product.productId)\(product.reviewScreenshot)")
@ -634,8 +639,9 @@ extension APUploadIAPListVC {
ascAPI.addMessage("订阅商品:\(product.productId) 无送审截图或未上传截图~")
return
}
let imaUrl = URL.init(fileURLWithPath: imgPath)
let uploadFileName = imaUrl.lastPathComponent.isEmpty ? imgName : imaUrl.lastPathComponent
guard let fileMD5 = URL.init(fileURLWithPath: imgPath).fileMD5() else {
ascAPI.addMessage("订阅商品截图文件错误:\(imgPath) ,无法生成 md5 值~")
return
@ -650,16 +656,20 @@ extension APUploadIAPListVC {
ascAPI.addMessage("订阅商品截图创建失败:\(imgName) ,无法删除旧截图~")
}
}
ascAPI.addMessage("创建新的送审截图:\(product.reviewScreenshot)")
ascAPI.addMessage("创建新的送审截图:\(uploadFileName)")
//
let imaSize = imaUrl.fileSizeInt()
guard let shot = await ascAPI.createSubscriptionScreenshot(iapId: iapId, fileName: imgName, fileSize: imaSize) else {
guard imaSize > 0 else {
ascAPI.addMessage("订阅商品截图文件错误:\(imgPath) ,文件大小为 0~")
return
}
guard let shot = await ascAPI.createSubscriptionScreenshot(iapId: iapId, fileName: uploadFileName, fileSize: imaSize) else {
//
ascAPI.addMessage("订阅商品:\(product.productId) ,创建送审截图失败!❌ ")
return
}
//
guard let method = shot.attributes?.uploadOperations?.first?.method,
let url = shot.attributes?.uploadOperations?.first?.url,
@ -668,14 +678,14 @@ extension APUploadIAPListVC {
ascAPI.addMessage("订阅商品:\(product.productId) ,创建送审截图失败!苹果参数异常~ ❌ ")
return
}
var request = URLRequest(url: baseURL)
request.httpMethod = method
for header in requestHeaders {
request.headers[header.name ?? ""] = header.value ?? ""
}
ascAPI.addMessage("上传新的送审截图:\(product.reviewScreenshot)")
ascAPI.addMessage("上传新的送审截图:\(uploadFileName)")
//
guard let response = try? await URLSession.shared.upload(for: request, fromFile: imaUrl) else {
ascAPI.addMessage("订阅商品:\(product.productId) ,创建送审截图失败!上传图片异常~ ❌ ")
@ -685,8 +695,8 @@ extension APUploadIAPListVC {
ascAPI.addMessage("订阅商品:\(product.productId) ,创建送审截图失败!上传图片异常 \(response.1.description)~ ❌ ")
return
}
ascAPI.addMessage("提交新的送审截图:\(product.reviewScreenshot)")
ascAPI.addMessage("提交新的送审截图:\(uploadFileName)")
//
if ((await ascAPI.updateSubscriptionScreenshot(iapShotId: shot.id, fileMD5: fileMD5)) != nil) {
ascAPI.addMessage("订阅商品:\(product.productId) ,送审截图上传成功!✅ ")
@ -694,7 +704,7 @@ extension APUploadIAPListVC {
ascAPI.addMessage("订阅商品:\(product.productId) ,送审截图可能上传失败! ")
}
}
///
func updateSubscriptionAvailableTerritories(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
let inAll = product.territories.availableInAllTerritories
@ -702,7 +712,7 @@ extension APUploadIAPListVC {
let summary = territoryInfo(product: product)
let newTerritory = inNew ? "将来新国家(地区)时自动提供!" : "将来新国家(地区)时不自动提供!"
ascAPI.addMessage("开始更新订阅商品的销售国家/地区:\(summary)")
guard !inAll else {
var allTerritories: [[String: String]] = []
if let territories = await ascAPI.territories() {
@ -723,7 +733,7 @@ extension APUploadIAPListVC {
}
return
}
///
var territories: [[String: String]] = []
product.territories.territories?.forEach({ territory in
@ -732,7 +742,7 @@ extension APUploadIAPListVC {
"id": territory.id
])
})
let customerTerritory = product.territories.territories?.map({ $0.id }).joined(separator: ",") ?? ""
if (await ascAPI.updateSubscriptionAvailabilityTerritories(iapId: iapId, availableTerritories: territories, availableInNewTerritories: inNew)) != nil {
ascAPI.addMessage("订阅商品的销售国家/地区:\(customerTerritory) ,更新成功!✅ ")
@ -744,7 +754,7 @@ extension APUploadIAPListVC {
// MARK: - Privacy Method
extension APUploadIAPListVC {
func territoryInfo(product: IAPProduct) -> String {
let inAll = product.territories.availableInAllTerritories
let inNew = product.territories.availableInNewTerritories
@ -762,7 +772,7 @@ extension APUploadIAPListVC: NSTableViewDelegate, NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return iaps.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let iap = iaps[row]
switch tableColumn?.identifier.enumValue() {
@ -815,7 +825,7 @@ extension APUploadIAPListVC: NSTableViewDelegate, NSTableViewDataSource {
return nil
}
}
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
let prices = iaps[row].priceSchedules?.manualPrices.count ?? 0
let count = max(prices + 2, 3)

View File

@ -34,14 +34,14 @@ typealias ASCSubscriptionAvailability = AppStoreConnect_Swift_SDK.SubscriptionAv
class APASCAPI {
public var updateMsg: (([String])->Void)?
private var message = [String]() {
didSet {
updateMsg?(message)
}
}
//
private let date = Date()
var logPath: String {
@ -52,14 +52,14 @@ class APASCAPI {
return "/AppleParty/ASCAPI/log_\(currentDate).txt"
}
}
/// Go to https://appstoreconnect.apple.com/access/api and create your own key. This is also the page to find the private key ID and the issuer ID.
/// Download the private key and open it in a text editor. Remove the enters and copy the contents over to the private key parameter.
//private let configuration = APIConfiguration(issuerID: "<YOUR ISSUER ID>", privateKeyID: "<YOUR PRIVATE KEY ID>", privateKey: "<YOUR PRIVATE KEY>")
//private lazy var provider: APIProvider = APIProvider(configuration: configuration)
private var provider: APIProvider?
private var rateLimitPublisher: AnyCancellable?
init(issuerID: String, privateKeyID: String, privateKey: String, showApiRateLimit: Bool) {
do {
let configuration = try APIConfiguration(issuerID: issuerID, privateKeyID: privateKeyID, privateKey: privateKey)
@ -73,7 +73,7 @@ class APASCAPI {
handleError("初始化失败: \(error.localizedDescription)")
}
}
/// app
/// - Returns: apps
func apps() async -> [ASCApp]? {
@ -97,7 +97,7 @@ class APASCAPI {
return nil
}
}
/// App Store
/// - Returns: apps
func territories() async -> [ASCTerritory]? {
@ -117,9 +117,9 @@ class APASCAPI {
return nil
}
}
// MARK: - API
/// app
/// - Parameter appId: apple id
/// - Returns:
@ -141,9 +141,9 @@ class APASCAPI {
handleError("获取内购列表失败: \(error.localizedDescription)")
return []
}
}
///
/// - Parameters:
/// - appId: apple id
@ -172,7 +172,7 @@ class APASCAPI {
"type": "inAppPurchases"
]
]
do {
guard let provider = provider else {
return nil
@ -190,7 +190,7 @@ class APASCAPI {
return nil
}
///
/// - Parameters:
/// - iapId: id
@ -209,7 +209,7 @@ class APASCAPI {
"type": "inAppPurchases"
]
]
do {
guard let provider = provider else {
return nil
@ -227,7 +227,7 @@ class APASCAPI {
return nil
}
///
/// - Parameters:
/// - iapId: id
@ -248,8 +248,8 @@ class APASCAPI {
}
return 400
}
///
/// - Parameters:
/// - iapId: id
@ -276,8 +276,8 @@ class APASCAPI {
return []
}
}
///
/// - Parameters:
/// - scheduleId: id
@ -308,8 +308,8 @@ class APASCAPI {
]
]
}
///
/// - Parameters:
/// - iapId: id
@ -371,7 +371,7 @@ class APASCAPI {
// ]
// ]
]
do {
guard let provider = provider else {
return nil
@ -388,8 +388,8 @@ class APASCAPI {
}
return nil
}
///
/// - Parameters:
/// - iapId: id
@ -413,8 +413,8 @@ class APASCAPI {
return []
}
}
///
/// - Parameters:
/// - iapId: id
@ -439,7 +439,7 @@ class APASCAPI {
"type": "inAppPurchaseLocalizations"
]
]
do {
guard let provider = provider else {
return nil
@ -457,7 +457,7 @@ class APASCAPI {
return nil
}
///
/// - Parameters:
/// - iapLocaleId: id
@ -491,7 +491,7 @@ class APASCAPI {
return nil
}
///
/// - Parameters:
/// - iapLocaleId: id
@ -512,8 +512,8 @@ class APASCAPI {
}
return 400
}
///
/// - Parameter iapId: id
/// - Returns:
@ -530,7 +530,7 @@ class APASCAPI {
return nil
}
}
///
/// - Parameters:
/// - iapId: id
@ -555,7 +555,7 @@ class APASCAPI {
"type": "inAppPurchaseAppStoreReviewScreenshots"
]
]
do {
guard let provider = provider else {
return nil
@ -572,8 +572,8 @@ class APASCAPI {
}
return nil
}
///
/// - Parameters:
/// - iapShotId: id
@ -590,7 +590,7 @@ class APASCAPI {
"type": "inAppPurchaseAppStoreReviewScreenshots"
]
]
do {
guard let provider = provider else {
return nil
@ -607,7 +607,7 @@ class APASCAPI {
}
return nil
}
///
/// - Parameters:
/// - iapShotId: id
@ -628,8 +628,8 @@ class APASCAPI {
}
return 400
}
///
/// - Parameters:
/// - iapId: id
@ -662,7 +662,7 @@ class APASCAPI {
]
]
]
do {
guard let provider = provider else {
return nil
@ -679,11 +679,11 @@ class APASCAPI {
}
return nil
}
// MARK: - API
///
/// - Parameters:
/// - appId: apple id
@ -707,8 +707,8 @@ class APASCAPI {
return []
}
}
///
/// - Parameters:
/// - appId: apple id
@ -731,7 +731,7 @@ class APASCAPI {
"type": "subscriptionGroups"
]
]
do {
guard let provider = provider else {
return nil
@ -748,8 +748,8 @@ class APASCAPI {
}
return nil
}
///
/// - Parameters:
/// - iapGroupId: id
@ -773,8 +773,8 @@ class APASCAPI {
return []
}
}
///
/// - Parameters:
/// - iapGroupId: id
@ -801,7 +801,7 @@ class APASCAPI {
"type": "subscriptionGroupLocalizations"
]
]
do {
guard let provider = provider else {
return nil
@ -818,8 +818,8 @@ class APASCAPI {
}
return nil
}
///
/// - Parameters:
/// - appId: apple id
@ -843,8 +843,8 @@ class APASCAPI {
return []
}
}
///
/// - Parameters:
/// - iapGroupId: apple id
@ -873,7 +873,7 @@ class APASCAPI {
"type": "subscriptions"
]
]
do {
guard let provider = provider else {
return nil
@ -890,8 +890,8 @@ class APASCAPI {
}
return nil
}
func updateSubscription(iapId: String, product: IAPProduct) async -> ASCSubscription? {
let body = [
"data": [
@ -923,8 +923,8 @@ class APASCAPI {
}
return nil
}
///
/// - Parameters:
/// - iapId: id
@ -949,8 +949,8 @@ class APASCAPI {
}
}
///
/// - Parameters:
/// - iapId: id
@ -975,7 +975,7 @@ class APASCAPI {
"type": "subscriptionLocalizations"
]
]
do {
guard let provider = provider else {
return nil
@ -993,7 +993,7 @@ class APASCAPI {
return nil
}
///
/// - Parameters:
/// - iapLocaleId: id
@ -1027,8 +1027,8 @@ class APASCAPI {
}
return nil
}
///
/// - Parameters:
/// - iapId: id
@ -1055,8 +1055,8 @@ class APASCAPI {
return []
}
}
///
/// - Parameters:
/// - pointId: id
@ -1083,8 +1083,8 @@ class APASCAPI {
return []
}
}
///
/// - Parameters:
/// - iapId: id
@ -1115,7 +1115,7 @@ class APASCAPI {
]
]
]
do {
guard let provider = provider else {
return nil
@ -1132,8 +1132,8 @@ class APASCAPI {
}
return nil
}
///
func fetchSubscriptionScreenshot(iapId: String) async -> ASCSubscriptionScreenshot? {
let request = APIEndpoint.v1.subscriptions.id(iapId).appStoreReviewScreenshot.get()
@ -1143,12 +1143,23 @@ class APASCAPI {
}
let shot = try await provider.request(request).data
return shot
} catch APIProvider.Error.decodingError(let decodeError, let data) {
if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
json["data"] is NSNull {
return nil
}
let err = APIProvider.Error.decodingError(decodeError, data)
handleError("获取订阅商品的送审截图异常: \(err.localizedDescription)")
return nil
} catch APIProvider.Error.requestFailure(let statusCode, let errorResponse, _) {
handleRequestFailure(statusCode, errorResponse)
return nil
} catch {
handleError("获取订阅商品的送审截图异常: \(error.localizedDescription)")
return nil
}
}
///
func createSubscriptionScreenshot(iapId: String, fileName: String, fileSize: Int) async -> ASCSubscriptionScreenshot? {
let body = [
@ -1203,7 +1214,7 @@ class APASCAPI {
"type": "subscriptionAppStoreReviewScreenshots"
]
]
do {
guard let provider = provider else {
return nil
@ -1220,7 +1231,7 @@ class APASCAPI {
}
return nil
}
///
/// - Parameters:
/// - iapShotId: id
@ -1241,8 +1252,8 @@ class APASCAPI {
}
return 400
}
///
/// - Parameters:
/// - iapId: id
@ -1275,7 +1286,7 @@ class APASCAPI {
]
]
]
do {
guard let provider = provider else {
return nil
@ -1297,30 +1308,30 @@ class APASCAPI {
// MARK: - handle log
extension APASCAPI {
func handleRequestFailure(_ statusCode: Int, _ errorResponse: ErrorResponse?) {
print("Request failed with statuscode: \(statusCode) and the following errors:")
errorResponse?.errors?.forEach({ error in
handleError("Error code: \(error.code), title: \(error.title), detail: \(String(describing: error.detail))")
})
}
func handleError(_ msg: String) {
addMessage(msg)
print(msg)
}
func addMessage(_ msg: String) {
let dateFormatter : DateFormatter = DateFormatter()
dateFormatter.dateFormat = "[MM-dd HH:mm:ss] "
let currentDateString = dateFormatter.string(from: Date())
let log = currentDateString + msg
message.append(log)
//
saveLogs(log: log)
}
func saveLogs(log: String, retry: Int = 3) {
do {
try log.appendLine(to: logPath.createFilePath)