1
+ //
2
+ // ContentView.swift
3
+ // VocabularyApp
4
+ //
5
+ // Created by Parth Desai on 1/10/25.
6
+ //
7
+
1
8
import SwiftUI
2
9
import AppKit
3
10
4
11
struct ContentView : View {
5
12
@State private var word : String = " Loading... "
6
13
@State private var meaning : String = " Loading... "
7
14
@State private var example : String = " Loading... "
8
- @State private var wordID : String ? = nil
15
+ @State private var memorizedWords : [ String ] = [ ]
16
+ private var apiKey : String
17
+ private var sheetID : String
18
+
19
+ init ( ) {
20
+ if let path = Bundle . main. path ( forResource: " Config " , ofType: " plist " ) ,
21
+ let config = NSDictionary ( contentsOfFile: path) ,
22
+ let apiKey = config [ " API_KEY " ] as? String ,
23
+ let sheetID = config [ " SHEET_ID " ] as? String {
24
+ self . apiKey = apiKey
25
+ self . sheetID = sheetID
26
+ } else {
27
+ fatalError ( " API_KEY or SHEET_ID not found in Config.plist " )
28
+ }
29
+ }
9
30
10
31
var body : some View {
11
32
VStack ( spacing: 16 ) {
12
- // Word Section
33
+
13
34
Text ( word)
14
35
. font ( . system( size: 28 , weight: . bold) )
15
36
. foregroundColor ( . primary)
@@ -28,7 +49,7 @@ struct ContentView: View {
28
49
}
29
50
. frame ( maxWidth: . infinity, alignment: . center)
30
51
31
- // Meaning Section
52
+
32
53
VStack ( alignment: . leading, spacing: 8 ) {
33
54
Text ( " Meaning: " )
34
55
. font ( . headline)
@@ -37,15 +58,15 @@ struct ContentView: View {
37
58
. font ( . body)
38
59
. foregroundColor ( . primary)
39
60
. multilineTextAlignment ( . leading)
40
- . fixedSize ( horizontal: false , vertical: true ) // Add this
61
+ . fixedSize ( horizontal: false , vertical: true )
41
62
. padding ( )
42
63
. background ( Color . gray. opacity ( 0.1 ) )
43
64
. cornerRadius ( 8 )
44
65
}
45
66
. padding ( . horizontal)
46
67
. frame ( maxWidth: . infinity, alignment: . leading)
47
68
48
- // Example Section
69
+
49
70
VStack ( alignment: . leading, spacing: 8 ) {
50
71
Text ( " Example: " )
51
72
. font ( . headline)
@@ -55,7 +76,7 @@ struct ContentView: View {
55
76
. italic ( )
56
77
. foregroundColor ( . primary)
57
78
. multilineTextAlignment ( . leading)
58
- . fixedSize ( horizontal: false , vertical: true ) // Add this
79
+ . fixedSize ( horizontal: false , vertical: true )
59
80
. padding ( )
60
81
. background ( Color . gray. opacity ( 0.1 ) )
61
82
. cornerRadius ( 8 )
@@ -65,7 +86,7 @@ struct ContentView: View {
65
86
66
87
Divider ( )
67
88
68
- // Buttons Section
89
+
69
90
HStack {
70
91
Button ( " Memorized it " ) {
71
92
markAsMemorized ( )
@@ -87,6 +108,7 @@ struct ContentView: View {
87
108
. cornerRadius ( 12 )
88
109
. shadow ( radius: 8 )
89
110
. onAppear {
111
+ loadMemorizedWords ( )
90
112
loadRandomWord ( )
91
113
}
92
114
}
@@ -97,8 +119,6 @@ struct ContentView: View {
97
119
}
98
120
99
121
func loadRandomWord( ) {
100
- let sheetID = " 1U66wi1O42CeuC_7QuMTbF2hlewJinCAmgTOJpZFbV1k "
101
- let apiKey = " AIzaSyBjNi7cYMOO_RL_qPqI4wuVP71UZPs72Jg "
102
122
let url = URL ( string: " https://sheets.googleapis.com/v4/spreadsheets/ \( sheetID) /values/Sheet1?key= \( apiKey) " ) !
103
123
104
124
var request = URLRequest ( url: url)
@@ -114,7 +134,10 @@ struct ContentView: View {
114
134
let decodedResponse = try JSONDecoder ( ) . decode ( GoogleSheetResponse . self, from: data)
115
135
let rows = decodedResponse. values. dropFirst ( )
116
136
117
- if let randomRow = rows. randomElement ( ) {
137
+
138
+ let unmemorizedRows = rows. filter { !memorizedWords. contains ( $0 [ 0 ] ) }
139
+
140
+ if let randomRow = unmemorizedRows. randomElement ( ) {
118
141
DispatchQueue . main. async {
119
142
word = randomRow [ 0 ]
120
143
meaning = randomRow [ 1 ]
@@ -123,7 +146,7 @@ struct ContentView: View {
123
146
} else {
124
147
DispatchQueue . main. async {
125
148
word = " No more words! "
126
- meaning = " All words are marked as memorized ."
149
+ meaning = " You have memorized all words ."
127
150
example = " "
128
151
}
129
152
}
@@ -134,37 +157,39 @@ struct ContentView: View {
134
157
}
135
158
136
159
func markAsMemorized( ) {
137
- guard let wordID = wordID else { return }
160
+ guard !word. isEmpty, !memorizedWords. contains ( word) else { return }
161
+ memorizedWords. append ( word)
138
162
139
- let sheetID = " 1U66wi1O42CeuC_7QuMTbF2hlewJinCAmgTOJpZFbV1k "
140
- let apiKey = " AIzaSyBjNi7cYMOO_RL_qPqI4wuVP71UZPs72Jg "
141
- let url = URL ( string: " https://sheets.googleapis.com/v4/spreadsheets/ \( sheetID) /values/Sheet1!D \( wordID) :D \( wordID) ?key= \( apiKey) " ) !
142
163
143
- var request = URLRequest ( url: url)
144
- request. httpMethod = " PUT "
145
- request. setValue ( " application/json " , forHTTPHeaderField: " Content-Type " )
146
-
147
- let body : [ String : Any ] = [
148
- " range " : " Sheet1!D \( wordID) :D \( wordID) " ,
149
- " values " : [ [ " TRUE " ] ]
150
- ]
164
+ saveMemorizedWords ( )
165
+ loadRandomWord ( )
166
+ }
151
167
152
- request. httpBody = try ? JSONSerialization . data ( withJSONObject: body, options: [ ] )
168
+ func saveMemorizedWords( ) {
169
+ let fileURL = getFileURL ( )
170
+ do {
171
+ let data = try JSONEncoder ( ) . encode ( memorizedWords)
172
+ try data. write ( to: fileURL)
173
+ print ( " Memorized words saved to file. " )
174
+ } catch {
175
+ print ( " Failed to save memorized words: \( error. localizedDescription) " )
176
+ }
177
+ }
153
178
154
- URLSession . shared. dataTask ( with: request) { data, response, error in
155
- guard let data = data, error == nil else {
156
- print ( " Error updating data: \( error? . localizedDescription ?? " Unknown error " ) " )
157
- return
158
- }
179
+ func loadMemorizedWords( ) {
180
+ let fileURL = getFileURL ( )
181
+ do {
182
+ let data = try Data ( contentsOf: fileURL)
183
+ memorizedWords = try JSONDecoder ( ) . decode ( [ String ] . self, from: data)
184
+ print ( " Loaded memorized words: \( memorizedWords) " )
185
+ } catch {
186
+ print ( " No existing file found, starting fresh. " )
187
+ }
188
+ }
159
189
160
- if let response = response as? HTTPURLResponse , response. statusCode == 200 {
161
- print ( " Marked as memorized successfully " )
162
- DispatchQueue . main. async {
163
- loadRandomWord ( )
164
- }
165
- } else {
166
- print ( " Failed to mark as memorized " )
167
- }
168
- } . resume ( )
190
+ func getFileURL( ) -> URL {
191
+ let fileManager = FileManager . default
192
+ let documentsURL = fileManager. urls ( for: . documentDirectory, in: . userDomainMask) . first!
193
+ return documentsURL. appendingPathComponent ( " memorized_words.json " )
169
194
}
170
- }
195
+ }
0 commit comments