@@ -3,28 +3,192 @@ import UniformTypeIdentifiers
33import ZIPFoundation
44import ZsignSwift
55
6+ // MARK: - Small models
67struct FileItem {
78 var url : URL ?
89 var name : String { url? . lastPathComponent ?? " " }
910}
1011
12+ struct Credit : Identifiable {
13+ var id = UUID ( )
14+ var name : String
15+ var role : String
16+ var profileURL : URL
17+ var avatarURL : URL
18+ }
19+
20+ // MARK: - App
1121@main
1222struct ZsignOnDeviceApp : App {
1323 var body : some Scene {
1424 WindowGroup {
15- ContentView ( )
16- . navigationViewStyle ( StackNavigationViewStyle ( ) ) // Fix iPad sidebar bug
25+ MainTabView ( )
26+ }
27+ }
28+ }
29+
30+ // MARK: - Main tab view
31+ struct MainTabView : View {
32+ var body : some View {
33+ TabView {
34+ NavigationView {
35+ AboutView ( )
36+ }
37+ . tabItem {
38+ Image ( systemName: " info.circle " )
39+ Text ( " About " )
40+ }
41+
42+ NavigationView {
43+ SignerView ( )
44+ }
45+ . tabItem {
46+ Image ( systemName: " hammer " )
47+ Text ( " Signer " )
48+ }
49+ }
50+ }
51+ }
52+
53+ // MARK: - About view
54+ struct AboutView : View {
55+ // credits data
56+ private let credits : [ Credit ] = [
57+ Credit (
58+ name: " zhlynn " ,
59+ role: " Original zsign " ,
60+ profileURL: URL ( string: " https://github.com/zhlynn " ) !,
61+ avatarURL: URL ( string: " https://github.com/zhlynn.png " ) !
62+ ) ,
63+ Credit (
64+ name: " Khcrysalis " ,
65+ role: " Zsign-Package (fork) " ,
66+ profileURL: URL ( string: " https://github.com/khcrysalis " ) !,
67+ avatarURL: URL ( string: " https://github.com/khcrysalis.png " ) !
68+ ) ,
69+ Credit (
70+ name: " SuperGamer474 " ,
71+ role: " Developer " ,
72+ profileURL: URL ( string: " https://github.com/SuperGamer474 " ) !,
73+ avatarURL: URL ( string: " https://github.com/SuperGamer474.png " ) !
74+ )
75+ ]
76+
77+ private var appIconURL : URL ? {
78+ URL ( string: " https://raw.githubusercontent.com/ProStore-iOS/ProStore/main/Sources/prostore/Assets.xcassets/AppIcon.appiconset/Icon-1024.png " )
79+ }
80+
81+ private var versionString : String {
82+ let version = Bundle . main. object ( forInfoDictionaryKey: " CFBundleShortVersionString " ) as? String ?? " 1.0 "
83+ let build = Bundle . main. object ( forInfoDictionaryKey: " CFBundleVersion " ) as? String ?? " "
84+ return build. isEmpty ? version : " \( version) ( \( build) ) "
85+ }
86+
87+ var body : some View {
88+ List {
89+ VStack ( spacing: 8 ) {
90+ if let url = appIconURL {
91+ AsyncImage ( url: url) { phase in
92+ if let img = phase. image {
93+ img
94+ . resizable ( )
95+ . scaledToFit ( )
96+ . frame ( width: 120 , height: 120 )
97+ . clipShape ( RoundedRectangle ( cornerRadius: 20 , style: . continuous) )
98+ . shadow ( radius: 6 )
99+ } else if phase. error != nil {
100+ // fallback
101+ Image ( systemName: " app.fill " )
102+ . resizable ( )
103+ . scaledToFit ( )
104+ . frame ( width: 80 , height: 80 )
105+ . foregroundColor ( . secondary)
106+ } else {
107+ ProgressView ( )
108+ . frame ( width: 80 , height: 80 )
109+ }
110+ }
111+ }
112+
113+ Text ( " ProStore " )
114+ . font ( . title2)
115+ . fontWeight ( . semibold)
116+
117+ Text ( " Version \( versionString) " )
118+ . font ( . footnote)
119+ . foregroundColor ( . secondary)
120+ }
121+ . frame ( maxWidth: . infinity)
122+ . padding ( . vertical, 20 )
123+ . listRowInsets ( EdgeInsets ( ) )
124+
125+ Section ( header: Text ( " Credits " ) ) {
126+ ForEach ( credits) { c in
127+ CreditRow ( credit: c)
128+ }
129+ }
130+
131+ // optional sponsors or other sections can go here
132+ }
133+ . listStyle ( InsetGroupedListStyle ( ) )
134+ . navigationTitle ( " About " )
135+ }
136+ }
137+
138+ struct CreditRow : View {
139+ let credit : Credit
140+ @Environment ( \. openURL) var openURL
141+
142+ var body : some View {
143+ HStack ( spacing: 12 ) {
144+ AsyncImage ( url: credit. avatarURL) { phase in
145+ if let img = phase. image {
146+ img
147+ . resizable ( )
148+ . scaledToFill ( )
149+ } else if phase. error != nil {
150+ Image ( systemName: " person.crop.circle.fill " )
151+ . resizable ( )
152+ . scaledToFill ( )
153+ } else {
154+ ProgressView ( )
155+ }
156+ }
157+ . frame ( width: 44 , height: 44 )
158+ . clipShape ( Circle ( ) )
159+ . overlay ( Circle ( ) . stroke ( Color ( UIColor . separator) , lineWidth: 0.5 ) )
160+
161+ VStack ( alignment: . leading, spacing: 2 ) {
162+ Text ( credit. name)
163+ . font ( . body)
164+ Text ( credit. role)
165+ . font ( . subheadline)
166+ . foregroundColor ( . secondary)
167+ }
168+
169+ Spacer ( )
170+
171+ Button {
172+ openURL ( credit. profileURL)
173+ } label: {
174+ Image ( systemName: " arrow.up.right.square " )
175+ . imageScale ( . large)
176+ . foregroundColor ( . accentColor)
177+ }
178+ . buttonStyle ( BorderlessButtonStyle ( ) )
17179 }
180+ . padding ( . vertical, 8 )
18181 }
19182}
20183
21- struct ContentView : View {
184+ // MARK: - SignerView (your original ContentView moved here)
185+ struct SignerView : View {
22186 @State private var ipa = FileItem ( )
23187 @State private var p12 = FileItem ( )
24188 @State private var prov = FileItem ( )
25189 @State private var p12Password = " "
26190 @State private var isProcessing = false
27- @State private var progressMessage = " Idle 😎 "
191+ @State private var progressMessage = " "
28192 @State private var showActivity = false
29193 @State private var activityURL : URL ? = nil
30194 @State private var showPickerFor : PickerKind ?
@@ -41,67 +205,64 @@ struct ContentView: View {
41205 }
42206
43207 var body : some View {
44- NavigationView {
45- Form {
46- Section ( header: Text ( " Inputs " ) ) {
47- HStack {
48- Text ( " IPA: " )
49- Spacer ( )
50- Text ( ipa. name. isEmpty ? " none " : ipa. name) . foregroundColor ( . secondary)
51- Button ( " Pick " ) { showPickerFor = . ipa }
52- }
208+ Form {
209+ Section ( header: Text ( " Inputs " ) ) {
210+ HStack {
211+ Text ( " IPA: " )
212+ Spacer ( )
213+ Text ( ipa. name. isEmpty ? " none " : ipa. name) . foregroundColor ( . secondary)
214+ Button ( " Pick " ) { showPickerFor = . ipa }
215+ }
216+ HStack {
217+ Text ( " P12: " )
218+ Spacer ( )
219+ Text ( p12. name. isEmpty ? " none " : p12. name) . foregroundColor ( . secondary)
220+ Button ( " Pick " ) { showPickerFor = . p12 }
221+ }
222+ HStack {
223+ Text ( " MobileProvision: " )
224+ Spacer ( )
225+ Text ( prov. name. isEmpty ? " none " : prov. name) . foregroundColor ( . secondary)
226+ Button ( " Pick " ) { showPickerFor = . prov }
227+ }
228+ SecureField ( " P12 Password " , text: $p12Password)
229+ }
230+
231+ Section {
232+ Button ( action: runSign) {
53233 HStack {
54- Text ( " P12: " )
55234 Spacer ( )
56- Text ( p12. name. isEmpty ? " none " : p12. name) . foregroundColor ( . secondary)
57- Button ( " Pick " ) { showPickerFor = . p12 }
58- }
59- HStack {
60- Text ( " MobileProvision: " )
235+ Text ( " Sign IPA " ) . bold ( )
61236 Spacer ( )
62- Text ( prov. name. isEmpty ? " none " : prov. name) . foregroundColor ( . secondary)
63- Button ( " Pick " ) { showPickerFor = . prov }
64- }
65- SecureField ( " P12 Password " , text: $p12Password)
66- }
67-
68- Section {
69- Button ( action: runSign) {
70- HStack {
71- Spacer ( )
72- if isProcessing { ProgressView ( progressMessage) }
73- Text ( " Sign IPA " ) . bold ( )
74- Spacer ( )
75- }
76237 }
77- . disabled ( isProcessing || ipa. url == nil || p12. url == nil || prov. url == nil )
78- }
79-
80- Section ( header: Text ( " Status " ) ) {
81- Text ( progressMessage) . foregroundColor ( . primary)
82238 }
239+ . disabled ( isProcessing || ipa. url == nil || p12. url == nil || prov. url == nil )
83240 }
84- . navigationTitle ( " Zsign On Device " )
85- . navigationViewStyle ( StackNavigationViewStyle ( ) ) // Fix iPad sidebar issue
86- . sheet ( item: $showPickerFor, onDismiss: nil ) { kind in
87- DocumentPicker ( kind: kind, onPick: { url in
88- switch kind {
89- case . ipa: ipa. url = url
90- case . p12: p12. url = url
91- case . prov: prov. url = url
92- }
93- } )
241+
242+ Section ( header: Text ( " Status " ) ) {
243+ Text ( progressMessage) . foregroundColor ( . primary)
94244 }
95- . sheet ( isPresented: $showActivity) {
96- if let u = activityURL {
97- ActivityView ( url: u)
98- } else {
99- Text ( " No file to share " )
245+ }
246+ . navigationTitle ( " Signer " )
247+ . sheet ( item: $showPickerFor, onDismiss: nil ) { kind in
248+ DocumentPicker ( kind: kind, onPick: { url in
249+ switch kind {
250+ case . ipa: ipa. url = url
251+ case . p12: p12. url = url
252+ case . prov: prov. url = url
100253 }
254+ } )
255+ }
256+ . sheet ( isPresented: $showActivity) {
257+ if let u = activityURL {
258+ ActivityView ( url: u)
259+ } else {
260+ Text ( " No file to share " )
101261 }
102262 }
103263 }
104264
265+ // --- runSign same as your original; adapted to compile in this struct scope
105266 func runSign( ) {
106267 guard let ipaURL = ipa. url, let p12URL = p12. url, let provURL = prov. url else {
107268 progressMessage = " Pick all input files first 😅 "
@@ -230,9 +391,9 @@ struct ContentView: View {
230391 }
231392}
232393
233- // DocumentPicker wrapper
394+ // MARK: - DocumentPicker wrapper (same as yours)
234395struct DocumentPicker : UIViewControllerRepresentable {
235- var kind : ContentView . PickerKind
396+ var kind : SignerView . PickerKind
236397 var onPick : ( URL ) -> Void
237398
238399 func makeCoordinator( ) -> Coordinator { Coordinator ( self ) }
@@ -255,11 +416,11 @@ struct DocumentPicker: UIViewControllerRepresentable {
255416 }
256417}
257418
258- // ActivityView wrapper
419+ // MARK: - ActivityView wrapper
259420struct ActivityView : UIViewControllerRepresentable {
260421 let url : URL
261422 func makeUIViewController( context: Context ) -> UIActivityViewController {
262423 UIActivityViewController ( activityItems: [ url] , applicationActivities: nil )
263424 }
264425 func updateUIViewController( _ uiViewController: UIActivityViewController , context: Context ) { }
265- }
426+ }
0 commit comments