This commit is contained in:
zeng 2025-10-28 11:11:21 +08:00
parent d454db55d4
commit 52d4104632
165 changed files with 26440 additions and 1 deletions

3
.gitignore vendored
View File

@ -38,7 +38,8 @@ playground.xcworkspace
# you should judge for yourself, the pros and cons are mentioned at: # you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
# #
# Pods/ Pods/
Podfile.lock
# #
# Add this line if you want to avoid checking in source code from the Xcode workspace # Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace # *.xcworkspace

View File

@ -0,0 +1,24 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Version**
- OS:
- AppleParty:

View File

@ -0,0 +1,14 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Tell us how we can improve AppleParty**
**Is your feature request related to a problem? Please describe.**
**What would you like to see? How would you like it to work?**

99
AppleParty/.gitignore vendored Normal file
View File

@ -0,0 +1,99 @@
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
xcuserdata/
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
## Obj-C/Swift specific
*.hmap
## App packaging
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm
.build/
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
Pods/
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
# Accio dependency management
Dependencies/
.accio/
# fastlane
#
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/
## Other
*.xcuserstate
## Obj-C/Swift specific
*.hmap
*.ipa
*.swp
.DS_Store

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:AppleParty.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,96 @@
{
"originHash" : "9d0d9b5cf3459d54c921a64b1bb7a803edf7e8255bc0e8227a135b3820aeee10",
"pins" : [
{
"identity" : "appstoreconnect-swift-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/AvdLee/appstoreconnect-swift-sdk.git",
"state" : {
"revision" : "78b2be2f68f30141fca2f7bce45ca7866535cf28",
"version" : "4.0.2"
}
},
{
"identity" : "bluecryptor",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Kitura/BlueCryptor.git",
"state" : {
"revision" : "cec97c24b111351e70e448972a7d3fe68a756d6d",
"version" : "2.0.2"
}
},
{
"identity" : "bluesocket",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Kitura/BlueSocket.git",
"state" : {
"revision" : "7b23a867008e0027bfd6f4d398d44720707bc8ca",
"version" : "2.0.4"
}
},
{
"identity" : "bluesslservice",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Kitura/BlueSSLService.git",
"state" : {
"revision" : "b27a94d063962dfa1bba9f79814c4ef202cf33a4",
"version" : "2.0.2"
}
},
{
"identity" : "loggerapi",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Kitura/LoggerAPI.git",
"state" : {
"revision" : "e82d34eab3f0b05391082b11ea07d3b70d2f65bb",
"version" : "1.9.200"
}
},
{
"identity" : "swift-asn1",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-asn1.git",
"state" : {
"revision" : "f70225981241859eb4aa1a18a75531d26637c8cc",
"version" : "1.4.0"
}
},
{
"identity" : "swift-crypto",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-crypto.git",
"state" : {
"revision" : "95ba0316a9b733e92bb6b071255ff46263bbe7dc",
"version" : "3.15.1"
}
},
{
"identity" : "swift-log",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "6fe203dc33195667ce1759bf0182975e4653ba1c",
"version" : "1.4.4"
}
},
{
"identity" : "swift-smtp",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Kitura/Swift-SMTP",
"state" : {
"revision" : "4b7666bb8cee33f0cb367786af17b9a2ebb63047",
"version" : "6.0.0"
}
},
{
"identity" : "urlqueryencoder",
"kind" : "remoteSourceControl",
"location" : "https://github.com/CreateAPI/URLQueryEncoder.git",
"state" : {
"revision" : "4ce950479707ea109f229d7230ec074a133b15d7",
"version" : "0.2.1"
}
}
],
"version" : 3
}

View File

@ -0,0 +1,52 @@
//
// AppDelegate.swift
// AppleParty
//
// Created by HTC on 2022/3/10.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
import Sparkle
@main
class AppDelegate: NSObject, NSApplicationDelegate {
var mainWindow: NSWindow?
@IBOutlet weak var updaterController: SPUStandardUpdaterController!
func applicationDidFinishLaunching(_ aNotification: Notification) {
//
updaterController.updater.checkForUpdatesInBackground()
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
/// 退app
/// - Parameter sender:
/// - Returns: true-false-
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
true
}
///
///
/// - Parameters:
/// - sender:
/// - flag:
/// - Returns:
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
return true
}
@IBAction func showHelp(_ sender: Any) {
let url = URL(string: kApplePartyWiKi)
NSWorkspace.shared.open(url!)
}
}

View File

@ -0,0 +1,76 @@
//
// APAppListAdapter.swift
// AppleParty
//
// Created by HTC on 2022/3/17.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
class APAppListAdapter: NSObject {
public var purchseHandle: ((_ app: App) -> Void)?
public var screenshotHandle: ((_ app: App) -> Void)?
fileprivate static let numberOfSections = 1
fileprivate static let itemId = "APAppListCell"
fileprivate var items = [App]() {
didSet {
collectionView.reloadData()
}
}
private var collectionView: NSCollectionView
init(collectionView: NSCollectionView) {
self.collectionView = collectionView
super.init()
self.collectionView.dataSource = self
self.collectionView.delegate = self
self.collectionView.register(APAppListCell.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: APAppListAdapter.itemId))
let itemWidth = CGFloat(350.0)
let itemHeight = CGFloat(150.0)
let itemSpacing = CGFloat(80.0)
let itemPadding = CGFloat(30.0)
let flowLayout = NSCollectionViewFlowLayout()
flowLayout.scrollDirection = .vertical
flowLayout.itemSize = NSMakeSize(itemWidth, itemHeight)
flowLayout.minimumInteritemSpacing = itemSpacing
flowLayout.minimumLineSpacing = itemSpacing
flowLayout.sectionInset = NSEdgeInsetsMake(itemPadding, itemPadding, itemPadding, itemPadding)
self.collectionView.collectionViewLayout = flowLayout
}
func set(items: [App]) {
self.items = items
}
}
extension APAppListAdapter: NSCollectionViewDataSource, NSCollectionViewDelegate {
func numberOfSectionsInCollectionView(collectionView: NSCollectionView) -> Int {
return APAppListAdapter.numberOfSections
}
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: APAppListAdapter.itemId), for: indexPath)
guard let collectionViewItem = item as? APAppListCell else { return item }
collectionViewItem.configure(app: items[indexPath.item])
collectionViewItem.purchseHandle = purchseHandle
collectionViewItem.screenshotHandle = screenshotHandle
return item
}
func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
collectionView.deselectItems(at: indexPaths)
}
}

View File

@ -0,0 +1,44 @@
//
// APAppListCell.swift
// AppleParty
//
// Created by HTC on 2022/3/17.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
class APAppListCell: NSCollectionViewItem {
public var purchseHandle: ((_ app: App) -> Void)?
public var screenshotHandle: ((_ app: App) -> Void)?
@IBOutlet weak var imgView: NSImageView!
@IBOutlet weak var nameView: NSTextField!
private var app: App?
override func viewDidLoad() {
super.viewDidLoad()
nameView.maximumNumberOfLines = 2
imgView.wantsLayer = true
imgView.layer?.cornerRadius = 22
imgView.layer?.masksToBounds = true
}
@IBAction func clickedPurchseItem(_ sender: Any) {
purchseHandle?(app!)
}
@IBAction func clickedScreenshotItem(_ sender: Any) {
screenshotHandle?(app!)
}
func configure(app: App) {
self.app = app
nameView.stringValue = app.appName
imgView?.showWebImage(app.iconUrl)
}
}

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="APAppListCell" customModule="AppleParty" customModuleProvider="target">
<connections>
<outlet property="imgView" destination="ua3-NY-53n" id="kAD-Bq-3qV"/>
<outlet property="nameView" destination="66e-57-Sbq" id="3Bf-jl-Gem"/>
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="Hz6-mo-xeY">
<rect key="frame" x="0.0" y="0.0" width="404" height="182"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ua3-NY-53n">
<rect key="frame" x="10" y="20" width="142" height="142"/>
<constraints>
<constraint firstAttribute="width" secondItem="ua3-NY-53n" secondAttribute="height" id="HYa-6B-qMQ"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="PlaceholderIcon" id="WgU-9p-su6"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="66e-57-Sbq">
<rect key="frame" x="170" y="141" width="216" height="21"/>
<textFieldCell key="cell" lineBreakMode="charWrapping" truncatesLastVisibleLine="YES" allowsUndo="NO" title="App" id="0Xh-fB-Cb2">
<font key="font" metaFont="systemBold" size="18"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aV2-cM-e0o">
<rect key="frame" x="165" y="64" width="132" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="ZLF-9M-xkM"/>
</constraints>
<buttonCell key="cell" type="push" title="上传商店图/视频" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="bPm-Fb-nKp">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="13" name="STSongti-SC-Regular"/>
</buttonCell>
<connections>
<action selector="clickedScreenshotItem:" target="-2" id="8Nl-TD-kpN"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="osx-a5-YMW">
<rect key="frame" x="165" y="99" width="114" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="7Jd-yY-gR9"/>
</constraints>
<buttonCell key="cell" type="push" title="上传内购项目" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="w9k-1j-6eh">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="clickedPurchseItem:" target="-2" id="Rze-yt-gng"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="ua3-NY-53n" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="20" id="51T-Sw-w41"/>
<constraint firstItem="aV2-cM-e0o" firstAttribute="leading" secondItem="66e-57-Sbq" secondAttribute="leading" id="VlU-Rb-hoR"/>
<constraint firstAttribute="trailing" secondItem="66e-57-Sbq" secondAttribute="trailing" constant="20" id="dic-eb-ugT"/>
<constraint firstItem="66e-57-Sbq" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="20" symbolic="YES" id="gaf-lX-5U5"/>
<constraint firstItem="ua3-NY-53n" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="10" id="j8M-Z4-uEW"/>
<constraint firstItem="osx-a5-YMW" firstAttribute="top" secondItem="66e-57-Sbq" secondAttribute="bottom" constant="15" id="kpc-Ag-sOu"/>
<constraint firstItem="66e-57-Sbq" firstAttribute="leading" secondItem="ua3-NY-53n" secondAttribute="trailing" constant="20" id="tvr-pZ-tsU"/>
<constraint firstItem="osx-a5-YMW" firstAttribute="leading" secondItem="66e-57-Sbq" secondAttribute="leading" id="uFZ-bD-xHJ"/>
<constraint firstItem="ua3-NY-53n" firstAttribute="centerY" secondItem="Hz6-mo-xeY" secondAttribute="centerY" id="uo4-mG-tIg"/>
<constraint firstAttribute="bottom" secondItem="ua3-NY-53n" secondAttribute="bottom" constant="20" id="wJA-Fd-dWN"/>
<constraint firstItem="aV2-cM-e0o" firstAttribute="top" secondItem="osx-a5-YMW" secondAttribute="bottom" constant="15" id="zua-6z-GtP"/>
</constraints>
<point key="canvasLocation" x="116" y="129"/>
</customView>
</objects>
<resources>
<image name="PlaceholderIcon" width="141" height="141"/>
</resources>
</document>

View File

@ -0,0 +1,83 @@
//
// APAppListModel.swift
// AppleParty
//
// Created by HTC on 2022/3/17.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Foundation
// MARK: -
struct AppList {
var games: [App]
init(body: [String: Any]) {
games = [App]()
let included = dictionaryArray(body["included"])
let apps = dictionaryArray(body["data"])
for software in apps {
var game = App()
let attributes = dictionary(software["attributes"])
game.appId = string(from: software["id"])
game.appName = string(from: attributes["name"])
game.platforms = string(from: attributes["distributionType"])
game.bundleId = string(from: attributes["bundleId"])
game.sku = string(from: attributes["sku"])
game.primaryLocale = string(from: attributes["primaryLocale"])
// icon
let appVersion = dictionaryArray( dictionary( dictionary(software["relationships"])["appStoreVersions"])["data"]).first
if let version = appVersion {
let vid = string(from: version["id"])
for info in included {
let iid = string(from: info["id"])
if vid == iid, vid.count > 0 {
let info_att = dictionary(info["attributes"])
let storeIcon = dictionary(info_att["storeIcon"])
let templateUrl = string(from: storeIcon["templateUrl"])
if templateUrl.count > 0 {
game.iconUrl = templateUrl.replacingOccurrences(of: "{w}x{h}bb.{f}", with: "500x500bb.png")
}
break
}
}
}
games.append(game)
}
games = games.sorted(by: { (g1, g2) -> Bool in
g1.appName < g2.appName
})
}
}
struct App {
var appId: String = ""
var appName: String = ""
var platforms: String = ""
var iconUrl: String = ""
var bundleId: String = ""
var sku: String = ""
var primaryLocale: String = ""
}
struct AppInfo {
var name: String = ""
var bundleId: String = ""
var bundleIdReferenceName: String = ""
var distributionType: String = ""
var educationDiscountType: String = ""
var sku: String = ""
var primaryLocale: String = ""
init(body: [String: Any]) {
let data = dictionary(body["data"])
let attributes = dictionary(data["attributes"])
name = string(from: attributes["name"])
bundleId = string(from: attributes["bundleId"])
bundleIdReferenceName = string(from: attributes["bundleIdReferenceName"])
distributionType = string(from: attributes["distributionType"])
educationDiscountType = string(from: attributes["educationDiscountType"])
sku = string(from: attributes["sku"])
primaryLocale = string(from: attributes["primaryLocale"])
}
}

View File

@ -0,0 +1,58 @@
//
// APAppListVC.swift
// AppleParty
//
// Created by HTC on 2022/3/17.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
class APAppListVC: NSViewController {
fileprivate var adapter: APAppListAdapter?
override func viewDidLoad() {
super.viewDidLoad()
configureCollectionView()
fetchAppList()
}
///
func configureCollectionView() {
let colview = APCollectionView()
colview.configure(superView: view)
adapter = APAppListAdapter(collectionView: colview.collectionView)
adapter?.purchseHandle = { [weak self] app in
let sb = NSStoryboard(name: "APInAppPurchseVC", bundle: nil)
let wc = sb.instantiateController(withIdentifier: "APInAppPurchseVC") as! NSWindowController
let vc = wc.contentViewController as! APInAppPurchseVC
vc.currentApp = app
wc.showWindow(self)
}
adapter?.screenshotHandle = { [weak self] app in
let sb = NSStoryboard(name: "ScreenShotUpload", bundle: nil)
let wc = sb.instantiateController(withIdentifier: "ScreenShotUploadVC") as! NSWindowController
let vc = wc.contentViewController as! ScreenShotUploadVC
vc.currentApp = app
wc.showWindow(self)
}
}
}
// MARK: -
extension APAppListVC {
func fetchAppList() {
APClient.appList(status: .filter(nil)).request(showLoading: true, inView: self.view) { [weak self] result, response, error in
guard let err = error else {
let gamelist = AppList(body: result)
self?.adapter?.set(items: gamelist.games)
return
}
APHUD.hide(message: err.localizedDescription)
}
}
}

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="f9w-9S-vuR">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Window Controller-->
<scene sceneID="W73-xg-4SH">
<objects>
<windowController storyboardIdentifier="AppList" id="f9w-9S-vuR" sceneMemberID="viewController">
<window key="window" title="My Apps" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="mcp-fI-0bQ">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="270" y="342" width="600" height="450"/>
<rect key="screenRect" x="0.0" y="0.0" width="1800" height="1125"/>
<value key="minSize" type="size" width="900" height="560"/>
<view key="contentView" id="ukO-uZ-d7I">
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<connections>
<outlet property="delegate" destination="f9w-9S-vuR" id="AQB-f7-cDr"/>
</connections>
</window>
<connections>
<segue destination="xCt-xq-Ud0" kind="relationship" relationship="window.shadowedContentViewController" id="xMD-q7-vQF"/>
</connections>
</windowController>
<customObject id="LJV-vg-xy1" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-188" y="160"/>
</scene>
<!--App ListVC-->
<scene sceneID="RSl-ja-SD9">
<objects>
<viewController id="xCt-xq-Ud0" customClass="APAppListVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="JzO-os-HSp">
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
<autoresizingMask key="autoresizingMask"/>
</view>
</viewController>
<customObject id="nCT-dm-ck1" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="515" y="151"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,158 @@
//
// APInAppPurchseCell.swift
// AppleParty
//
// Created by HTC on 2022/3/28.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
class APInAppPurchseCell: NSTableCellView {
}
class ImageViewCell: NSTableCellView {
@IBOutlet weak var imgSel: NSImageView!
override func awakeFromNib() {
super.awakeFromNib()
}
}
class UploadCell: NSTableCellView {
var row: Int = 0
@IBOutlet weak var imgSel: NSImageView!
@IBOutlet weak var dragView: DragView!
@IBOutlet weak var dragBox: NSView!
typealias CallBackFunc = (_ path: String, _ row: Int) -> Void
var callBackFunc: CallBackFunc?
override func awakeFromNib() {
super.awakeFromNib()
dragView.delegate = self
}
}
extension UploadCell: DragViewDelegate {
func dragView(_ path: String?) {
if let path = path {
debugPrint(path)
imgSel.image = NSImage(contentsOfFile: path)
if let callBackFunc = callBackFunc {
callBackFunc(path, row)
}
} else {
imgSel.image = nil
}
}
}
enum ColumnIdetifier: String {
case id
case productID
case productName
case priceLevel
case appleid
case price
case type
case state
// list
case productPds
case level
case status
case screenshot
case language
case upload
case picname
var columnValue: NSUserInterfaceItemIdentifier {
return NSUserInterfaceItemIdentifier(rawValue: self.rawValue+"Column")
}
var cellValue: NSUserInterfaceItemIdentifier {
return NSUserInterfaceItemIdentifier(rawValue: self.rawValue+"Cell")
}
}
extension NSUserInterfaceItemIdentifier {
func stringValue() -> String {
switch self {
case ColumnIdetifier.id.columnValue:
return ColumnIdetifier.id.rawValue
case ColumnIdetifier.productID.columnValue:
return ColumnIdetifier.productID.rawValue
case ColumnIdetifier.productName.columnValue:
return ColumnIdetifier.productName.rawValue
case ColumnIdetifier.price.columnValue:
return ColumnIdetifier.price.rawValue
case ColumnIdetifier.type.columnValue:
return ColumnIdetifier.type.rawValue
case ColumnIdetifier.state.columnValue:
return ColumnIdetifier.state.rawValue
case ColumnIdetifier.productPds.columnValue:
return ColumnIdetifier.productPds.rawValue
case ColumnIdetifier.level.columnValue:
return ColumnIdetifier.level.rawValue
case ColumnIdetifier.status.columnValue:
return ColumnIdetifier.status.rawValue
case ColumnIdetifier.appleid.columnValue:
return ColumnIdetifier.appleid.rawValue
case ColumnIdetifier.priceLevel.columnValue:
return ColumnIdetifier.priceLevel.rawValue
case ColumnIdetifier.screenshot.columnValue:
return ColumnIdetifier.screenshot.rawValue
case ColumnIdetifier.picname.columnValue:
return ColumnIdetifier.picname.rawValue
case ColumnIdetifier.upload.columnValue:
return ColumnIdetifier.upload.rawValue
case ColumnIdetifier.language.columnValue:
return ColumnIdetifier.language.rawValue
default:
return "none"
}
}
func enumValue() -> NSUserInterfaceItemIdentifier {
switch self {
case ColumnIdetifier.id.columnValue:
return ColumnIdetifier.id.cellValue
case ColumnIdetifier.productID.columnValue:
return ColumnIdetifier.productID.cellValue
case ColumnIdetifier.productName.columnValue:
return ColumnIdetifier.productName.cellValue
case ColumnIdetifier.price.columnValue:
return ColumnIdetifier.price.cellValue
case ColumnIdetifier.type.columnValue:
return ColumnIdetifier.type.cellValue
case ColumnIdetifier.state.columnValue:
return ColumnIdetifier.state.cellValue
case ColumnIdetifier.productPds.columnValue:
return ColumnIdetifier.productPds.cellValue
case ColumnIdetifier.level.columnValue:
return ColumnIdetifier.level.cellValue
case ColumnIdetifier.status.columnValue:
return ColumnIdetifier.status.cellValue
case ColumnIdetifier.appleid.columnValue:
return ColumnIdetifier.appleid.cellValue
case ColumnIdetifier.priceLevel.columnValue:
return ColumnIdetifier.priceLevel.cellValue
case ColumnIdetifier.screenshot.columnValue:
return ColumnIdetifier.screenshot.cellValue
case ColumnIdetifier.picname.columnValue:
return ColumnIdetifier.picname.cellValue
case ColumnIdetifier.upload.columnValue:
return ColumnIdetifier.upload.cellValue
case ColumnIdetifier.language.columnValue:
return ColumnIdetifier.language.cellValue
default:
return NSUserInterfaceItemIdentifier(rawValue: "none")
}
}
}

View File

@ -0,0 +1,475 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23727"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Window Controller-->
<scene sceneID="yaf-bM-ro6">
<objects>
<windowController storyboardIdentifier="APInAppPurchseVC" id="GBA-jI-3If" sceneMemberID="viewController">
<window key="window" title="内购买项目管理" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="nwi-c6-OjX">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="425" y="462" width="600" height="450"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<value key="minSize" type="size" width="600" height="450"/>
<view key="contentView" id="eYM-5O-yjP">
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<connections>
<outlet property="delegate" destination="GBA-jI-3If" id="5Vc-2R-wtG"/>
</connections>
</window>
<connections>
<segue destination="WHe-rZ-F7O" kind="relationship" relationship="window.shadowedContentViewController" id="KEB-Al-cX7"/>
</connections>
</windowController>
<customObject id="fxq-MF-Z4G" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-193" y="236"/>
</scene>
<!--In App PurchseVC-->
<scene sceneID="tfY-7B-aIS">
<objects>
<viewController id="WHe-rZ-F7O" customClass="APInAppPurchseVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="9ub-G9-EoX">
<rect key="frame" x="0.0" y="0.0" width="790" height="225"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView autohidesScrollers="YES" horizontalLineScroll="52" horizontalPageScroll="10" verticalLineScroll="52" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="V54-fz-e6e">
<rect key="frame" x="0.0" y="0.0" width="790" height="164"/>
<clipView key="contentView" ambiguous="YES" id="i9I-WP-piD">
<rect key="frame" x="1" y="1" width="788" height="162"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<outlineView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="50" rowSizeStyle="automatic" headerView="Vci-E4-hZF" viewBased="YES" indentationPerLevel="16" outlineTableColumn="kmX-0Y-Prt" id="Yd4-ve-MqM">
<rect key="frame" x="0.0" y="0.0" width="788" height="134"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<tableViewGridLines key="gridStyleMask" vertical="YES" horizontal="YES"/>
<color key="gridColor" red="0.91372549020000005" green="0.91372549020000005" blue="0.91372549020000005" alpha="1" colorSpace="calibratedRGB"/>
<tableColumns>
<tableColumn identifier="idColumn" width="60" minWidth="60" maxWidth="70" id="kmX-0Y-Prt">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="ID">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="GpP-s7-Ouk">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="idCell" id="lp2-R8-h3a">
<rect key="frame" x="1" y="1" width="65" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="6Wx-lc-fpC">
<rect key="frame" x="6" y="17" width="16" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="aNd-6b-TqP"/>
<constraint firstAttribute="width" constant="16" id="mLS-PQ-s6k"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="unselect" id="woP-Wq-aBW"/>
</imageView>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="aNG-P7-E3Y">
<rect key="frame" x="23" y="17" width="96" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="hjI-ir-7iT">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="aNG-P7-E3Y" firstAttribute="centerY" secondItem="6Wx-lc-fpC" secondAttribute="centerY" id="CMf-lo-HE5"/>
<constraint firstItem="6Wx-lc-fpC" firstAttribute="centerY" secondItem="lp2-R8-h3a" secondAttribute="centerY" id="RE7-tC-iFF"/>
<constraint firstItem="6Wx-lc-fpC" firstAttribute="leading" secondItem="lp2-R8-h3a" secondAttribute="leading" constant="6" id="VKE-G2-7hp"/>
<constraint firstItem="aNG-P7-E3Y" firstAttribute="leading" secondItem="6Wx-lc-fpC" secondAttribute="trailing" constant="3" id="yxl-Wx-leE"/>
</constraints>
<connections>
<outlet property="textField" destination="aNG-P7-E3Y" id="eZN-eA-lug"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="productIDColumn" width="149.5" minWidth="40" maxWidth="1000" id="dWg-Q6-LNF">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Product ID">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="coU-2a-usX">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="productIDCell" id="HMV-5V-AY4">
<rect key="frame" x="69" y="1" width="150" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6sM-uW-9cW">
<rect key="frame" x="0.0" y="17" width="149" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="5d1-mj-cla">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="6sM-uW-9cW" id="jSl-Zp-v33"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="productNameColumn" width="101.5" minWidth="40" maxWidth="1000" id="cu0-Fg-efY">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="品项名称">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="DUN-WZ-MnU">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="productNameCell" id="GlU-7h-ss9">
<rect key="frame" x="221.5" y="1" width="101" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9VL-cf-yHZ">
<rect key="frame" x="-1" y="17" width="101" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Q3a-OZ-g6W">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="9VL-cf-yHZ" id="2Lk-ZX-d07"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="priceLevelColumn" width="54.5" minWidth="40" maxWidth="100" id="QvX-dg-tZC">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="销售范围">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<comboBoxCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" alignment="center" buttonBordered="NO" completes="NO" numberOfVisibleItems="5" id="KhK-VP-9gv">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<objectValues>
<string>6</string>
<string>12</string>
<string>18</string>
</objectValues>
</comboBoxCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="priceLevelCell" id="Ixq-9W-hrr">
<rect key="frame" x="326" y="1" width="55" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nh9-yz-azS">
<rect key="frame" x="-1" y="17" width="55" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="CfZ-Ol-bRD">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="nh9-yz-azS" id="HB2-QL-ySt"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="priceColumn" width="78" minWidth="60" maxWidth="100" id="vDg-bl-irs">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="价格机制">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<comboBoxCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" alignment="center" buttonBordered="NO" completes="NO" numberOfVisibleItems="5" id="Qh1-4g-d40">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<objectValues>
<string>6</string>
<string>12</string>
<string>18</string>
</objectValues>
</comboBoxCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="priceCell" id="fHK-u3-suB">
<rect key="frame" x="383.5" y="1" width="78" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="JSr-Bq-gso">
<rect key="frame" x="-1" y="17" width="78" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="XMd-gw-vxG">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="JSr-Bq-gso" id="SkZ-iA-u7Q"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="appleidColumn" width="120" minWidth="40" maxWidth="130" id="FFJ-KU-Lsi">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Apple ID">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<comboBoxCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" alignment="center" buttonBordered="NO" completes="NO" numberOfVisibleItems="5" id="89a-ev-30f">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<objectValues>
<string>6</string>
<string>12</string>
<string>18</string>
</objectValues>
</comboBoxCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="appleidCell" id="2r2-YN-gxc">
<rect key="frame" x="464.5" y="1" width="120" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Zr2-bI-CNv">
<rect key="frame" x="-1" y="17" width="120" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="rHF-4a-zo7">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="Zr2-bI-CNv" id="R0Z-ey-UBH"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="typeColumn" width="99.5" minWidth="40" maxWidth="100" id="BAk-nA-FHU">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="品项类型">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="ePF-D6-oMG">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="typeCell" id="Lym-V5-iX8">
<rect key="frame" x="587.5" y="1" width="99" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KmJ-Eh-Ikj">
<rect key="frame" x="-1" y="17" width="99" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="TQy-ks-kac">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="KmJ-Eh-Ikj" id="U29-Bd-kYT"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="stateColumn" width="58.5" minWidth="40" maxWidth="110" id="zNJ-vs-M9B">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="状态">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="Ny4-hA-TaW">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="stateCell" id="JqD-IF-M9E">
<rect key="frame" x="690" y="1" width="63" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bt8-9h-N0G">
<rect key="frame" x="0.0" y="17" width="62" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="hXq-cj-HG6">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="bt8-9h-N0G" id="ZlI-AB-lkJ"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
</tableColumns>
</outlineView>
</subviews>
</clipView>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="150" id="ysI-Go-iNV"/>
</constraints>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="oPx-Do-j7O">
<rect key="frame" x="1" y="35" width="1113" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="yaM-yh-Nqt">
<rect key="frame" x="804" y="29" width="15" height="42"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" wantsLayer="YES" id="Vci-E4-hZF">
<rect key="frame" x="0.0" y="0.0" width="788" height="28"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="RWS-I4-Hj5">
<rect key="frame" x="18" y="189" width="72" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="b1e-La-oHG"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="AppName" id="ocn-p7-7tY">
<font key="font" metaFont="systemBold" size="14"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="g1s-qC-zZv">
<rect key="frame" x="248" y="172" width="94" height="48"/>
<buttonCell key="cell" type="push" title="刷新" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="PBX-Aw-4gO">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<constraints>
<constraint firstAttribute="width" constant="80" id="KLz-QE-MgQ"/>
<constraint firstAttribute="height" constant="36" id="VRS-Wp-jqM"/>
</constraints>
<connections>
<action selector="reloadIAPs:" target="WHe-rZ-F7O" id="NKR-Dj-n4M"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="g4b-K9-FLu">
<rect key="frame" x="338" y="172" width="114" height="48"/>
<buttonCell key="cell" type="push" title="导入表格" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="vsX-vy-Zvo">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<constraints>
<constraint firstAttribute="width" constant="100" id="duV-PA-5xP"/>
<constraint firstAttribute="height" constant="36" id="wU1-by-aaT"/>
</constraints>
<connections>
<action selector="importExcel:" target="WHe-rZ-F7O" id="NyY-A2-rYY"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="DoY-Xj-sox">
<rect key="frame" x="448" y="172" width="114" height="48"/>
<buttonCell key="cell" type="push" title="导出表格" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Qpw-5P-Y95">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<constraints>
<constraint firstAttribute="height" constant="36" id="S2N-Zb-bUT"/>
<constraint firstAttribute="width" constant="100" id="SQi-d8-LOD"/>
</constraints>
<connections>
<action selector="outputExcel:" target="WHe-rZ-F7O" id="07F-Jd-GLD"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pDs-nU-YDZ">
<rect key="frame" x="668" y="172" width="114" height="48"/>
<buttonCell key="cell" type="push" title="下载表格示例" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="cwE-He-2kf">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<constraints>
<constraint firstAttribute="height" constant="36" id="6T9-y8-fcw"/>
<constraint firstAttribute="width" constant="100" id="cHT-mK-iEg"/>
</constraints>
<connections>
<action selector="downloadExcel:" target="WHe-rZ-F7O" id="Hdd-v5-BPn"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fCY-Bo-Sre">
<rect key="frame" x="558" y="172" width="114" height="48"/>
<buttonCell key="cell" type="push" title="导出品项ID" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="cwm-HN-x0f">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<constraints>
<constraint firstAttribute="width" constant="100" id="LMH-kg-AeM"/>
<constraint firstAttribute="height" constant="36" id="RFy-br-N3a"/>
</constraints>
<connections>
<action selector="outputProductID:" target="WHe-rZ-F7O" id="JyB-hQ-Tn0"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="V54-fz-e6e" firstAttribute="top" secondItem="g4b-K9-FLu" secondAttribute="bottom" constant="15" id="3V5-A4-VMG"/>
<constraint firstItem="fCY-Bo-Sre" firstAttribute="centerY" secondItem="g1s-qC-zZv" secondAttribute="centerY" id="3wr-s7-mlV"/>
<constraint firstItem="fCY-Bo-Sre" firstAttribute="leading" secondItem="DoY-Xj-sox" secondAttribute="trailing" constant="10" id="657-v3-Nxa"/>
<constraint firstItem="RWS-I4-Hj5" firstAttribute="leading" secondItem="9ub-G9-EoX" secondAttribute="leading" constant="20" symbolic="YES" id="6NV-i6-UqO"/>
<constraint firstItem="RWS-I4-Hj5" firstAttribute="centerY" secondItem="g1s-qC-zZv" secondAttribute="centerY" id="8pU-c6-L46"/>
<constraint firstItem="DoY-Xj-sox" firstAttribute="centerY" secondItem="g1s-qC-zZv" secondAttribute="centerY" id="9f0-vD-ZSY"/>
<constraint firstAttribute="bottom" secondItem="V54-fz-e6e" secondAttribute="bottom" id="BQe-7k-3BG"/>
<constraint firstItem="g4b-K9-FLu" firstAttribute="top" secondItem="9ub-G9-EoX" secondAttribute="top" constant="10" id="Loj-uR-Xw3"/>
<constraint firstItem="g4b-K9-FLu" firstAttribute="leading" secondItem="g1s-qC-zZv" secondAttribute="trailing" constant="10" id="QdO-2i-xQc"/>
<constraint firstItem="V54-fz-e6e" firstAttribute="leading" secondItem="9ub-G9-EoX" secondAttribute="leading" id="RJg-65-WD6"/>
<constraint firstItem="g1s-qC-zZv" firstAttribute="top" secondItem="9ub-G9-EoX" secondAttribute="top" constant="10" id="TmA-QA-knT"/>
<constraint firstItem="pDs-nU-YDZ" firstAttribute="leading" secondItem="fCY-Bo-Sre" secondAttribute="trailing" constant="10" id="al9-zg-mfC"/>
<constraint firstAttribute="trailing" secondItem="pDs-nU-YDZ" secondAttribute="trailing" constant="15" id="ems-r3-Hzs"/>
<constraint firstItem="pDs-nU-YDZ" firstAttribute="centerY" secondItem="g1s-qC-zZv" secondAttribute="centerY" id="f8v-tI-hgZ"/>
<constraint firstAttribute="trailing" secondItem="V54-fz-e6e" secondAttribute="trailing" id="gDP-cl-PDs"/>
<constraint firstItem="DoY-Xj-sox" firstAttribute="leading" secondItem="g4b-K9-FLu" secondAttribute="trailing" constant="10" id="oVv-xx-EqB"/>
</constraints>
</view>
<connections>
<outlet property="appNameView" destination="RWS-I4-Hj5" id="B2s-1y-ClK"/>
<outlet property="outlineView" destination="Yd4-ve-MqM" id="gzd-Pc-XWm"/>
<outlet property="outputIAPListBtn" destination="DoY-Xj-sox" id="Age-qN-Qlj"/>
</connections>
</viewController>
<customObject id="LWD-js-20w" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="651" y="228.5"/>
</scene>
</scenes>
<resources>
<image name="unselect" width="16" height="16"/>
</resources>
</document>

View File

@ -0,0 +1,302 @@
//
// APInAppPurchseVC.swift
// AppleParty
//
// Created by HTC on 2022/3/28.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
class APInAppPurchseVC: NSViewController {
public var currentApp: App? {
didSet {
fetchIAPs()
appNameView.stringValue = currentApp?.appName ?? ""
}
}
var iapList: [IAPList.IAP] = []
private var countryCode = ""
private var checkPrice = [String: String]()
@IBOutlet weak var outputIAPListBtn: NSButton!
@IBOutlet weak var appNameView: NSTextField!
@IBOutlet weak var outlineView: NSOutlineView!
override func viewDidLoad() {
super.viewDidLoad()
self.outlineView.delegate = self
self.outlineView.dataSource = self
self.outlineView.columnAutoresizingStyle = .uniformColumnAutoresizingStyle
self.outlineView.selectionHighlightStyle = .none
self.outlineView.allowsMultipleSelection = true
self.outlineView.sizeToFit()
}
@IBAction func reloadIAPs(_ sender: Any) {
fetchIAPs()
}
@IBAction func importExcel(_ sender: Any) {
let openPanel = NSOpenPanel()
openPanel.canChooseFiles = true
openPanel.canChooseDirectories = true
openPanel.allowsMultipleSelection = false
openPanel.beginSheetModal(for: self.view.window!) { [self] (modalResponse: NSApplication.ModalResponse) in
if modalResponse == .OK, let filePath = openPanel.url {
handelExcel(filePath)
}
}
}
@IBAction func outputExcel(_ sender: Any) {
guard iapList.count > 0 else {
NSAlert.show("当前商品为空~")
return
}
//
var iaps = "productId, 商品名称, 价格等级, 价格(\(countryCode)), AppleID, 商品类型, 状态, 送审图片\n"
let separator = "\",\""
iaps += iapList.map { item -> String in
return "\"" + item.vendorId + separator + item.referenceName + separator + item.priceTier + separator + (checkPrice[item.priceTier] ?? "-") + separator + item.adamId
+ separator + item.addOnType.CNValue() + separator + item.iTunesConnectStatus.statusValue.0 + separator + item.reviewScreenshot + "\""
}.joined(separator: "\n")
//
let dateFormatter : DateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMdd_HHmm_"
let currentDate = dateFormatter.string(from: Date())
let mySave = NSSavePanel()
mySave.allowedFileTypes = ["csv"]
mySave.nameFieldStringValue = "内购列表" + currentDate + currentApp!.appName.replacingOccurrences(of: " ", with: "-")
mySave.begin { (result) -> Void in
if result == .OK {
let filePath = mySave.url
do {
// excelCSVASNIBOMunicode
var data = Data([0xEF, 0xBB, 0xBF])
data.append(contentsOf: iaps.data(using: .utf8) ?? Data())
try data.write(to: filePath!)
} catch {
NSAlert.show("导出失败:\(error.localizedDescription)")
}
}
}
}
@IBAction func outputProductID(_ sender: Any) {
let mainStoryboard = NSStoryboard(name: "InAppPurchseView", bundle: Bundle(for: self.classForCoder))
let outputVC = mainStoryboard.instantiateController(withIdentifier: "OutputExcelVCID") as? OutputExcelVC
outputVC?.iapList = iapList
presentAsSheet(outputVC!)
}
@IBAction func downloadExcel(_ sender: Any) {
XMLManager.copySimpleExel()
}
}
// MARK: -
extension APInAppPurchseVC {
//
func fetchIAPs() {
APClient.iaps(appid: currentApp!.appId).request(showLoading: true, inView: self.view) { [weak self] result, response, error in
guard let err = error else {
guard let app = self?.currentApp else { return } //
let iapL = IAPList(body:result, app: app)
self?.iapList = iapL.iapList
self?.outlineView.reloadData()
self?.updateRowInfo()
return
}
APHUD.hide(message: err.localizedDescription, view: self?.view ?? currentView())
}
}
func updateRowInfo() {
// TODO:
return;
guard self.iapList.count > 0 else {
return
}
outputIAPListBtn.isEnabled = false
let group = DispatchGroup()
for i in 0..<self.iapList.count {
group.enter()
let iapid = iapList[i].adamId
//
APClient.inAppPurchaseDetail(iapid: iapid).request { [weak self] result, response, error in
group.leave()
guard let err = error else {
if var newModel = self?.iapList[i] {
newModel.updateDetail(body: result)
self?.iapList[i] = newModel
self?.outlineView.reloadData()
group.enter()
//
APClient.inAppPurchasePrices(iapid: iapid).request { [weak self] result, response, error in
group.leave()
guard let err = error else {
if var newModel = self?.iapList[i] {
newModel.updatePrices(body: result)
self?.iapList[i] = newModel
self?.outlineView.reloadData()
}
return
}
APHUD.hide(message: err.localizedDescription, view: self?.view ?? currentView())
}
}
return
}
APHUD.hide(message: err.localizedDescription, view: self?.view ?? currentView())
}
}
group.notify(queue: .main) {
self.outputIAPListBtn.isEnabled = true
self.outlineView.reloadData()
}
}
}
// MARK: -
extension APInAppPurchseVC {
func handelExcel(_ excelFilePath: URL) {
let iaps = IAPExcelParser.parser(excelFilePath)
//
if iapList.isNotEmpty {
let pids = iaps.map { $0.productId }
let iaps = iapList.map { $0.vendorId }
let warns = pids.filter { iaps.contains($0) }
if warns.isNotEmpty {
NSAlert.show("警告已经存在相同商品id的品项请检查\(warns)\n⚠️提示:如果继续上传,将会覆盖已有品项的信息!")
}
}
//
let iapNames = iaps.map { $0.name }
let iapUniNames = Array(Set(iapNames))
if iapNames.isNotEmpty, iapNames.count != iapUniNames.count {
NSAlert.show("‼️警告:存在相同参考名字的商品!请检查~\n⚠️提示:如果继续上传,同名的商品只会有一个能创建成功!")
}
let sb = NSStoryboard(name: "InAppPurchseView", bundle: Bundle(for: self.classForCoder))
let wc = sb.instantiateController(withIdentifier: "InputExcelList") as? NSWindowController
let vc = wc?.contentViewController as? APUploadIAPListVC
vc?.currentApp = currentApp
vc?.iaps = iaps
wc?.showWindow(self)
}
func setModels(models: [IAPList.IAP]) {
iapList = models
outlineView.reloadData()
outlineView.sizeToFit()
updateRowInfo()
}
}
// MARK: - NSOutlineViewDelegate
extension APInAppPurchseVC: NSOutlineViewDelegate, NSOutlineViewDataSource {
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
if let item = item as? IAPList.IAP {
switch tableColumn?.identifier.enumValue() {
case ColumnIdetifier.id.cellValue:
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.id.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = String(item.curid)
return cell
case ColumnIdetifier.productID.cellValue:
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.productID.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = item.vendorId
return cell
case ColumnIdetifier.productName.cellValue:
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.productName.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = item.referenceName
return cell
case ColumnIdetifier.price.cellValue:
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.price.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = checkPrice[item.priceTier] ?? "-"
return cell
case ColumnIdetifier.priceLevel.cellValue:
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.priceLevel.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = item.priceTier
return cell
case ColumnIdetifier.appleid.cellValue:
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.appleid.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = item.adamId
return cell
case ColumnIdetifier.type.cellValue:
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.type.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = item.addOnType.CNValue()
return cell
case ColumnIdetifier.state.cellValue:
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.state.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = item.iTunesConnectStatus.statusValue.0
cell?.textField?.textColor = item.iTunesConnectStatus.statusValue.1
return cell
default:
return nil
}
}else if let item = item as? String {
switch tableColumn?.identifier.enumValue() {
case ColumnIdetifier.productID.cellValue:
let imgView = NSImageView()
imgView.showWebImage(item)
return imgView
default:
return nil
}
}else {
return nil
}
}
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
if item is IAPList.IAP {
return 1
}else {
return iapList.count
}
}
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
if item is IAPList.IAP {
return true
}else {
return false
}
}
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
if let item = item as? IAPList.IAP {
return item.reviewScreenshot as Any
}
return iapList[index]
}
func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat {
if item is IAPList.IAP {
return 35
}else {
return 100
}
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner"/>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
</objects>
</document>

View File

@ -0,0 +1,806 @@
//
// InputTableListVC.swift
// AppleParty
//
// Created by on 2020/12/15.
//
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()
}
}
public var iaps = [IAPProduct]() {
didSet {
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) {
let mainStoryboard = NSStoryboard(name: "InAppPurchseView", bundle: Bundle(for: self.classForCoder))
let upVC = mainStoryboard.instantiateController(withIdentifier: "IAPUploadVCID") as? IAPUploadImageVC
let screenshot = self.iaps.filter({ $0.reviewScreenshot.count > 0 }).map({ $0.reviewScreenshot })
//
let uniquedshot = screenshot.enumerated().filter { (index, value) -> Bool in
return screenshot.firstIndex(of: value) == index
}.map { $0.element }
upVC?.picnames = uniquedshot
upVC?.callBackFunc = { paths in
self.screenshotPaths = paths
self.tableView.reloadData()
}
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
if let ascKey = password {
uploadIAPs(ascKey)
}
}
presentAsSheet(vc)
return
}
uploadIAPs(ascKey)
}
}
// MARK: -
extension APUploadIAPListVC {
func updateInAppPurchse(iaps: [IAPProduct], appId: String, ascKey: AppStoreConnectKey) {
let showApiRateLimit = showApiRateLimitLogsBtn.state.rawValue == 1
let ascAPI = APASCAPI.init(issuerID: ascKey.issuerID,
privateKeyID: ascKey.privateKeyID,
privateKey: ascKey.privateKey,
showApiRateLimit: showApiRateLimit)
ascAPI.addMessage("密钥信息:\(ascKey.issuerID), \(ascKey.privateKeyID), \(ascKey.privateKey)")
Task {
// 1 app app
guard let apps = await ascAPI.apps() else {
self.enterBtn.isEnabled = true
APHUD.hide()
APHUD.hide(message: "当前请求异常,请检查密钥是否正确~", delayTime: 2)
return
}
let app = apps.filter { $0.id == appId }
guard app.count > 0 else {
self.enterBtn.isEnabled = true
APHUD.hide()
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
newWC?.window?.title = "内购批量上传日志"
let logVC = newWC?.contentViewController as? APDebugVC
logVC?.debugLog = "开始上传"
newWC?.showWindow(self)
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 {
//
if product.inAppPurchaseType == .AUTO_RENEWABLE {
await createRenewSubscription(appId: appId, product: product, ascAPI: ascAPI)
} else {
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) ")
//
let iaps = oldIAPs.filter({ $0.attributes?.productID == product.productId })
if let iap = iaps.first {
ascAPI.addMessage("内购已经存在:\(product.productId) ,开始更新信息中...")
// 0. 使
var product = product
if let note = iap.attributes?.reviewNote, product.reviewNote.isEmpty {
product.reviewNote = note
}
// 1.
guard let iap = await ascAPI.updateInAppPurchases(iapId: iap.id, product: product) else {
//
ascAPI.addMessage("内购已经存在:\(product.productId) ,更新信息失败!❌ ")
return
}
// 2.
await updateIAPPricePoint(iapId: iap.id, product: product, ascAPI: ascAPI)
// 3.
ascAPI.addMessage("开始更新内购本地化版本:\(product.productId)")
let localizations = await ascAPI.fetchInAppPurchasesLocalizations(iapId: iap.id)
for localization in product.localizations {
//
if let locale = localizations.filter({ $0.attributes?.locale == localization.locale }).first {
//
ascAPI.addMessage("内购已存在本地化版本:\(localization.locale),开始更新信息中...")
if (await ascAPI.updateInAppPurchasesLocalization(iapLocaleId: locale.id, localization: localization)) != nil {
//
ascAPI.addMessage("内购本地化版本:\(localization.locale) ,更新语言成功!✅ ")
} else {
//
ascAPI.addMessage("内购本地化版本:\(localization.locale) ,更新语言失败!❌ ")
}
} else {
//
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)
} else {
// 1.
guard let iap = await ascAPI.createInAppPurchases(appId: appId, product: product) else {
//
ascAPI.addMessage("内购商品:\(product.productId) ,创建失败!❌ ")
return
}
// 2.
await updateIAPPricePoint(iapId: iap.id, product: product, ascAPI: ascAPI)
// 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)
}
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
let customerPrice = pricePoint.customerPrice.normalizePrice()
let points = await ascAPI.fetchPricePoints(iapId: iapId, territory: [territory])
if let point = points.filter({ $0.attributes?.customerPrice!.normalizePrice() == customerPrice }).first {
manualPrices.append(["id": "${\(territory)-\(included.count)}", "type": "inAppPurchasePrices"])
included.append(ascAPI.fetchInAppPurchasePriceSchedule(scheduleId: territory, pricePointId: point.id, iapId: iapId, index: included.count))
} else {
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) ,更新价格成功!✅ ")
} else {
//
ascAPI.addMessage("内购价格点:\(baseTerritory)\(baseCustomerPrice) ,更新价格失败!❌ ")
}
} else {
//
ascAPI.addMessage("基准国家的内购价格点:\(baseTerritory)\(baseCustomerPrice) ,未找到此档位!❌ ")
}
}
///
func createIAPLocalization(iapId: String, localization: IAPLocalization, product: IAPProduct, ascAPI: APASCAPI) async {
ascAPI.addMessage("开始更新本地化版本:\(product.productId)\(localization.locale)")
if (await ascAPI.createInAppPurchasesLocalization(iapId: iapId, localization: localization)) != nil {
//
ascAPI.addMessage("内购本地化版本:\(localization.locale) ,更新语言成功!✅ ")
} else {
//
ascAPI.addMessage("内购本地化版本:\(localization.locale) ,更新语言失败!❌ ")
}
}
///
func createIAPScreenshot(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
ascAPI.addMessage("开始更新内购商品的送审截图:\(product.productId)\(product.reviewScreenshot)")
let imgName = product.reviewScreenshot
guard let imgPath = screenshotPaths[imgName] else {
ascAPI.addMessage("内购商品:\(product.productId) 无送审截图或未上传截图~")
return
}
let imaUrl = URL.init(fileURLWithPath: imgPath)
guard let fileMD5 = URL.init(fileURLWithPath: imgPath).fileMD5() else {
ascAPI.addMessage("内购商品截图文件错误:\(imgPath) ,无法生成 md5 值~")
return
}
let oldShot = await ascAPI.fetchInAppPurchasesScreenshot(iapId: iapId)
//
if let ost = oldShot {
ascAPI.addMessage("删除旧的送审截图:\(ost.attributes?.fileName ?? "")")
let status = await ascAPI.deleteInAppPurchasesScreenshot(iapShotId: ost.id)
if status != 204 {
ascAPI.addMessage("内购商品截图创建失败:\(imgName) ,无法删除旧截图~")
}
}
ascAPI.addMessage("创建新的送审截图:\(product.reviewScreenshot)")
//
let imaSize = imaUrl.fileSizeInt()
guard let shot = await ascAPI.createInAppPurchasesScreenshot(iapId: iapId, fileName: imgName, fileSize: imaSize) else {
//
ascAPI.addMessage("内购商品:\(product.productId) ,创建送审截图失败!❌ ")
return
}
//
guard let method = shot.attributes?.uploadOperations?.first?.method,
let url = shot.attributes?.uploadOperations?.first?.url,
let requestHeaders = shot.attributes?.uploadOperations?.first?.requestHeaders,
let baseURL = URL(string: url) else {
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)")
//
guard let response = try? await URLSession.shared.upload(for: request, fromFile: imaUrl) else {
ascAPI.addMessage("内购商品:\(product.productId) ,创建送审截图失败!上传图片异常~ ❌ ")
return
}
guard let responseCode = (response.1 as? HTTPURLResponse)?.statusCode, responseCode == 200 else {
ascAPI.addMessage("内购商品:\(product.productId) ,创建送审截图失败!上传图片异常 \(response.1.description)~ ❌ ")
return
}
ascAPI.addMessage("提交新的送审截图:\(product.reviewScreenshot)")
//
if ((await ascAPI.updateInAppPurchasesScreenshot(iapShotId: shot.id, fileMD5: fileMD5)) != nil) {
ascAPI.addMessage("内购商品:\(product.productId) ,送审截图上传成功!✅ ")
} else {
ascAPI.addMessage("内购商品:\(product.productId) ,送审截图可能上传失败! ")
}
}
///
func updateIAPAvailableTerritories(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
let inAll = product.territories.availableInAllTerritories
let inNew = product.territories.availableInNewTerritories
let summary = territoryInfo(product: product)
let newTerritory = inNew ? "将来新国家(地区)时自动提供!" : "将来新国家(地区)时不自动提供!"
ascAPI.addMessage("开始更新内购商品的销售国家/地区:\(summary)")
guard !inAll else {
var allTerritories: [[String: String]] = []
if let territories = await ascAPI.territories() {
territories.forEach { territory in
allTerritories.append([
"type": "territories",
"id": territory.id
])
}
//
if (await ascAPI.updateInAppPurchasesAvailabilityTerritories(iapId: iapId, availableTerritories: allTerritories, availableInNewTerritories: inNew)) != nil {
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),更新成功!✅ ")
} else {
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),更新失败!❌ ")
}
} else {
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),无法设置!获取国家标识码失败!❌ ")
}
return
}
///
var territories: [[String: String]] = []
product.territories.territories?.forEach({ territory in
territories.append([
"type": "territories",
"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) ,更新成功!✅ ")
} else {
ascAPI.addMessage("内购商品的销售国家/地区:\(customerTerritory) ,更新失败!❌ ")
}
}
// MARK: -
///
func createRenewSubscription(appId: String, product: IAPProduct, ascAPI: APASCAPI) async {
let groupName = product.groupName
var currentSubGroup: ASCSubscriptionGroup?
// 1
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 {
for localization in product.localizations {
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) ,开始更新信息中...")
// 0. 使
var product = product
if let note = sub.attributes?.reviewNote, product.reviewNote.isEmpty {
product.reviewNote = note
}
// 1.
guard let iap = await ascAPI.updateSubscription(iapId: sub.id, product: product) else {
//
ascAPI.addMessage("订阅商品已经存在:\(product.productId) ,更新信息失败!❌ ")
return
}
// 2.
ascAPI.addMessage("开始更新订阅商品本地化版本:\(product.productId)")
let localizations = await ascAPI.fetchSubscriptionLocalizations(iapId: iap.id)
for localization in product.localizations {
//
if let locale = localizations.filter({ $0.attributes?.locale == localization.locale }).first {
//
ascAPI.addMessage("订阅商品已存在本地化版本:\(localization.locale),开始更新信息中...")
if (await ascAPI.updateSubscriptionLocalization(iapLocaleId: locale.id, localization: localization)) != nil {
//
ascAPI.addMessage("订阅商品本地化版本:\(localization.locale) ,更新语言成功!✅ ")
} else {
//
ascAPI.addMessage("订阅商品本地化版本:\(localization.locale) ,更新语言失败!❌ ")
}
} else {
//
await createSubscriptionLocalization(iapId: iap.id, localization: localization, product: product, ascAPI: ascAPI)
}
}
// 3.
await updateSubscriptionPricePoint(iapId: iap.id, product: product, ascAPI: ascAPI)
// 4.
await createSubscriptionScreenshot(iapId: iap.id, product: product, ascAPI: ascAPI)
// 5.
await updateSubscriptionAvailableTerritories(iapId: iap.id, product: product, ascAPI: ascAPI)
} else {
// 1.
guard let iapGroupId = currentSubGroup?.id, let iap = await ascAPI.createSubscription(iapGroupId: iapGroupId, product: product) else {
//
ascAPI.addMessage("订阅商品:\(product.productId) ,创建失败!❌ ")
return
}
// 2.
for localization in product.localizations {
await createSubscriptionLocalization(iapId: iap.id, localization: localization, product: product, ascAPI: ascAPI)
}
// 3.
await updateSubscriptionPricePoint(iapId: iap.id, product: product, ascAPI: ascAPI)
// 4.
await createSubscriptionScreenshot(iapId: iap.id, product: product, ascAPI: ascAPI)
// 5.
await updateSubscriptionAvailableTerritories(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
customerPriceSchedules.append(IAPPricePoint(territory: baseTerritory, customerPrice: baseCustomerPrice))
let manualPricesTerritory: [String] = customerPriceSchedules.map({ $0.territory })
//
for pricePoint in customerPriceSchedules {
let territory = pricePoint.territory
let customerPrice = pricePoint.customerPrice.normalizePrice()
let points = await ascAPI.fetchSubscriptionPricePoints(iapId: iapId, territory: [territory])
if let point = points.filter({ $0.attributes?.customerPrice!.normalizePrice() == customerPrice }).first {
if (await ascAPI.updateSubscriptionPricePoint(iapId: iapId, pricePointId: point.id, preserveCurrentPrice: isPreservePrice)) != nil {
ascAPI.addMessage("自定价格的订阅商品的价格点:\(territory)\(customerPrice) ,更新价格成功!✅ ")
} else {
ascAPI.addMessage("自定价格的订阅商品的价格点:\(territory)\(customerPrice) ,更新价格失败!❌ ")
}
} else {
ascAPI.addMessage("自定价格的订阅商品价格点:\(territory)\(customerPrice) ,未找到此档位!❌ ")
}
}
ascAPI.addMessage("开始更新全球均衡价格:")
// API
let allPoints = await ascAPI.fetchSubscriptionPricePointsEqualizations(pointId: point.id, territory: nil)
for apoint in allPoints {
let territory = apoint.relationships?.territory?.data?.id ?? ""
//
if manualPricesTerritory.contains(territory) {
continue
}
let customerPrice = apoint.attributes?.customerPrice ?? ""
if (await ascAPI.updateSubscriptionPricePoint(iapId: iapId, pricePointId: apoint.id, preserveCurrentPrice: isPreservePrice)) != nil {
//
ascAPI.addMessage("全球均衡价格的订阅商品的价格点:\(territory)\(customerPrice) ,更新价格成功!✅ ")
} else {
//
ascAPI.addMessage("全球均衡价格的订阅商品的价格点:\(territory)\(customerPrice) ,更新价格失败!❌ ")
}
}
} else {
//
ascAPI.addMessage("基准国家的订阅商品价格点:\(baseTerritory)\(baseCustomerPrice) ,未找到此档位!❌ ")
}
}
///
func createSubscriptionLocalization(iapId: String, localization: IAPLocalization, product: IAPProduct, ascAPI: APASCAPI) async {
ascAPI.addMessage("开始更新订阅商品本地化版本:\(product.productId)\(localization.locale)")
if (await ascAPI.createSubscriptionLocalization(iapId: iapId, localization: localization)) != nil {
//
ascAPI.addMessage("订阅商品本地化版本:\(localization.locale) ,更新语言成功!✅ ")
} else {
//
ascAPI.addMessage("订阅商品本地化版本:\(localization.locale) ,更新语言失败!❌ ")
}
}
///
func createSubscriptionScreenshot(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
ascAPI.addMessage("开始更新订阅商品的送审截图:\(product.productId)\(product.reviewScreenshot)")
let imgName = product.reviewScreenshot
guard let imgPath = screenshotPaths[imgName] else {
ascAPI.addMessage("订阅商品:\(product.productId) 无送审截图或未上传截图~")
return
}
let imaUrl = URL.init(fileURLWithPath: imgPath)
guard let fileMD5 = URL.init(fileURLWithPath: imgPath).fileMD5() else {
ascAPI.addMessage("订阅商品截图文件错误:\(imgPath) ,无法生成 md5 值~")
return
}
let oldShot = await ascAPI.fetchSubscriptionScreenshot(iapId: iapId)
//
if let ost = oldShot {
ascAPI.addMessage("删除旧的送审截图:\(ost.attributes?.fileName ?? "")")
let status = await ascAPI.deleteSubscriptionScreenshot(iapShotId: ost.id)
if status != 204 {
ascAPI.addMessage("订阅商品截图创建失败:\(imgName) ,无法删除旧截图~")
}
}
ascAPI.addMessage("创建新的送审截图:\(product.reviewScreenshot)")
//
let imaSize = imaUrl.fileSizeInt()
guard let shot = await ascAPI.createSubscriptionScreenshot(iapId: iapId, fileName: imgName, fileSize: imaSize) else {
//
ascAPI.addMessage("订阅商品:\(product.productId) ,创建送审截图失败!❌ ")
return
}
//
guard let method = shot.attributes?.uploadOperations?.first?.method,
let url = shot.attributes?.uploadOperations?.first?.url,
let requestHeaders = shot.attributes?.uploadOperations?.first?.requestHeaders,
let baseURL = URL(string: url) else {
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)")
//
guard let response = try? await URLSession.shared.upload(for: request, fromFile: imaUrl) else {
ascAPI.addMessage("订阅商品:\(product.productId) ,创建送审截图失败!上传图片异常~ ❌ ")
return
}
guard let responseCode = (response.1 as? HTTPURLResponse)?.statusCode, responseCode == 200 else {
ascAPI.addMessage("订阅商品:\(product.productId) ,创建送审截图失败!上传图片异常 \(response.1.description)~ ❌ ")
return
}
ascAPI.addMessage("提交新的送审截图:\(product.reviewScreenshot)")
//
if ((await ascAPI.updateSubscriptionScreenshot(iapShotId: shot.id, fileMD5: fileMD5)) != nil) {
ascAPI.addMessage("订阅商品:\(product.productId) ,送审截图上传成功!✅ ")
} else {
ascAPI.addMessage("订阅商品:\(product.productId) ,送审截图可能上传失败! ")
}
}
///
func updateSubscriptionAvailableTerritories(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
let inAll = product.territories.availableInAllTerritories
let inNew = product.territories.availableInNewTerritories
let summary = territoryInfo(product: product)
let newTerritory = inNew ? "将来新国家(地区)时自动提供!" : "将来新国家(地区)时不自动提供!"
ascAPI.addMessage("开始更新订阅商品的销售国家/地区:\(summary)")
guard !inAll else {
var allTerritories: [[String: String]] = []
if let territories = await ascAPI.territories() {
territories.forEach { territory in
allTerritories.append([
"type": "territories",
"id": territory.id
])
}
//
if (await ascAPI.updateSubscriptionAvailabilityTerritories(iapId: iapId, availableTerritories: allTerritories, availableInNewTerritories: inNew)) != nil {
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),更新成功!✅ ")
} else {
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),更新失败!❌ ")
}
} else {
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),无法设置!获取国家标识码失败!❌ ")
}
return
}
///
var territories: [[String: String]] = []
product.territories.territories?.forEach({ territory in
territories.append([
"type": "territories",
"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) ,更新成功!✅ ")
} else {
ascAPI.addMessage("订阅商品的销售国家/地区:\(customerTerritory) ,更新失败!❌ ")
}
}
}
// MARK: - Privacy Method
extension APUploadIAPListVC {
func territoryInfo(product: IAPProduct) -> String {
let inAll = product.territories.availableInAllTerritories
let inNew = product.territories.availableInNewTerritories
let customerTerritory = product.territories.territories?.map({ $0.id }).joined(separator: ",") ?? ""
let off = !inAll && !inNew && (product.territories.territories?.isEmpty ?? true)
let territory = off ? "下架" : (customerTerritory.isEmpty ? (inAll ? "全部" : "当前下架") : customerTerritory)
let stringValue = "在所有国家/地区销售:'\(inAll ? "" : "")'\n将来新国家/地区自动提供:'\(inNew ? "" : "")'\n指定国家/地区销售:\(territory)"
return stringValue
}
}
// MARK: - NSTableViewDelegate
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() {
case ColumnIdetifier.id.cellValue:
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.id.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = String(row+1)
return cell
case ColumnIdetifier.productID.cellValue:
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.productID.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = iap.productId
return cell
case ColumnIdetifier.productName.cellValue:
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.productName.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = iap.name
return cell
case ColumnIdetifier.price.cellValue:
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.price.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = territoryInfo(product: iap)
return cell
case ColumnIdetifier.level.cellValue:
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.level.cellValue, owner: self) as? NSTableCellView
let territory = iap.priceSchedules?.baseTerritory ?? "-"
let price = iap.priceSchedules?.baseCustomerPrice ?? "-"
let customerPrice = iap.priceSchedules?.manualPrices.map({ pp in
"{'国家:'\(pp.territory)', '自定价格':'\(pp.customerPrice)'}\n"
}).joined() ?? "-"
cell?.textField?.stringValue = "基准国家:'\(territory)'\n基准价格:'\(price)'\n\(customerPrice)"
return cell
case ColumnIdetifier.productPds.cellValue:
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.productPds.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = iap.reviewNote
return cell
case ColumnIdetifier.state.cellValue:
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.state.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = iap.inAppPurchaseType.CNValue()
return cell
case ColumnIdetifier.screenshot.cellValue:
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.screenshot.cellValue, owner: self) as? ImageViewCell
let file_name = iap.reviewScreenshot
let imgPath = screenshotPaths[file_name] ?? ""
cell?.imgSel.image = NSImage(contentsOfFile: imgPath)
return cell
case ColumnIdetifier.language.cellValue:
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.language.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = iap.localizations.map({ lz in
"{'locale:'\(lz.locale)', 'title':'\(lz.name)', 'desc':'\(lz.description)'}\n"
}).joined()
return cell
default:
return nil
}
}
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
let prices = iaps[row].priceSchedules?.manualPrices.count ?? 0
let count = max(prices + 2, 3)
return count > 10 ? CGFloat(20 * count) : CGFloat(25 * count)
}
}

View File

@ -0,0 +1,64 @@
//
// DragView.swift
// AppleParty
//
// Created by on 2020/12/16.
//
import Cocoa
protocol DragViewDelegate {
func dragView(_ path: String?)
}
class DragView: NSView {
var delegate: DragViewDelegate?
private var fileTypeIsOk = false
let NSFilenamesPboardType = NSPasteboard.PasteboardType("NSFilenamesPboardType")
let fileTypes = ["jpg", "jpeg", "png"]
var droppedFilePath: String?
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
// Declare and register an array of accepted types
registerForDraggedTypes([NSPasteboard.PasteboardType(kUTTypeFileURL as String),
NSPasteboard.PasteboardType(kUTTypeItem as String)])
}
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
fileTypeIsOk = checkExtension(drag: sender)
return []
}
override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation {
return fileTypeIsOk ? .link : []
}
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
if let board = sender.draggingPasteboard.propertyList(forType: NSFilenamesPboardType) as? NSArray, let imagePath = board[0] as? String {
// THIS IS WERE YOU GET THE PATH FOR THE DROPPED FILE
droppedFilePath = imagePath
if fileTypeIsOk {
delegate?.dragView(droppedFilePath)
}
return true
}
return false
}
fileprivate func checkExtension(drag: NSDraggingInfo) -> Bool {
if let board = drag.draggingPasteboard.propertyList(forType: NSFilenamesPboardType) as? NSArray, let path = board[0] as? String {
let url = NSURL(fileURLWithPath: path)
if let fileExtension = url.pathExtension?.lowercased() {
return fileTypes.contains(fileExtension)
}
}
return false
}
}

View File

@ -0,0 +1,105 @@
//
// UploadVC.swift
// AppleParty
//
// Created by on 2021/6/3.
//
import AppKit
import Foundation
class IAPUploadImageVC: NSViewController {
@IBOutlet weak var tableView: NSTableView!
@IBOutlet weak var cancelBtm: NSButton!
@IBOutlet weak var submitBtm: NSButton!
@IBOutlet weak var tipLb: NSTextField!
var picnames = [String]()
var resultPaths = [String: String]()
typealias CallBackFunc = (_ paths: [String: String]) -> Void
var callBackFunc: CallBackFunc?
fileprivate lazy var fileTypes: [String] = {
return ["jpg", "jpeg", "png"]
}()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tipLb.stringValue = "需要上传\(picnames.count)张图片"
}
@IBAction func clickedBatchUploadBtn(_ sender: Any) {
let openPanel = NSOpenPanel()
openPanel.canChooseFiles = true
openPanel.canChooseDirectories = false
openPanel.allowsMultipleSelection = true
openPanel.allowedFileTypes = fileTypes
openPanel.beginSheetModal(for: self.view.window!) { (modalResponse: NSApplication.ModalResponse) in
if modalResponse == .OK {
openPanel.urls.forEach { url in
let picname = url.lastPathComponent
debugPrint(picname)
if self.picnames.contains(picname) {
debugPrint("contains")
self.resultPaths[picname] = url.path
}
}
self.tableView.reloadData()
}
}
}
@IBAction func cancel(_ sender: Any) {
dismiss(self)
}
@IBAction func submit(_ sender: Any) {
guard picnames.count == resultPaths.keys.count else {
APHUD.hide(message: "必须图片数量不正确!", view: self.view)
return
}
dismiss(self)
if let callBackFunc = callBackFunc {
callBackFunc(resultPaths)
}
}
}
extension IAPUploadImageVC: NSTableViewDelegate, NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return picnames.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
switch tableColumn?.identifier.enumValue() {
case ColumnIdetifier.picname.cellValue:
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.picname.cellValue, owner: self) as? NSTableCellView
cell?.textField?.stringValue = picnames[row]
return cell
case ColumnIdetifier.upload.cellValue:
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.upload.cellValue, owner: self) as? UploadCell
cell?.row = row
cell?.dragView(resultPaths[picnames[row]])
cell?.callBackFunc = { path,crow in
self.resultPaths[self.picnames[crow]] = path
}
return cell
default:
return nil
}
}
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
100
}
func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
return false
}
}

View File

@ -0,0 +1,993 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23727"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Window Controller-->
<scene sceneID="6Md-ET-vaH">
<objects>
<windowController storyboardIdentifier="InputExcelList" id="x3B-Ma-He7" sceneMemberID="viewController">
<window key="window" title="批量内购买项目上传" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="baa-hG-CGu">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="425" y="432" width="600" height="400"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<value key="minSize" type="size" width="600" height="380"/>
<view key="contentView" id="iWX-hs-skU">
<rect key="frame" x="0.0" y="0.0" width="600" height="400"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<connections>
<outlet property="delegate" destination="x3B-Ma-He7" id="e65-q9-nQL"/>
</connections>
</window>
<connections>
<segue destination="HcW-sO-g2Q" kind="relationship" relationship="window.shadowedContentViewController" id="HBc-rj-vrU"/>
</connections>
</windowController>
<customObject id="3MN-ty-i6n" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-950" y="1930"/>
</scene>
<!--批量内购买项目上传-->
<scene sceneID="biu-hN-rXF">
<objects>
<viewController title="批量内购买项目上传" id="HcW-sO-g2Q" customClass="APUploadIAPListVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" misplaced="YES" id="K0H-Tj-XIL">
<rect key="frame" x="0.0" y="0.0" width="816" height="363"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView autohidesScrollers="YES" horizontalLineScroll="40" horizontalPageScroll="10" verticalLineScroll="40" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2iO-iU-0Fy">
<rect key="frame" x="0.0" y="100" width="796" height="263"/>
<clipView key="contentView" ambiguous="YES" id="TPK-e2-6v9">
<rect key="frame" x="1" y="1" width="794" height="261"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowHeight="40" headerView="5s8-Sc-F8G" viewBased="YES" id="FfT-VW-kGY">
<rect key="frame" x="0.0" y="0.0" width="794" height="236"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<tableViewGridLines key="gridStyleMask" vertical="YES" horizontal="YES"/>
<color key="gridColor" red="0.93333333330000001" green="0.93333333330000001" blue="0.93333333330000001" alpha="1" colorSpace="calibratedRGB"/>
<tableColumns>
<tableColumn identifier="idColumn" width="30" minWidth="30" maxWidth="40" id="vMK-OX-shk">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="id">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="EXT-G7-gaH">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="idCell" id="ANH-fd-8Ev">
<rect key="frame" x="0.0" y="0.0" width="36" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="QOn-S3-Fa5">
<rect key="frame" x="0.0" y="12" width="36" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="1" id="LsW-Zq-hB0">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="QOn-S3-Fa5" id="adV-BV-fd5"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="screenshotColumn" width="75" minWidth="40" maxWidth="120" id="B7q-XM-l6y">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="截图">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="OtQ-JF-R0L">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="screenshotCell" id="aFa-Mz-jnj" customClass="ImageViewCell" customModule="AppleParty" customModuleProvider="target">
<rect key="frame" x="36" y="0.0" width="75" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="hys-p0-19y">
<rect key="frame" x="0.0" y="0.0" width="75" height="40"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="C9E-lZ-VJ9"/>
</imageView>
</subviews>
<constraints>
<constraint firstItem="hys-p0-19y" firstAttribute="leading" secondItem="aFa-Mz-jnj" secondAttribute="leading" id="5aT-8N-sLh"/>
<constraint firstItem="hys-p0-19y" firstAttribute="top" secondItem="aFa-Mz-jnj" secondAttribute="top" id="PcE-OE-rpA"/>
<constraint firstAttribute="trailing" secondItem="hys-p0-19y" secondAttribute="trailing" id="QaH-hj-2yB"/>
<constraint firstAttribute="bottom" secondItem="hys-p0-19y" secondAttribute="bottom" id="SDW-Qd-ZWN"/>
</constraints>
<connections>
<outlet property="imgSel" destination="hys-p0-19y" id="FJd-2g-vzv"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="productIDColumn" width="118" minWidth="40" maxWidth="150" id="AaY-pT-reC">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Product ID">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="urO-vI-Sw4">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="productIDCell" id="3o0-NI-ewo">
<rect key="frame" x="111" y="0.0" width="118" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="c8C-RK-lNM">
<rect key="frame" x="0.0" y="12" width="118" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="OzZ-QS-Xrv">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="c8C-RK-lNM" id="yrw-fd-VbS"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="stateColumn" width="60" minWidth="40" maxWidth="90" id="K81-yv-X5l">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="类型">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="zey-VL-KI9">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="stateCell" id="hKB-hg-TrY">
<rect key="frame" x="229" y="0.0" width="60" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="D1n-4P-b0Q">
<rect key="frame" x="0.0" y="12" width="60" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="1" id="sq1-BL-Rcf">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="D1n-4P-b0Q" id="uHy-rg-Ef7"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="productNameColumn" width="102" minWidth="40" maxWidth="150" id="9em-uk-bzW">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="参考名字">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="mik-0H-hQ1">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="productNameCell" id="Wp7-Z8-fyG">
<rect key="frame" x="289" y="0.0" width="102" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="V05-T9-An4">
<rect key="frame" x="0.0" y="12" width="102" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Fyk-ea-jSK">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="V05-T9-An4" id="eHR-Tz-Q5j"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="priceColumn" width="112" minWidth="40" maxWidth="400" id="dHE-xa-6WE">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="销售范围">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="9cd-hH-o3v">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="priceCell" id="M23-59-mE6">
<rect key="frame" x="391" y="0.0" width="112" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LdV-Er-PG6">
<rect key="frame" x="0.0" y="12" width="112" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<textFieldCell key="cell" lineBreakMode="charWrapping" sendsActionOnEndEditing="YES" title="1" id="k5H-G7-RvE">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="LdV-Er-PG6" id="hKk-WY-If1"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="levelColumn" width="102" minWidth="40" maxWidth="400" id="RAv-kj-cld">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="价格机制">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="2so-VG-q75">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="levelCell" id="Pz4-4W-nO5">
<rect key="frame" x="503" y="0.0" width="102" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="e8p-aX-EOQ">
<rect key="frame" x="0.0" y="12" width="102" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<textFieldCell key="cell" lineBreakMode="charWrapping" sendsActionOnEndEditing="YES" title="1" id="txV-L7-8pc">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="e8p-aX-EOQ" id="cFO-zR-V7E"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="languageColumn" width="100" minWidth="40" maxWidth="550" id="cPw-xm-W9c">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="本地化语言">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="S22-0C-i35">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="languageCell" id="qG2-Ag-GoS">
<rect key="frame" x="605" y="0.0" width="100" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4pw-S3-VJ2">
<rect key="frame" x="0.0" y="3" width="100" height="34"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<textFieldCell key="cell" lineBreakMode="charWrapping" sendsActionOnEndEditing="YES" title="1" id="cgN-Ef-H9K">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="4pw-S3-VJ2" id="yZO-no-fs9"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="productPdsColumn" width="38" minWidth="30" maxWidth="150" id="Lic-uH-erC">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="审核备注(可选)">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="6Ts-zi-iQ8">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="productPdsCell" id="tSv-x2-iSQ">
<rect key="frame" x="705" y="0.0" width="44" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="CcF-6d-Y0Y">
<rect key="frame" x="0.0" y="12" width="44" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="charWrapping" sendsActionOnEndEditing="YES" title="1" id="RdH-e2-lmI">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="CcF-6d-Y0Y" id="epa-re-I4A"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
</tableColumns>
<connections>
<outlet property="dataSource" destination="HcW-sO-g2Q" id="TAQ-cE-1Hb"/>
<outlet property="delegate" destination="HcW-sO-g2Q" id="cV7-Es-JKZ"/>
</connections>
</tableView>
</subviews>
</clipView>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="250" id="88B-Ob-G5p"/>
</constraints>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="KNl-rP-gjC">
<rect key="frame" x="1" y="246" width="794" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="z1V-UM-aHa">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" identifier="languageColumn" wantsLayer="YES" id="5s8-Sc-F8G">
<rect key="frame" x="0.0" y="0.0" width="794" height="25"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
<box boxType="custom" cornerRadius="4" translatesAutoresizingMaskIntoConstraints="NO" id="DFI-Sf-hCb">
<rect key="frame" x="0.0" y="0.0" width="796" height="100"/>
<view key="contentView" id="6rW-KE-ixB">
<rect key="frame" x="1" y="1" width="794" height="98"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="yl7-CE-urn">
<rect key="frame" x="340" y="25" width="114" height="47"/>
<buttonCell key="cell" type="push" title="提交" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ysI-I3-a0g">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
<color key="contentTintColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="height" constant="35" id="L7q-rE-qUa"/>
<constraint firstAttribute="width" constant="100" id="luq-WV-0zV"/>
</constraints>
<connections>
<action selector="createIAP:" target="HcW-sO-g2Q" id="mZJ-aY-eOM"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Wiy-jc-2Pw">
<rect key="frame" x="700" y="13" width="86" height="32"/>
<buttonCell key="cell" type="push" title="设置密钥" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="AHB-ou-qRW">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<constraints>
<constraint firstAttribute="height" constant="20" id="Sbb-sq-gsR"/>
<constraint firstAttribute="height" constant="20" id="gLt-9i-Kpk"/>
</constraints>
<connections>
<action selector="clickedSPasswordBtn:" target="HcW-sO-g2Q" id="4WZ-cz-seF"/>
</connections>
</button>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gwi-GP-uQf">
<rect key="frame" x="184" y="8" width="426" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="注请检查数据与表格是否一致提交成功后商品id和类型不能再更改" id="04V-rH-2el">
<font key="font" metaFont="system"/>
<color key="textColor" name="systemOrangeColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sv7-52-2Cw">
<rect key="frame" x="700" y="45" width="86" height="32"/>
<buttonCell key="cell" type="push" title="上传截图" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="n9B-2Z-TXF">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<constraints>
<constraint firstAttribute="height" constant="20" id="Os2-cR-YSb"/>
</constraints>
<connections>
<action selector="clickedUploadShotBtn:" target="HcW-sO-g2Q" id="pfM-0y-Fwy"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mWR-IS-Cxn">
<rect key="frame" x="18" y="56" width="194" height="18"/>
<buttonCell key="cell" type="check" title="保留自动续期订阅者现有定价" bezelStyle="regularSquare" imagePosition="left" inset="2" id="rcb-F8-Wjv">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<constraints>
<constraint firstAttribute="height" constant="16" id="jCn-pM-TqY"/>
</constraints>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="O5k-L7-xuw">
<rect key="frame" x="18" y="30" width="200" height="18"/>
<buttonCell key="cell" type="check" title="显示 ASC API 请求速率的阈值" bezelStyle="regularSquare" imagePosition="left" inset="2" id="7jz-Eq-MTQ">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<constraints>
<constraint firstAttribute="height" constant="16" id="UTk-E1-F8x"/>
</constraints>
</button>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="gwi-GP-uQf" secondAttribute="bottom" constant="8" id="446-fY-M9B"/>
<constraint firstAttribute="bottom" secondItem="Wiy-jc-2Pw" secondAttribute="bottom" constant="20" symbolic="YES" id="7js-1K-u7U"/>
<constraint firstItem="O5k-L7-xuw" firstAttribute="top" secondItem="mWR-IS-Cxn" secondAttribute="bottom" constant="10" id="8dK-sn-ZXM"/>
<constraint firstItem="yl7-CE-urn" firstAttribute="centerY" secondItem="6rW-KE-ixB" secondAttribute="centerY" id="ITd-YX-Trm"/>
<constraint firstAttribute="trailing" secondItem="sv7-52-2Cw" secondAttribute="trailing" constant="15" id="QUP-CD-c2a"/>
<constraint firstItem="Wiy-jc-2Pw" firstAttribute="top" secondItem="sv7-52-2Cw" secondAttribute="bottom" constant="12" id="Rp0-lh-D4G"/>
<constraint firstItem="Wiy-jc-2Pw" firstAttribute="trailing" secondItem="sv7-52-2Cw" secondAttribute="trailing" id="a9w-yY-EWd"/>
<constraint firstItem="yl7-CE-urn" firstAttribute="centerX" secondItem="6rW-KE-ixB" secondAttribute="centerX" id="efU-0I-QhV"/>
<constraint firstItem="mWR-IS-Cxn" firstAttribute="leading" secondItem="6rW-KE-ixB" secondAttribute="leading" constant="20" symbolic="YES" id="hjT-ey-2ZV"/>
<constraint firstItem="O5k-L7-xuw" firstAttribute="leading" secondItem="6rW-KE-ixB" secondAttribute="leading" constant="20" symbolic="YES" id="htV-cS-Gxk"/>
<constraint firstItem="gwi-GP-uQf" firstAttribute="centerX" secondItem="6rW-KE-ixB" secondAttribute="centerX" id="o1O-9Y-VOc"/>
<constraint firstItem="mWR-IS-Cxn" firstAttribute="top" secondItem="6rW-KE-ixB" secondAttribute="top" constant="25" id="yx3-wU-dQT"/>
</constraints>
</view>
<constraints>
<constraint firstAttribute="height" constant="100" id="Nfj-Ag-iuW"/>
</constraints>
<color key="borderColor" name="quaternaryLabelColor" catalog="System" colorSpace="catalog"/>
</box>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="2iO-iU-0Fy" secondAttribute="trailing" id="0jj-T0-SZ6"/>
<constraint firstItem="2iO-iU-0Fy" firstAttribute="top" secondItem="K0H-Tj-XIL" secondAttribute="top" id="Dh4-Ma-QQZ"/>
<constraint firstItem="2iO-iU-0Fy" firstAttribute="leading" secondItem="K0H-Tj-XIL" secondAttribute="leading" id="GhX-65-sYx"/>
<constraint firstItem="DFI-Sf-hCb" firstAttribute="top" secondItem="2iO-iU-0Fy" secondAttribute="bottom" id="U03-9h-hLB"/>
<constraint firstAttribute="bottom" secondItem="DFI-Sf-hCb" secondAttribute="bottom" id="X11-LN-u3M"/>
<constraint firstAttribute="trailing" secondItem="DFI-Sf-hCb" secondAttribute="trailing" id="YC4-6m-bim"/>
<constraint firstItem="DFI-Sf-hCb" firstAttribute="leading" secondItem="K0H-Tj-XIL" secondAttribute="leading" id="ieA-uv-pUf"/>
</constraints>
</view>
<connections>
<outlet property="enterBtn" destination="yl7-CE-urn" id="fxo-dC-Eo4"/>
<outlet property="preserveCurrentPriceBtn" destination="mWR-IS-Cxn" id="kC5-n4-ddM"/>
<outlet property="showApiRateLimitLogsBtn" destination="O5k-L7-xuw" id="NkA-Kq-jRr"/>
<outlet property="tableView" destination="FfT-VW-kGY" id="eo0-Hf-anq"/>
</connections>
</viewController>
<customObject id="oPL-uS-kEl" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-175" y="1918.5"/>
</scene>
<!--Upload ImageVC-->
<scene sceneID="l5Q-5j-Ieg">
<objects>
<viewController storyboardIdentifier="IAPUploadVCID" id="ZGW-X2-eY0" customClass="IAPUploadImageVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="XlO-n7-hEA">
<rect key="frame" x="0.0" y="0.0" width="449" height="241"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<box boxType="custom" cornerRadius="4" title="Box" translatesAutoresizingMaskIntoConstraints="NO" id="4UC-ET-dPZ">
<rect key="frame" x="0.0" y="0.0" width="449" height="241"/>
<view key="contentView" id="bXG-f2-WGM">
<rect key="frame" x="1" y="1" width="447" height="239"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rfb-ot-DNY">
<rect key="frame" x="13" y="193" width="105" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="需要传入x张图片" id="oMa-Eg-4Q4">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" red="0.90980392160000001" green="0.4431372549" blue="0.28235294119999998" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kcS-un-E1x">
<rect key="frame" x="353" y="192" width="86" height="32"/>
<buttonCell key="cell" type="push" title="批量上传" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="W6k-qu-hFp">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<constraints>
<constraint firstAttribute="height" constant="20" id="Nq6-G6-yns"/>
</constraints>
<connections>
<action selector="clickedBatchUploadBtn:" target="ZGW-X2-eY0" id="s3T-H7-xH7"/>
</connections>
</button>
<scrollView autohidesScrollers="YES" horizontalLineScroll="102" horizontalPageScroll="10" verticalLineScroll="102" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="92G-BP-MIt">
<rect key="frame" x="15" y="80" width="417" height="103"/>
<clipView key="contentView" ambiguous="YES" id="KqA-NU-fUc">
<rect key="frame" x="1" y="1" width="415" height="101"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" alternatingRowBackgroundColors="YES" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowHeight="100" headerView="9ym-TL-xUJ" viewBased="YES" id="Pkx-YF-hF7">
<rect key="frame" x="0.0" y="0.0" width="415" height="76"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<tableViewGridLines key="gridStyleMask" vertical="YES" horizontal="YES"/>
<color key="gridColor" red="0.9137254901960784" green="0.9137254901960784" blue="0.9137254901960784" alpha="1" colorSpace="calibratedRGB"/>
<tableColumns>
<tableColumn identifier="picnameColumn" width="151" minWidth="40" maxWidth="1000" id="vzw-Wm-X2f">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="图片名称">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="nPA-Qe-y92">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="picnameCell" id="nHX-Ef-hlo">
<rect key="frame" x="1" y="1" width="156" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="hmR-mk-1ol">
<rect key="frame" x="-2" y="-1" width="160" height="20"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="Seh-ch-rSz"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="center" title="Table View Cell" id="hoj-H0-NhX">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="hmR-mk-1ol" firstAttribute="leading" secondItem="nHX-Ef-hlo" secondAttribute="leading" id="BBN-E4-pLd"/>
<constraint firstItem="hmR-mk-1ol" firstAttribute="centerX" secondItem="nHX-Ef-hlo" secondAttribute="centerX" id="Otu-Lw-wvf"/>
<constraint firstItem="hmR-mk-1ol" firstAttribute="centerY" secondItem="nHX-Ef-hlo" secondAttribute="centerY" id="fK5-ug-JU3"/>
</constraints>
<connections>
<outlet property="textField" destination="hmR-mk-1ol" id="Ufw-70-c7l"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="uploadColumn" width="220" minWidth="40" maxWidth="1000" id="E8R-ck-u3Z">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="上传图片">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="b6z-Gv-phs">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="uploadCell" id="kSW-b0-iBl" customClass="UploadCell" customModule="AppleParty" customModuleProvider="target">
<rect key="frame" x="160" y="1" width="224" height="100"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="fOf-F6-66M">
<rect key="frame" x="0.0" y="0.0" width="224" height="100"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="YKV-PN-c9B"/>
</imageView>
<view id="Ml3-Ia-Ewi">
<rect key="frame" x="86" y="24" width="121" height="52"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<box boxType="custom" cornerRadius="4" title="Box" translatesAutoresizingMaskIntoConstraints="NO" id="y7w-3d-1iH">
<rect key="frame" x="0.0" y="0.0" width="121" height="52"/>
<view key="contentView" id="dTZ-YH-cOp">
<rect key="frame" x="1" y="1" width="119" height="50"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
<color key="borderColor" red="0.97254901959999995" green="0.97254901959999995" blue="0.97254901959999995" alpha="1" colorSpace="calibratedRGB"/>
<color key="fillColor" name="selectedTextBackgroundColor" catalog="System" colorSpace="catalog"/>
</box>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="8nS-Sr-Tez">
<rect key="frame" x="45" y="16.5" width="32.5" height="40"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="8ms-3v-6Vf"/>
<constraint firstAttribute="height" constant="32" id="oxZ-DL-wE0"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSAddTemplate" id="qFS-6U-ZiY"/>
</imageView>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8Zb-BZ-4UZ">
<rect key="frame" x="20" y="4" width="82" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="拖拽图片上传" id="0LG-sI-2vV">
<font key="font" metaFont="system"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="zSP-IZ-bba" customClass="DragView" customModule="AppleParty" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="121" height="52"/>
</customView>
</subviews>
<constraints>
<constraint firstItem="y7w-3d-1iH" firstAttribute="top" secondItem="Ml3-Ia-Ewi" secondAttribute="top" id="6r8-S0-IOe"/>
<constraint firstItem="8Zb-BZ-4UZ" firstAttribute="top" secondItem="8nS-Sr-Tez" secondAttribute="bottom" id="A3W-gz-1Gn"/>
<constraint firstAttribute="bottom" secondItem="y7w-3d-1iH" secondAttribute="bottom" id="Jpg-KB-D6M"/>
<constraint firstAttribute="trailing" secondItem="zSP-IZ-bba" secondAttribute="trailing" id="MyV-Rc-dU6"/>
<constraint firstAttribute="trailing" secondItem="y7w-3d-1iH" secondAttribute="trailing" id="N7y-lw-ncn"/>
<constraint firstItem="8nS-Sr-Tez" firstAttribute="centerX" secondItem="Ml3-Ia-Ewi" secondAttribute="centerX" id="Uce-u0-e1b"/>
<constraint firstItem="8Zb-BZ-4UZ" firstAttribute="centerX" secondItem="8nS-Sr-Tez" secondAttribute="centerX" id="hEt-M5-oOZ"/>
<constraint firstItem="y7w-3d-1iH" firstAttribute="leading" secondItem="Ml3-Ia-Ewi" secondAttribute="leading" id="uZv-kV-R1v"/>
<constraint firstAttribute="bottom" secondItem="zSP-IZ-bba" secondAttribute="bottom" id="vDY-zD-VN0"/>
<constraint firstItem="zSP-IZ-bba" firstAttribute="leading" secondItem="Ml3-Ia-Ewi" secondAttribute="leading" id="wHv-9r-cPP"/>
<constraint firstItem="zSP-IZ-bba" firstAttribute="top" secondItem="Ml3-Ia-Ewi" secondAttribute="top" id="xDb-7m-ju4"/>
<constraint firstItem="8nS-Sr-Tez" firstAttribute="top" secondItem="Ml3-Ia-Ewi" secondAttribute="top" id="zaV-gB-RH9"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstItem="fOf-F6-66M" firstAttribute="top" secondItem="kSW-b0-iBl" secondAttribute="top" id="7Sz-wg-BVW"/>
<constraint firstAttribute="trailing" secondItem="fOf-F6-66M" secondAttribute="trailing" id="IZf-Mm-zGq"/>
<constraint firstItem="fOf-F6-66M" firstAttribute="leading" secondItem="kSW-b0-iBl" secondAttribute="leading" id="h7c-Qw-WYp"/>
<constraint firstAttribute="bottom" secondItem="fOf-F6-66M" secondAttribute="bottom" id="yaU-zc-Om0"/>
</constraints>
<connections>
<outlet property="dragBox" destination="Ml3-Ia-Ewi" id="QV5-l0-sSy"/>
<outlet property="dragView" destination="zSP-IZ-bba" id="Lmb-dX-AQf"/>
<outlet property="imgSel" destination="fOf-F6-66M" id="cNh-TV-TVQ"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
</tableColumns>
</tableView>
</subviews>
</clipView>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="100" id="ycy-R4-dvl"/>
</constraints>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="uRq-Iv-4qH">
<rect key="frame" x="1" y="383" width="415" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="Icy-PU-IhP">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" wantsLayer="YES" id="9ym-TL-xUJ">
<rect key="frame" x="0.0" y="0.0" width="415" height="25"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="QQe-WG-p4R">
<rect key="frame" x="227" y="13" width="94" height="52"/>
<buttonCell key="cell" type="push" title="提交" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="4u8-R4-iJl">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
<color key="contentTintColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="width" constant="80" id="5lo-X9-l3L"/>
<constraint firstAttribute="height" constant="40" id="t3W-Aj-MWR"/>
</constraints>
<connections>
<action selector="submit:" target="ZGW-X2-eY0" id="XBS-Cc-kwA"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="4yB-HB-Sko">
<rect key="frame" x="132" y="13" width="94" height="52"/>
<buttonCell key="cell" type="push" title="取消" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="pTB-tq-Xcl">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
Gw
</string>
</buttonCell>
<color key="contentTintColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="width" constant="80" id="Vxa-b9-goA"/>
<constraint firstAttribute="height" constant="40" id="uMw-Ja-95b"/>
</constraints>
<connections>
<action selector="cancel:" target="ZGW-X2-eY0" id="giL-cF-Fnw"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="QQe-WG-p4R" firstAttribute="leading" secondItem="4yB-HB-Sko" secondAttribute="trailing" constant="15" id="0by-Cs-rAn"/>
<constraint firstItem="kcS-un-E1x" firstAttribute="top" secondItem="bXG-f2-WGM" secondAttribute="top" constant="20" symbolic="YES" id="AJN-8G-5DG"/>
<constraint firstAttribute="bottom" secondItem="92G-BP-MIt" secondAttribute="bottom" constant="80" id="Fg8-sp-FRb"/>
<constraint firstItem="4yB-HB-Sko" firstAttribute="centerY" secondItem="QQe-WG-p4R" secondAttribute="centerY" id="JID-Ip-h5y"/>
<constraint firstItem="92G-BP-MIt" firstAttribute="top" secondItem="rfb-ot-DNY" secondAttribute="bottom" constant="10" id="N6h-wM-NNs"/>
<constraint firstItem="QQe-WG-p4R" firstAttribute="centerX" secondItem="bXG-f2-WGM" secondAttribute="centerX" constant="50" id="TD1-kT-Y8m"/>
<constraint firstItem="rfb-ot-DNY" firstAttribute="leading" secondItem="92G-BP-MIt" secondAttribute="leading" id="fZ3-jp-zmA"/>
<constraint firstAttribute="trailing" secondItem="92G-BP-MIt" secondAttribute="trailing" constant="15" id="hN7-OK-yLE"/>
<constraint firstItem="rfb-ot-DNY" firstAttribute="top" secondItem="bXG-f2-WGM" secondAttribute="top" constant="30" id="hWX-u6-nVQ"/>
<constraint firstAttribute="trailing" secondItem="kcS-un-E1x" secondAttribute="trailing" constant="15" id="nPY-XB-593"/>
<constraint firstItem="92G-BP-MIt" firstAttribute="leading" secondItem="bXG-f2-WGM" secondAttribute="leading" constant="15" id="ohu-Ls-Ekd"/>
<constraint firstItem="QQe-WG-p4R" firstAttribute="top" secondItem="92G-BP-MIt" secondAttribute="bottom" constant="20" id="zVx-Ed-CpA"/>
</constraints>
</view>
<color key="borderColor" name="quaternaryLabelColor" catalog="System" colorSpace="catalog"/>
</box>
</subviews>
<constraints>
<constraint firstItem="4UC-ET-dPZ" firstAttribute="leading" secondItem="XlO-n7-hEA" secondAttribute="leading" id="7fN-p4-G0s"/>
<constraint firstItem="4UC-ET-dPZ" firstAttribute="top" secondItem="XlO-n7-hEA" secondAttribute="top" id="RtL-aw-X7P"/>
<constraint firstAttribute="bottom" secondItem="4UC-ET-dPZ" secondAttribute="bottom" id="pkF-XL-mVD"/>
<constraint firstAttribute="trailing" secondItem="4UC-ET-dPZ" secondAttribute="trailing" id="uMi-vb-NaW"/>
</constraints>
</view>
<connections>
<outlet property="cancelBtm" destination="4yB-HB-Sko" id="cPP-RC-on7"/>
<outlet property="submitBtm" destination="QQe-WG-p4R" id="fhA-8S-Xdr"/>
<outlet property="tableView" destination="Pkx-YF-hF7" id="PfU-Tz-mHg"/>
<outlet property="tipLb" destination="rfb-ot-DNY" id="K0Q-YO-BoS"/>
</connections>
</viewController>
<customObject id="s7R-ka-Qj0" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-148" y="2436"/>
</scene>
<!--导出excel-->
<scene sceneID="v0g-L8-OB0">
<objects>
<viewController title="导出excel" storyboardIdentifier="OutputExcelVCID" id="MF2-R2-2hO" customClass="OutputExcelVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="4gc-AE-KbR">
<rect key="frame" x="0.0" y="0.0" width="500" height="600"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<box boxType="custom" cornerRadius="4" translatesAutoresizingMaskIntoConstraints="NO" id="C5N-3t-BHc">
<rect key="frame" x="0.0" y="0.0" width="500" height="600"/>
<view key="contentView" id="vgI-Wv-mhT">
<rect key="frame" x="1" y="1" width="498" height="598"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="FdO-Rj-ruG">
<rect key="frame" x="28" y="553" width="40" height="15"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="商品id" id="QYB-Zw-eHV">
<font key="font" metaFont="cellTitle"/>
<color key="textColor" name="AccentColor"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<scrollView borderType="line" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UeB-cG-eE4">
<rect key="frame" x="30" y="150" width="200" height="393"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="IQM-Fb-jKv">
<rect key="frame" x="1" y="1" width="198" height="391"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="ceg-vF-fQf">
<rect key="frame" x="0.0" y="0.0" width="198" height="391"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="198" height="391"/>
<size key="maxSize" width="248" height="10000000"/>
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
</textView>
</subviews>
</clipView>
<constraints>
<constraint firstAttribute="width" constant="200" id="FOJ-YZ-qvi"/>
</constraints>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="rFr-Dg-h0l">
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="EPk-ve-LJd">
<rect key="frame" x="183" y="1" width="16" height="391"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="s72-vm-I76">
<rect key="frame" x="237" y="552" width="86" height="15"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="对应的AppleID" id="avW-ms-Mra">
<font key="font" metaFont="cellTitle"/>
<color key="textColor" name="AccentColor"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<scrollView hidden="YES" borderType="line" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YhA-gX-xjC">
<rect key="frame" x="239" y="150" width="229" height="392"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="mqr-je-Omp">
<rect key="frame" x="1" y="1" width="227" height="390"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="Z0K-AM-ybS">
<rect key="frame" x="0.0" y="0.0" width="227" height="390"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="227" height="390"/>
<size key="maxSize" width="248" height="10000000"/>
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
</textView>
</subviews>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Jg2-AF-jRX">
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="xVd-kl-KcI">
<rect key="frame" x="212" y="1" width="16" height="390"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="HH6-Lm-CXI">
<rect key="frame" x="380" y="23" width="94" height="52"/>
<buttonCell key="cell" type="push" title="提交" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="wti-uT-BJP">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
<color key="contentTintColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="width" constant="80" id="7Pu-0Y-cZl"/>
<constraint firstAttribute="height" constant="40" id="Tl3-IB-jTG"/>
</constraints>
<connections>
<action selector="commit:" target="MF2-R2-2hO" id="zUs-9M-dat"/>
</connections>
</button>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="voo-el-aSb">
<rect key="frame" x="195" y="131" width="36" height="15"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="0/100" id="Rvu-t4-mqS">
<font key="font" metaFont="cellTitle"/>
<color key="textColor" red="0.80000000000000004" green="0.80000000000000004" blue="0.80000000000000004" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fJh-Pa-GRs">
<rect key="frame" x="433" y="131" width="36" height="15"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="0/100" id="xGg-Co-KSE">
<font key="font" metaFont="cellTitle"/>
<color key="textColor" red="0.80000000000000004" green="0.80000000000000004" blue="0.80000000000000004" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gHe-JD-W0l">
<rect key="frame" x="285" y="23" width="94" height="52"/>
<buttonCell key="cell" type="push" title="关闭" bezelStyle="rounded" alignment="center" borderStyle="border" inset="2" id="mdF-cu-Xk3">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
Gw
</string>
</buttonCell>
<constraints>
<constraint firstAttribute="width" constant="80" id="DGd-OO-fun"/>
<constraint firstAttribute="height" constant="40" id="aeW-Ej-VAh"/>
</constraints>
<connections>
<action selector="close:" target="MF2-R2-2hO" id="4dP-Wn-RS1"/>
</connections>
</button>
<scrollView borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" horizontalScrollElasticity="none" verticalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="mqs-KO-hcm">
<rect key="frame" x="31" y="81" width="436" height="40"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="Na5-JS-qH5">
<rect key="frame" x="0.0" y="0.0" width="436" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView editable="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="ccx-Sz-Ndo">
<rect key="frame" x="0.0" y="0.0" width="436" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="436" height="40"/>
<size key="maxSize" width="438" height="10000000"/>
<attributedString key="textStorage">
<fragment content="操作说明将excel表格中商品id列的数据粘贴到输入框点击提交后自动生成对应的AppleID。商品不存在时为空行">
<attributes>
<color key="NSColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
<font key="NSFont" size="12" name="PingFangSC-Regular"/>
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
</attributedString>
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
</textView>
</subviews>
</clipView>
<constraints>
<constraint firstAttribute="height" constant="40" id="3kc-P8-Mm2"/>
</constraints>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="clV-B4-N7M">
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="tQq-xq-oSv">
<rect key="frame" x="-100" y="-100" width="15" height="40"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
</subviews>
<constraints>
<constraint firstItem="YhA-gX-xjC" firstAttribute="top" secondItem="s72-vm-I76" secondAttribute="bottom" constant="10" id="7Jq-tR-fOe"/>
<constraint firstItem="UeB-cG-eE4" firstAttribute="leading" secondItem="FdO-Rj-ruG" secondAttribute="leading" id="AjD-0r-0jb"/>
<constraint firstItem="YhA-gX-xjC" firstAttribute="leading" secondItem="ceg-vF-fQf" secondAttribute="trailing" constant="10" id="H8P-yH-SL9"/>
<constraint firstItem="UeB-cG-eE4" firstAttribute="top" secondItem="FdO-Rj-ruG" secondAttribute="bottom" constant="10" id="KCv-Ah-SLf"/>
<constraint firstItem="mqs-KO-hcm" firstAttribute="leading" secondItem="ceg-vF-fQf" secondAttribute="leading" id="NHT-nW-xzZ"/>
<constraint firstItem="gHe-JD-W0l" firstAttribute="centerY" secondItem="HH6-Lm-CXI" secondAttribute="centerY" id="RMB-mz-vBd"/>
<constraint firstItem="YhA-gX-xjC" firstAttribute="leading" secondItem="s72-vm-I76" secondAttribute="leading" id="Sgp-QJ-OXe"/>
<constraint firstItem="HH6-Lm-CXI" firstAttribute="top" secondItem="mqs-KO-hcm" secondAttribute="bottom" constant="11" id="Up0-1N-Qrl"/>
<constraint firstAttribute="trailing" secondItem="YhA-gX-xjC" secondAttribute="trailing" constant="30" id="ViH-MG-fzQ"/>
<constraint firstItem="voo-el-aSb" firstAttribute="trailing" secondItem="ceg-vF-fQf" secondAttribute="trailing" id="WWa-Sf-kRh"/>
<constraint firstItem="fJh-Pa-GRs" firstAttribute="trailing" secondItem="xVd-kl-KcI" secondAttribute="trailing" id="Wak-yX-deV"/>
<constraint firstItem="voo-el-aSb" firstAttribute="top" secondItem="EPk-ve-LJd" secondAttribute="bottom" constant="5" id="XPn-up-SMl"/>
<constraint firstAttribute="bottom" secondItem="HH6-Lm-CXI" secondAttribute="bottom" constant="30" id="aen-UO-NPm"/>
<constraint firstItem="FdO-Rj-ruG" firstAttribute="leading" secondItem="vgI-Wv-mhT" secondAttribute="leading" constant="30" id="cH1-sV-aTc"/>
<constraint firstItem="mqs-KO-hcm" firstAttribute="trailing" secondItem="Z0K-AM-ybS" secondAttribute="trailing" id="dkZ-4E-thk"/>
<constraint firstItem="FdO-Rj-ruG" firstAttribute="top" secondItem="vgI-Wv-mhT" secondAttribute="top" constant="30" id="epk-Ii-nZC"/>
<constraint firstItem="HH6-Lm-CXI" firstAttribute="leading" secondItem="gHe-JD-W0l" secondAttribute="trailing" constant="15" id="fZ1-bH-ib2"/>
<constraint firstAttribute="bottom" secondItem="YhA-gX-xjC" secondAttribute="bottom" constant="150" id="q9r-vD-dwI"/>
<constraint firstItem="HH6-Lm-CXI" firstAttribute="trailing" secondItem="fJh-Pa-GRs" secondAttribute="trailing" id="wlF-3S-KCy"/>
<constraint firstItem="YhA-gX-xjC" firstAttribute="top" secondItem="ceg-vF-fQf" secondAttribute="top" id="ymP-Xb-sKJ"/>
<constraint firstItem="fJh-Pa-GRs" firstAttribute="top" secondItem="xVd-kl-KcI" secondAttribute="bottom" constant="5" id="ywL-9o-fuz"/>
<constraint firstAttribute="bottom" secondItem="UeB-cG-eE4" secondAttribute="bottom" constant="150" id="zen-rj-VXk"/>
</constraints>
</view>
<color key="borderColor" name="quaternaryLabelColor" catalog="System" colorSpace="catalog"/>
</box>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="C5N-3t-BHc" secondAttribute="trailing" id="4eZ-yr-AoV"/>
<constraint firstItem="C5N-3t-BHc" firstAttribute="top" secondItem="4gc-AE-KbR" secondAttribute="top" id="7U4-CP-ET9"/>
<constraint firstItem="C5N-3t-BHc" firstAttribute="leading" secondItem="4gc-AE-KbR" secondAttribute="leading" id="bPx-xX-ROK"/>
<constraint firstAttribute="bottom" secondItem="C5N-3t-BHc" secondAttribute="bottom" id="cFe-Zn-SIN"/>
</constraints>
</view>
<connections>
<outlet property="inputCount" destination="voo-el-aSb" id="u2W-KU-hpu"/>
<outlet property="inputText" destination="ceg-vF-fQf" id="Xg7-8E-VkG"/>
<outlet property="outputCount" destination="fJh-Pa-GRs" id="E51-EA-iaR"/>
<outlet property="outputText" destination="Z0K-AM-ybS" id="uJz-Fy-lhw"/>
<outlet property="outputView" destination="YhA-gX-xjC" id="wKl-if-GTQ"/>
</connections>
</viewController>
<customObject id="JEX-pt-NDP" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-887" y="2554"/>
</scene>
</scenes>
<resources>
<image name="NSAddTemplate" width="18" height="17"/>
<namedColor name="AccentColor">
<color red="0.0" green="0.46000000000000002" blue="0.89000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

View File

@ -0,0 +1,421 @@
//
// IAPExcelParser.swift
// AppleParty
//
// Created by HTC on 2022/11/10.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Foundation
import CoreXLSX
///
enum IAPType: String {
case CONSUMABLE = "CONSUMABLE"
case NON_CONSUMABLE = "NON_CONSUMABLE"
case NON_RENEWING_SUBSCRIPTION = "NON_RENEWING_SUBSCRIPTION"
case AUTO_RENEWABLE = "auto-renewable"
case UNKNOW = "unknown"
static func type(name: String) -> IAPType {
switch name {
case "消耗型":
return .CONSUMABLE
case "非消耗型":
return .NON_CONSUMABLE
case "非续期订阅":
return .NON_RENEWING_SUBSCRIPTION
case "自动续期订阅":
return .AUTO_RENEWABLE
default:
return .UNKNOW
}
}
func CNValue() -> String {
switch self {
case .CONSUMABLE:
return "消耗型"
case .NON_CONSUMABLE:
return "非消耗型"
case .NON_RENEWING_SUBSCRIPTION:
return "非续期订阅"
case .AUTO_RENEWABLE:
return "自动续期订阅"
default:
return "未知"
}
}
}
//
struct IAPLocalization {
var name: String = ""
var description: String = ""
var locale: String = ""
}
//
struct IAPSubscriptions {
var groupLevel: Int = 1
var subscriptionPeriod: String = "ONE_MONTH" //ONE_WEEK, ONE_MONTH, TWO_MONTHS, THREE_MONTHS, SIX_MONTHS, ONE_YEAR
}
///
struct IAPProduct {
var name: String = ""
var groupName: String = ""
var productId: String = ""
var reviewNote: String = ""
var reviewScreenshot: String = ""
var familySharable: Bool = false
var inAppPurchaseType: IAPType = .UNKNOW //# CONSUMABLENON_CONSUMABLENON_RENEWING_SUBSCRIPTION
var localizations: [IAPLocalization] = []
//
var subscriptions: IAPSubscriptions?
//
var priceSchedules: IAPPriceSchedules?
//
var territories: IAPTerritories = IAPTerritories()
}
///
struct IAPPriceSchedules {
var productId: String = ""
var baseTerritory: String = ""
var baseCustomerPrice: String = ""
var manualPrices: [IAPPricePoint] = []
}
///
struct IAPPricePoint {
var territory: String
var customerPrice: String
}
///
struct IAPTerritories {
var productId: String = ""
/// ()
var availableInAllTerritories: Bool = true
/// /
var availableInNewTerritories: Bool = true
var territories: [IAPTerritory]?
}
struct IAPTerritory {
var id: String
}
struct IAPExcelParser {
static func parser(_ filePath: URL) -> [IAPProduct] {
guard let file = XLSXFile(filepath: filePath.path) else {
fatalError("XLSX file at \(filePath.path) is corrupted or does not exist")
}
//
let priceSchedules = parserPricePoints(file)
//
let territories = parserTerritories(file)
var result: [IAPProduct] = []
for wbk in try! file.parseWorkbooks() {
for (name, path) in try! file.parseWorksheetPathsAndNames(workbook: wbk) {
if let worksheetName = name {
print("This worksheet has a name: \(worksheetName)")
}
if name != "AppleParty" {
continue
}
guard let worksheet = try? file.parseWorksheet(at: path),
let sharedStrings = try! file.parseSharedStrings() else {
print("This worksheet/sharedStrings is null")
return result
}
var index = 0
var columnTitles = [String]()
var columnIndexs = [String]()
for row in worksheet.data?.rows ?? [] {
var columnValues = [String: String]()
index += 1
for cell in row.cells {
let key = cell.reference.column.value
var columnStrings = cell.stringValue(sharedStrings) ?? ""
//
var richStr = ""
let richColumnCString = cell.richStringValue(sharedStrings)
for richChar in richColumnCString {
richStr += string(from: richChar.text)
}
columnStrings += richStr
//
if index == 1 {
columnTitles.append(columnStrings)
columnIndexs.append(key)
} else {
columnValues[key] = columnStrings
}
}
if index == 1 {
print(columnTitles)
print(columnIndexs)
continue
}
// Product ID () () zh-Hans zh-Hans ja ja ko ko
var iap = IAPProduct()
iap.groupName = columnValues["A"] ?? ""
iap.productId = columnValues["B"] ?? ""
iap.name = columnValues["C"] ?? ""
let productType = columnValues["D"] ?? ""
iap.inAppPurchaseType = IAPType.type(name: productType)
//
if iap.inAppPurchaseType == .AUTO_RENEWABLE {
let period = columnValues["E"] ?? ""
iap.subscriptions = IAPSubscriptions()
iap.subscriptions?.subscriptionPeriod = period
}
iap.reviewScreenshot = columnValues["F"] ?? ""
iap.reviewNote = columnValues["G"] ?? ""
//
if iap.productId.isEmpty, iap.name.isEmpty {
continue
}
// ID 线_. 2 ~ 100
if iap.productId.count < 2 || iap.productId.count > 100 {
NSAlert.show("Product ID 长度为2~100 字符!")
}
//
if iap.name.count < 2 || iap.name.count > 64 {
NSAlert.show("\(iap.productId):“参考名字”长度超过 2~64 字符!")
}
//
let schedules = priceSchedules.filter({ $0.productId == iap.productId })
if let schedule = schedules.first {
iap.priceSchedules = schedule
}
//
let territorys = territories.filter({ $0.productId == iap.productId })
if let territory = territorys.first {
iap.territories = territory
}
//
var localizations: [IAPLocalization] = []
// 7
let columeMax = columnIndexs.count
let columeEndIndex = columnIndexs.count - 1
for idx in stride(from: 7, to: columeEndIndex, by: 2){
if idx + 1 <= columeMax {
let locale = columnTitles[idx]
let key1 = columnIndexs[idx]
let key2 = columnIndexs[idx+1]
let name = columnValues[key1] ?? ""
let description = columnValues[key2] ?? ""
if !name.isEmpty && !description.isEmpty {
var localization = IAPLocalization()
localization.locale = locale
localization.name = name
localization.description = description
localizations.append(localization)
}
}
}
iap.localizations = localizations
result.append(iap)
}
}
}
return result
}
///
fileprivate static func handleRowContents(_ row: Row, _ sharedStrings: SharedStrings, _ index: Int, _ columnIndexs: inout [String], _ columnValues: inout [String : String]) {
for cell in row.cells {
let key = cell.reference.column.value
var columnStrings = cell.stringValue(sharedStrings) ?? ""
//
var richStr = ""
let richColumnCString = cell.richStringValue(sharedStrings)
for richChar in richColumnCString {
richStr += string(from: richChar.text)
}
columnStrings += richStr
//
if index == 1 {
columnIndexs.append(key)
} else {
columnValues[key] = columnStrings
}
}
}
///
static func parserPricePoints(_ file: XLSXFile) -> [IAPPriceSchedules] {
var result: [IAPPriceSchedules] = []
for wbk in try! file.parseWorkbooks() {
for (name, path) in try! file.parseWorksheetPathsAndNames(workbook: wbk) {
if let worksheetName = name {
print("This worksheet has a name: \(worksheetName)")
}
if name != "PricePoints" {
continue
}
guard let worksheet = try? file.parseWorksheet(at: path),
let sharedStrings = try! file.parseSharedStrings() else {
print("This worksheet/sharedStrings is null")
return result
}
var index = 0
var columnIndexs = [String]()
for row in worksheet.data?.rows ?? [] {
var columnValues = [String: String]()
index += 1
handleRowContents(row, sharedStrings, index, &columnIndexs, &columnValues)
//
if index == 1 {
print(columnIndexs)
continue
}
// Product ID () 1 1 2 2
let productId = columnValues["A"] ?? ""
let baseTerritory = columnValues["B"] ?? ""
let baseCustomerPrice = (columnValues["C"] ?? "").twoDecimalPrice()
//
if productId.isEmpty, baseTerritory.isEmpty, baseCustomerPrice.isEmpty {
continue
}
// ID 线_. 2 ~ 100
if productId.count < 2 || productId.count > 100 {
NSAlert.show("PricePoints Product ID 长度为2~100 字符!")
}
//
var schedule = IAPPriceSchedules(productId: productId, baseTerritory: baseTerritory, baseCustomerPrice: baseCustomerPrice)
//
var manualPrices: [IAPPricePoint] = []
// 3
let columeMax = columnValues.count
let columeEndIndex = columnValues.count - 1
for idx in stride(from: 3, to: columeEndIndex, by: 2) {
if idx + 1 <= columeMax, columnIndexs.count > idx+1 {
let key1 = columnIndexs[idx]
let key2 = columnIndexs[idx+1]
let name = columnValues[key1] ?? ""
let price = (columnValues[key2] ?? "").twoDecimalPrice()
if !name.isEmpty && !price.isEmpty {
let pricePoint = IAPPricePoint(territory: name, customerPrice: price)
manualPrices.append(pricePoint)
}
}
}
schedule.manualPrices = manualPrices
result.append(schedule)
}
}
}
return result
}
///
static func parserTerritories(_ file: XLSXFile) -> [IAPTerritories] {
var result: [IAPTerritories] = []
for wbk in try! file.parseWorkbooks() {
for (name, path) in try! file.parseWorksheetPathsAndNames(workbook: wbk) {
if let worksheetName = name {
print("This worksheet has a name: \(worksheetName)")
}
if name != "Territories" {
continue
}
guard let worksheet = try? file.parseWorksheet(at: path),
let sharedStrings = try! file.parseSharedStrings() else {
print("This worksheet/sharedStrings is null")
return result
}
var index = 0
var columnIndexs = [String]()
for row in worksheet.data?.rows ?? [] {
var columnValues = [String: String]()
index += 1
handleRowContents(row, sharedStrings, index, &columnIndexs, &columnValues)
//
if index == 1 {
print(columnIndexs)
continue
}
// Product ID /(10) /(10) 1 2 ...
let productId = columnValues["A"] ?? ""
let availableInAllTerritories = columnValues["B"] ?? ""
let availableInNewTerritories = columnValues["C"] ?? ""
let isInAll = availableInAllTerritories == "1" ? true : false
let isInNew = availableInNewTerritories == "1" ? true : false
//
if productId.isEmpty {
continue
}
// ID 线_. 2 ~ 100
if productId.count < 2 || productId.count > 100 {
NSAlert.show("Territories Product ID 长度为2~100 字符!")
}
//
var territory = IAPTerritories(productId: productId, availableInAllTerritories: isInAll, availableInNewTerritories: isInNew)
//
if isInAll {
territory.availableInNewTerritories = true
result.append(territory)
continue
}
//
var territories: [IAPTerritory] = []
// 3
let columeMax = columnValues.count
for idx in stride(from: 3, to: columeMax, by: 1) {
let key = columnIndexs[idx]
let name = columnValues[key] ?? ""
if !name.isEmpty {
let pricePoint = IAPTerritory(id: name)
territories.append(pricePoint)
}
}
territory.territories = territories
result.append(territory)
}
}
}
return result
}
}

View File

@ -0,0 +1,341 @@
//
// ITCResponseModel.swift
// AppleParty
//
// Created by on 2021/5/13.
//
// HTTP
import Foundation
import Cocoa
/* consumable
non-consumable
auto-renewable
subscription
*/
//
enum InAppPurchaseType: String {
case CONSUMABLE = "consumable"
case NON_CONSUMABLE = "non-consumable"
case AUTO_RENEWABLE = "auto-renewable"
case FREE_SUBSCRIPTION = "free-subscription"
case SUBSCRIPTION = "subscription"
case UNKNOW = "unknown"
func CNValue() -> String {
switch self {
case .CONSUMABLE:
return "消耗型"
case .NON_CONSUMABLE:
return "非消耗型"
case .SUBSCRIPTION:
return "非续订型"
case .AUTO_RENEWABLE:
return "自动续订型"
case .FREE_SUBSCRIPTION:
return "免费订阅型"
default:
return "未知"
}
}
}
/*
"ITC.addons.type.consumable": "消耗型项目",
"ITC.addons.type.freeSubscription": "免费订阅",
"ITC.addons.type.nonConsumable": "非消耗型项目",
"ITC.addons.type.recurring": "自动续期订阅",
"ITC.addons.type.subscription": "非续期订阅",
*/
//
enum ITCAddOnType: String {
case consumable = "ITC.addons.type.consumable"
case subscription = "ITC.addons.type.subscription"
case free_subscription = "ITC.addons.type.freeSubscription"
case non_consumable = "ITC.addons.type.nonConsumable"
case auto_renewable = "ITC.addons.type.recurring"
case unknown
func CNValue() -> String {
switch self {
case .consumable:
return "消耗型项目"
case .non_consumable:
return "非消耗型项目"
case .subscription:
return "非续期订阅"
case .auto_renewable:
return "自动续期订阅"
case .free_subscription:
return "免费订阅"
default:
return "未知"
}
}
}
/*
"approved": "已批准",
"created": "已创建",
"deleted": "已删除",
"deletePending": "正在删除",
"developerActionNeeded": "需要开发人员操作",
"developerRemovedFromSale": "被开发人员下架",
"developerSignedOff": "开发人员签名",
"inReview": "正在审核",
"missingMetadata": "元数据丢失",
"pendingBinaryApproval": "正在审核",
"pendingDeveloperRelease": "等待开发人员发布",
"pendingScreenshot": "正在等待屏幕快照",
"prepareForSubmission": "准备提交",
"processingContentUpload": "正在处理",
"readyForSale": "已批准",
"readyToSubmit": "准备提交",
"rejected": "被拒绝",
"removedFromSale": "被下架",
"replaced": "被替换",
"waitingForContentUpload": "正在等待上传",
"waitingForReview": "正在等待审核",
*/
// iap
enum InAppPurchaseState: String {
case readyForSale
case missingMetadata
case developerActionNeeded
case developerRemovedFromSale
case readyToSubmit
case prepareForSubmission
case waitingForReview
case waitingForContentUpload
case inReview
case pendingBinaryApproval
case pendingDeveloperRelease
case rejected
case removedFromSale
case approved
case created
case deleted
case deletePending
case developerSignedOff
case pendingScreenshot
case processingContentUpload
case replaced
case unknown
var statusValue: (String, NSColor) {
switch self {
case .readyForSale:
return ("可供销售", NSColor(calibratedRed: 0.23, green: 0.64, blue: 0.40, alpha: 1.00))
case .missingMetadata:
return ("元数据丢失", NSColor(calibratedRed: 0.97, green: 0.50, blue: 0.19, alpha: 1.00))
case .developerActionNeeded:
return ("需要开发人员操作", NSColor(calibratedRed: 0.95, green: 0.00, blue: 0.13, alpha: 1.00))
case .developerRemovedFromSale:
return ("被开发人员下架", NSColor(calibratedRed: 0.95, green: 0.00, blue: 0.13, alpha: 1.00))
case .readyToSubmit, .prepareForSubmission:
return ("准备提交", NSColor(calibratedRed: 0.23, green: 0.64, blue: 0.40, alpha: 1.00))
case .waitingForReview:
return ("正在等待审核", NSColor(calibratedRed: 0.900, green:0.658, blue:0.625, alpha:1.000))
case .waitingForContentUpload:
return ("正在等待上传", NSColor(calibratedRed: 0.23, green: 0.64, blue: 0.40, alpha: 1.00))
case .inReview, .pendingBinaryApproval:
return ("正在审核", NSColor(calibratedRed: 0.999, green:0.775, blue:0.031, alpha:1.000))
case .pendingDeveloperRelease:
return ("等待开发人员发布", NSColor(calibratedRed: 0.23, green: 0.64, blue: 0.40, alpha: 1.00))
case .rejected:
return ("被拒绝", NSColor(calibratedRed:0.548, green:0.145, blue:0.781, alpha:1.000))
case .removedFromSale:
return ("被下架", NSColor(calibratedRed:0.906, green:0.148, blue:0.155, alpha:1.000))
default:
return (self.rawValue, NSColor.secondaryLabelColor)
}
}
}
// MARK: - -
struct Product {
var type: String = ""
var id: String = ""
var referenceName: String = ""
var productId: String = ""
var inAppPurchaseType: InAppPurchaseType = .UNKNOW
var state: InAppPurchaseState = .unknown
}
struct ProductList {
var products: [Product]
init(body: [String: Any]) {
products = [Product]()
let data = dictionaryArray(body["data"])
for temp in data {
var product = Product()
product.type = string(from: temp["type"])
product.id = string(from: temp["id"])
let attributes = dictionary(temp["attributes"])
product.referenceName = string(from: attributes["referenceName"])
product.productId = string(from: attributes["productId"])
product.inAppPurchaseType = InAppPurchaseType(rawValue: string(from: attributes["inAppPurchaseType"])) ?? .UNKNOW
product.state = InAppPurchaseState(rawValue: string(from: attributes["state"])) ?? .unknown
products.append(product)
}
}
}
// MARK: - -
struct IAPList {
struct IAP {
struct Version {
var screenshotUrl: String = ""
var itunesConnectStatus: String = ""
var issuesCount: Int = 0
var canSubmit: Bool = false
init(dict: [String: Any]) {
screenshotUrl = string(from: dict["screenshotUrl"])
itunesConnectStatus = string(from: dict["itunesConnectStatus"])
issuesCount = int(from: dict["issuesCount"]) ?? 0
canSubmit = bool(from: dict["canSubmit"])
}
}
var familyReferenceName: String = ""
var durationDays: Int = 0
var numberOfCodes: Int = 0
var maximumNumberOfCodes: Int = 0
var appMaximumNumberOfCodes: Int = 0
var isEditable: Bool = false
var isRequired: Bool = false
var canDeleteAddOn: Bool = false
var errorKeys: String = ""
var itcsubmitNextVersion: Bool = false
var isEmptyValue: Bool = false
var adamId: String = "" // appleid
var referenceName: String = "" //
var vendorId: String = "" // id
var addOnType: ITCAddOnType = .unknown //
var versions: [Version] = []
var purpleSoftwareAdamIds: [String] = []
var lastModifiedDate: String = ""
var isNewsSubscription: Bool = false
var iTunesConnectStatus: InAppPurchaseState = .unknown
// detail
var status: String = "" //
var familySharable: Bool = false //
var availableInAllTerritories: Bool = true //
var reviewNote: String = "" //
var reviewScreenshot: String = "" //
var localizations: [IAPLocalization] = [] //
mutating func updateDetail(body: [String: Any]) {
let data = dictionary(body["data"])
status = string(from: dictionary(dictionary(data)["attributes"])["state"])
reviewNote = string(from: dictionary(dictionary(data)["attributes"])["reviewNote"])
familySharable = bool(from: dictionary(dictionary(data)["attributes"])["familySharable"])
let included = dictionaryArray(body["included"])
for include in included {
guard let type = include["type"] as? String else {
return
}
if type == "inAppPurchaseLocalizations" {
let attr = dictionary(include["attributes"])
let name = string(from: attr["name"])
let locale = string(from: attr["locale"])
let description = string(from: attr["description"])
localizations.append(IAPLocalization(name: name, description: description, locale: locale))
}
if type == "inAppPurchaseAppStoreReviewScreenshots" {
let attr = dictionary(include["attributes"])
let asset = dictionary(attr["imageAsset"])
let width = string(from: asset["width"])
let height = string(from: asset["height"])
let templateUrl = string(from: asset["templateUrl"])
// {w}x{h}bb.{f}
let reviewScreenshot = templateUrl
.replacingOccurrences(of: "{w}", with: width)
.replacingOccurrences(of: "{h}", with: height)
.replacingOccurrences(of: "{f}", with: "png")
self.reviewScreenshot = reviewScreenshot
}
}
}
// price
var priceTier: String = "" //
mutating func updatePrices(body: [String: Any]) {
let included = dictionaryArray(body["included"])
for include in included {
let attr = dictionary(include["attributes"])
self.priceTier = string(from: attr["priceTier"])
}
}
///
var isSelected = false //
var curid = 0 //
var app: App
init(app: App) {
self.app = app
}
}
var iapList: [IAP]
var app: App
init(body: [String: Any], app: App) {
iapList = [IAP]()
let data = dictionaryArray(body["data"])
for i in 0..<data.count {
let temp = data[i]
var iap = IAP(app: app)
iap.curid = i+1
iap.familyReferenceName = string(from: temp["familyReferenceName"])
iap.durationDays = int(from: temp["durationDays"]) ?? 0
iap.numberOfCodes = int(from: temp["numberOfCodes"]) ?? 0
iap.maximumNumberOfCodes = int(from: temp["maximumNumberOfCodes"]) ?? 0
iap.appMaximumNumberOfCodes = int(from: temp["appMaximumNumberOfCodes"]) ?? 0
iap.isEditable = bool(from: temp["isEditable"])
iap.isRequired = bool(from: temp["isRequired"])
iap.canDeleteAddOn = bool(from: temp["canDeleteAddOn"])
iap.errorKeys = string(from: temp["errorKeys"])
iap.itcsubmitNextVersion = bool(from: temp["itcsubmitNextVersion"])
iap.isEmptyValue = bool(from: temp["isEmptyValue"])
iap.adamId = string(from: temp["adamId"])
iap.referenceName = string(from: temp["referenceName"])
iap.vendorId = string(from: temp["vendorId"])
iap.addOnType = ITCAddOnType(rawValue: string(from: temp["addOnType"])) ?? .unknown
let verArr = dictionaryArray(temp["versions"])
for ver in verArr {
let newVer = IAP.Version(dict: ver)
iap.versions.append(newVer)
}
iap.purpleSoftwareAdamIds = stringArray(temp["purpleSoftwareAdamIds"])
iap.lastModifiedDate = string(from: temp["lastModifiedDate"])
iap.isNewsSubscription = bool(from: temp["isNewsSubscription"])
iap.iTunesConnectStatus = InAppPurchaseState(rawValue: string(from: temp["iTunesConnectStatus"])) ?? .unknown
iapList.append(iap)
}
//
iapList = iapList.sorted(by: { (iap1, iap2) -> Bool in
let nonDigits = CharacterSet.decimalDigits.inverted
let numStr1 = iap1.vendorId.trimmingCharacters(in: nonDigits)
let numStr2 = iap2.vendorId.trimmingCharacters(in: nonDigits)
return int(from: numStr1) ?? 0 < int(from: numStr2) ?? 0
})
self.app = app
}
}

View File

@ -0,0 +1,364 @@
//
// XMLModel.swift
// AppleParty
//
// Created by on 2021/5/26.
//
import Foundation
import SWXMLHash
struct In_App_Purchase {
var product_id = "" // id
var reference_name = "" //
var type: InAppPurchaseType = .UNKNOW //
var wholesale_price_tier = 0 //
var title = "" // title
var description = "" //
var file_name = "" //
var size = "" //
var checksum = "" // md5
var review_notes = "" //
var lang = "zh-Hans" //
var inputPrice = "" //
}
struct Screen_Shot {
var file_name = "" //
var size = "" //
var checksum = "" // md5
var position = "0" //
var preview_time = "00:00:05:00" //
}
struct XMLModel {
var provider = UserCenter.shared.developerTeamId //
var team_id = UserCenter.shared.developerTeamId // teamid
var vendor_id = "" // sku id
//
var iaps: [In_App_Purchase] = []
//
var app_locale = "zh-Hans" //
var app_platform = "ios" // ios osx
var app_version = "" //
var app_title = "" //
var shots: [String: [Screen_Shot]] = [:]
var videos: [String: [Screen_Shot]] = [:]
// ipa
var apple_id = "" // apple id
var archive_type = "bundle" //
var ipa_name = "ipa.ipa" //
var ipa_size = ""
var ipa_md5 = ""
// [fileName: fileURL]
var filePaths: [String: String] = [:]
func createIAP(directoryPath: String) {
//
let root = GDataXMLNode.element(withName: "package")
// package
let version = GDataXMLNode.attribute(withName: "version", stringValue: "software5.11") as? GDataXMLNode
let xmlns = GDataXMLNode.attribute(withName: "xmlns", stringValue: "http://apple.com/itunes/importer") as? GDataXMLNode
root?.addAttribute(version)
root?.addAttribute(xmlns)
// provider\team_id
let pro = GDataXMLNode.element(withName: "provider", stringValue: provider)
let tid = GDataXMLNode.element(withName: "team_id", stringValue: team_id)
root?.addChild(pro)
root?.addChild(tid)
// software
// vendor_id
let software = GDataXMLNode.element(withName: "software")
let vid = GDataXMLNode.element(withName: "vendor_id", stringValue: vendor_id)
software?.addChild(vid)
// software_metadata/in_app_purchases
let software_metadata = GDataXMLNode.element(withName: "software_metadata")
let in_app_purchases = GDataXMLNode.element(withName: "in_app_purchases")
// in_app_purchase array
for iap in iaps {
let in_app_purchase = GDataXMLNode.element(withName: "in_app_purchase")
// product_id/reference_name/type
let product_id = GDataXMLNode.element(withName: "product_id", stringValue: iap.product_id)
let reference_name = GDataXMLNode.element(withName: "reference_name", stringValue: iap.reference_name)
let type = GDataXMLNode.element(withName: "type", stringValue: iap.type.rawValue)
// products
let products = GDataXMLNode.element(withName: "products")
let product = GDataXMLNode.element(withName: "product")
let cleared_for_sale = GDataXMLNode.element(withName: "cleared_for_sale", stringValue: "true")
let wholesale_price_tier = GDataXMLNode.element(withName: "wholesale_price_tier", stringValue: String(iap.wholesale_price_tier))
product?.addChild(cleared_for_sale)
product?.addChild(wholesale_price_tier)
products?.addChild(product)
// locales
let locales = GDataXMLNode.element(withName: "locales")
let locale = GDataXMLNode.element(withName: "locale")
let name = GDataXMLNode.attribute(withName: "name", stringValue: iap.lang) as? GDataXMLNode
locale?.addAttribute(name)
let title = GDataXMLNode.element(withName: "title", stringValue: iap.title)
let description = GDataXMLNode.element(withName: "description", stringValue: iap.description)
locale?.addChild(title)
locale?.addChild(description)
locales?.addChild(locale)
// review_screenshot
let review_screenshot = GDataXMLNode.element(withName: "review_screenshot")
let file_name = GDataXMLNode.element(withName: "file_name", stringValue: iap.file_name)
let size = GDataXMLNode.element(withName: "size", stringValue: iap.size)
let checksum = GDataXMLNode.element(withName: "checksum", stringValue: iap.checksum)
let checksum_type = GDataXMLNode.attribute(withName: "type", stringValue: "md5") as? GDataXMLNode
checksum?.addAttribute(checksum_type)
review_screenshot?.addChild(file_name)
review_screenshot?.addChild(size)
review_screenshot?.addChild(checksum)
// review_notes
let review_notes = GDataXMLNode.element(withName: "review_notes", stringValue: iap.review_notes)
// in_app_purchase
in_app_purchase?.addChild(product_id)
in_app_purchase?.addChild(reference_name)
in_app_purchase?.addChild(type)
in_app_purchase?.addChild(products)
in_app_purchase?.addChild(locales)
in_app_purchase?.addChild(review_screenshot)
in_app_purchase?.addChild(review_notes)
in_app_purchases?.addChild(in_app_purchase)
}
software_metadata?.addChild(in_app_purchases)
software?.addChild(software_metadata)
root?.addChild(software)
// xml
let xmlDoc = GDataXMLDocument(rootElement: root)
let data = xmlDoc?.xmlData()
// let xmlString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
//
//
if !FileManager.default.fileExists(atPath: directoryPath) {
do {
try FileManager.default.createDirectory(atPath: directoryPath, withIntermediateDirectories: true, attributes: nil)
} catch {
print(error.localizedDescription);
}
}
//
let filePath = directoryPath + "/metadata.xml"
debugPrint(filePath)
if !FileManager.default.fileExists(atPath: filePath) {
FileManager.default.createFile(atPath: filePath, contents: data, attributes: nil)
}else {
try? FileManager.default.removeItem(atPath: filePath)
FileManager.default.createFile(atPath: filePath, contents: data, attributes: nil)
}
for name in filePaths.keys {
let path = filePaths[name] ?? ""
try? FileManager.default.copyItem(atPath: path, toPath: directoryPath + "/" + name)
}
}
func createShots(directoryPath: String) {
//
let root = GDataXMLNode.element(withName: "package")
// package
let version = GDataXMLNode.attribute(withName: "version", stringValue: "software5.11") as? GDataXMLNode
let xmlns = GDataXMLNode.attribute(withName: "xmlns", stringValue: "http://apple.com/itunes/importer") as? GDataXMLNode
root?.addAttribute(version)
root?.addAttribute(xmlns)
// provider\team_id
let pro = GDataXMLNode.element(withName: "provider", stringValue: provider)
let tid = GDataXMLNode.element(withName: "team_id", stringValue: team_id)
root?.addChild(pro)
root?.addChild(tid)
// software
// vendor_id
let software = GDataXMLNode.element(withName: "software")
let vid = GDataXMLNode.element(withName: "vendor_id", stringValue: vendor_id)
software?.addChild(vid)
// software_metadata/in_app_purchases
let software_metadata = GDataXMLNode.element(withName: "software_metadata")
let app_platform = GDataXMLNode.attribute(withName: "app_platform", stringValue: app_platform) as? GDataXMLNode
software_metadata?.addAttribute(app_platform)
// versions
let s_versions = GDataXMLNode.element(withName: "versions")
let s_version = GDataXMLNode.element(withName: "version")
let app_version = GDataXMLNode.attribute(withName: "string", stringValue: app_version) as? GDataXMLNode
s_version?.addAttribute(app_version)
// locales
let locales = GDataXMLNode.element(withName: "locales")
let locale = GDataXMLNode.element(withName: "locale")
let locale_name = GDataXMLNode.attribute(withName: "name", stringValue: app_locale) as? GDataXMLNode
locale?.addAttribute(locale_name)
let locale_title = GDataXMLNode.element(withName: "title", stringValue: app_title)
locale?.addChild(locale_title)
// app_previews
if videos.filter({ $0.value.count > 0 }).count > 0 {
let app_previews = GDataXMLNode.element(withName: "app_previews")
videos.forEach { (key: String, value: [Screen_Shot]) in
// app_preview
value.forEach { video in
let app_preview = GDataXMLNode.element(withName: "app_preview")
// display_target
let display_target = GDataXMLNode.attribute(withName: "display_target", stringValue: key) as? GDataXMLNode
app_preview?.addAttribute(display_target)
let position = GDataXMLNode.attribute(withName: "position", stringValue: video.position) as? GDataXMLNode
app_preview?.addAttribute(position)
// data_file
let data_file = GDataXMLNode.element(withName: "data_file")
let file_role = GDataXMLNode.attribute(withName: "role", stringValue: "source") as? GDataXMLNode
data_file?.addAttribute(file_role)
let file_size = GDataXMLNode.element(withName: "size", stringValue: video.size)
data_file?.addChild(file_size)
let file_name = GDataXMLNode.element(withName: "file_name", stringValue: video.file_name)
data_file?.addChild(file_name)
let checksum = GDataXMLNode.element(withName: "checksum", stringValue: video.checksum)
data_file?.addChild(checksum)
// data_file
let preview_image_time = GDataXMLNode.element(withName: "preview_image_time", stringValue: video.preview_time)
let preview_format = GDataXMLNode.attribute(withName: "format", stringValue: "30/1:1/nonDrop") as? GDataXMLNode
preview_image_time?.addAttribute(preview_format)
//
app_preview?.addChild(data_file)
app_preview?.addChild(preview_image_time)
app_previews?.addChild(app_preview)
}
}
locale?.addChild(app_previews)
}
if shots.filter({ $0.value.count > 0 }).count > 0 {
let app_screenshots = GDataXMLNode.element(withName: "software_screenshots")
shots.forEach { (key: String, value: [Screen_Shot]) in
// app_preview
value.forEach { video in
let app_screenshot = GDataXMLNode.element(withName: "software_screenshot")
// display_target
let display_target = GDataXMLNode.attribute(withName: "display_target", stringValue: key) as? GDataXMLNode
app_screenshot?.addAttribute(display_target)
let position = GDataXMLNode.attribute(withName: "position", stringValue: video.position) as? GDataXMLNode
app_screenshot?.addAttribute(position)
// data_file
let file_size = GDataXMLNode.element(withName: "size", stringValue: video.size)
app_screenshot?.addChild(file_size)
let file_name = GDataXMLNode.element(withName: "file_name", stringValue: video.file_name)
app_screenshot?.addChild(file_name)
let checksum = GDataXMLNode.element(withName: "checksum", stringValue: video.checksum)
app_screenshot?.addChild(checksum)
let checksum_type = GDataXMLNode.attribute(withName: "type", stringValue: "md5") as? GDataXMLNode
checksum?.addAttribute(checksum_type)
//
app_screenshots?.addChild(app_screenshot)
}
}
locale?.addChild(app_screenshots)
}
locales?.addChild(locale)
s_version?.addChild(locales)
s_versions?.addChild(s_version)
software_metadata?.addChild(s_versions)
software?.addChild(software_metadata)
root?.addChild(software)
// xml
let xmlDoc = GDataXMLDocument(rootElement: root)
let data = xmlDoc?.xmlData()
//let xmlString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
//
if !FileManager.default.fileExists(atPath: directoryPath) {
do {
try FileManager.default.createDirectory(atPath: directoryPath, withIntermediateDirectories: true, attributes: nil)
} catch {
print(error.localizedDescription);
}
}
//
let filePath = directoryPath + "/metadata.xml"
debugPrint(filePath)
if !FileManager.default.fileExists(atPath: filePath) {
FileManager.default.createFile(atPath: filePath, contents: data, attributes: nil)
}else {
try? FileManager.default.removeItem(atPath: filePath)
FileManager.default.createFile(atPath: filePath, contents: data, attributes: nil)
}
//
for name in filePaths.keys {
let path = filePaths[name] ?? ""
try? FileManager.default.copyItem(atPath: path, toPath: directoryPath + "/" + name)
}
}
func createIpaFile(directoryPath: String) {
//
let root = GDataXMLNode.element(withName: "package")
// package
let version = GDataXMLNode.attribute(withName: "version", stringValue: "software5.11") as? GDataXMLNode
let xmlns = GDataXMLNode.attribute(withName: "xmlns", stringValue: "http://apple.com/itunes/importer") as? GDataXMLNode
root?.addAttribute(version)
root?.addAttribute(xmlns)
// software_assets
let software_assets = GDataXMLNode.element(withName: "software_assets")
let apple_id = GDataXMLNode.attribute(withName: "apple_id", stringValue: apple_id) as? GDataXMLNode
let app_platform = GDataXMLNode.attribute(withName: "app_platform", stringValue: app_platform) as? GDataXMLNode
software_assets?.addAttribute(apple_id)
software_assets?.addAttribute(app_platform)
// asset
let asset = GDataXMLNode.element(withName: "asset")
let asset_type = GDataXMLNode.attribute(withName: "type", stringValue: archive_type) as? GDataXMLNode
asset?.addAttribute(asset_type)
//data_file
let data_file = GDataXMLNode.element(withName: "data_file")
let size = GDataXMLNode.element(withName: "size", stringValue: ipa_size)
let file_name = GDataXMLNode.element(withName: "file_name", stringValue: ipa_name)
let checksum = GDataXMLNode.element(withName: "checksum", stringValue: ipa_md5)
let checksum_type = GDataXMLNode.attribute(withName: "type", stringValue: "md5") as? GDataXMLNode
checksum?.addAttribute(checksum_type)
//
data_file?.addChild(size)
data_file?.addChild(file_name)
data_file?.addChild(checksum)
asset?.addChild(data_file)
software_assets?.addChild(asset)
root?.addChild(software_assets)
// xml
let xmlDoc = GDataXMLDocument(rootElement: root)
let data = xmlDoc?.xmlData()
let xmlString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print(xmlString as Any)
//
if !FileManager.default.fileExists(atPath: directoryPath) {
do {
try FileManager.default.createDirectory(atPath: directoryPath, withIntermediateDirectories: true, attributes: nil)
} catch {
print(error.localizedDescription);
}
}
//
let filePath = directoryPath + "/metadata.xml"
debugPrint(filePath)
if !FileManager.default.fileExists(atPath: filePath) {
FileManager.default.createFile(atPath: filePath, contents: data, attributes: nil)
}else {
try? FileManager.default.removeItem(atPath: filePath)
FileManager.default.createFile(atPath: filePath, contents: data, attributes: nil)
}
//
for name in filePaths.keys {
let path = filePaths[name] ?? ""
try? FileManager.default.copyItem(atPath: path, toPath: directoryPath + "/" + name)
}
}
}

View File

@ -0,0 +1,67 @@
//
// OutputExcelVC.swift
// AppleParty
//
// Created by on 2020/12/23.
//
import Cocoa
class OutputExcelVC: NSViewController, NSTextViewDelegate {
@IBOutlet var inputText: NSTextView!
@IBOutlet weak var outputView: NSScrollView!
@IBOutlet var outputText: NSTextView!
@IBOutlet weak var inputCount: NSTextField!
@IBOutlet weak var outputCount: NSTextField!
var inputs = [String]()
var outputs = [String]()
var iapList: [IAPList.IAP] = []
override func viewDidLoad() {
super.viewDidLoad()
inputText.delegate = self
}
@IBAction func close(_ sender: Any) {
dismiss(self)
}
@IBAction func commit(_ sender: Any) {
outputs.removeAll()
for i in 0..<inputs.count {
for iap in iapList {
if inputs[i] == iap.vendorId {
outputs.append(iap.adamId)
break
}
}
// id
if outputs.count != i+1 {
outputs.append("")
}
}
outputView.isHidden = false
outputText.string = outputs.joined(separator: "\n")
}
func textViewDidChangeSelection(_ notification: Notification) {
inputs = inputText.string.components(separatedBy: CharacterSet(["\r", "\n"]))
inputs = inputs.filter { $0.count > 0 }
inputCount.stringValue = String(inputs.count)+"/100"
}
func checkCount() -> Bool {
guard inputs.count == outputs.count else {
inputCount.textColor = NSColor.red
outputCount.textColor = NSColor.red
return false
}
inputCount.textColor = NSColor.lightGray
outputCount.textColor = NSColor.lightGray
return true
}
}

View File

@ -0,0 +1,18 @@
//
// ScreenShotHelpPopoverVC.swift
// AppleParty
//
// Created by HTC on 2022/2/28.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
class ScreenShotHelpPopoverVC: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,526 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Window Controller-->
<scene sceneID="ere-Be-a90">
<objects>
<windowController storyboardIdentifier="ScreenShotUploadVC" id="3W3-GI-a0f" sceneMemberID="viewController">
<window key="window" title="App 预览和屏幕快照" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="BM8-gz-zq1">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="425" y="462" width="651" height="582"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<value key="minSize" type="size" width="651" height="582"/>
<view key="contentView" id="uHV-KU-1f4">
<rect key="frame" x="0.0" y="0.0" width="651" height="582"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<connections>
<outlet property="delegate" destination="3W3-GI-a0f" id="dJN-a3-pmC"/>
</connections>
</window>
<connections>
<segue destination="0Dc-hX-r4A" kind="relationship" relationship="window.shadowedContentViewController" id="gH6-kg-BmL"/>
</connections>
</windowController>
<customObject id="Iam-wp-ls0" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-206" y="-129"/>
</scene>
<!--Screen Shot UploadVC-->
<scene sceneID="sHf-r7-vGK">
<objects>
<viewController id="0Dc-hX-r4A" customClass="ScreenShotUploadVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" identifier="fileName" misplaced="YES" id="85F-uq-hwS">
<rect key="frame" x="0.0" y="0.0" width="878" height="549"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Mxb-rl-K9i">
<rect key="frame" x="18" y="470" width="294" height="27"/>
<constraints>
<constraint firstAttribute="height" constant="27" id="Lv9-ug-a94"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="App 预览和屏幕快照批量上传" id="uj6-Ck-fci">
<font key="font" textStyle="title1" name=".SFNS-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3MQ-6H-3p4">
<rect key="frame" x="28" y="439" width="72" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="MSZ-z2-cOa"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="App Name:" id="IWG-xW-Lzs">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZlO-v7-4q0">
<rect key="frame" x="28" y="411" width="62" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="Bss-QQ-gWA"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="App SKU:" id="Esn-Cx-dqY">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="20s-B0-gsg">
<rect key="frame" x="28" y="382" width="57" height="17"/>
<constraints>
<constraint firstAttribute="height" constant="17" id="ekU-ib-I7r"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="Team ID:" id="28f-6H-Z8f">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="UtW-mq-cag">
<rect key="frame" x="28" y="343" width="82" height="27"/>
<constraints>
<constraint firstAttribute="height" constant="27" id="3eX-Ed-aLC"/>
<constraint firstAttribute="width" constant="78" id="8Sb-z0-LeY"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="本地化语言:" id="i7o-yB-2hn">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KuX-1c-GZT">
<rect key="frame" x="694" y="366" width="142" height="48"/>
<constraints>
<constraint firstAttribute="width" constant="128" id="Rmb-yW-0Aa"/>
<constraint firstAttribute="height" constant="36" id="eTA-Jd-hvB"/>
</constraints>
<buttonCell key="cell" type="push" title="提交上传" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="hrB-5Z-Y0F">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
<connections>
<action selector="clickedUploadButton:" target="0Dc-hX-r4A" id="u4G-Sv-SxO"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Jvd-lw-qhr" userLabel="Line Label">
<rect key="frame" x="20" y="327" width="829" height="1"/>
<constraints>
<constraint firstAttribute="height" constant="0.5" id="aJu-0a-X2Z"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" borderStyle="bezel" drawsBackground="YES" id="Xa3-zH-NIv">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="spK-09-Iv9">
<rect key="frame" x="28" y="251" width="167" height="19"/>
<constraints>
<constraint firstAttribute="height" constant="19" id="tEG-yM-eor"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="iPhone 5.5 英寸显示屏" id="wxW-1V-RfC">
<font key="font" metaFont="system" size="16"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dep-lv-PPg">
<rect key="frame" x="206" y="251" width="206" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="a0I-Ni-9u6"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="0/3 个 App 预览 | 0/10 张屏幕快照" id="5fc-UX-tt6">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" red="0.90980392160000001" green="0.4431372549" blue="0.28235294119999998" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<scrollView misplaced="YES" autohidesScrollers="YES" horizontalLineScroll="46" horizontalPageScroll="10" verticalLineScroll="46" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aVs-J6-lEK">
<rect key="frame" x="26" y="15" width="817" height="228"/>
<clipView key="contentView" id="p4Q-t6-wzE">
<rect key="frame" x="1" y="1" width="815" height="226"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" autosaveColumns="NO" rowHeight="46" rowSizeStyle="automatic" headerView="Ku2-Vf-JWY" viewBased="YES" id="p1m-uP-OeG">
<rect key="frame" x="0.0" y="0.0" width="815" height="198"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="17" height="0.0"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<tableViewGridLines key="gridStyleMask" horizontal="YES"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="review" width="103" minWidth="10" maxWidth="3.4028234663852886e+38" id="fHy-Ho-wZH">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="预览">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="ha0-0M-Ffe">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView id="KdO-NE-A5Z">
<rect key="frame" x="8" y="0.0" width="101" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="NAG-NQ-ohy">
<rect key="frame" x="3" y="5" width="95" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Aph-Kz-rKF">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="NAG-NQ-ohy" firstAttribute="leading" secondItem="KdO-NE-A5Z" secondAttribute="leading" constant="5" id="Qzd-pY-YNu"/>
<constraint firstAttribute="trailing" secondItem="NAG-NQ-ohy" secondAttribute="trailing" constant="5" id="Z7l-cP-soS"/>
<constraint firstAttribute="bottom" secondItem="NAG-NQ-ohy" secondAttribute="bottom" constant="5" id="mqp-Cr-oTB"/>
</constraints>
<connections>
<outlet property="textField" destination="NAG-NQ-ohy" id="JDc-27-na2"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="fileName" width="107" minWidth="10" maxWidth="3.4028234663852886e+38" id="XoH-de-TQc">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="文件名">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="PAy-jN-amB">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView id="8kw-Oh-5eV">
<rect key="frame" x="126" y="0.0" width="107" height="46"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="ZpI-yX-cwI">
<rect key="frame" x="8" y="15" width="91" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Nuc-wp-Y8q">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="ZpI-yX-cwI" secondAttribute="bottom" constant="15" id="9CM-5H-EDv"/>
<constraint firstItem="ZpI-yX-cwI" firstAttribute="top" secondItem="8kw-Oh-5eV" secondAttribute="top" constant="15" id="XJl-uJ-BTy"/>
<constraint firstItem="ZpI-yX-cwI" firstAttribute="leading" secondItem="8kw-Oh-5eV" secondAttribute="leading" constant="10" id="og2-26-a3E"/>
<constraint firstAttribute="trailing" secondItem="ZpI-yX-cwI" secondAttribute="trailing" constant="10" id="zHv-Br-7Ec"/>
</constraints>
<connections>
<outlet property="textField" destination="ZpI-yX-cwI" id="y0B-aw-4ha"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="setting" width="401" minWidth="40" maxWidth="1000" id="3KK-up-tOz">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="设置">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="yaD-XC-Wsc">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView id="cyo-FF-nHa" customClass="ScreenShotUploadCell" customModule="AppleParty" customModuleProvider="target">
<rect key="frame" x="250" y="0.0" width="401" height="77"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="E01-10-bG2">
<rect key="frame" x="8" y="47" width="97" height="20"/>
<constraints>
<constraint firstAttribute="width" constant="93" id="SSF-7C-Gu5"/>
<constraint firstAttribute="height" constant="20" id="ejh-gc-2QL"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="显示排序位置:" id="cfR-We-ve3">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kZA-xD-IWD">
<rect key="frame" x="8" y="12" width="97" height="20"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="jsB-v9-1VC"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="默认海报帧:" id="IYp-rd-BXS">
<font key="font" size="13" name="STSongti-SC-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" tag="1" translatesAutoresizingMaskIntoConstraints="NO" id="uxr-F9-mJi">
<rect key="frame" x="108" y="46" width="273" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="5pH-Pj-fbY"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="显示顺序从1开始排序" drawsBackground="YES" id="L6y-Sf-TMN">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" tag="2" translatesAutoresizingMaskIntoConstraints="NO" id="YcP-ee-RZT">
<rect key="frame" x="108" y="6" width="273" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="gdz-We-LIn"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" title="00:00" placeholderString="时间格式SS:mm 秒:毫秒 数值为0~29间。例如16:07 、05:29最高为 30:00。不能高于视频最长时间。" drawsBackground="YES" id="dzm-Es-S5j">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="kZA-xD-IWD" firstAttribute="leading" secondItem="cyo-FF-nHa" secondAttribute="leading" constant="10" id="3B9-4Z-nZw"/>
<constraint firstItem="uxr-F9-mJi" firstAttribute="top" secondItem="E01-10-bG2" secondAttribute="top" id="7f7-M1-GGN"/>
<constraint firstItem="kZA-xD-IWD" firstAttribute="width" secondItem="E01-10-bG2" secondAttribute="width" id="DQC-79-ehS"/>
<constraint firstAttribute="trailing" secondItem="YcP-ee-RZT" secondAttribute="trailing" constant="20" id="EYn-dY-rP9"/>
<constraint firstAttribute="trailing" secondItem="uxr-F9-mJi" secondAttribute="trailing" constant="20" id="Jn6-44-sqT"/>
<constraint firstItem="YcP-ee-RZT" firstAttribute="leading" secondItem="kZA-xD-IWD" secondAttribute="trailing" constant="5" id="JtC-OE-Kwq"/>
<constraint firstItem="E01-10-bG2" firstAttribute="leading" secondItem="cyo-FF-nHa" secondAttribute="leading" constant="10" id="Q03-Qy-7Q0"/>
<constraint firstItem="YcP-ee-RZT" firstAttribute="top" secondItem="kZA-xD-IWD" secondAttribute="top" constant="5" id="ZYC-Gh-ASH"/>
<constraint firstItem="kZA-xD-IWD" firstAttribute="top" secondItem="E01-10-bG2" secondAttribute="bottom" constant="15" id="cFz-HX-QOp"/>
<constraint firstItem="E01-10-bG2" firstAttribute="top" secondItem="cyo-FF-nHa" secondAttribute="top" constant="10" id="fqy-H3-EiP"/>
<constraint firstItem="uxr-F9-mJi" firstAttribute="leading" secondItem="E01-10-bG2" secondAttribute="trailing" constant="5" id="kGR-Bo-qFu"/>
</constraints>
<connections>
<outlet property="cellTopConstraint" destination="fqy-H3-EiP" id="emc-ci-ZXC"/>
<outlet property="sortField" destination="uxr-F9-mJi" id="VMZ-ff-1bN"/>
<outlet property="videoField" destination="YcP-ee-RZT" id="gMs-cb-IYj"/>
<outlet property="videoTitleField" destination="kZA-xD-IWD" id="I0e-oS-eCl"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="operation" width="107" minWidth="10" maxWidth="3.4028234663852886e+38" id="cMb-Af-EgP">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="操作">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="Jwz-5G-aFH">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView id="hhC-jQ-V0O" customClass="ScreenShotDeleteCell" customModule="AppleParty" customModuleProvider="target">
<rect key="frame" x="668" y="0.0" width="104" height="46"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="JVG-Tc-6tD">
<rect key="frame" x="8" y="15" width="88" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" id="bHJ-JZ-aNx">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Bwd-Sl-ANk">
<rect key="frame" x="22" y="13" width="60" height="19"/>
<constraints>
<constraint firstAttribute="width" constant="60" id="HYq-OQ-l58"/>
<constraint firstAttribute="height" constant="18" id="s97-ed-fpd"/>
</constraints>
<buttonCell key="cell" type="roundRect" title="删除" bezelStyle="roundedRect" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="GjW-md-LGa">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="cellTitle"/>
</buttonCell>
<connections>
<action selector="clickedDeleteBtn:" target="hhC-jQ-V0O" id="LT8-Wz-RW8"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="Bwd-Sl-ANk" firstAttribute="centerX" secondItem="JVG-Tc-6tD" secondAttribute="centerX" id="2HE-Jv-Gsk"/>
<constraint firstItem="JVG-Tc-6tD" firstAttribute="leading" secondItem="hhC-jQ-V0O" secondAttribute="leading" constant="10" id="2Yv-Im-yn9"/>
<constraint firstAttribute="bottom" secondItem="JVG-Tc-6tD" secondAttribute="bottom" constant="15" id="Dwk-rZ-aMz"/>
<constraint firstItem="JVG-Tc-6tD" firstAttribute="top" secondItem="hhC-jQ-V0O" secondAttribute="top" constant="15" id="ITt-0w-o5i"/>
<constraint firstItem="Bwd-Sl-ANk" firstAttribute="centerY" secondItem="JVG-Tc-6tD" secondAttribute="centerY" id="M8u-c0-abZ"/>
<constraint firstAttribute="trailing" secondItem="JVG-Tc-6tD" secondAttribute="trailing" constant="10" id="WTf-Uj-Jbw"/>
</constraints>
<connections>
<outlet property="deleteBtn" destination="Bwd-Sl-ANk" id="F67-Zi-wkL"/>
<outlet property="textField" destination="JVG-Tc-6tD" id="rsf-6P-99d"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
</tableColumns>
</tableView>
</subviews>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Hkn-Y6-aEG">
<rect key="frame" x="1" y="166" width="737" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="NZV-o5-AMa">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" wantsLayer="YES" id="Ku2-Vf-JWY">
<rect key="frame" x="0.0" y="0.0" width="815" height="28"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
<button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nCH-Zn-5hw">
<rect key="frame" x="816" y="470" width="25" height="25"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="F5P-Yf-BiP"/>
<constraint firstAttribute="width" constant="20" id="YYQ-Ev-d7y"/>
</constraints>
<buttonCell key="cell" type="help" bezelStyle="helpButton" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="jfD-lj-NmL">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="clickedHelp:" target="0Dc-hX-r4A" id="tfT-3q-gD0"/>
</connections>
</button>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ehn-6X-54w">
<rect key="frame" x="115" y="341" width="97" height="30"/>
<constraints>
<constraint firstAttribute="width" constant="90" id="Fno-Uj-HPM"/>
<constraint firstAttribute="height" constant="25" id="a5m-i1-Yfm"/>
</constraints>
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" arrowPosition="arrowAtCenter" id="g9M-Bc-Q1m">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="zbe-7T-qiI"/>
</popUpButtonCell>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="at4-iG-YQR">
<rect key="frame" x="30" y="227" width="801" height="14"/>
<constraints>
<constraint firstAttribute="height" constant="14" id="cqp-a6-Vxq"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="图片1242 x 2208、2208 x 1242视频1080 x 1920、1920 x 1080" id="DWg-8k-ghA">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Nej-FK-Dzi">
<rect key="frame" x="222" y="340" width="109" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="Gzp-sC-fV6"/>
<constraint firstAttribute="width" constant="95" id="eev-KL-t4w"/>
</constraints>
<buttonCell key="cell" type="push" title="刷新App信息" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="CAs-4h-gHg">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="reloadAppData:" target="0Dc-hX-r4A" id="SCO-to-s2U"/>
</connections>
</button>
<button verticalHuggingPriority="750" tag="100" translatesAutoresizingMaskIntoConstraints="NO" id="PIn-kO-4pl">
<rect key="frame" x="338" y="340" width="114" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="IrN-eM-S0v"/>
<constraint firstAttribute="width" constant="100" id="hIt-id-j80"/>
</constraints>
<buttonCell key="cell" type="push" title="素材一键导入" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Ziv-5L-rRw">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="uploadFiles:" target="0Dc-hX-r4A" id="Zfc-Qu-PD1"/>
</connections>
</button>
<segmentedControl verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hHc-Te-K7g">
<rect key="frame" x="30" y="284" width="801" height="24"/>
<segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="selectOne" id="mBw-8Z-Jk1">
<font key="font" metaFont="system"/>
<segments>
<segment label="iPhone 5.5 英寸显示屏" width="197" selected="YES"/>
<segment label="iPhone 6.5 英寸显示屏" width="196" tag="1"/>
<segment label="iPad Pro 12.9 英寸显示屏" width="195" tag="2"/>
</segments>
</segmentedCell>
<connections>
<action selector="changeSegmentedControl:" target="0Dc-hX-r4A" id="Djb-aJ-oLH"/>
</connections>
</segmentedControl>
</subviews>
<constraints>
<constraint firstItem="spK-09-Iv9" firstAttribute="top" secondItem="hHc-Te-K7g" secondAttribute="bottom" constant="15" id="4cX-f4-l1P"/>
<constraint firstItem="ZlO-v7-4q0" firstAttribute="top" secondItem="3MQ-6H-3p4" secondAttribute="bottom" constant="12" id="5Ps-J2-pOj"/>
<constraint firstItem="aVs-J6-lEK" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="26" id="AFt-L6-42t"/>
<constraint firstItem="at4-iG-YQR" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="32" id="BOm-qs-DjO"/>
<constraint firstItem="20s-B0-gsg" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="30" id="Btu-xj-Wg4"/>
<constraint firstItem="ZlO-v7-4q0" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="30" id="C9X-av-LFu"/>
<constraint firstItem="hHc-Te-K7g" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="32" id="CuY-Lx-weE"/>
<constraint firstAttribute="trailing" secondItem="Jvd-lw-qhr" secondAttribute="trailing" constant="20" symbolic="YES" id="EyO-Yp-0aB"/>
<constraint firstItem="ehn-6X-54w" firstAttribute="top" secondItem="UtW-mq-cag" secondAttribute="top" id="Ez2-k9-idp"/>
<constraint firstItem="hHc-Te-K7g" firstAttribute="top" secondItem="Jvd-lw-qhr" secondAttribute="bottom" constant="20" id="FUG-WX-bgc"/>
<constraint firstAttribute="bottom" secondItem="aVs-J6-lEK" secondAttribute="bottom" constant="15" id="FbI-zp-JSu"/>
<constraint firstAttribute="trailing" secondItem="hHc-Te-K7g" secondAttribute="trailing" constant="40" id="Fxj-LZ-Q4z"/>
<constraint firstItem="dep-lv-PPg" firstAttribute="leading" secondItem="spK-09-Iv9" secondAttribute="trailing" constant="15" id="NtE-hZ-KKZ"/>
<constraint firstItem="Nej-FK-Dzi" firstAttribute="leading" secondItem="ehn-6X-54w" secondAttribute="trailing" constant="21" id="P8h-PO-Y8M"/>
<constraint firstItem="3MQ-6H-3p4" firstAttribute="top" secondItem="Mxb-rl-K9i" secondAttribute="bottom" constant="15" id="PRO-Jm-Ssn"/>
<constraint firstItem="20s-B0-gsg" firstAttribute="top" secondItem="ZlO-v7-4q0" secondAttribute="bottom" constant="12" id="PsX-tY-Q46"/>
<constraint firstItem="Mxb-rl-K9i" firstAttribute="top" secondItem="85F-uq-hwS" secondAttribute="top" constant="20" symbolic="YES" id="Rkh-Lj-Ck2"/>
<constraint firstAttribute="trailing" secondItem="KuX-1c-GZT" secondAttribute="trailing" constant="40" id="T5P-Xc-38I"/>
<constraint firstAttribute="trailing" secondItem="aVs-J6-lEK" secondAttribute="trailing" constant="26" id="UAF-fF-Sik"/>
<constraint firstItem="at4-iG-YQR" firstAttribute="top" secondItem="spK-09-Iv9" secondAttribute="bottom" constant="10" id="VAg-k8-pvN"/>
<constraint firstItem="PIn-kO-4pl" firstAttribute="centerY" secondItem="Nej-FK-Dzi" secondAttribute="centerY" id="Vsj-Qj-KvF"/>
<constraint firstItem="aVs-J6-lEK" firstAttribute="top" secondItem="at4-iG-YQR" secondAttribute="bottom" constant="12" id="b8f-1e-2KH"/>
<constraint firstItem="Mxb-rl-K9i" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="20" symbolic="YES" id="cmx-sk-H0h"/>
<constraint firstItem="Nej-FK-Dzi" firstAttribute="centerY" secondItem="ehn-6X-54w" secondAttribute="centerY" id="cye-es-vQJ"/>
<constraint firstItem="dep-lv-PPg" firstAttribute="bottom" secondItem="spK-09-Iv9" secondAttribute="bottom" id="fHM-de-aYs"/>
<constraint firstItem="Jvd-lw-qhr" firstAttribute="top" secondItem="UtW-mq-cag" secondAttribute="bottom" constant="15" id="iEn-ye-6b5"/>
<constraint firstItem="3MQ-6H-3p4" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="30" id="igN-zI-akK"/>
<constraint firstItem="UtW-mq-cag" firstAttribute="top" secondItem="20s-B0-gsg" secondAttribute="bottom" constant="12" id="l7B-Bm-WY9"/>
<constraint firstItem="UtW-mq-cag" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="30" id="pW0-Ng-65T"/>
<constraint firstItem="PIn-kO-4pl" firstAttribute="leading" secondItem="Nej-FK-Dzi" secondAttribute="trailing" constant="21" id="qIZ-Gq-oEG"/>
<constraint firstAttribute="trailing" secondItem="nCH-Zn-5hw" secondAttribute="trailing" constant="30" id="rtJ-UI-jgY"/>
<constraint firstAttribute="trailing" secondItem="at4-iG-YQR" secondAttribute="trailing" constant="40" id="s3i-uE-Ywh"/>
<constraint firstItem="nCH-Zn-5hw" firstAttribute="top" secondItem="85F-uq-hwS" secondAttribute="top" constant="23" id="tLD-px-sAZ"/>
<constraint firstItem="spK-09-Iv9" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="30" id="wLU-yw-vve"/>
<constraint firstItem="ehn-6X-54w" firstAttribute="leading" secondItem="UtW-mq-cag" secondAttribute="trailing" constant="10" id="wkp-Bu-SXk"/>
<constraint firstItem="Jvd-lw-qhr" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="20" symbolic="YES" id="ydi-DL-qcW"/>
<constraint firstItem="KuX-1c-GZT" firstAttribute="bottom" secondItem="UtW-mq-cag" secondAttribute="bottom" constant="-30" id="zjy-Sp-N01"/>
</constraints>
</view>
<connections>
<outlet property="appName" destination="3MQ-6H-3p4" id="Wbh-6s-F6V"/>
<outlet property="appSKU" destination="ZlO-v7-4q0" id="Jzc-ud-uAe"/>
<outlet property="appleID" destination="20s-B0-gsg" id="f9M-XH-Bu0"/>
<outlet property="localButton" destination="ehn-6X-54w" id="rmS-y1-F1h"/>
<outlet property="tableView" destination="p1m-uP-OeG" id="xmk-vt-0eI"/>
<outlet property="tips_count" destination="dep-lv-PPg" id="mR6-By-bUY"/>
<outlet property="tips_desc" destination="at4-iG-YQR" id="C2L-ly-Vhd"/>
<outlet property="tips_title" destination="spK-09-Iv9" id="h4X-0x-ewy"/>
</connections>
</viewController>
<customObject id="Ih9-u3-myc" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="714" y="-140.5"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,74 @@
//
// ScreenShotUploadCell.swift
// AppleParty
//
// Created by HTC on 2022/2/25.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
import Foundation
class ScreenShotDeleteCell: NSTableCellView {
typealias CallFunc = (_ row: Int) -> Void
var deleteCell: CallFunc?
var row: Int = 0
@IBOutlet weak var deleteBtn: NSButton!
@IBAction func clickedDeleteBtn(_ sender: NSButton) {
if let callBack = deleteCell {
callBack(row)
}
}
}
class ScreenShotUploadCell: NSTableCellView {
typealias CallBackHandler = (_ value: String, _ row: Int) -> Void
var changeSortIndex: CallBackHandler?
var changeVideoFrame: CallBackHandler?
var row: Int = 0
@IBOutlet weak var sortField: NSTextField!
@IBOutlet weak var videoField: NSTextField!
@IBOutlet weak var videoTitleField: NSTextField!
@IBOutlet weak var cellTopConstraint: NSLayoutConstraint!
override func awakeFromNib() {
super.awakeFromNib()
sortField.delegate = self
videoField.delegate = self
}
func updateData(sort: String, frame: String) {
sortField.stringValue = sort
videoField.stringValue = frame
}
func showVideoView(_ show: Bool) {
videoField.isHidden = !show
videoTitleField.isHidden = !show
cellTopConstraint.constant = show ? 10.0 : 20.0
}
}
extension ScreenShotUploadCell: NSTextFieldDelegate {
///
func controlTextDidChange(_ obj: Notification) {
let textField = obj.object as! NSTextField
let value = textField.stringValue
if textField.tag == sortField.tag, let callBack = changeSortIndex {
callBack(value, row)
}
if textField.tag == videoField.tag, let callBack = changeVideoFrame {
callBack(value, row)
}
}
}

View File

@ -0,0 +1,515 @@
//
// ScreenShotUploadVC.swift
// AppleParty
//
// Created by HTC on 2022/2/25.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
import AVFoundation
enum ScreenShotType: Int {
case iOS5_5 = 0 //iPhone 5.5
case iOS6_5 = 1 //iPhone 6.5
case iPad_Pro = 2 //iPad Pro 12.9
}
class ScreenShotUploadVC: NSViewController {
public var currentApp: App? {
didSet {
fetchAppInfo()
fetchAppVersionData()
}
}
public var appInfo: AppInfo?
@IBOutlet weak var appName: NSTextField!
@IBOutlet weak var appSKU: NSTextField!
@IBOutlet weak var appleID: NSTextField!
@IBOutlet weak var localButton: NSPopUpButton!
@IBOutlet weak var tableView: NSTableView!
@IBOutlet weak var tips_title: NSTextField!
@IBOutlet weak var tips_count: NSTextField!
@IBOutlet weak var tips_desc: NSTextField!
private var selectTag = 0
private var filesData = [[[String:String]]](repeating: [[String:String]](), count: 3)
private let imageTypes = ["jpg", "jpeg", "png", "JPG", "JPEG", "PNG"]
private let videoTypes = ["mov", "m4v", "mp4", "MOV", "M4V", "MP4"]
private let locales = ["zh-Hans", "zh-Hant", "ko", "ja"]
private let imageSizes = ["0": ["1242x2208", "2208x1242"],
"1": ["1242x2688", "2688x1242"],
"2": ["2048x2732", "2732x2048"]]
private let videoSizes = ["0": ["1080x1920", "1920x1080"],
"1": ["886x1920", "1920x886"],
"2": ["1200x1600", "1600x1200"]]
private var app_name = ""
private var app_version = ""
private var uploadModel = XMLModel()
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
func setupView() {
tableView.delegate = self
tableView.dataSource = self
tableView.selectionHighlightStyle = .none
localButton.removeAllItems()
for locale in locales {
localButton.addItem(withTitle: locale)
}
localButton.selectItem(at: 0)
let teamId = UserCenter.shared.developerTeamId
if teamId.isEmpty {
fetchAccountTeamInfo()
} else {
appleID.stringValue = "Team ID: " + UserCenter.shared.developerTeamId
}
}
@IBAction func clickedUploadButton(_ sender: NSButton) {
guard filesData.filter({ $0.count > 0 }).count > 0 else {
APHUD.hide(message: "图片或视频不能为空!", view: self.view, delayTime: 1)
return
}
guard app_name.count > 0, app_version.count > 0 else {
APHUD.hide(message: "应用名和版本获取失败!请刷新重试~", view: self.view, delayTime: 1)
return
}
guard let sp = UserCenter.shared.currentSPassword else {
let vc = APSPasswordSettingVC()
vc.updateCompletion = { [weak self] spassword in
if let sp = spassword {
self?.uploadData(sp)
}
}
presentAsSheet(vc)
return
}
uploadData(sp)
}
@IBAction func reloadAppData(_ sender: Any) {
fetchAppVersionData()
}
@IBAction func clickedHelp(_ sender: NSButton) {
let vc = ScreenShotHelpPopoverVC()
self.present(vc, asPopoverRelativeTo: sender.frame, of: self.view, preferredEdge: .maxX, behavior:.transient)
}
@IBAction func changeSegmentedControl(_ sender: NSSegmentedControl) {
selectTag = sender.selectedTag()
reloadTableView(ScreenShotType.init(rawValue: selectTag) ?? .iOS5_5)
}
@IBAction func uploadFiles(_ sender: NSButton) {
let openPanel = NSOpenPanel()
openPanel.canChooseFiles = true
openPanel.canChooseDirectories = true
openPanel.allowsMultipleSelection = true
openPanel.allowedFileTypes = imageTypes + videoTypes
openPanel.beginSheetModal(for: self.view.window!) { [self] (modalResponse) in
if modalResponse == .OK {
handleAutoImages(openPanel.urls)
}
}
}
}
// MARK: -
extension ScreenShotUploadVC {
func fetchAccountTeamInfo() {
// Team id
APClient.ascProvider.request { [weak self] result, response, error in
guard let err = error else {
self?.appleID.stringValue = "Team ID: " + UserCenter.shared.developerTeamId
return
}
APHUD.hide(message: err.localizedDescription, delayTime: 2)
}
}
func fetchAppInfo(_ replay: Int = 3) {
guard let appid = currentApp?.appId else {
APHUD.hide(message: "当前 App 的 appleid 为空!", delayTime: 1)
return
}
APClient.appInfo(appid: appid).request(showLoading: true) { [weak self] result, response, error in
if let err = error {
if replay > 0 {
self?.fetchAppInfo(replay-1)
} else {
NSAlert.show(err.localizedDescription)
}
return
}
let info = AppInfo(body: result)
self?.appInfo = info
self?.appSKU.stringValue = "App SKU: \(info.sku)"
}
}
func fetchAppVersionData(_ replay: Int = 3) {
guard let appid = currentApp?.appId else {
APHUD.hide(message: "当前 App 的 appleid 为空!", delayTime: 1)
return
}
APClient.appVersion(appid: appid).request { [weak self] data, response, error in
if let err = error {
if replay > 0 {
self?.fetchAppVersionData(replay-1)
} else {
NSAlert.show(err.localizedDescription)
}
return
}
let data = data["data"] as? [String: Any]
var dict = [String: Any]()
if let data = data, let platforms = data["platforms"] as? [[String: Any]] {
platforms.forEach { pf in
let platformString = pf["platformString"] as! String
dict[platformString] = pf
}
}
guard let ios = dict["ios"] as? [String: Any], let inFlightVersion = ios["inFlightVersion"] as? [String: Any] else {
NSAlert.show("当前 App 无待送审的版本,请检查确认!")
return
}
self?.app_version = inFlightVersion["version"] as! String
if let data = data, let titles = data["localizedMetadata"] as? [[String: Any]] {
if titles.count > 0 {
//
self?.app_name = titles[0]["name"] as! String
}
}
DispatchQueue.main.async { [self] in
self?.appName.stringValue = "App Name: \(self!.app_name) (\(self!.app_version))"
}
}
}
}
// MARK: - NSTableViewDelegate
extension ScreenShotUploadVC: NSTableViewDelegate, NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return filesData[selectTag].count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
var data = filesData[selectTag][row]
let isImg = data["type"]! == "1"
switch tableColumn!.identifier.rawValue {
case "review":
let imgView = NSImageView()
if let imageRef = NSImage(byReferencingFile: data["url"]!) {
imgView.image = imageRef
}
if !isImg {
let url = URL(fileURLWithPath: data["url"]!)
let videoImg = previewImageForLocalVideo(url)
imgView.image = videoImg
}
return imgView
case "fileName":
let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "fileName"), owner: self) as? NSTableCellView
cell?.textField?.stringValue = "\(isImg ? "图片" : "视频")\n\(data["name"]!)"
return cell
case "setting":
let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "setting"), owner: self) as? ScreenShotUploadCell
cell?.row = row
cell?.showVideoView(!isImg)
cell?.updateData(sort: data["index"] ?? "", frame: data["frame"] ?? "00:00")
cell?.changeSortIndex = { index, crow in
data["index"] = index
self.filesData[self.selectTag][row] = data
}
cell?.changeVideoFrame = { frame, crow in
data["frame"] = frame
self.filesData[self.selectTag][row] = data
}
return cell
case "operation":
let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "operation"), owner: self) as? ScreenShotDeleteCell
cell?.row = row
cell?.deleteCell = { row in
self.filesData[self.selectTag].remove(at: row)
self.tableView.reloadData()
self.reloadTableView(ScreenShotType(rawValue: self.selectTag) ?? .iOS5_5)
}
return cell
default:
return nil
}
}
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
80
}
func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
// macOS 11 textfield
return true
}
}
// MARK: - Upload
extension ScreenShotUploadVC {
func uploadData(_ sp: SPassword) {
APHUD.show(message: "上传中", view: self.view)
let localIndex = localButton.indexOfSelectedItem
DispatchQueue.global(qos: .userInitiated).async { [self] in
uploadModel = XMLModel()
guard let info = appInfo else {
fetchAppInfo()
APHUD.hide(message: "加载数据异常,请刷新后重试~")
return
}
uploadModel.vendor_id = info.sku
// itms
let filePath = XMLManager.getShotsPath(currentApp!.appId)
//
XMLManager.deleteITMS(filePath)
//
handleDataToModel(localIndex)
uploadModel.createShots(directoryPath: filePath)
let result = XMLManager.uploadITMS(account: sp.account, pwd: sp.password, filePath: filePath)
DispatchQueue.main.async {
APHUD.hide()
self.closeSelfAndCallBack(result)
}
}
}
func handleDataToModel(_ localIndex: Int) {
let locale = locales[localIndex]
uploadModel.app_locale = locale
uploadModel.app_title = app_name
uploadModel.app_version = app_version
let iOS5_5 = filesData[0]
let iOS6_5 = filesData[1]
let iPad = filesData[2]
//iPhone 5.5
let (img5_5, video5_5, set5_5) = getScreenShotModel(iOS5_5)
uploadModel.shots["iOS-5.5-in"] = img5_5
uploadModel.videos["iOS-5.5-in"] = video5_5
//iPhone 6.5
let (img6_5, video6_5, set6_5) = getScreenShotModel(iOS6_5)
uploadModel.shots["iOS-6.5-in"] = img6_5
uploadModel.videos["iOS-6.5-in"] = video6_5
let (imgiPad, videoiPad, setiPad) = getScreenShotModel(iPad)
//iPad Pro2 12.9
uploadModel.shots["iOS-iPad-Pro"] = imgiPad
uploadModel.videos["iOS-iPad-Pro"] = videoiPad
//iPad Pro3 12.9
uploadModel.shots["iOS-iPad-Pro-2018"] = imgiPad
uploadModel.videos["iOS-iPad-Pro-2018"] = videoiPad
//
let fileURLs = set5_5.union(set6_5).union(setiPad)
var dict = [String: String]()
fileURLs.forEach { value in
value.forEach { dict[$0] = $1 }
}
uploadModel.filePaths = dict
}
func getScreenShotModel(_ shots: [[String: String]]) -> ([Screen_Shot], [Screen_Shot], Set<[String:String]>) {
var imageList = [Screen_Shot]()
var videoList = [Screen_Shot]()
var urlList: Set<[String:String]> = []
shots.forEach { data in
let isImg = data["type"]! == "1"
let url = data["url"] ?? ""
let kind = data["kind"] ?? ""
let name = kind + (data["name"] ?? "") //kind+name使
let index = data["index"] ?? ""
let frame = "00:00:" + (data["frame"] ?? "00:00")
let size = URL.init(fileURLWithPath: url).fileSize()
let md5 = URL.init(fileURLWithPath: url).fileMD5()
let item = Screen_Shot(file_name: name, size: size, checksum: md5 ?? "", position: index, preview_time: frame)
urlList.insert([name: url])
isImg ? imageList.append(item) : videoList.append(item)
}
return (imageList, videoList, urlList)
}
func closeSelfAndCallBack(_ result: (Int32, String?)) {
if result.0 == 0 {
NSAlert.show("上传成功!稍后可在苹果后台查看~")
//
let filePath = XMLManager.getShotsPath(currentApp!.appId)
XMLManager.deleteITMS(filePath)
} else {
let sb = NSStoryboard(name: "APDebugVC", bundle: Bundle(for: self.classForCoder))
let newWC = sb.instantiateController(withIdentifier: "APDebugWC") as? NSWindowController
let logVC = newWC?.contentViewController as? APDebugVC
newWC?.window?.title = "上传错误日志"
logVC?.debugLog = result.1 ?? ""
newWC?.showWindow(self)
}
}
}
// MARK: -
extension ScreenShotUploadVC {
func handleAutoImages(_ files: [URL]) {
files.forEach { url in
//
if url.hasDirectoryPath {
let urls = subFilesInDirectory(url: url)
handleAutoImages(urls)
return
}
var size = "0x0"
var sizes = [String: [String]]()
var type = "1"
let fileType = url.pathExtension
if imageTypes.contains(fileType) {
let image = NSImage(contentsOf: url)
guard let rep = image?.representations.first as? NSBitmapImageRep else {
return
}
size = "\(rep.pixelsWide)x\(rep.pixelsHigh)"
sizes = imageSizes
type = "1"
} else if videoTypes.contains(fileType) {
guard let track = AVURLAsset(url: url).tracks(withMediaType: AVMediaType.video).first else {
return
}
let rep = track.naturalSize.applying(track.preferredTransform)
size = "\(Int(rep.width))x\(Int(rep.height))"
sizes = videoSizes
type = "2"
}
sizes.forEach { (index: String, value: [String]) in
if value.contains(size) {
//
var dict = [String: String]()
dict["url"] = url.path
dict["name"] = url.lastPathComponent
dict["type"] = type
dict["kind"] = index
//
var value = filesData[Int(index)!]
value.append(dict)
filesData[Int(index)!] = value
}
}
}
//
reloadTableView(ScreenShotType.init(rawValue: selectTag) ?? .iOS5_5)
}
func subFilesInDirectory(url: URL) -> [URL] {
var urls = [URL]()
//
let files = FileManager.default.enumerator(atPath: url.path)
while let file = files?.nextObject() {
if let file = file as? String {
let nextFile = url.appendingPathComponent(file)
if !nextFile.hasDirectoryPath {
urls.append(nextFile)
}
}
}
return urls
}
func reloadTableView(_ index: ScreenShotType) {
let list = filesData[index.rawValue]
let images = list.filter({ $0["type"] == "1" })
tips_count.stringValue = "\(list.count - images.count)/3 个 App 预览 | \(images.count)/10 张屏幕快照"
switch index {
case .iOS5_5:
tips_title.stringValue = "iPhone 5.5 英寸显示屏"
tips_desc.stringValue = "图片1242 x 2208、2208 x 1242视频1080 x 1920、1920 x 1080"
case .iOS6_5:
tips_title.stringValue = "iPhone 6.5 英寸显示屏"
tips_desc.stringValue = "图片1242 x 2688、2688 x 1242视频886 x 1920、1920 x 886"
case .iPad_Pro:
tips_title.stringValue = "iPad Pro 12.9 英寸显示屏"
tips_desc.stringValue = "图片2048 x 2732、2732 x 2048视频1200 x 1600、1600 x 1200"
}
tableView.reloadData()
}
func previewImageForLocalVideo(_ url: URL) -> NSImage? {
let asset = AVAsset(url: url)
let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.appliesPreferredTrackTransform = true
var time = asset.duration
//If possible - take not the first frame (it could be completely black or white on camara's videos)
time.value = min(time.value, 2)
do {
let imageRef = try imageGenerator.copyCGImage(at: time, actualTime: nil)
let track = asset.tracks(withMediaType: AVMediaType.video).first
let rep = track?.naturalSize.applying(track?.preferredTransform ?? CGAffineTransform())
return NSImage(cgImage: imageRef, size: NSSize(width: rep?.width ?? 120, height: rep?.height ?? 60))
}
catch let error as NSError
{
print("Image generation failed with error \(error)")
return nil
}
}
}

View File

@ -0,0 +1,3 @@
/* Class = "NSWindow"; title = "My Apps"; ObjectID = "mcp-fI-0bQ"; */
"mcp-fI-0bQ.title" = "我的 App";

View File

@ -0,0 +1,49 @@
//
// SettingVC.swift
// AppleParty
//
// Created by HTC on 2022/3/25.
// Copyright © 2021 37 Mobile Games. All rights reserved.
//
import Cocoa
class APSettingVC: NSViewController {
var isLoginViewShow: Bool {
get { return false }
set {
sPasswordBtn.isHidden = newValue
clearCacheBtn.isHidden = !newValue
}
}
@IBOutlet weak var trusDeviceBtn: NSButton!
@IBOutlet weak var sPasswordBtn: NSButton!
@IBOutlet weak var clearCacheBtn: NSButton!
@IBAction func clickedTrusDeviceBtn(_ sender: NSButton) {
InfoCenter.shared.trusDevice = sender.state == .on ? true : false
}
@IBAction func clickedSPasswordBtn(_ sender: Any) {
let vc = APSPasswordSettingVC()
presentAsSheet(vc)
}
@IBAction func clickedClearCacheBtn(_ sender: NSButton) {
//
HTTPCookieStorage.shared.cookies?.forEach(HTTPCookieStorage.shared.deleteCookie)
InfoCenter.shared.cookies = []
APHUD.hide(message: "清掉缓存成功", view: self.view)
}
override func viewDidLoad() {
super.viewDidLoad()
title = "App设置"
trusDeviceBtn.state = InfoCenter.shared.trusDevice ? .on : .off
}
}

View File

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="APSettingVC">
<connections>
<outlet property="clearCacheBtn" destination="CIQ-yY-qAH" id="Mk4-R7-k6m"/>
<outlet property="sPasswordBtn" destination="OeI-Rx-XtW" id="qOB-78-ZuO"/>
<outlet property="trusDeviceBtn" destination="nK7-5f-UIm" id="bjH-fk-npo"/>
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="Hz6-mo-xeY">
<rect key="frame" x="0.0" y="0.0" width="479" height="376"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BKC-66-mUu">
<rect key="frame" x="23" y="333" width="98" height="28"/>
<constraints>
<constraint firstAttribute="height" constant="28" id="vQz-oQ-tcK"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="设置选项" id="kek-10-Cgd">
<font key="font" metaFont="system" size="23"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nK7-5f-UIm">
<rect key="frame" x="23" y="287" width="106" height="22"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="hLE-Pd-isf"/>
</constraints>
<buttonCell key="cell" type="check" title="信任当前设备" bezelStyle="regularSquare" imagePosition="left" lineBreakMode="truncatingTail" state="on" inset="2" id="xCh-9D-HiS">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="clickedTrusDeviceBtn:" target="-2" id="n1j-ha-ysp"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OeI-Rx-XtW">
<rect key="frame" x="147" y="281" width="114" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="QVA-uj-MiU"/>
</constraints>
<buttonCell key="cell" type="push" title="设置专用密码" bezelStyle="rounded" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="8of-Bf-BU2">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" usesAppearanceFont="YES"/>
</buttonCell>
<connections>
<action selector="clickedSPasswordBtn:" target="-2" id="et4-Z4-s9t"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="TXz-pa-Bnd" userLabel="Line Label">
<rect key="frame" x="15" y="252" width="449" height="1"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="Hb5-8R-DdZ"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" borderStyle="bezel" drawsBackground="YES" id="FWN-Oq-vFj">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button hidden="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CIQ-yY-qAH">
<rect key="frame" x="272" y="281" width="114" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="J5b-3X-pyU"/>
</constraints>
<buttonCell key="cell" type="push" title="清除缓存数据" bezelStyle="rounded" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ylL-yw-axd">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="clickedClearCacheBtn:" target="-2" id="Sy2-qH-JPk"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="OeI-Rx-XtW" firstAttribute="centerY" secondItem="nK7-5f-UIm" secondAttribute="centerY" id="1fH-HN-CXg"/>
<constraint firstAttribute="trailing" secondItem="TXz-pa-Bnd" secondAttribute="trailing" constant="15" id="7d6-CI-IjY"/>
<constraint firstItem="BKC-66-mUu" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="25" id="9hM-UB-Bbc"/>
<constraint firstItem="CIQ-yY-qAH" firstAttribute="leading" secondItem="nK7-5f-UIm" secondAttribute="trailing" priority="550" constant="25" id="FAu-ZM-maQ"/>
<constraint firstItem="TXz-pa-Bnd" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="15" id="GHp-tK-8s3"/>
<constraint firstItem="CIQ-yY-qAH" firstAttribute="centerY" secondItem="nK7-5f-UIm" secondAttribute="centerY" id="Koe-bl-rog"/>
<constraint firstItem="TXz-pa-Bnd" firstAttribute="top" secondItem="nK7-5f-UIm" secondAttribute="bottom" constant="35" id="Nue-2l-Tec"/>
<constraint firstItem="BKC-66-mUu" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="15" id="bcl-Ls-nqp"/>
<constraint firstItem="nK7-5f-UIm" firstAttribute="top" secondItem="BKC-66-mUu" secondAttribute="bottom" constant="25" id="l1u-Xr-4yX"/>
<constraint firstItem="nK7-5f-UIm" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="25" id="n3v-kC-eX9"/>
<constraint firstItem="OeI-Rx-XtW" firstAttribute="leading" secondItem="nK7-5f-UIm" secondAttribute="trailing" constant="25" id="qUw-wS-OCL"/>
<constraint firstItem="CIQ-yY-qAH" firstAttribute="leading" secondItem="OeI-Rx-XtW" secondAttribute="trailing" priority="750" constant="25" id="wnS-ya-Ulb"/>
</constraints>
<point key="canvasLocation" x="48.5" y="194"/>
</customView>
</objects>
</document>

View File

@ -0,0 +1,7 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "GDataXMLNode.h"
#import "QrcodeUtil.h"
#import "MBProgressHUD.h"

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

View File

@ -0,0 +1,61 @@
//
// EmailSettingVC.swift
// AppleParty
//
// Created by HTC on 2022/3/29.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
struct EamilConfigs {
var name: String = ""
var addr: String = ""
var pwd: String = ""
var smtp: String = ""
}
var eamilConfigs: EamilConfigs? {
get {
let value = (try? APUtil.keychain.getString("APEmailSetting_Key")) ?? ""
let arr = value.components(separatedBy: "|")
guard arr.count == 4 else {
return nil
}
return EamilConfigs(name: arr[0], addr: arr[1], pwd: arr[2], smtp: arr[3])
}
set {
guard let newValue = newValue else { return }
let value = "\(newValue.name)|\(newValue.addr)|\(newValue.pwd)|\(newValue.smtp)"
try? APUtil.keychain.set(value, key: "APEmailSetting_Key")
}
}
class EmailSettingVC: NSViewController {
public var closeHandle: (() -> Void)?
@IBOutlet weak var emailNameView: NSTextField!
@IBOutlet weak var emailAddrView: NSTextField!
@IBOutlet weak var emailPwdView: NSTextField!
@IBOutlet weak var emailSMTPView: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
}
@IBAction func clickedCancelBtn(_ sender: Any) {
closeHandle?()
}
@IBAction func clickedSubmitBtn(_ sender: Any) {
eamilConfigs = EamilConfigs(name: emailNameView.stringValue, addr: emailAddrView.stringValue, pwd: emailPwdView.stringValue, smtp: emailSMTPView.stringValue)
closeHandle?()
}
}

View File

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="EmailSettingVC" customModule="AppleParty" customModuleProvider="target">
<connections>
<outlet property="emailAddrView" destination="xC8-OP-5OB" id="iA5-lT-uGT"/>
<outlet property="emailNameView" destination="eDA-H4-wtu" id="ggR-LW-T6u"/>
<outlet property="emailPwdView" destination="VVK-4v-kKh" id="gkL-zS-b4P"/>
<outlet property="emailSMTPView" destination="Fnq-BH-an4" id="c0U-Xb-4Ak"/>
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="Hz6-mo-xeY">
<rect key="frame" x="0.0" y="0.0" width="319" height="328"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="UGV-LT-zwz">
<rect key="frame" x="8" y="283" width="303" height="25"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="MTZ-og-I8m"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="设置邮箱账户" id="Txf-cs-igw">
<font key="font" metaFont="systemBold" size="17"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fOt-av-hwc">
<rect key="frame" x="206" y="18" width="80" height="34"/>
<constraints>
<constraint firstAttribute="height" constant="22" id="8Vn-vC-kMW"/>
<constraint firstAttribute="width" constant="66" id="II3-ZH-6L6"/>
</constraints>
<buttonCell key="cell" type="push" title="设置" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="P5t-QJ-zKH">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="clickedSubmitBtn:" target="-2" id="5bV-hs-YGt"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LT7-p4-ggn">
<rect key="frame" x="33" y="18" width="80" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="66" id="Le4-RM-yje"/>
</constraints>
<buttonCell key="cell" type="push" title="取消" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="zzW-EO-bUm">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="13" name="STSongti-SC-Regular"/>
</buttonCell>
<connections>
<action selector="clickedCancelBtn:" target="-2" id="Fdm-gw-h3c"/>
</connections>
</button>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="eDA-H4-wtu">
<rect key="frame" x="40" y="219" width="239" height="34"/>
<constraints>
<constraint firstAttribute="height" constant="34" id="Pha-T8-v9U"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="发送人名字" bezelStyle="round" id="u8o-KC-IxS">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xC8-OP-5OB">
<rect key="frame" x="40" y="173" width="239" height="34"/>
<constraints>
<constraint firstAttribute="height" constant="34" id="80Y-rx-GcW"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="发送邮箱地址" bezelStyle="round" id="g7M-ao-gse">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="VVK-4v-kKh">
<rect key="frame" x="40" y="127" width="239" height="34"/>
<constraints>
<constraint firstAttribute="height" constant="34" id="kT1-yB-MMR"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="发送邮箱密码" bezelStyle="round" id="Doe-al-eff">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Fnq-BH-an4">
<rect key="frame" x="40" y="81" width="239" height="34"/>
<constraints>
<constraint firstAttribute="height" constant="34" id="zSX-jX-gJu"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="邮箱服务地址(如 smtp.xx.com)" bezelStyle="round" id="otT-Sn-TWW">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="VVK-4v-kKh" firstAttribute="top" secondItem="xC8-OP-5OB" secondAttribute="bottom" constant="12" id="5gY-FH-ljk"/>
<constraint firstItem="xC8-OP-5OB" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="40" id="Ax7-4Z-zIa"/>
<constraint firstAttribute="trailing" secondItem="eDA-H4-wtu" secondAttribute="trailing" constant="40" id="BaG-jk-2M6"/>
<constraint firstItem="Fnq-BH-an4" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="40" id="FCR-Fd-als"/>
<constraint firstItem="Fnq-BH-an4" firstAttribute="top" secondItem="VVK-4v-kKh" secondAttribute="bottom" constant="12" id="Lsf-Gg-Hi5"/>
<constraint firstItem="LT7-p4-ggn" firstAttribute="leading" secondItem="Fnq-BH-an4" secondAttribute="leading" id="NqC-bU-xow"/>
<constraint firstAttribute="bottom" secondItem="LT7-p4-ggn" secondAttribute="bottom" constant="25" id="Pgl-yp-65W"/>
<constraint firstItem="fOt-av-hwc" firstAttribute="trailing" secondItem="Fnq-BH-an4" secondAttribute="trailing" id="TNu-gl-01i"/>
<constraint firstItem="VVK-4v-kKh" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="40" id="TeH-8x-GIb"/>
<constraint firstItem="eDA-H4-wtu" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="40" id="UtA-LQ-pGW"/>
<constraint firstItem="UGV-LT-zwz" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="20" symbolic="YES" id="XAE-zG-uBj"/>
<constraint firstItem="xC8-OP-5OB" firstAttribute="top" secondItem="eDA-H4-wtu" secondAttribute="bottom" constant="12" id="fG3-IY-ptx"/>
<constraint firstAttribute="trailing" secondItem="Fnq-BH-an4" secondAttribute="trailing" constant="40" id="oJO-fs-eS0"/>
<constraint firstAttribute="trailing" secondItem="VVK-4v-kKh" secondAttribute="trailing" constant="40" id="olk-5z-Atb"/>
<constraint firstAttribute="bottom" secondItem="fOt-av-hwc" secondAttribute="bottom" constant="25" id="qUL-ii-wn5"/>
<constraint firstAttribute="trailing" secondItem="UGV-LT-zwz" secondAttribute="trailing" constant="10" id="rew-BG-Egu"/>
<constraint firstAttribute="trailing" secondItem="xC8-OP-5OB" secondAttribute="trailing" constant="40" id="z5o-8S-Uvi"/>
<constraint firstItem="eDA-H4-wtu" firstAttribute="top" secondItem="UGV-LT-zwz" secondAttribute="bottom" constant="30" id="zDm-hN-QrO"/>
<constraint firstItem="UGV-LT-zwz" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="10" id="zVD-UO-S2w"/>
</constraints>
<point key="canvasLocation" x="58.5" y="182"/>
</customView>
</objects>
</document>

View File

@ -0,0 +1,251 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Window Controller-->
<scene sceneID="6Wf-BN-a9j">
<objects>
<windowController storyboardIdentifier="EmailTool" id="T4u-uh-EbA" sceneMemberID="viewController">
<window key="window" title="邮件助手" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="qJz-u6-Hg1">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="359" y="388" width="600" height="450"/>
<rect key="screenRect" x="0.0" y="0.0" width="2240" height="1235"/>
<view key="contentView" id="mmv-OD-ZTq">
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<connections>
<outlet property="delegate" destination="T4u-uh-EbA" id="dxr-DV-lYU"/>
</connections>
</window>
<connections>
<segue destination="hI8-xu-11y" kind="relationship" relationship="window.shadowedContentViewController" id="XrI-xM-H9W"/>
</connections>
</windowController>
<customObject id="a65-QT-Nbx" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-249" y="220"/>
</scene>
<!--邮件助手-->
<scene sceneID="NVw-xI-A8X">
<objects>
<viewController title="邮件助手" id="hI8-xu-11y" customClass="EmailToolVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="fZy-4A-sDv">
<rect key="frame" x="0.0" y="0.0" width="600" height="557"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="RWF-3X-mT6">
<rect key="frame" x="20" y="505" width="377" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="32" id="XIR-wi-l2X"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="收件人邮箱地址" bezelStyle="round" id="j19-N4-QQn">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="czV-Gg-3Ri">
<rect key="frame" x="20" y="453" width="467" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="32" id="Osc-bj-ZTE"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="邮件标题" bezelStyle="round" id="GwA-zk-eAs">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aBl-I5-nCZ">
<rect key="frame" x="495" y="447" width="92" height="42"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="1zZ-G3-6Qa"/>
<constraint firstAttribute="width" constant="78" id="s1Y-ba-LVb"/>
</constraints>
<buttonCell key="cell" type="push" title="发送" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="msG-2W-3ir">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="sendEmailButton:" target="hI8-xu-11y" id="6D3-P8-7tz"/>
</connections>
</button>
<scrollView borderType="line" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bZ7-lQ-t1B">
<rect key="frame" x="20" y="300" width="560" height="125"/>
<clipView key="contentView" drawsBackground="NO" id="EkG-rr-J9X">
<rect key="frame" x="1" y="1" width="558" height="123"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" allowsUndo="YES" spellingCorrection="YES" smartInsertDelete="YES" id="4ah-H5-AbK">
<rect key="frame" x="0.0" y="0.0" width="558" height="123"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="558" height="123"/>
<size key="maxSize" width="758" height="10000000"/>
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
</textView>
</subviews>
</clipView>
<constraints>
<constraint firstAttribute="height" constant="125" id="DGW-ts-Y3w"/>
</constraints>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="w5S-wA-NZW">
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="I17-we-bph">
<rect key="frame" x="543" y="1" width="16" height="123"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<scrollView hidden="YES" borderType="line" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Dd7-vC-Tdz">
<rect key="frame" x="20" y="30" width="560" height="220"/>
<clipView key="contentView" drawsBackground="NO" id="nJr-qZ-vEo">
<rect key="frame" x="1" y="1" width="558" height="218"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView editable="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="egz-HU-sRx">
<rect key="frame" x="0.0" y="0.0" width="558" height="218"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="558" height="218"/>
<size key="maxSize" width="758" height="10000000"/>
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
</textView>
</subviews>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="C94-21-myA">
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="qOY-uP-fav">
<rect key="frame" x="543" y="1" width="16" height="218"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hPQ-8D-VUS">
<rect key="frame" x="18" y="268" width="87" height="18"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="85" id="CGO-Yn-TVv"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="16" id="Kbs-f6-qvx"/>
<constraint firstAttribute="height" constant="16" id="MYL-95-hpC"/>
</constraints>
<buttonCell key="cell" type="check" title="多个附件" bezelStyle="regularSquare" imagePosition="left" inset="2" id="cMY-nK-eFc">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="ChangeMultipleFiles:" target="hI8-xu-11y" id="SS4-P3-ac7"/>
</connections>
</button>
<button hidden="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YDc-3K-JgG">
<rect key="frame" x="113" y="255" width="114" height="42"/>
<constraints>
<constraint firstAttribute="width" constant="100" id="cHP-vM-kak"/>
<constraint firstAttribute="height" constant="30" id="iaX-PB-BFX"/>
</constraints>
<buttonCell key="cell" type="push" title="批量选择附件" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="b4z-Tm-zdY">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="selectFiles:" target="hI8-xu-11y" id="cJV-aH-jrm"/>
</connections>
</button>
<button hidden="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fkW-LQ-FK3">
<rect key="frame" x="473" y="255" width="114" height="42"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="Dc4-0C-chh"/>
<constraint firstAttribute="width" constant="100" id="UkJ-G9-xpX"/>
</constraints>
<buttonCell key="cell" type="push" title="清空所有附件" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Eol-UM-Fq4">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="13" name="Times-Roman"/>
</buttonCell>
<connections>
<action selector="clearnAllFiles:" target="hI8-xu-11y" id="aQ3-JN-lkH"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nEa-8J-VHH">
<rect key="frame" x="510" y="511" width="78" height="19"/>
<constraints>
<constraint firstAttribute="height" constant="18" id="SNh-ap-QDY"/>
<constraint firstAttribute="width" constant="78" id="akd-Tt-PbM"/>
</constraints>
<buttonCell key="cell" type="roundRect" title="邮箱设置" bezelStyle="roundedRect" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="5t6-n1-Yfi">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="cellTitle"/>
</buttonCell>
<connections>
<action selector="clickedEmailSettingButton:" target="hI8-xu-11y" id="KkT-rB-A5p"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wKD-68-g5N">
<rect key="frame" x="410" y="512" width="80" height="18"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="Am0-qN-l34"/>
<constraint firstAttribute="width" constant="78" id="C2k-Ct-lk8"/>
</constraints>
<buttonCell key="cell" type="check" title="记住邮箱" bezelStyle="regularSquare" imagePosition="left" inset="2" id="XZa-0z-9Wx">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="rememberEmail:" target="hI8-xu-11y" id="Yk7-qe-oOx"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="fkW-LQ-FK3" firstAttribute="centerY" secondItem="YDc-3K-JgG" secondAttribute="centerY" id="3vf-mh-NPN"/>
<constraint firstAttribute="trailing" secondItem="aBl-I5-nCZ" secondAttribute="trailing" constant="20" symbolic="YES" id="D6v-Dw-Hce"/>
<constraint firstItem="czV-Gg-3Ri" firstAttribute="top" secondItem="RWF-3X-mT6" secondAttribute="bottom" constant="20" id="EUY-Fa-byW"/>
<constraint firstItem="hPQ-8D-VUS" firstAttribute="leading" secondItem="fZy-4A-sDv" secondAttribute="leading" constant="20" symbolic="YES" id="Gff-XY-Gge"/>
<constraint firstItem="bZ7-lQ-t1B" firstAttribute="top" secondItem="czV-Gg-3Ri" secondAttribute="bottom" constant="28" id="GhT-KF-C9V"/>
<constraint firstItem="aBl-I5-nCZ" firstAttribute="leading" secondItem="czV-Gg-3Ri" secondAttribute="trailing" constant="15" id="Ie7-SA-119"/>
<constraint firstItem="Dd7-vC-Tdz" firstAttribute="leading" secondItem="fZy-4A-sDv" secondAttribute="leading" constant="20" symbolic="YES" id="V6i-qP-2gR"/>
<constraint firstAttribute="bottom" secondItem="Dd7-vC-Tdz" secondAttribute="bottom" constant="30" id="Wb4-eP-RbL"/>
<constraint firstItem="YDc-3K-JgG" firstAttribute="centerY" secondItem="hPQ-8D-VUS" secondAttribute="centerY" id="XCM-Ec-Gh8"/>
<constraint firstItem="bZ7-lQ-t1B" firstAttribute="leading" secondItem="fZy-4A-sDv" secondAttribute="leading" constant="20" symbolic="YES" id="Z0A-we-uv2"/>
<constraint firstItem="nEa-8J-VHH" firstAttribute="centerY" secondItem="RWF-3X-mT6" secondAttribute="centerY" id="c43-YH-VpN"/>
<constraint firstItem="nEa-8J-VHH" firstAttribute="leading" secondItem="wKD-68-g5N" secondAttribute="trailing" constant="20" id="dAP-vZ-QuW"/>
<constraint firstItem="aBl-I5-nCZ" firstAttribute="centerY" secondItem="czV-Gg-3Ri" secondAttribute="centerY" id="eOU-ym-ifE"/>
<constraint firstItem="RWF-3X-mT6" firstAttribute="top" secondItem="fZy-4A-sDv" secondAttribute="top" constant="20" symbolic="YES" id="gTw-GK-wmY"/>
<constraint firstItem="wKD-68-g5N" firstAttribute="leading" secondItem="RWF-3X-mT6" secondAttribute="trailing" constant="15" id="iq6-W1-gyS"/>
<constraint firstItem="czV-Gg-3Ri" firstAttribute="leading" secondItem="fZy-4A-sDv" secondAttribute="leading" constant="20" symbolic="YES" id="lRZ-eR-gSE"/>
<constraint firstAttribute="trailing" secondItem="fkW-LQ-FK3" secondAttribute="trailing" constant="20" symbolic="YES" id="lTW-1f-YpI"/>
<constraint firstAttribute="trailing" secondItem="bZ7-lQ-t1B" secondAttribute="trailing" constant="20" symbolic="YES" id="ldL-eS-Nkc"/>
<constraint firstItem="hPQ-8D-VUS" firstAttribute="top" secondItem="bZ7-lQ-t1B" secondAttribute="bottom" constant="15" id="lhC-BK-8Tf"/>
<constraint firstItem="YDc-3K-JgG" firstAttribute="leading" secondItem="hPQ-8D-VUS" secondAttribute="trailing" constant="15" id="nVw-TE-aXd"/>
<constraint firstItem="wKD-68-g5N" firstAttribute="centerY" secondItem="RWF-3X-mT6" secondAttribute="centerY" id="uBR-yz-DpK"/>
<constraint firstItem="RWF-3X-mT6" firstAttribute="leading" secondItem="fZy-4A-sDv" secondAttribute="leading" constant="20" symbolic="YES" id="vI6-lv-Ec5"/>
<constraint firstAttribute="trailing" secondItem="Dd7-vC-Tdz" secondAttribute="trailing" constant="20" symbolic="YES" id="wyE-Sc-s5e"/>
<constraint firstAttribute="trailing" secondItem="nEa-8J-VHH" secondAttribute="trailing" constant="12" id="xMj-dk-PSK"/>
<constraint firstItem="Dd7-vC-Tdz" firstAttribute="top" secondItem="YDc-3K-JgG" secondAttribute="bottom" constant="12" id="xRd-FW-dKv"/>
</constraints>
</view>
<connections>
<outlet property="clearnAllFilesButton" destination="fkW-LQ-FK3" id="xYy-yP-YKJ"/>
<outlet property="emailContentTextView" destination="4ah-H5-AbK" id="Khn-UX-lUg"/>
<outlet property="emailRecipientTF" destination="RWF-3X-mT6" id="yKO-Jx-mde"/>
<outlet property="emailSendButton" destination="aBl-I5-nCZ" id="Vw5-h1-kJe"/>
<outlet property="emailTitleTF" destination="czV-Gg-3Ri" id="nXa-9E-AF0"/>
<outlet property="emialContentView" destination="bZ7-lQ-t1B" id="xQU-qv-3Af"/>
<outlet property="multipleFilesButton" destination="hPQ-8D-VUS" id="mCJ-3B-Fve"/>
<outlet property="rememberEmailButton" destination="wKD-68-g5N" id="Gwn-wI-TB3"/>
<outlet property="selectFilesButton" destination="YDc-3K-JgG" id="HJv-eO-apA"/>
<outlet property="selectFilesView" destination="Dd7-vC-Tdz" id="BXM-HP-Y0A"/>
<outlet property="selectilesTextView" destination="egz-HU-sRx" id="7kr-5W-ahZ"/>
</connections>
</viewController>
<customObject id="NPu-9T-cfy" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="455" y="211.5"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,247 @@
//
// EmailToolVC.swift
// AppleParty
//
// Created by iHTC on 20211025.
// Copyright © 2021 37 Mobile Games. All rights reserved.
//
import Cocoa
class EmailToolVC: NSViewController {
var emailTitle: String? {
didSet {
if let text = emailTitle {
emailTitleTF.stringValue = text
}
}
}
var emailContent: String? {
didSet {
if let text = emailContent {
emailContentTextView.string = text
}
}
}
var attachmentFileUrl: URL? {
didSet {
if let url = attachmentFileUrl {
fileURLs?.append(url)
fileDropZoneView.setFile(url)
}
}
}
@IBOutlet weak var emailRecipientTF: NSTextField!
@IBOutlet weak var rememberEmailButton: NSButton!
@IBOutlet weak var emailTitleTF: NSTextField!
@IBOutlet weak var emailSendButton: NSButton!
@IBOutlet weak var emailContentTextView: NSTextView!
@IBOutlet weak var emialContentView: NSScrollView!
@IBOutlet weak var multipleFilesButton: NSButton!
@IBOutlet weak var selectFilesButton: NSButton!
@IBOutlet weak var clearnAllFilesButton: NSButton!
@IBOutlet weak var selectFilesView: NSScrollView!
@IBOutlet weak var selectilesTextView: NSTextView!
private var fileDropZoneView = DropZoneView(fileTypes: [], text: "点击或拖拽文件到这里")
private var fileURLs: [URL]?
//
private var emailsString: String {
get { string(from: UserDefaults.standard.object(forKey: "EmailToolVC_RememberEmailString")) }
set { UserDefaults.standard.setValue(newValue, forKey: "EmailToolVC_RememberEmailString") }
}
private lazy var settingPopover: NSPopover = {
let settingPopover = NSPopover()
let vc = EmailSettingVC()
vc.closeHandle = {
settingPopover.close()
}
settingPopover.contentViewController = vc
return settingPopover
}()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
func setupUI() {
if emailsString.count > 0 {
emailRecipientTF.stringValue = emailsString
rememberEmailButton.state = .on
} else {
rememberEmailButton.state = .off
}
fileDropZoneView.translatesAutoresizingMaskIntoConstraints = false
fileDropZoneView.delegate = self
view.addSubview(fileDropZoneView)
fileDropZoneView.snp.makeConstraints { (make) in
make.top.equalTo(multipleFilesButton.snp.bottom).offset(15)
make.left.equalToSuperview().offset(20)
make.right.equalToSuperview().offset(-20)
make.bottom.equalToSuperview().offset(-30)
}
}
func validEmail() -> [String]? {
let recipient = emailRecipientTF.stringValue
guard recipient.count > 0 else {
APHUD.hide(message: "收件人邮箱不能为空!", view: self.view, delayTime: 2)
return nil
}
let allEmails = recipient.components(separatedBy: [";", "", ",", ""]).filter({!$0.isEmpty})
let emails = allEmails.filter({ isEmailValid($0) })
if emails.isEmpty {
APHUD.hide(message: "收件人邮箱格式不正确!", view: self.view, delayTime: 2)
return nil
}
return emails
}
@IBAction func clickedEmailSettingButton(_ sender: NSButton) {
if settingPopover.isShown {
settingPopover.performClose(self)
}else {
settingPopover.show(relativeTo: sender.bounds, of: sender, preferredEdge: NSRectEdge.minY)
}
}
@IBAction func sendEmailButton(_ sender: NSButton) {
guard let config = eamilConfigs else {
APHUD.hide(message: "请先设置邮箱服务器信息!", view: self.view, delayTime: 2)
return
}
//
guard let emails = validEmail() else {
return
}
sender.isEnabled = false
APHUD.show(message: "发送中...", view: self.view)
var title = emailTitleTF.stringValue
if title.isEmpty {
title = "邮件助手"
}
let contents = "<p style='white-space:pre-wrap;'>\(emailContentTextView.textStorage?.string ?? "")</p>".replacingOccurrences(of: "\n", with: "<br>")
var files = [String]()
fileURLs?.forEach({ url in
files.append(url.path)
})
//
EmailUtils.autoSendAtts(subject: "AppleParty — \(title)", recipients: emails, htmlContent: contents, attachmentFiles: files, config: config) { error in
DispatchQueue.main.async {
sender.isEnabled = true
APHUD.hide()
debugPrint(error as Any)
var msg = "邮箱发送成功~"
if let err = error {
msg = "邮箱发送失败:\(String(describing: err))"
}
NSAlert.show(msg)
}
}
}
@IBAction func rememberEmail(_ sender: Any) {
//
guard validEmail() != nil else {
return
}
emailsString = emailRecipientTF.stringValue
}
@IBAction func ChangeMultipleFiles(_ sender: NSButton) {
clearnAllFiles(clearnAllFilesButton)
if sender.state == .on {
fileDropZoneView.isHidden = true
selectFilesButton.isHidden = false
clearnAllFilesButton.isHidden = false
selectFilesView.isHidden = false
} else {
fileDropZoneView.reset()
fileDropZoneView.isHidden = false
selectFilesButton.isHidden = true
clearnAllFilesButton.isHidden = true
selectFilesView.isHidden = true
}
}
@IBAction func clearnAllFiles(_ sender: NSButton) {
fileURLs = []
selectilesTextView.string = ""
}
@IBAction func selectFiles(_ sender: Any) {
let openPanel = NSOpenPanel()
openPanel.canChooseFiles = true
openPanel.canChooseDirectories = false
openPanel.allowsMultipleSelection = true
openPanel.beginSheetModal(for: self.view.window!) { [self] (modalResponse) in
if modalResponse == .OK {
self.fileURLs?.append(contentsOf: openPanel.urls)
self.updateFilesView()
}
}
}
func updateFilesView() {
fileURLs?.forEach({ file in
let path = file.lastPathComponent
selectilesTextView.string.append(path + "\n")
})
selectilesTextView.scrollRangeToVisible(NSMakeRange(selectilesTextView.string.count, 0))
}
}
// MARK: - DropZoneViewDelegate
extension EmailToolVC: DropZoneViewDelegate {
func receivedFile(dropZoneView: DropZoneView, fileURL: URL) {
fileURLs = [fileURL]
}
func receivedMouseDown(dropZoneView: DropZoneView, theEvent: NSEvent) {
let openPanel = NSOpenPanel()
openPanel.canChooseFiles = true
openPanel.canChooseDirectories = false
openPanel.allowsMultipleSelection = false
openPanel.beginSheetModal(for: self.view.window!) { (modalResponse) in
if modalResponse == .OK {
if let fileURL = openPanel.url {
self.fileURLs = [fileURL]
dropZoneView.setFile(fileURL)
}
}
}
}
}

View File

@ -0,0 +1,174 @@
//
// APIPAUploadVC.swift
// AppleParty
//
// Created by HTC on 2022/5/12.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
class APIPAUploadVC: NSViewController {
@IBOutlet weak var appIdTextView: NSTextField!
@IBOutlet weak var appIdTextField: NSTextField!
@IBOutlet weak var spasswordLbl: NSTextField!
@IBOutlet weak var submitBtn: NSButton!
// apple id
var apple_id: String? {
didSet {
if let appId = apple_id {
appIdTextView.stringValue = appId
appIdTextView.isHidden = false
appIdTextField.isHidden = true
} else {
appIdTextView.stringValue = ""
appIdTextView.isHidden = true
appIdTextField.isHidden = false
}
}
}
private var ipaFileURL: URL?
private var fileDropZoneView = DropZoneView(fileTypes: [".ipa"], text: "点击或拖拽IPA到这里")
private var uploadModel = XMLModel()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
updateSPasswordUI()
}
func setupUI() {
fileDropZoneView.translatesAutoresizingMaskIntoConstraints = false
fileDropZoneView.delegate = self
view.addSubview(fileDropZoneView)
fileDropZoneView.snp.makeConstraints { (make) in
make.top.equalTo(submitBtn.snp.bottom).offset(15)
make.left.equalToSuperview().offset(20)
make.right.equalToSuperview().offset(-20)
make.bottom.equalToSuperview().offset(-30)
}
}
func updateSPasswordUI() {
if let sp = UserCenter.shared.currentSPassword {
spasswordLbl.stringValue = "(当前选择:\(sp.account))"
} else {
spasswordLbl.stringValue = "(错误:当前未指定专用密码!)"
}
}
@IBAction func clickedSPasswordBtn(_ sender: NSButton) {
let vc = APSPasswordSettingVC()
vc.updateCompletion = { [weak self] ps in
self?.updateSPasswordUI()
}
presentAsSheet(vc)
}
@IBAction func clickedSubmitBtn(_ sender: NSButton) {
uploadIpaFile()
}
}
// MARK: - Private Method
extension APIPAUploadVC {
private func uploadIpaFile() {
var appId = appIdTextField.stringValue
if let appleId = apple_id {
appId = appleId
}
guard !appId.isEmpty else {
APHUD.hide(message: "请先填写 app id ~", delayTime: 1)
return
}
guard let sp = UserCenter.shared.currentSPassword else {
let vc = APSPasswordSettingVC()
vc.updateCompletion = { [weak self] spassword in
self?.uploadIpaFile()
}
presentAsSheet(vc)
APHUD.hide(message: "请先设置或指定专用密码~", delayTime: 1)
return
}
guard let ipaFileURL = ipaFileURL else {
APHUD.hide(message: "请先上传 ipa 文件~", delayTime: 1)
return
}
APHUD.show(message: "上传中", view: self.view)
DispatchQueue.global(qos: .userInitiated).async { [self] in
uploadModel = XMLModel()
uploadModel.apple_id = appId
uploadModel.ipa_size = ipaFileURL.fileSize()
uploadModel.ipa_md5 = ipaFileURL.fileMD5() ?? ""
uploadModel.filePaths = ["ipa.ipa": ipaFileURL.path]
// itms
let filePath = XMLManager.getIpaPath(appId)
//
XMLManager.deleteITMS(filePath)
uploadModel.createIpaFile(directoryPath: filePath)
let result = XMLManager.uploadITMS(account: sp.account, pwd: sp.password, filePath: filePath)
DispatchQueue.main.async {
APHUD.hide()
self.closeSelfAndCallBack(result)
}
}
}
func closeSelfAndCallBack(_ result: (Int32, String?)) {
if result.0 == 0 {
NSAlert.show("ipa文件上传成功稍后可在苹果后台查看~")
}else {
let sb = NSStoryboard(name: "APDebugVC", bundle: Bundle(for: self.classForCoder))
let newWC = sb.instantiateController(withIdentifier: "APDebugWC") as? NSWindowController
let logVC = newWC?.contentViewController as? APDebugVC
newWC?.window?.title = "ipa上传错误日志"
logVC?.debugLog = result.1 ?? ""
newWC?.showWindow(self)
}
}
}
// MARK: - DropZoneViewDelegate
extension APIPAUploadVC: DropZoneViewDelegate {
func receivedFile(dropZoneView: DropZoneView, fileURL: URL) {
ipaFileURL = fileURL
}
func receivedMouseDown(dropZoneView: DropZoneView, theEvent: NSEvent) {
let openPanel = NSOpenPanel()
openPanel.canChooseFiles = true
openPanel.canChooseDirectories = false
openPanel.allowsMultipleSelection = false
openPanel.allowedFileTypes = ["ipa"]
openPanel.beginSheetModal(for: self.view.window!) { (modalResponse) in
if modalResponse == .OK {
if let fileURL = openPanel.url {
self.ipaFileURL = fileURL
dropZoneView.setFile(fileURL)
}
}
}
}
}

View File

@ -0,0 +1,159 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Window Controller-->
<scene sceneID="BXd-4M-62l">
<objects>
<windowController storyboardIdentifier="IPAUpload" id="l7J-hr-7bm" sceneMemberID="viewController">
<window key="window" title="包体工具" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="5Sf-fH-lyY">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="425" y="462" width="600" height="450"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<view key="contentView" id="UUK-JK-BBb">
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<connections>
<outlet property="delegate" destination="l7J-hr-7bm" id="Vt0-Yd-g5X"/>
</connections>
</window>
<connections>
<segue destination="xPR-tb-wUt" kind="relationship" relationship="window.shadowedContentViewController" id="eDd-Pz-xpA"/>
</connections>
</windowController>
<customObject id="D9C-vY-7M4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-31" y="405"/>
</scene>
<!--UploadVC-->
<scene sceneID="VOc-VL-qdq">
<objects>
<viewController id="xPR-tb-wUt" customClass="APIPAUploadVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="ctU-VA-UqD">
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Te0-Hx-wTO">
<rect key="frame" x="33" y="406" width="82" height="19"/>
<constraints>
<constraint firstAttribute="height" constant="19" id="Rgk-aT-Iwk"/>
<constraint firstAttribute="width" constant="78" id="qg2-Md-a3M"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="Apple ID:" id="fkc-Rs-IVT">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" contentType="oneTimeCode" translatesAutoresizingMaskIntoConstraints="NO" id="Lhz-Ap-f59">
<rect key="frame" x="123" y="405" width="210" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="n3H-wV-TtL"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="0Lr-Jh-f79">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Jq1-9w-hV3">
<rect key="frame" x="121" y="405" width="214" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="6lr-jv-iUD"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="Label" id="uJY-N7-9LD">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZET-hK-4Dw">
<rect key="frame" x="33" y="362" width="82" height="19"/>
<constraints>
<constraint firstAttribute="height" constant="19" id="IIt-f5-FPd"/>
<constraint firstAttribute="width" constant="78" id="u0I-9g-53R"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="专用密码:" id="bhu-jM-Adb">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="F8M-wi-RjZ">
<rect key="frame" x="116" y="355" width="92" height="32"/>
<buttonCell key="cell" type="push" title="设置" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="pSe-lK-tax">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="clickedSPasswordBtn:" target="xPR-tb-wUt" id="0Uf-7X-evH"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qR2-3g-X5d">
<rect key="frame" x="445" y="347" width="142" height="48"/>
<constraints>
<constraint firstAttribute="width" constant="128" id="PrA-4z-KZy"/>
<constraint firstAttribute="height" constant="36" id="xi3-s8-gg0"/>
</constraints>
<buttonCell key="cell" type="push" title="提交上传" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="nTI-pN-mGw">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
<connections>
<action selector="clickedSubmitBtn:" target="xPR-tb-wUt" id="xf5-GT-zom"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KEp-13-nh2">
<rect key="frame" x="209" y="364" width="237" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="NPB-8q-bdI"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="Label" id="Ecv-9j-HZC">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="qR2-3g-X5d" secondAttribute="trailing" constant="20" symbolic="YES" id="0Uy-Lw-xpg"/>
<constraint firstItem="Jq1-9w-hV3" firstAttribute="centerY" secondItem="Te0-Hx-wTO" secondAttribute="centerY" id="6dM-w3-SvM"/>
<constraint firstItem="Lhz-Ap-f59" firstAttribute="leading" secondItem="Te0-Hx-wTO" secondAttribute="trailing" constant="10" id="8Ge-q1-Fzi"/>
<constraint firstItem="Jq1-9w-hV3" firstAttribute="width" secondItem="ctU-VA-UqD" secondAttribute="width" multiplier="0.35" id="FJr-Ge-3pT"/>
<constraint firstItem="Jq1-9w-hV3" firstAttribute="leading" secondItem="Te0-Hx-wTO" secondAttribute="trailing" constant="10" id="I4f-um-gMO"/>
<constraint firstItem="F8M-wi-RjZ" firstAttribute="centerY" secondItem="ZET-hK-4Dw" secondAttribute="centerY" id="ISv-kZ-IpT"/>
<constraint firstItem="F8M-wi-RjZ" firstAttribute="leading" secondItem="ZET-hK-4Dw" secondAttribute="trailing" constant="10" id="KHu-g2-Cm7"/>
<constraint firstItem="qR2-3g-X5d" firstAttribute="centerY" secondItem="F8M-wi-RjZ" secondAttribute="centerY" id="Nje-9G-sfa"/>
<constraint firstItem="Lhz-Ap-f59" firstAttribute="centerY" secondItem="Te0-Hx-wTO" secondAttribute="centerY" id="dKg-Mn-cCx"/>
<constraint firstItem="qR2-3g-X5d" firstAttribute="leading" secondItem="KEp-13-nh2" secondAttribute="trailing" constant="8" symbolic="YES" id="emp-Rf-Ffv"/>
<constraint firstItem="Te0-Hx-wTO" firstAttribute="top" secondItem="ctU-VA-UqD" secondAttribute="top" constant="25" id="fW0-CL-6J3"/>
<constraint firstItem="F8M-wi-RjZ" firstAttribute="width" secondItem="ZET-hK-4Dw" secondAttribute="width" id="kKn-eJ-49J"/>
<constraint firstItem="Lhz-Ap-f59" firstAttribute="width" secondItem="ctU-VA-UqD" secondAttribute="width" multiplier="0.35" id="kUG-Gm-30o"/>
<constraint firstItem="KEp-13-nh2" firstAttribute="centerY" secondItem="F8M-wi-RjZ" secondAttribute="centerY" id="rtO-eP-MzZ"/>
<constraint firstItem="ZET-hK-4Dw" firstAttribute="leading" secondItem="ctU-VA-UqD" secondAttribute="leading" constant="35" id="uFt-Iq-dPR"/>
<constraint firstItem="ZET-hK-4Dw" firstAttribute="top" secondItem="Te0-Hx-wTO" secondAttribute="bottom" constant="25" id="uXz-9Y-XME"/>
<constraint firstItem="Te0-Hx-wTO" firstAttribute="leading" secondItem="ctU-VA-UqD" secondAttribute="leading" constant="35" id="zPb-Pz-oFy"/>
<constraint firstItem="KEp-13-nh2" firstAttribute="leading" secondItem="F8M-wi-RjZ" secondAttribute="trailing" constant="10" id="zpc-3A-CaC"/>
</constraints>
</view>
<connections>
<outlet property="appIdTextField" destination="Lhz-Ap-f59" id="hfK-xk-cJS"/>
<outlet property="appIdTextView" destination="Jq1-9w-hV3" id="SFH-1T-gQ9"/>
<outlet property="spasswordLbl" destination="KEp-13-nh2" id="m2o-KO-BYO"/>
<outlet property="submitBtn" destination="qR2-3g-X5d" id="bA7-h8-oEq"/>
</connections>
</viewController>
<customObject id="NBS-xt-Oic" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="664" y="405"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>SUFeedURL</key>
<string>https://raw.githubusercontent.com/37iOS/AppleParty/main/AppleParty/SparkleUpdate/update.xml</string>
<key>SUPublicEDKey</key>
<string>ItVUtr4L9w9VfMGlzg7+cIcvSkruiygDcarlq8PTF7I=</string>
</dict>
</plist>

View File

@ -0,0 +1,199 @@
//
// APLogin2FAVC.swift
// AppleParty
//
// Created by HTC on 2022/3/17.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
class APLogin2FAVC: NSViewController {
public var cancelHandle: (() -> Void)?
public var successHandle: (() -> Void)?
@IBOutlet weak var phoneListBtn: NSPopUpButton!
@IBOutlet weak var sendCodeBtn: NSButton!
@IBOutlet weak var voiceCodeBtn: NSButton!
@IBOutlet weak var phoneCodeView: NSTextField!
@IBOutlet weak var tipsWarningView: NSTextField!
@IBOutlet weak var trusDeviceBtn: NSButton!
@IBOutlet weak var indicatorView: NSProgressIndicator!
@IBOutlet weak var verifyBtn: NSButton!
private var numbers: [PNumber] = [] //
private var isPhoneSecurity = false //
//
private var verifyCodeTimer: Timer?
private var lastTime: Int = 30
override func viewDidLoad() {
super.viewDidLoad()
phoneCodeView.delegate = self
fetchPhoneList()
trusDeviceBtn.state = InfoCenter.shared.trusDevice ? .on : .off
}
@IBAction func clickedCancelBtn(_ sender: NSButton) {
closeView()
cancelHandle?()
}
@IBAction func clickedSendCodeBtn(_ sender: NSButton) {
submitSecurityCode()
}
@IBAction func changeVoiceCodeBtn(_ sender: NSButton) {
sendCodeBtn.title = sender.state == .on ? "拨打语音验证码" : "发送短信验证码"
}
@IBAction func clickedVerifyBtn(_ sender: NSButton) {
verifySecurityCode()
}
@IBAction func clickedTrusDeviceBtn(_ sender: NSButton) {
InfoCenter.shared.trusDevice = sender.state == .on ? true : false
}
}
// MARK: -
extension APLogin2FAVC {
func fetchPhoneList() {
APClient.verifySecurityPhone(mode: "sms", phoneid: 0).request(showLoading: true) { [weak self] result, response, error in
if let err = error, let type = APClientErrorCode(rawValue: err.code) {
switch type {
case .privacyAcknowledgementRequired:
// phoneid
self?.phoneListBtn.removeAllItems()
let model = PhoneNumbers(body: result)
self?.numbers = model.numbers
for number in model.numbers {
self?.phoneListBtn.addItem(withTitle: number.num)
}
self?.phoneListBtn.selectItem(at: 0)
self?.showTips("一条包含验证码的信息已发送至您的设备。可输入设备验证码后点击验证以继续。\n或者点击“发送短信验证码”获取短信验证码。")
default:
APHUD.hide(message: err.localizedDescription)
}
}
}
}
func submitSecurityCode() {
isPhoneSecurity = true
let mode = voiceCodeBtn.state == .on ? "voice" : "sms"
let phoneId = numbers[phoneListBtn.indexOfSelectedItem].id
APClient.verifySecurityPhone(mode: mode, phoneid: phoneId).request(showLoading: true) { [weak self] result, response, error in
let code = response?.statusCode
if [200, 423].contains(code) {
let msg = self?.voiceCodeBtn.state == .on ? "请求拨打语音电话,请收听~" : "验证码已发送,请查收~"
self?.showTips(msg)
self?.verifyCodeCountdown()
} else {
self?.showTips("\(code ?? 0),\(error.debugDescription)")
}
}
}
func verifySecurityCode() {
let code = phoneCodeView.stringValue
let phoneId = numbers[phoneListBtn.indexOfSelectedItem].id
let mode = voiceCodeBtn.state == .on ? "voice" : "sms"
let type = isPhoneSecurity ? APClient.SecurityCode.sms(code: code, phoneNumberId: phoneId, mode: mode) : APClient.SecurityCode.device(code: code)
viewEnabled(false)
APClient.submitSecurityCode(code: type).request { [weak self] result, response, error in
self?.viewEnabled(true)
let code = response?.statusCode
switch code {
case 200, 201, 202, 203, 204:
self?.validateSession()
case 400:
let errors = dictionaryArray(result["service_errors"])
let msg = string(from: errors.first?["message"])
self?.showTips(msg)
default:
self?.showTips("\(code ?? 0),\(error.debugDescription)")
}
}
}
func validateSession() {
viewEnabled(false)
APClient.signInSession.request { [weak self] result, response, error in
self?.viewEnabled(true)
let code = response?.statusCode
switch code {
case 200, 201:
UserCenter.shared.isAuthorized = true
self?.successHandle?()
self?.closeView()
default:
let errors = dictionaryArray(result["serviceErrors"])
let msg = string(from: errors.first?["message"])
self?.showTips(msg.isEmpty ? error.debugDescription : msg)
}
}
}
}
// MARK: -
extension APLogin2FAVC {
func closeView() {
guard let window = view.window, let parent = window.sheetParent
else { return }
parent.endSheet(window)
}
func showTips(_ text: String) {
if text.isEmpty {
tipsWarningView.isHidden = true
tipsWarningView.stringValue = ""
} else {
tipsWarningView.stringValue = text
tipsWarningView.isHidden = false
}
}
func viewEnabled(_ isEnabled: Bool) {
showTips("")
verifyBtn.isEnabled = isEnabled
isEnabled ? indicatorView.stopAnimation(nil) : indicatorView.startAnimation(nil)
}
func verifyCodeCountdown() {
self.lastTime = 30
self.sendCodeBtn.title = "\(self.lastTime)s 后重试"
self.sendCodeBtn.isEnabled = false
self.verifyCodeTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.verifyCodeTime), userInfo: nil, repeats: true)
}
//
@objc func verifyCodeTime() {
lastTime -= 1
sendCodeBtn.title = "\(self.lastTime)s 后重试"
if lastTime <= 0 {
sendCodeBtn.title = "重新发送验证码"
sendCodeBtn.isEnabled = true
verifyCodeTimer?.invalidate()
}
}
}
// MARK: - NSTextFieldDelegate
extension APLogin2FAVC: NSTextFieldDelegate {
func controlTextDidChange(_ obj: Notification) {
if phoneCodeView.stringValue.count == 6 {
verifyBtn.isEnabled = true
} else {
verifyBtn.isEnabled = false
}
}
}

View File

@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="APLogin2FAVC" customModule="AppleParty" customModuleProvider="target">
<connections>
<outlet property="indicatorView" destination="rsC-Tg-KKs" id="OOV-8V-cF0"/>
<outlet property="phoneCodeView" destination="npy-jX-GQb" id="aXK-hj-Jg3"/>
<outlet property="phoneListBtn" destination="YPd-lO-U4e" id="Sg5-Hb-iUb"/>
<outlet property="sendCodeBtn" destination="rx9-Oh-Gtq" id="cYT-hl-Kc7"/>
<outlet property="tipsWarningView" destination="ijo-CH-sfg" id="PLO-uS-eBh"/>
<outlet property="trusDeviceBtn" destination="8SV-WX-fvn" id="kyK-aJ-vJK"/>
<outlet property="verifyBtn" destination="E5o-kz-kvG" id="kcn-Ez-LwY"/>
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
<outlet property="voiceCodeBtn" destination="kff-Qr-scI" id="khK-sS-hCc"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="Hz6-mo-xeY">
<rect key="frame" x="0.0" y="0.0" width="511" height="352"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BiF-4T-JA0">
<rect key="frame" x="53" y="8" width="94" height="37"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="43L-Br-dDy"/>
<constraint firstAttribute="width" constant="80" id="OjC-db-Fqd"/>
</constraints>
<buttonCell key="cell" type="push" title="取消" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="IIa-Xh-1A4">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
Gw
</string>
</buttonCell>
<connections>
<action selector="clickedCancelBtn:" target="-2" id="KIQ-L6-769"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="E5o-kz-kvG">
<rect key="frame" x="364" y="8" width="94" height="37"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="8Om-Qw-Vr5"/>
<constraint firstAttribute="width" constant="80" id="vyG-oy-cDO"/>
</constraints>
<buttonCell key="cell" type="push" title="验证" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" inset="2" id="HKe-2K-33K">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
<connections>
<action selector="clickedVerifyBtn:" target="-2" id="9hq-Pv-iKB"/>
</connections>
</button>
<progressIndicator maxValue="100" displayedWhenStopped="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="rsC-Tg-KKs">
<rect key="frame" x="427" y="20" width="16" height="16"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="16" id="P2w-80-9Ta"/>
<constraint firstAttribute="height" constant="16" id="Qu4-Tu-Jfl"/>
<constraint firstAttribute="width" constant="16" id="WQE-Yc-Xrv"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="16" id="dQk-O7-QHd"/>
</constraints>
</progressIndicator>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BC7-jj-els">
<rect key="frame" x="28" y="307" width="455" height="25"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="25" id="gUT-eG-Utf"/>
<constraint firstAttribute="height" constant="25" id="s9O-0v-3Ni"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="双重认证" id="FQq-Av-0eD">
<font key="font" metaFont="system" size="18"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YPd-lO-U4e">
<rect key="frame" x="187" y="248" width="268" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="yKX-Nl-Hdr"/>
</constraints>
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" arrowPosition="arrowAtCenter" id="vpb-by-gTu">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="msT-eh-QNY"/>
</popUpButtonCell>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="vqk-uI-9AU">
<rect key="frame" x="58" y="257" width="124" height="15"/>
<constraints>
<constraint firstAttribute="height" constant="15" id="DII-jQ-Tud"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="选择用手机短信接收:" id="Lbc-GQ-7wH">
<font key="font" metaFont="cellTitle"/>
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="npy-jX-GQb">
<rect key="frame" x="60" y="127" width="391" height="35"/>
<constraints>
<constraint firstAttribute="height" constant="35" id="5C0-ZB-9O0"/>
</constraints>
<textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" focusRingType="none" alignment="center" placeholderString="请输入验证码" bezelStyle="round" id="Ukz-vL-Zpn">
<font key="font" metaFont="systemBold" size="20"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rx9-Oh-Gtq">
<rect key="frame" x="324" y="210" width="134" height="32"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="120" id="D0A-gg-DAF"/>
<constraint firstAttribute="height" constant="20" id="bE0-bG-WSX"/>
</constraints>
<buttonCell key="cell" type="push" title="发送短信验证码" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="54B-tN-4ys">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="clickedSendCodeBtn:" target="-2" id="UE7-hg-Lnj"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kff-Qr-scI">
<rect key="frame" x="344" y="186" width="107" height="22"/>
<constraints>
<constraint firstAttribute="width" constant="105" id="MxK-Vd-EtE"/>
<constraint firstAttribute="height" constant="20" id="OqC-X2-qa4"/>
</constraints>
<buttonCell key="cell" type="check" title="拨打语音来电" bezelStyle="regularSquare" imagePosition="left" inset="2" id="gVy-us-tZh">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="changeVoiceCodeBtn:" target="-2" id="8jt-HG-YUL"/>
</connections>
</button>
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ijo-CH-sfg">
<rect key="frame" x="58" y="101" width="395" height="16"/>
<constraints>
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="500" id="53u-f7-HK1"/>
</constraints>
<textFieldCell key="cell" truncatesLastVisibleLine="YES" allowsUndo="NO" title="请输入正确的验证码" id="omC-g0-Aha">
<font key="font" metaFont="system"/>
<color key="textColor" name="systemOrangeColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8SV-WX-fvn">
<rect key="frame" x="283" y="20" width="80" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="mX6-l7-rZj"/>
<constraint firstAttribute="width" constant="78" id="vDy-Dk-GSE"/>
</constraints>
<buttonCell key="cell" type="check" title="信任设备" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="VBA-AP-I7T">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" size="13" name="STSongti-SC-Regular"/>
</buttonCell>
<connections>
<action selector="clickedTrusDeviceBtn:" target="-2" id="iIm-cY-7Z6"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="BiF-4T-JA0" secondAttribute="bottom" constant="15" id="1Fq-AL-3Ws"/>
<constraint firstItem="E5o-kz-kvG" firstAttribute="centerY" secondItem="BiF-4T-JA0" secondAttribute="centerY" id="612-VZ-uMP"/>
<constraint firstAttribute="trailing" secondItem="YPd-lO-U4e" secondAttribute="trailing" constant="60" id="EJc-Vs-Afo"/>
<constraint firstAttribute="trailing" secondItem="rx9-Oh-Gtq" secondAttribute="trailing" constant="60" id="Euw-SM-wUx"/>
<constraint firstItem="YPd-lO-U4e" firstAttribute="leading" secondItem="vqk-uI-9AU" secondAttribute="trailing" constant="10" id="G4u-U5-hRW"/>
<constraint firstItem="vqk-uI-9AU" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="Kat-r9-QXI"/>
<constraint firstItem="8SV-WX-fvn" firstAttribute="centerY" secondItem="E5o-kz-kvG" secondAttribute="centerY" id="Pfn-8j-tgF"/>
<constraint firstItem="E5o-kz-kvG" firstAttribute="leading" secondItem="8SV-WX-fvn" secondAttribute="trailing" constant="8" id="Q3C-bW-QSW"/>
<constraint firstAttribute="trailing" secondItem="kff-Qr-scI" secondAttribute="trailing" constant="60" id="Sxo-QH-rVU"/>
<constraint firstAttribute="trailing" secondItem="npy-jX-GQb" secondAttribute="trailing" constant="60" id="WBf-2q-NVj"/>
<constraint firstItem="npy-jX-GQb" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="WxQ-Kb-Jj4"/>
<constraint firstAttribute="trailing" secondItem="BC7-jj-els" secondAttribute="trailing" constant="30" id="cTr-XH-o6b"/>
<constraint firstAttribute="trailing" secondItem="ijo-CH-sfg" secondAttribute="trailing" constant="60" id="ccV-Qd-svA"/>
<constraint firstItem="YPd-lO-U4e" firstAttribute="centerY" secondItem="vqk-uI-9AU" secondAttribute="centerY" id="eus-h0-cM0"/>
<constraint firstItem="npy-jX-GQb" firstAttribute="top" secondItem="kff-Qr-scI" secondAttribute="bottom" constant="25" id="fDH-b7-v0g"/>
<constraint firstAttribute="trailing" secondItem="E5o-kz-kvG" secondAttribute="trailing" constant="60" id="gdM-HS-dPr"/>
<constraint firstItem="rsC-Tg-KKs" firstAttribute="trailing" secondItem="E5o-kz-kvG" secondAttribute="trailing" constant="-8" id="hFD-yR-GoO"/>
<constraint firstItem="BiF-4T-JA0" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="nQA-ny-dXA"/>
<constraint firstItem="kff-Qr-scI" firstAttribute="top" secondItem="rx9-Oh-Gtq" secondAttribute="bottom" constant="10" id="oaD-ya-2cr"/>
<constraint firstItem="ijo-CH-sfg" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="p0O-Ua-B6c"/>
<constraint firstItem="vqk-uI-9AU" firstAttribute="top" secondItem="BC7-jj-els" secondAttribute="bottom" constant="35" id="p9I-Me-6fq"/>
<constraint firstItem="BC7-jj-els" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="20" symbolic="YES" id="qEA-eh-nav"/>
<constraint firstItem="rsC-Tg-KKs" firstAttribute="centerY" secondItem="E5o-kz-kvG" secondAttribute="centerY" id="rlF-J0-c0j"/>
<constraint firstItem="ijo-CH-sfg" firstAttribute="top" secondItem="npy-jX-GQb" secondAttribute="bottom" constant="10" id="voh-Sr-6pf"/>
<constraint firstItem="rx9-Oh-Gtq" firstAttribute="top" secondItem="YPd-lO-U4e" secondAttribute="bottom" constant="15" id="vvK-es-FLv"/>
<constraint firstItem="BC7-jj-els" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="30" id="yL4-AI-CiL"/>
</constraints>
<point key="canvasLocation" x="154.5" y="194"/>
</customView>
</objects>
</document>

View File

@ -0,0 +1,234 @@
//
// APLoginVC.swift
// AppleParty
//
// Created by HTC on 2022/3/17.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
class APLoginVC: NSViewController {
public var cancelHandle: (() -> Void)?
public var successHandle: (() -> Void)?
@IBOutlet weak var accountView: NSTextField!
@IBOutlet weak var passwordView: NSSecureTextField!
//
@IBOutlet weak var historyBox: NSBox!
@IBOutlet weak var tableView: NSTableView!
@IBOutlet weak var tipsWarningView: NSTextField!
@IBOutlet weak var autoLoginBtn: NSButton!
@IBOutlet weak var indicatorView: NSProgressIndicator!
@IBOutlet weak var loginBtn: NSButton!
override func viewDidLoad() {
super.viewDidLoad()
accountView.delegate = self
passwordView.delegate = self
tipsWarningView.maximumNumberOfLines = 5
//
let user = UserCenter.shared.loginedUser
let name = user.appleid
let pwd = user.password
guard name.count > 0, pwd.count > 0 else { return }
accountView.stringValue = name
passwordView.stringValue = pwd
viewEnabled(true)
//
if UserCenter.shared.isAutoLogin || UserCenter.shared.isFirstTime {
loginAccount()
UserCenter.shared.isFirstTime = false
}
}
@IBAction func clickedCancelBtn(_ sender: NSButton) {
closeView()
cancelHandle?()
}
@IBAction func showAccountHistoryList(_ sender: Any) {
if historyBox.isHidden {
tableView.delegate = self
tableView.dataSource = self
tableView.reloadData()
}
historyBox.isHidden = !historyBox.isHidden
}
@IBAction func clickedLoginBtn(_ sender: NSButton) {
loginAccount()
}
}
// MARK: -
extension APLoginVC {
func loginAccount() {
if accountView.stringValue.isEmpty {
showTips("苹果账号不能为空!")
return
}
if passwordView.stringValue.isEmpty {
showTips("密码不能为空!")
return
}
viewEnabled(false)
let account = accountView.stringValue
let pwd = passwordView.stringValue
APClient.signIn(account: account, password: pwd).request { [weak self] result, response, error in
self?.viewEnabled(true)
if let err = error, let type = APClientErrorCode(rawValue: err.code) {
switch type {
case .notAuthorized:
self?.showTips("Apple ID 或密码不正确")
case .twoStepOrFactor:
//
if self?.autoLoginBtn.state == .on {
UserCenter.shared.isAutoLogin = true
UserCenter.shared.loginedUser = User(appleid: account, password: pwd)
}
//
let vc = APLogin2FAVC()
vc.cancelHandle = { [weak self] in
self?.viewEnabled(true)
}
vc.successHandle = { [weak self] in
self?.trusDevice()
self?.successHandle?()
self?.closeView()
}
let pannel = NSPanel(contentViewController: vc)
pannel.setFrame(NSRect(origin: .zero, size: NSSize(width: 500, height: 360)), display: true)
self?.view.window?.beginSheet(pannel, completionHandler: nil)
default:
self?.showTips(err.localizedDescription)
}
return
}
let code = response?.statusCode
//
if code == 200 {
//
if self?.autoLoginBtn.state == .on {
UserCenter.shared.isAutoLogin = true
UserCenter.shared.loginedUser = User(appleid: account, password: pwd)
}
self?.validateSession()
} else {
self?.showTips("\(code ?? 0),\(error.debugDescription)")
}
}
}
func validateSession() {
viewEnabled(false)
APClient.signInSession.request { [weak self] result, response, error in
self?.viewEnabled(true)
let code = response?.statusCode
switch code {
case 200, 201:
UserCenter.shared.isAuthorized = true
self?.trusDevice()
self?.successHandle?()
self?.closeView()
default:
let errors = dictionaryArray(result["serviceErrors"])
let msg = string(from: errors.first?["message"])
self?.showTips(msg.isEmpty ? error.debugDescription : msg)
}
}
}
func trusDevice() {
guard InfoCenter.shared.trusDevice else { return}
APClient.trusDevice(isTrus: true).request { result, response, error in
if response?.statusCode == 204 {
debugPrint("信任设备成功~")
}
}
}
}
// MARK: -
extension APLoginVC {
func showTips(_ text: String) {
if text.isEmpty {
tipsWarningView.isHidden = true
tipsWarningView.stringValue = ""
} else {
tipsWarningView.stringValue = text
tipsWarningView.isHidden = false
}
}
func viewEnabled(_ isEnabled: Bool) {
showTips("")
loginBtn.isEnabled = isEnabled
historyBox.isHidden = true
isEnabled ? indicatorView.stopAnimation(nil) : indicatorView.startAnimation(nil)
}
func closeView() {
guard let window = view.window, let parent = window.sheetParent
else { return }
parent.endSheet(window)
}
}
// MARK: - NSTextFieldDelegate
extension APLoginVC: NSTextFieldDelegate {
func controlTextDidChange(_ obj: Notification) {
if !accountView.stringValue.isEmpty && !passwordView.stringValue.isEmpty {
loginBtn.isEnabled = true
} else {
loginBtn.isEnabled = false
showTips("")
}
}
}
// MARK: - NSTableViewDelegate
extension APLoginVC: NSTableViewDelegate, NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return UserCenter.shared.historyUser.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
if tableColumn?.identifier == NSUserInterfaceItemIdentifier(rawValue: "nameColumn") {
let cellIdentifier = NSUserInterfaceItemIdentifier(rawValue: "nameCell")
guard let cellView = tableView.makeView(withIdentifier: cellIdentifier, owner: self) as? NSTableCellView else { return nil }
cellView.textField?.stringValue = UserCenter.shared.historyUser[row].appleid
return cellView
}
return nil
}
func tableViewSelectionDidChange(_ notification: Notification){
let tableView = notification.object as! NSTableView
let clickedRow = tableView.selectedRow
guard clickedRow >= 0 else {
return
}
tableView.deselectRow(clickedRow)
accountView.stringValue = UserCenter.shared.historyUser[clickedRow].appleid
passwordView.stringValue = UserCenter.shared.historyUser[clickedRow].password
showAccountHistoryList(clickedRow)
}
}

View File

@ -0,0 +1,258 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="APLoginVC" customModule="AppleParty" customModuleProvider="target">
<connections>
<outlet property="accountView" destination="7Gg-CB-YAd" id="HvI-ne-iPZ"/>
<outlet property="autoLoginBtn" destination="aZC-zX-iLA" id="QDv-Kf-saw"/>
<outlet property="historyBox" destination="w4L-Kl-k0V" id="4gE-iK-bgm"/>
<outlet property="indicatorView" destination="3JL-7f-Xfx" id="ebS-gn-1fo"/>
<outlet property="loginBtn" destination="Idm-D5-Bl1" id="V3g-00-OAu"/>
<outlet property="passwordView" destination="Jin-lZ-ObE" id="sWU-2K-aBK"/>
<outlet property="tableView" destination="Hst-cR-jaZ" id="PWz-Me-Trw"/>
<outlet property="tipsWarningView" destination="A1W-oM-xUt" id="O66-oN-G3J"/>
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="Hz6-mo-xeY">
<rect key="frame" x="0.0" y="0.0" width="480" height="301"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sBv-Ga-3Ig">
<rect key="frame" x="8" y="256" width="464" height="25"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="dMh-mt-gPl"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="App Store Connect" id="4jB-x9-D95">
<font key="font" metaFont="systemBold" size="18"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" textCompletion="NO" contentType="username" translatesAutoresizingMaskIntoConstraints="NO" id="7Gg-CB-YAd">
<rect key="frame" x="60" y="176" width="360" height="45"/>
<constraints>
<constraint firstAttribute="height" constant="45" id="S1v-qX-ZK3"/>
</constraints>
<textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" focusRingType="none" alignment="left" placeholderString="请输入苹果账号" usesSingleLineMode="YES" bezelStyle="round" id="EKF-vP-tSd">
<font key="font" metaFont="system" size="16"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button translatesAutoresizingMaskIntoConstraints="NO" id="oho-cw-blu">
<rect key="frame" x="379" y="186" width="26.5" height="26"/>
<constraints>
<constraint firstAttribute="width" constant="26" id="20z-6h-Nzr"/>
<constraint firstAttribute="height" constant="26" id="tTj-x3-L8V"/>
</constraints>
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="NSTouchBarGoDownTemplate" imagePosition="only" alignment="center" state="on" imageScaling="proportionallyUpOrDown" inset="2" id="6R3-hY-fkM">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="showAccountHistoryList:" target="-2" id="KJ0-C0-1ma"/>
</connections>
</button>
<secureTextField focusRingType="none" verticalHuggingPriority="750" contentType="password" translatesAutoresizingMaskIntoConstraints="NO" id="Jin-lZ-ObE">
<rect key="frame" x="60" y="106" width="360" height="45"/>
<constraints>
<constraint firstAttribute="height" constant="45" id="wdb-sF-BFJ"/>
</constraints>
<secureTextFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" focusRingType="none" placeholderString="请输入密码" usesSingleLineMode="YES" bezelStyle="round" id="TVN-dn-g47">
<font key="font" metaFont="system" size="16"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</secureTextFieldCell>
</secureTextField>
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="A1W-oM-xUt">
<rect key="frame" x="58" y="80" width="364" height="16"/>
<constraints>
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="500" id="ErD-KS-kJh"/>
</constraints>
<textFieldCell key="cell" truncatesLastVisibleLine="YES" allowsUndo="NO" title="请输入正确的苹果账号" id="Los-Ya-nHe">
<font key="font" metaFont="system"/>
<color key="textColor" name="systemOrangeColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Idm-D5-Bl1">
<rect key="frame" x="333" y="13" width="94" height="37"/>
<constraints>
<constraint firstAttribute="width" constant="80" id="VNU-X5-3RU"/>
<constraint firstAttribute="height" constant="25" id="edt-sA-1o8"/>
</constraints>
<buttonCell key="cell" type="push" title="登陆" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" inset="2" id="ack-oU-iyj">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
<connections>
<action selector="clickedLoginBtn:" target="-2" id="4L9-TV-8kK"/>
</connections>
</button>
<progressIndicator maxValue="100" displayedWhenStopped="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="3JL-7f-Xfx">
<rect key="frame" x="396" y="25" width="16" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="Ayq-6g-553"/>
<constraint firstAttribute="width" constant="16" id="La3-z9-Sui"/>
</constraints>
</progressIndicator>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xXW-Zy-0Qc">
<rect key="frame" x="53" y="13" width="94" height="37"/>
<constraints>
<constraint firstAttribute="width" constant="80" id="ADG-NQ-ah3"/>
<constraint firstAttribute="height" constant="25" id="J7q-04-O9h"/>
</constraints>
<buttonCell key="cell" type="push" title="取消" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="jWN-Ih-eRv">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
Gw
</string>
</buttonCell>
<connections>
<action selector="clickedCancelBtn:" target="-2" id="hIR-7f-UuB"/>
</connections>
</button>
<box hidden="YES" boxType="custom" cornerRadius="3" translatesAutoresizingMaskIntoConstraints="NO" id="w4L-Kl-k0V">
<rect key="frame" x="60" y="89" width="360" height="87"/>
<view key="contentView" id="h0y-kI-zOZ">
<rect key="frame" x="1" y="1" width="358" height="85"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="57z-B0-8Z4">
<rect key="frame" x="0.0" y="0.0" width="358" height="85"/>
<clipView key="contentView" ambiguous="YES" id="cZ6-vs-KNu">
<rect key="frame" x="1" y="1" width="356" height="83"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" viewBased="YES" id="Hst-cR-jaZ">
<rect key="frame" x="0.0" y="0.0" width="356" height="83"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="keyboardFocusIndicatorColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="nameColumn" width="293" minWidth="40" maxWidth="1000" id="Elj-Mz-Pc2">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="7J8-oV-aGc">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="nameCell" id="vOy-rZ-Yvz">
<rect key="frame" x="1" y="1" width="302" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="VBI-EM-0Az">
<rect key="frame" x="0.0" y="1" width="302" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="lel-L2-fmK">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="VBI-EM-0Az" firstAttribute="centerY" secondItem="vOy-rZ-Yvz" secondAttribute="centerY" id="b5F-uT-Da1"/>
<constraint firstItem="VBI-EM-0Az" firstAttribute="centerX" secondItem="vOy-rZ-Yvz" secondAttribute="centerX" id="mCL-MA-lFT"/>
<constraint firstItem="VBI-EM-0Az" firstAttribute="leading" secondItem="vOy-rZ-Yvz" secondAttribute="leading" constant="2" id="qWM-YN-llR"/>
</constraints>
<connections>
<outlet property="textField" destination="VBI-EM-0Az" id="yNq-5s-Bj4"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
</tableColumns>
</tableView>
</subviews>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</clipView>
<constraints>
<constraint firstAttribute="height" constant="85" id="3Ms-uE-pue"/>
</constraints>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="NQa-5q-5Ii">
<rect key="frame" x="1" y="118" width="238" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="JCe-qU-DMF">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="57z-B0-8Z4" secondAttribute="trailing" id="n5T-rg-MeV"/>
<constraint firstItem="57z-B0-8Z4" firstAttribute="top" secondItem="h0y-kI-zOZ" secondAttribute="top" id="sUR-ay-LmY"/>
<constraint firstAttribute="bottom" secondItem="57z-B0-8Z4" secondAttribute="bottom" id="su9-ZN-m2r"/>
<constraint firstItem="57z-B0-8Z4" firstAttribute="leading" secondItem="h0y-kI-zOZ" secondAttribute="leading" id="w9o-Jf-QKB"/>
</constraints>
</view>
<color key="borderColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="fillColor" name="keyboardFocusIndicatorColor" catalog="System" colorSpace="catalog"/>
</box>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aZC-zX-iLA">
<rect key="frame" x="252" y="24" width="80" height="18"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="L2B-IE-Iw5"/>
<constraint firstAttribute="width" constant="78" id="UAX-pP-1Ux"/>
</constraints>
<buttonCell key="cell" type="check" title="自动登陆" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="1M1-Hc-ZVP">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" size="13" name=".PingFangSC-Regular"/>
</buttonCell>
</button>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="Idm-D5-Bl1" secondAttribute="bottom" constant="20" symbolic="YES" id="2tD-oH-o3G"/>
<constraint firstItem="sBv-Ga-3Ig" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="20" symbolic="YES" id="4dv-jZ-P43"/>
<constraint firstItem="A1W-oM-xUt" firstAttribute="top" secondItem="Jin-lZ-ObE" secondAttribute="bottom" constant="10" id="9xE-bu-hFg"/>
<constraint firstAttribute="trailing" secondItem="sBv-Ga-3Ig" secondAttribute="trailing" constant="10" id="AkC-oy-LGi"/>
<constraint firstItem="oho-cw-blu" firstAttribute="centerY" secondItem="7Gg-CB-YAd" secondAttribute="centerY" id="AmX-5A-xy6"/>
<constraint firstItem="xXW-Zy-0Qc" firstAttribute="leading" secondItem="7Gg-CB-YAd" secondAttribute="leading" id="Bpi-sa-Ze6"/>
<constraint firstItem="aZC-zX-iLA" firstAttribute="centerY" secondItem="Idm-D5-Bl1" secondAttribute="centerY" id="CBg-Vo-SEB"/>
<constraint firstItem="7Gg-CB-YAd" firstAttribute="top" secondItem="sBv-Ga-3Ig" secondAttribute="bottom" constant="35" id="Jcm-UL-9NZ"/>
<constraint firstAttribute="trailing" secondItem="A1W-oM-xUt" secondAttribute="trailing" constant="60" id="QMh-YH-RXq"/>
<constraint firstItem="w4L-Kl-k0V" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="RNm-RF-ypF"/>
<constraint firstItem="7Gg-CB-YAd" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="RdU-Dr-MaJ"/>
<constraint firstItem="Idm-D5-Bl1" firstAttribute="leading" secondItem="aZC-zX-iLA" secondAttribute="trailing" constant="8" id="SeL-eF-Ry3"/>
<constraint firstAttribute="trailing" secondItem="7Gg-CB-YAd" secondAttribute="trailing" constant="60" id="TSS-Ed-8Xq"/>
<constraint firstItem="w4L-Kl-k0V" firstAttribute="top" secondItem="7Gg-CB-YAd" secondAttribute="bottom" id="U5l-fh-Scp"/>
<constraint firstItem="xXW-Zy-0Qc" firstAttribute="centerY" secondItem="Idm-D5-Bl1" secondAttribute="centerY" id="UD7-om-oum"/>
<constraint firstItem="Idm-D5-Bl1" firstAttribute="trailing" secondItem="7Gg-CB-YAd" secondAttribute="trailing" id="cvS-ic-j7R"/>
<constraint firstItem="3JL-7f-Xfx" firstAttribute="centerY" secondItem="Idm-D5-Bl1" secondAttribute="centerY" id="dyq-1d-Hfr"/>
<constraint firstItem="A1W-oM-xUt" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="eP5-pI-Qi5"/>
<constraint firstItem="oho-cw-blu" firstAttribute="trailing" secondItem="7Gg-CB-YAd" secondAttribute="trailing" constant="-15" id="f7e-fo-miL"/>
<constraint firstItem="3JL-7f-Xfx" firstAttribute="trailing" secondItem="Idm-D5-Bl1" secondAttribute="trailing" constant="-8" id="mnS-Xu-Lmx"/>
<constraint firstItem="sBv-Ga-3Ig" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="10" id="qrQ-Bw-EaS"/>
<constraint firstItem="Jin-lZ-ObE" firstAttribute="top" secondItem="7Gg-CB-YAd" secondAttribute="bottom" constant="25" id="sd5-U5-yuf"/>
<constraint firstAttribute="trailing" secondItem="w4L-Kl-k0V" secondAttribute="trailing" constant="60" id="veQ-tN-yXm"/>
<constraint firstAttribute="trailing" secondItem="Jin-lZ-ObE" secondAttribute="trailing" constant="60" id="zBA-Lg-g3u"/>
<constraint firstItem="Jin-lZ-ObE" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="zzz-se-IG0"/>
</constraints>
<point key="canvasLocation" x="-202" y="175.5"/>
</customView>
</objects>
<resources>
<image name="NSTouchBarGoDownTemplate" width="16" height="9"/>
</resources>
</document>

View File

@ -0,0 +1,137 @@
//
// APWebLoginVC.swift
// AppleParty
//
// Created by HTC on 2024/10/29.
// Copyright © 2024 37 Mobile Games. All rights reserved.
//
import Cocoa
class APWebLoginVC: NSViewController {
public var cancelHandle: (() -> Void)?
public var successHandle: (() -> Void)?
private var webCore: AppleWebLoginCore? = nil
@IBOutlet weak var loginBtn: NSButton!
@IBOutlet weak var cancelBtn: NSButton!
@IBOutlet weak var indicatorView: NSProgressIndicator!
@IBOutlet weak var tipsWarningView: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
}
@IBAction func clickedCancelBtn(_ sender: NSButton) {
closeView()
cancelHandle?()
}
@IBAction func clickedLoginBtn(_ sender: NSButton) {
validateSession()
}
//
func validateSession() {
viewEnabled(false)
APClient.signInSession.request { [weak self] result, response, error in
self?.viewEnabled(true)
let code = response?.statusCode
switch code {
case 200, 201:
UserCenter.shared.isAuthorized = true
self?.successHandle?()
self?.closeView()
default:
let errors = dictionaryArray(result["serviceErrors"])
let msg = string(from: errors.first?["message"])
self?.showTips(msg.isEmpty ? error.debugDescription : msg)
//
self?.cancelBtn.isEnabled = false
self?.loginBtn.isEnabled = false
self?.loginWithWeb()
}
}
}
func loginWithWeb() {
let appleWebLoginCore = AppleWebLoginCore()
// webView
self.view.addSubview(appleWebLoginCore.webView)
let closeButton = NSButton(title: "取消", target: self, action: #selector(closeButtonClicked))
closeButton.attributedTitle = NSAttributedString(string: "取消", attributes: [NSAttributedString.Key.foregroundColor: NSColor.gray])
closeButton.keyEquivalent = "\u{1B}" // `esc`
appleWebLoginCore.webView.addSubview(closeButton)
// webView
appleWebLoginCore.webView.translatesAutoresizingMaskIntoConstraints = false
closeButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
appleWebLoginCore.webView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
appleWebLoginCore.webView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
appleWebLoginCore.webView.topAnchor.constraint(equalTo: self.view.topAnchor),
appleWebLoginCore.webView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
closeButton.trailingAnchor.constraint(equalTo: appleWebLoginCore.webView.trailingAnchor, constant: -10),
closeButton.topAnchor.constraint(equalTo: appleWebLoginCore.webView.topAnchor, constant: 10)
])
//
appleWebLoginCore.installFirstLoadCompleteTrap {
//
print("First load complete")
}
appleWebLoginCore.installCredentialPopulationTrap { token, cookies in
//
print("Received cookies: \(cookies)")
print("Received token: \(token)")
if let cks = APClientSession.shared.config.httpCookieStorage?.cookies {
for ck in cks {
APClientSession.shared.config.httpCookieStorage?.deleteCookie(ck)
}
}
for cookie in cookies {
APClientSession.shared.config.httpCookieStorage?.setCookie(cookie)
}
// APClientSession.shared.config.headers.update(name: "Cookie", value: "myacinfo=\(token);")
self.validateSession()
}
self.webCore = appleWebLoginCore
}
@objc func closeButtonClicked() {
//
print("关闭按钮被点击")
closeView()
}
func viewEnabled(_ isEnabled: Bool) {
showTips("")
loginBtn.isEnabled = isEnabled
isEnabled ? indicatorView.stopAnimation(nil) : indicatorView.startAnimation(nil)
}
func showTips(_ text: String) {
if text.isEmpty {
tipsWarningView.isHidden = true
tipsWarningView.stringValue = ""
} else {
tipsWarningView.stringValue = text
tipsWarningView.isHidden = false
}
}
func closeView() {
guard let window = view.window, let parent = window.sheetParent
else { return }
parent.endSheet(window)
}
}

View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23094" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23094"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="APWebLoginVC" customModule="AppleParty" customModuleProvider="target">
<connections>
<outlet property="cancelBtn" destination="qxM-hm-PWT" id="UAd-lY-N0a"/>
<outlet property="indicatorView" destination="dtJ-5J-DxP" id="zQo-tM-9d5"/>
<outlet property="loginBtn" destination="umT-YS-2Tw" id="oY4-Wq-xzj"/>
<outlet property="tipsWarningView" destination="3FV-OQ-nKf" id="NkT-JR-Dnu"/>
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="Hz6-mo-xeY">
<rect key="frame" x="0.0" y="0.0" width="480" height="300"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="1rM-sz-rlk">
<rect key="frame" x="8" y="260" width="464" height="25"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="tve-kf-cij"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="App Store Connect" id="VHb-gH-1GO">
<font key="font" metaFont="systemBold" size="18"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="umT-YS-2Tw">
<rect key="frame" x="193" y="65" width="94" height="37"/>
<buttonCell key="cell" type="push" title="登录" bezelStyle="rounded" alignment="center" borderStyle="border" inset="2" id="ReJ-dw-xtp">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
<constraints>
<constraint firstAttribute="width" constant="80" id="Ebj-j4-bbZ"/>
<constraint firstAttribute="height" constant="25" id="erd-oj-9Nc"/>
</constraints>
<connections>
<action selector="clickedLoginBtn:" target="-2" id="bzx-ik-z4B"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qxM-hm-PWT">
<rect key="frame" x="193" y="28" width="94" height="37"/>
<buttonCell key="cell" type="push" title="取消" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Fhv-gb-seN">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
Gw
</string>
</buttonCell>
<constraints>
<constraint firstAttribute="height" constant="25" id="Zo8-yT-7mB"/>
<constraint firstAttribute="width" constant="80" id="aWd-Lq-3SZ"/>
</constraints>
<connections>
<action selector="clickedCancelBtn:" target="-2" id="Y33-Od-paG"/>
</connections>
</button>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3FV-OQ-nKf">
<rect key="frame" x="13" y="120" width="454" height="80"/>
<constraints>
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="550" id="Fad-vK-Ru4"/>
<constraint firstAttribute="height" constant="80" id="tdV-WD-iKH"/>
</constraints>
<textFieldCell key="cell" truncatesLastVisibleLine="YES" enabled="NO" allowsUndo="NO" alignment="center" id="i97-j7-mQi">
<font key="font" size="14" name=".PingFangUITextSC-Regular"/>
<string key="title">提示:
登录通过 https://appstoreconnect.apple.com 网页进行授权</string>
<color key="textColor" name="systemOrangeColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<progressIndicator maxValue="100" displayedWhenStopped="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="dtJ-5J-DxP">
<rect key="frame" x="262" y="77" width="16" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="5Gc-Ad-yiX"/>
<constraint firstAttribute="width" constant="16" id="l8L-wm-OxQ"/>
</constraints>
</progressIndicator>
</subviews>
<constraints>
<constraint firstItem="dtJ-5J-DxP" firstAttribute="centerY" secondItem="umT-YS-2Tw" secondAttribute="centerY" id="2ql-XU-Oy4"/>
<constraint firstAttribute="bottom" secondItem="qxM-hm-PWT" secondAttribute="bottom" constant="35" id="6oR-TQ-5jE"/>
<constraint firstItem="3FV-OQ-nKf" firstAttribute="centerY" secondItem="Hz6-mo-xeY" secondAttribute="centerY" constant="-10" id="Zus-xv-zaI"/>
<constraint firstItem="dtJ-5J-DxP" firstAttribute="centerX" secondItem="Hz6-mo-xeY" secondAttribute="centerX" constant="30" id="azK-M6-XaT"/>
<constraint firstAttribute="trailing" secondItem="3FV-OQ-nKf" secondAttribute="trailing" constant="15" id="div-BZ-Amk"/>
<constraint firstItem="1rM-sz-rlk" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="10" id="dld-UK-NGQ"/>
<constraint firstItem="1rM-sz-rlk" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="15" id="err-Ny-q6f"/>
<constraint firstItem="3FV-OQ-nKf" firstAttribute="centerX" secondItem="Hz6-mo-xeY" secondAttribute="centerX" id="f7R-Fn-bmc"/>
<constraint firstItem="umT-YS-2Tw" firstAttribute="centerX" secondItem="Hz6-mo-xeY" secondAttribute="centerX" id="fsS-52-w7f"/>
<constraint firstItem="3FV-OQ-nKf" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="15" id="fyj-Ld-adi"/>
<constraint firstAttribute="trailing" secondItem="1rM-sz-rlk" secondAttribute="trailing" constant="10" id="g6G-H2-CGw"/>
<constraint firstItem="qxM-hm-PWT" firstAttribute="centerX" secondItem="Hz6-mo-xeY" secondAttribute="centerX" id="l1Y-kM-2vx"/>
<constraint firstItem="qxM-hm-PWT" firstAttribute="top" secondItem="umT-YS-2Tw" secondAttribute="bottom" constant="12" id="ul4-6v-5hV"/>
</constraints>
<point key="canvasLocation" x="69" y="154"/>
</customView>
</objects>
</document>

View File

@ -0,0 +1,127 @@
//
// AppleWebLoginCore.swift
// AppleWebLogin
//
// Created by on 2024/10/23.
// ref: https://github.com/Lakr233/AppleWebLogin
import Combine
@preconcurrency import WebKit
//private let loginURL = URL(string: "https://account.apple.com/sign-in")!
private let loginURL = URL(string: "https://appstoreconnect.apple.com/login")!
public class AppleWebLoginCore: NSObject, WKUIDelegate, WKNavigationDelegate {
var webView: WKWebView {
associatedWebView
}
private let associatedWebView: WKWebView
private var dataPopulationTimer: Timer? = nil
private var firstLoadComplete = false
public private(set) var onFirstLoadComplete: (() -> Void)?
public var onCredentialPopulation: ((String, [HTTPCookie]) -> Void)?
override public init() {
let contentController = WKUserContentController()
let configuration = WKWebViewConfiguration()
configuration.defaultWebpagePreferences.allowsContentJavaScript = true
configuration.userContentController = contentController
configuration.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
configuration.websiteDataStore = .nonPersistent()
associatedWebView = .init(
frame: CGRect(x: 0, y: 0, width: 1920, height: 1080),
configuration: configuration
)
associatedWebView.isHidden = true
super.init()
associatedWebView.uiDelegate = self
associatedWebView.navigationDelegate = self
associatedWebView.load(.init(url: loginURL))
#if DEBUG
if associatedWebView.responds(to: Selector(("setInspectable:"))) {
associatedWebView.perform(Selector(("setInspectable:")), with: true)
}
#endif
let dataPopulationTimer = Timer(timeInterval: 1, repeats: true) { [weak self] _ in
guard let self else { return }
removeUnwantedElements()
populateData()
}
RunLoop.main.add(dataPopulationTimer, forMode: .common)
self.dataPopulationTimer = dataPopulationTimer
}
deinit {
dataPopulationTimer?.invalidate()
onCredentialPopulation = nil
}
public func webView(_: WKWebView, didFinish _: WKNavigation!) {
guard !firstLoadComplete else { return }
defer { firstLoadComplete = true }
associatedWebView.isHidden = false
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.onFirstLoadComplete?()
self.onFirstLoadComplete = nil
}
}
// public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping @MainActor (WKNavigationActionPolicy, WKWebpagePreferences) -> Void) {
// let request = navigationAction.request
// if let headers = request.allHTTPHeaderFields {
// print(request.url?.absoluteString)
// print("headers: \(headers)")
// if let scntValue = headers["scnt"] {
// print("scnt value: \(scntValue)")
// }
// }
// decisionHandler(.allow, preferences)
// }
public func installFirstLoadCompleteTrap(_ block: @escaping () -> Void) {
onFirstLoadComplete = block
}
public func installCredentialPopulationTrap(_ block: @escaping (String, [HTTPCookie]) -> Void) {
onCredentialPopulation = block
}
private func removeUnwantedElements() {
let removeElements = """
Element.prototype.remove = function() {
this.parentElement.removeChild(this);
}
NodeList.prototype.remove = HTMLCollection.prototype.remove = function() {
for(var i = this.length - 1; i >= 0; i--) {
if(this[i] && this[i].parentElement) {
this[i].parentElement.removeChild(this[i]);
}
}
}
document.getElementById("header").remove();
document.getElementsByClassName('landing__animation').remove();
"""
associatedWebView.evaluateJavaScript(removeElements) { _, _ in
}
}
private func populateData() {
guard let onCredentialPopulation else { return }
associatedWebView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in
//print(cookies)
for cookie in cookies where cookie.name == "myacinfo" {
let value = cookie.value
onCredentialPopulation(value, cookies)
self.onCredentialPopulation = nil
}
}
}
}

View File

@ -0,0 +1,27 @@
//
// PhoneNumbers.swift
// AppleParty
//
// Created by on 2021/6/2.
//
import Foundation
struct PNumber {
var num: String
var id: Int
}
// MARK: -
struct PhoneNumbers {
var numbers: [PNumber]
init(body: [String: Any]) {
numbers = [PNumber]()
let trustedPhoneNumbers = dictionaryArray(body["trustedPhoneNumbers"])
for phone in trustedPhoneNumbers {
numbers.append(PNumber(num: string(from: phone["numberWithDialCode"], defaultValue: "未知手机号"),
id: int(from: phone["id"]) ?? 0 ))
}
}
}

View File

@ -0,0 +1,271 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="f4w-gm-NjX">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Window Controller-->
<scene sceneID="Dz3-j1-bnD">
<objects>
<customObject id="Wso-0F-bWE" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
<windowController storyboardIdentifier="APQRcode" id="f4w-gm-NjX" sceneMemberID="viewController">
<window key="window" title="二维码工具" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="r7n-lN-NFg">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="425" y="462" width="475" height="364"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<view key="contentView" id="EJY-HM-BUX">
<rect key="frame" x="0.0" y="0.0" width="475" height="364"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<connections>
<outlet property="delegate" destination="f4w-gm-NjX" id="C4p-du-RcV"/>
</connections>
</window>
<connections>
<segue destination="JMg-NC-UdZ" kind="relationship" relationship="window.shadowedContentViewController" id="989-Yb-rd2"/>
</connections>
</windowController>
</objects>
<point key="canvasLocation" x="-344.5" y="156"/>
</scene>
<!--二维码工具-->
<scene sceneID="9pW-9x-UcR">
<objects>
<viewController title="二维码工具" id="JMg-NC-UdZ" customClass="APQRcodeVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="YL6-zb-Bcj">
<rect key="frame" x="0.0" y="0.0" width="698" height="403"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ug2-kb-Ht1">
<rect key="frame" x="20" y="341" width="552" height="42"/>
<constraints>
<constraint firstAttribute="height" constant="42" id="5u9-zd-zBc"/>
</constraints>
<textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="left" placeholderString="粘贴需要生成二维码的内容" drawsBackground="YES" id="Eeo-iN-PpZ">
<font key="font" metaFont="system" size="15"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kXS-QG-QKh">
<rect key="frame" x="573" y="345" width="109" height="33"/>
<constraints>
<constraint firstAttribute="width" constant="95" id="37D-fG-MjE"/>
<constraint firstAttribute="height" constant="21" id="H9R-dc-W0a"/>
</constraints>
<buttonCell key="cell" type="push" title="生成二维码" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Tw0-oT-hAN">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
<connections>
<action selector="createQrcode:" target="JMg-NC-UdZ" id="eqd-Jm-wdw"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aIK-jb-PcK">
<rect key="frame" x="411" y="201" width="214" height="37"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="9OI-Oe-yQA"/>
</constraints>
<buttonCell key="cell" type="push" title="复制二维码" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ucb-TM-gYn">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="copyQrcode:" target="JMg-NC-UdZ" id="m9E-O6-wh4"/>
</connections>
</button>
<scrollView hidden="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="RMS-8O-hMW">
<rect key="frame" x="28" y="16" width="345" height="299"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="Rb9-Io-7Mc">
<rect key="frame" x="0.0" y="0.0" width="345" height="299"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="g7E-G6-qr7">
<rect key="frame" x="0.0" y="0.0" width="345" height="299"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="345" height="299"/>
<size key="maxSize" width="345" height="10000000"/>
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
</textView>
</subviews>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Esa-gt-bwB">
<rect key="frame" x="-100" y="-100" width="225" height="15"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="6mG-be-32t">
<rect key="frame" x="329" y="0.0" width="16" height="299"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="FZk-nU-Hui">
<rect key="frame" x="50" y="15" width="301" height="301"/>
<constraints>
<constraint firstAttribute="width" secondItem="FZk-nU-Hui" secondAttribute="height" multiplier="1:1" id="Gbh-Ch-V3c"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="QRcode" id="gvL-Ja-sIo"/>
</imageView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Fdf-Fu-jC2">
<rect key="frame" x="411" y="153" width="214" height="37"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="n0o-Oe-hbP"/>
</constraints>
<buttonCell key="cell" type="push" title="保存二维码" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ixZ-mG-dr1">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="saveQrcode:" target="JMg-NC-UdZ" id="NMx-l9-AQT"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="DO5-oD-0Zz">
<rect key="frame" x="411" y="105" width="214" height="37"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="IBz-lN-MrF"/>
<constraint firstAttribute="height" constant="25" id="OlD-PH-AMy"/>
</constraints>
<buttonCell key="cell" type="push" title="分享二维码" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="OFP-an-Kde">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="shareQrcode:" target="JMg-NC-UdZ" id="E7u-EL-760"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="bhy-Kt-6FM">
<rect key="frame" x="411" y="57" width="214" height="37"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="EK2-O2-r9Z"/>
<constraint firstAttribute="height" constant="25" id="LdC-mT-hfw"/>
</constraints>
<buttonCell key="cell" type="push" title="AirDrop分享" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Ss3-R8-8ZV">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="shareQrcodeByAirDrop:" target="JMg-NC-UdZ" id="qck-Qz-CDH"/>
</connections>
</button>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kqV-kg-e6O">
<rect key="frame" x="515" y="252" width="107" height="25"/>
<popUpButtonCell key="cell" type="push" title="1024x1024" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="1024" imageScaling="proportionallyDown" inset="2" selectedItem="Ezh-Eq-5N3" id="tDM-tj-Cnk">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" title="分辨率" id="iDl-wk-ACt">
<items>
<menuItem title="300x300" tag="300" id="cd3-Tx-sIl"/>
<menuItem title="666x666" tag="680" id="J4k-L2-eI9"/>
<menuItem title="1024x1024" state="on" tag="1024" id="Ezh-Eq-5N3"/>
<menuItem title="1680x1680" tag="1680" id="yKW-h3-FzF"/>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nkA-C8-cXN">
<rect key="frame" x="416" y="258" width="94" height="16"/>
<constraints>
<constraint firstAttribute="width" constant="90" id="H24-4r-oi6"/>
<constraint firstAttribute="height" constant="16" id="svz-da-sgJ"/>
</constraints>
<textFieldCell key="cell" title="二维码分辨率:" id="lpe-Oy-Zj8">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="oFq-OZ-56q">
<rect key="frame" x="418" y="307" width="202" height="16"/>
<textFieldCell key="cell" id="M8S-NJ-r4H">
<font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Vpq-0F-Rre">
<rect key="frame" x="411" y="9" width="214" height="37"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="DX1-8F-PWN"/>
<constraint firstAttribute="height" constant="25" id="RZy-Lp-VQi"/>
</constraints>
<buttonCell key="cell" type="push" title="扫描屏幕上的二维码" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="IE9-17-MVr">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="scanQrcode:" target="JMg-NC-UdZ" id="nKQ-rX-scz"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="RMS-8O-hMW" firstAttribute="height" secondItem="FZk-nU-Hui" secondAttribute="height" multiplier="0.996337" id="0lC-La-Fe2"/>
<constraint firstAttribute="trailing" secondItem="Vpq-0F-Rre" secondAttribute="trailing" constant="80" id="3HB-cl-Bo7"/>
<constraint firstItem="kqV-kg-e6O" firstAttribute="leading" secondItem="nkA-C8-cXN" secondAttribute="trailing" constant="10" id="5Hd-0w-UDL"/>
<constraint firstItem="aIK-jb-PcK" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="5pi-bF-JY8"/>
<constraint firstAttribute="trailing" secondItem="aIK-jb-PcK" secondAttribute="trailing" constant="80" id="5xg-F6-DUj"/>
<constraint firstAttribute="trailing" secondItem="DO5-oD-0Zz" secondAttribute="trailing" constant="80" id="7vu-Wm-qhQ"/>
<constraint firstItem="Ug2-kb-Ht1" firstAttribute="top" secondItem="YL6-zb-Bcj" secondAttribute="top" constant="20" symbolic="YES" id="81I-ze-g3K"/>
<constraint firstAttribute="trailing" secondItem="Fdf-Fu-jC2" secondAttribute="trailing" constant="80" id="8PQ-BB-8bV"/>
<constraint firstAttribute="trailing" secondItem="Vpq-0F-Rre" secondAttribute="trailing" constant="80" id="GiS-So-8mk"/>
<constraint firstItem="bhy-Kt-6FM" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="LF9-2I-3uN"/>
<constraint firstItem="FZk-nU-Hui" firstAttribute="centerX" secondItem="RMS-8O-hMW" secondAttribute="centerX" id="NMm-m2-f84"/>
<constraint firstItem="oFq-OZ-56q" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="69" id="PzN-pg-eqG"/>
<constraint firstItem="FZk-nU-Hui" firstAttribute="centerY" secondItem="RMS-8O-hMW" secondAttribute="centerY" id="QxO-Wo-srO"/>
<constraint firstItem="Fdf-Fu-jC2" firstAttribute="top" secondItem="aIK-jb-PcK" secondAttribute="bottom" constant="23" id="V0h-t7-6Gj"/>
<constraint firstItem="FZk-nU-Hui" firstAttribute="leading" secondItem="YL6-zb-Bcj" secondAttribute="leading" constant="50" id="VDe-Q8-9PN"/>
<constraint firstAttribute="trailing" secondItem="oFq-OZ-56q" secondAttribute="trailing" constant="80" id="XJ6-yE-Ntt"/>
<constraint firstItem="Vpq-0F-Rre" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="Y7x-4H-RjY"/>
<constraint firstItem="kqV-kg-e6O" firstAttribute="top" secondItem="Ug2-kb-Ht1" secondAttribute="bottom" constant="65" id="YEL-BY-ut6"/>
<constraint firstItem="DO5-oD-0Zz" firstAttribute="top" secondItem="Fdf-Fu-jC2" secondAttribute="bottom" constant="23" id="Ypl-LZ-Mq5"/>
<constraint firstAttribute="trailing" secondItem="bhy-Kt-6FM" secondAttribute="trailing" constant="80" id="a0a-Oa-9kH"/>
<constraint firstAttribute="trailing" secondItem="kqV-kg-e6O" secondAttribute="trailing" constant="80" id="dBG-ER-u2w"/>
<constraint firstItem="nkA-C8-cXN" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="feM-R3-Qk4"/>
<constraint firstItem="oFq-OZ-56q" firstAttribute="top" secondItem="Ug2-kb-Ht1" secondAttribute="bottom" constant="18" id="g4b-8B-jEz"/>
<constraint firstItem="Vpq-0F-Rre" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="gBB-bU-bqb"/>
<constraint firstAttribute="trailing" secondItem="kXS-QG-QKh" secondAttribute="trailing" constant="23" id="gz3-mW-f9A"/>
<constraint firstItem="kXS-QG-QKh" firstAttribute="centerY" secondItem="Ug2-kb-Ht1" secondAttribute="centerY" id="iya-pl-bDW"/>
<constraint firstItem="RMS-8O-hMW" firstAttribute="width" secondItem="FZk-nU-Hui" secondAttribute="width" multiplier="1.14286" id="l0c-4A-01h"/>
<constraint firstAttribute="bottom" secondItem="FZk-nU-Hui" secondAttribute="bottom" constant="15" id="ohm-Zu-1WD"/>
<constraint firstItem="Vpq-0F-Rre" firstAttribute="top" secondItem="bhy-Kt-6FM" secondAttribute="bottom" constant="23" id="ooP-O8-GCs"/>
<constraint firstItem="bhy-Kt-6FM" firstAttribute="top" secondItem="DO5-oD-0Zz" secondAttribute="bottom" constant="23" id="ovb-Bw-TRf"/>
<constraint firstItem="kqV-kg-e6O" firstAttribute="centerY" secondItem="nkA-C8-cXN" secondAttribute="centerY" id="q3U-Le-a73"/>
<constraint firstItem="FZk-nU-Hui" firstAttribute="top" secondItem="Ug2-kb-Ht1" secondAttribute="bottom" constant="25" id="qQE-gB-m3H"/>
<constraint firstItem="aIK-jb-PcK" firstAttribute="top" secondItem="kqV-kg-e6O" secondAttribute="bottom" constant="23" id="rWH-hA-TD5"/>
<constraint firstItem="Fdf-Fu-jC2" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="sSR-g2-znd"/>
<constraint firstItem="Vpq-0F-Rre" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="tKR-17-heC"/>
<constraint firstItem="Ug2-kb-Ht1" firstAttribute="leading" secondItem="YL6-zb-Bcj" secondAttribute="leading" constant="20" id="tMb-EV-T0G"/>
<constraint firstItem="DO5-oD-0Zz" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="wNM-xz-93l"/>
<constraint firstItem="kXS-QG-QKh" firstAttribute="leading" secondItem="Ug2-kb-Ht1" secondAttribute="trailing" constant="8" id="xPZ-gg-ekQ"/>
<constraint firstAttribute="trailing" secondItem="Vpq-0F-Rre" secondAttribute="trailing" constant="80" id="zbv-Ik-hNs"/>
</constraints>
</view>
<connections>
<outlet property="copyQrcodeBtn" destination="aIK-jb-PcK" id="gof-pb-gv8"/>
<outlet property="createQrcodeBtn" destination="kXS-QG-QKh" id="4d9-ha-E2M"/>
<outlet property="inputTextField" destination="Ug2-kb-Ht1" id="dIn-HQ-wDi"/>
<outlet property="messageLbl" destination="oFq-OZ-56q" id="xKI-yE-tXi"/>
<outlet property="qrcodeImageView" destination="FZk-nU-Hui" id="bbk-Km-JV9"/>
<outlet property="qrcodeSizeBtn" destination="kqV-kg-e6O" id="Lac-Nf-7aT"/>
<outlet property="saveQrcodeBtn" destination="Fdf-Fu-jC2" id="DBR-Bz-crE"/>
<outlet property="scanQrcodeBtn" destination="Vpq-0F-Rre" id="Ew1-De-IaS"/>
<outlet property="shareQrcodeBtn" destination="DO5-oD-0Zz" id="bvQ-kp-gDU"/>
<outlet property="shareQrcodeByAirDropBtn" destination="bhy-Kt-6FM" id="xVg-Aw-9xp"/>
<outlet property="textScrollView" destination="RMS-8O-hMW" id="0yW-gv-qtS"/>
<outlet property="textView" destination="g7E-G6-qr7" id="vJy-Tg-oLR"/>
</connections>
</viewController>
<customObject id="3RX-1S-TLr" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="372" y="156.5"/>
</scene>
</scenes>
<resources>
<image name="QRcode" width="145" height="145"/>
</resources>
</document>

View File

@ -0,0 +1,199 @@
//
// APQRcodeVC.swift
// AppleParty
//
// Created by HTC on 2022/3/24.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
class APQRcodeVC: NSViewController {
@IBOutlet weak var inputTextField: NSTextField!
@IBOutlet weak var qrcodeSizeBtn: NSPopUpButton!
@IBOutlet weak var createQrcodeBtn: NSButton!
@IBOutlet weak var qrcodeImageView: NSImageView!
@IBOutlet weak var copyQrcodeBtn: NSButton!
@IBOutlet weak var saveQrcodeBtn: NSButton!
@IBOutlet weak var shareQrcodeBtn: NSButton!
@IBOutlet weak var shareQrcodeByAirDropBtn: NSButton!
@IBOutlet weak var scanQrcodeBtn: NSButton!
@IBOutlet weak var messageLbl: NSTextField!
@IBOutlet weak var textScrollView: NSScrollView!
@IBOutlet weak var textView: NSTextView!
@IBAction func createQrcode(_ sender: Any) {
let str = inputTextField!.stringValue
if str.isEmpty {
enableQrcode(false)
statusMessage("")
NSAlert.show("请输出需要生成二维码的文本!")
return
}
let img = createQRImage(str, NSMakeSize(360, 360))
qrcodeImageView.image = img
enableQrcode(true)
statusMessage("二维码生成成功")
}
@IBAction func copyQrcode(_ sender: Any) {
let str = inputTextField!.stringValue
if !str.isEmpty {
let img = createQRImage(str, getImageSize())
let pb = NSPasteboard.general
pb.clearContents()
if pb.writeObjects([img as NSPasteboardWriting]) {
statusMessage("Copy QRCode to clipboard")
} else {
statusMessage("Failed to copy QRCode to clipboard")
}
}
}
@IBAction func saveQrcode(_ sender: Any) {
let str = inputTextField!.stringValue
if str.isEmpty {
return statusMessage("请填写有效的文本内容!")
}
let savePanel = NSSavePanel()
savePanel.title = "Save QRCode As File"
savePanel.canCreateDirectories = true
savePanel.allowedFileTypes = ["png"]
savePanel.isExtensionHidden = false
savePanel.nameFieldStringValue = getImgaeName() + ".png"
savePanel.becomeKey()
let result = savePanel.runModal()
if (result == .OK && (savePanel.url) != nil) {
let img = createQRImage(str, getImageSize())
let imgRep = NSBitmapImageRep(data: img.tiffRepresentation!)
let data = imgRep?.representation(using: NSBitmapImageRep.FileType.png, properties: [:])
try! data?.write(to: savePanel.url!)
statusMessage("Save QRCode to \(savePanel.url!.absoluteString)")
}
}
@IBAction func shareQrcode(_ sender: Any) {
let str = inputTextField!.stringValue
if str.isEmpty {
return statusMessage("请填写有效的文本内容!")
}
let img = createQRImage(str, getImageSize())
let picker = NSSharingServicePicker(items: [img])
picker.delegate = self
picker.show(relativeTo: .zero, of: sender as! NSView, preferredEdge: .maxX)
}
@IBAction func shareQrcodeByAirDrop(_ sender: Any) {
let str = inputTextField!.stringValue
if str.isEmpty {
return statusMessage("请填写有效的文本内容!")
}
let img = createQRImage(str, getImageSize())
let service = NSSharingService(named: .sendViaAirDrop)!
let items: [NSImage] = [img]
if service.canPerform(withItems: items) {
service.delegate = self
service.perform(withItems: items)
} else {
statusMessage("Cannot perform AirDrop!")
}
}
@IBAction func scanQrcode(_ sender: Any) {
enableQrcode(false)
qrcodeImageView.isHidden = true
textView.string = ""
textScrollView.isHidden = false
// scan QRCode
let dict = scanQRCodeOnScreen() as! [String:Any]
let data = dict["qrcode"] as! Array<String>
if data.isEmpty {
textView.string = "Not found valid QRCode of screen!"
return
}
// output message
appendToTextView("识别到二维码个数:\(data.count)\n", coreText: "\(data.count)")
for (index, element) in data.enumerated() {
let k = index + 1
appendToTextView("\n\(k)个二维码内容:\n\n\(element)\n\n", coreText: element)
}
}
override func viewDidAppear() {
super.viewDidAppear()
inputTextField.becomeFirstResponder()
}
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
func setupUI() {
inputTextField.delegate = self
enableQrcode(false)
qrcodeImageView.image = NSImage(named: "QRcode")
}
func enableQrcode(_ enable: Bool) {
copyQrcodeBtn.isEnabled = enable
saveQrcodeBtn.isEnabled = enable
shareQrcodeBtn.isEnabled = enable
shareQrcodeByAirDropBtn.isEnabled = enable
textScrollView.isHidden = true
qrcodeImageView.isHidden = false
if !enable {
statusMessage("")
qrcodeImageView.image = NSImage(named: "QRcode")
}
}
func statusMessage(_ msg: String) {
messageLbl.stringValue = msg.count == 0 ? "" : "提示:\(msg)"
}
func getImageSize() -> NSSize {
let wh = CGFloat(qrcodeSizeBtn?.selectedTag() ?? 500)
let size = NSMakeSize(wh, wh)
return size
}
func getImgaeName() -> String {
let dateFormatter : DateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH-mm-ss"
let date = Date()
let dateString = dateFormatter.string(from: date)
return "AppleParty_qrcode-" + dateString
}
func appendToTextView(_ text: String, coreText: String) {
let attributes = [NSAttributedString.Key.foregroundColor: NSColor.labelColor]
let secondAttributes = [NSAttributedString.Key.foregroundColor: #colorLiteral(red: 0.3211918473, green: 0.7199308276, blue: 1, alpha: 1)]
let attr = NSMutableAttributedString.init(string: text, attributes: attributes)
attr.addAttributes(secondAttributes, range: (text as NSString).range(of: coreText))
textView.textStorage?.append(attr)
textView.scrollRangeToVisible(NSMakeRange(textView.string.count, 0))
}
}
extension APQRcodeVC: NSTextFieldDelegate {
func controlTextDidChange(_ obj: Notification) {
let textField = obj.object as! NSTextField
if textField.stringValue.isEmpty {
enableQrcode(false)
}
}
}
extension APQRcodeVC: NSSharingServicePickerDelegate, NSSharingServiceDelegate {
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "37M-logo.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "37M-slogan.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "37iOSTeam-Round.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,68 @@
{
"images" : [
{
"idiom" : "mac",
"size" : "16x16",
"filename" : "icon-16.png",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "16x16",
"filename" : "icon-32.png",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "32x32",
"filename" : "icon-32.png",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "32x32",
"filename" : "icon-64.png",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "128x128",
"filename" : "icon-128.png",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "128x128",
"filename" : "icon-256.png",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "256x256",
"filename" : "icon-256.png",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "256x256",
"filename" : "icon-512.png",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "512x512",
"filename" : "icon-512.png",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "512x512",
"filename" : "icon-1024.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon-256.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon-512.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "AppAnalytics@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Apps@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "FinancialReports@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "IPAUpload@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,52 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "PlaceholderIcon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "PlaceholderIcon_white@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "QRcode@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "SendEmail@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "VerifyReceipt@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://apple.com/itunes/importer" version="software5.9">
<provider>{team_id}</provider>
<software>
<vendor_id>{SKU}</vendor_id>
<software_metadata>
<in_app_purchases>
<in_app_purchase>
<!-- 可以由字母、数字、下划线_和句点.)构成,最多 255 个字符 -->
<product_id>com.app.1usd</product_id>
<reference_name>1ud2~64 个字符)</reference_name>
<!-- 消耗型项目consumable 非消耗型non-consumable 自动续期订阅auto-renewable 非续期订阅subscription -->
<type>consumable</type>
<products>
<product>
<cleared_for_sale>true</cleared_for_sale>
<wholesale_price_tier>3</wholesale_price_tier>
</product>
</products>
<locales>
<locale name="en-US">
<title>2~30个字符</title>
<description>至少10~45个字符</description>
</locale>
<locale name="zh-Hans">
<title>中文2~30个字符</title>
<description>至少10~45个字符</description>
</locale>
</locales>
<review_screenshot>
<!-- Supported dimensions are: 1334x750, 750x1334, 1024x768, 1024x748, 768x1024, 768x1004, 2048x1536, 2048x1496, 1536x2048, 1536x2008, 2224x1668, 1668x2224, 960x640, 960x600, 640x960, 640x920, 2208x1242, 1242x2208, 2436x1125, 1125x2436, 312x390, 1136x640, 1136x600, 640x1136, 640x1096, 2732x2048, 2048x2732" -->
<size>636132</size>
<file_name>IMG_5180.PNG</file_name>
<checksum type="md5">xxxxx</checksum>
</review_screenshot>
<review_notes>Some notes for the reviewer.2~4000字符</review_notes>
</in_app_purchase>
<in_app_purchase>
<!-- 可以由字母、数字、下划线_和句点.)构成,最多 255 个字符 -->
<product_id>com.app.3usd</product_id>
<reference_name>3ud2~64 个字符)</reference_name>
<type>non-consumable</type>
<products>
<product>
<cleared_for_sale>true</cleared_for_sale>
<wholesale_price_tier>3</wholesale_price_tier>
</product>
</products>
<locales>
<locale name="ko">
<title>2~30个字符</title>
<description>至少10~45个字符</description>
</locale>
</locales>
<review_screenshot>
<!-- Supported dimensions are: 1334x750, 750x1334, 1024x768, 1024x748, 768x1024, 768x1004, 2048x1536, 2048x1496, 1536x2048, 1536x2008, 2224x1668, 1668x2224, 960x640, 960x600, 640x960, 640x920, 2208x1242, 1242x2208, 2436x1125, 1125x2436, 312x390, 1136x640, 1136x600, 640x1136, 640x1096, 2732x2048, 2048x2732" -->
<size>636132</size>
<file_name>IMG_5180.PNG</file_name>
<checksum type="md5">xxxxx</checksum>
</review_screenshot>
<review_notes>Some notes for the reviewer.2~4000字符</review_notes>
</in_app_purchase>
<subscription_group name="订阅群组参考名称">
<locales>
<locale name="en-US">
<title>订阅群组显示名称(2-75)</title>
<app_name>App 名称显示选项:使用自定义名称(2-30)</app_name>
</locale>
</locales>
<in_app_purchase>
<product_id>every_movie_in_the_world_plus_1month</product_id>
<reference_name>参考名称</reference_name>
<type>auto-renewable</type>
<duration>1 Month</duration>
<bonus_duration>7 Days</bonus_duration>
<cleared_for_sale>true</cleared_for_sale>
<locales>
<locale name="en-US">
<title>订阅显示名称</title>
<description>订阅描述Every movie ever plus.</description>
</locale>
</locales>
<review_screenshot>
<size>636132</size>
<file_name>IMG_5180.PNG</file_name>
<checksum type="md5">xxxxx</checksum>
</review_screenshot>
<review_notes>Some notes for the reviewer.2~4000字符</review_notes>
</in_app_purchase>
</subscription_group>
<in_app_purchase>
<!-- 可以由字母、数字、下划线_和句点.)构成,最多 255 个字符 -->
<product_id>com.app.6usd</product_id>
<reference_name>6ud2~64 个字符)</reference_name>
<type>subscription</type>
<products>
<product>
<cleared_for_sale>true</cleared_for_sale>
<wholesale_price_tier>49</wholesale_price_tier>
</product>
</products>
<locales>
<locale name="ko">
<title>2~30个字符</title>
<description>至少10~45个字符</description>
</locale>
</locales>
<review_screenshot>
<!-- Supported dimensions are: 1334x750, 750x1334, 1024x768, 1024x748, 768x1024, 768x1004, 2048x1536, 2048x1496, 1536x2048, 1536x2008, 2224x1668, 1668x2224, 960x640, 960x600, 640x960, 640x920, 2208x1242, 1242x2208, 2436x1125, 1125x2436, 312x390, 1136x640, 1136x600, 640x1136, 640x1096, 2732x2048, 2048x2732" -->
<size>636132</size>
<file_name>IMG_5180.PNG</file_name>
<checksum type="md5">xxxxx</checksum>
</review_screenshot>
<review_notes>Some notes for the reviewer.2~4000字符</review_notes>
</in_app_purchase>
</in_app_purchases>
</software_metadata>
</software>
</package>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<package version="software5.10" xmlns="http://apple.com/itunes/importer">
<software_assets apple_id="{apple_id}" app_platform="{app_platform}">
<asset type="{archive_type}">
<data_file>
<size>{file_size}</size>
<file_name>{file_name}</file_name>
<checksum type="md5">{file_md5}</checksum>
</data_file>
</asset>
</software_assets>
</package>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<package version="software5.10" xmlns="http://apple.com/itunes/importer">
<provider>{provider}</provider>
<team_id>{provider}</team_id>
<software>
<vendor_id>{vendor_id}</vendor_id>
<software_metadata app_platform="{app_platform}">
<versions>
<version string="{app_version}">
<locales>
<locale name="{app_locale}">
<title>{title}</title>
<app_previews>
<app_preview display_target="{iOS-5.5-in}" position="{1}">
<data_file role="source">
<size>{video_size}</size>
<file_name>{video_name}</file_name>
<checksum>{video_md5}</checksum>
</data_file>
<preview_image_time format="30/1:1/nonDrop">{00:00:05:00}</preview_image_time>
</app_preview>
</app_previews>
<software_screenshots>
<software_screenshot display_target="{iOS-5.5-in}" position="{1}">
<size>{image_size}</size>
<file_name>{image_name}</file_name>
<checksum type="md5">{image_md5}</checksum>
</software_screenshot>
</software_screenshots>
</locale>
</locales>
</version>
</versions>
</software_metadata>
</software>
</package>

View File

@ -0,0 +1,61 @@
//
// APRootCollectionAdapter.swift
// AppleParty
//
// Created by HTC on 2022/3/14.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
class APRootCollectionAdapter: NSObject {
fileprivate static let numberOfSections = 1
fileprivate static let itemId = "APRootCollectionCell"
fileprivate var items = [APRootCollectionModel]() {
didSet {
collectionView.reloadData()
}
}
private var collectionView: NSCollectionView
init(collectionView: NSCollectionView) {
self.collectionView = collectionView
super.init()
self.collectionView.dataSource = self
self.collectionView.delegate = self
self.collectionView.register(APRootCollectionCell.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: APRootCollectionAdapter.itemId))
}
func set(items: [APRootCollectionModel]) {
self.items = items
}
}
extension APRootCollectionAdapter: NSCollectionViewDataSource, NSCollectionViewDelegate {
func numberOfSectionsInCollectionView(collectionView: NSCollectionView) -> Int {
return APRootCollectionAdapter.numberOfSections
}
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: APRootCollectionAdapter.itemId), for: indexPath)
guard let collectionViewItem = item as? APRootCollectionCell else { return item }
let name = items[indexPath.item].name
let icon = items[indexPath.item].icon
collectionViewItem.configure(name: name, icon: icon)
return item
}
func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
collectionView.deselectItems(at: indexPaths)
if let item = indexPaths.first?.item, let handler = items[item].handler {
handler()
}
}
}

View File

@ -0,0 +1,25 @@
//
// APRootCollectionCell.swift
// AppleParty
//
// Created by HTC on 2022/3/14.
// Copyright © 2022 37 Mobile Games. All rights reserved.
//
import Cocoa
class APRootCollectionCell: NSCollectionViewItem {
@IBOutlet weak var imgView: NSImageView!
@IBOutlet weak var nameView: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
}
func configure(name: String, icon: String) {
nameView.stringValue = name
imgView?.image = NSImage(named: icon)
}
}

Some files were not shown because too many files have changed in this diff Show More