回答編集履歴

2

簡単な例を追記

2020/09/10 10:53

投稿

TsukubaDepot
TsukubaDepot

スコア5086

test CHANGED
@@ -175,3 +175,103 @@
175
175
  }
176
176
 
177
177
  ```
178
+
179
+
180
+
181
+ ##追記
182
+
183
+ 今回の処理を簡素化したモデルです。
184
+
185
+ Playground でも実行できますので、よかったら試してみてください。
186
+
187
+
188
+
189
+ ```Swift
190
+
191
+ import UIKit
192
+
193
+
194
+
195
+ func delayFunc(completion: @escaping (String) -> ()) {
196
+
197
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
198
+
199
+ // 1秒後に実行されるコード
200
+
201
+
202
+
203
+ // (1) に相当
204
+
205
+ print("(1) 遅延処理クロージャの中の処理を開始")
206
+
207
+ completion("クロージャへ渡す文字列")
208
+
209
+ }
210
+
211
+ // (2)に相当
212
+
213
+ print("(2) クロージャ外の処理")
214
+
215
+ }
216
+
217
+
218
+
219
+ delayFunc(completion: { (message) in
220
+
221
+ print("(3) 処理完了クロージャでの処理")
222
+
223
+ print("渡された文字列は「(message)」")
224
+
225
+ })
226
+
227
+ ```
228
+
229
+
230
+
231
+ クロージャだけでなく、引数の型と個数が合えば関数も渡せます。
232
+
233
+
234
+
235
+ ```Swift
236
+
237
+ import UIKit
238
+
239
+
240
+
241
+ func delayFunc(completion: @escaping (String) -> ()) {
242
+
243
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
244
+
245
+ // 1秒後に実行されるコード
246
+
247
+
248
+
249
+ // (1) に相当
250
+
251
+ print("(1) 遅延処理クロージャの中の処理を開始")
252
+
253
+ completion("クロージャへ渡す文字列")
254
+
255
+ }
256
+
257
+ // (2)に相当
258
+
259
+ print("(2) クロージャ外の処理")
260
+
261
+ }
262
+
263
+
264
+
265
+ func completionFunction(_ msg: String) {
266
+
267
+ print("(4) 処理完了クロージャは関数でもいい")
268
+
269
+ print("渡された文字列は「(msg)」")
270
+
271
+ }
272
+
273
+
274
+
275
+ delayFunc(completion: completionFunction)
276
+
277
+ ```

1

MVC に沿って変更

2020/09/10 10:53

投稿

TsukubaDepot
TsukubaDepot

スコア5086

test CHANGED
@@ -1,4 +1,4 @@
1
- あるメソッドに引数として取られたクロージャからは、return を使ってメソッドの外側に値を返すことができないことはおそらくご存知かと思います。
1
+ あるメソッドに引数として取られたクロージャからは、`return` を使ってメソッドの外側に値を返すことができないことはおそらくご存知かと思います。
2
2
 
3
3
 
4
4
 
@@ -12,13 +12,13 @@
12
12
 
13
13
  ```Swift
14
14
 
15
- getElevation(_ Locations: [CLLocation], completionHandler: @escaping (Double?) -> Void, errorHandler: (Error?) -> Void )
15
+ getElevation(_ Locations: [CLLocation], completionHandler: @escaping (String?) -> Void, errorHandler: @escaping (Error?) -> Void )
16
16
 
17
17
  ```
18
18
 
19
19
 
20
20
 
21
- `completionHandler: `で与えるクロージャは、引数として `Double?`型が与えられます。処理内で正しい標高が得られなかった場合には `0.0` を返すようになっていましたが、実際は標高 `0.0` m という場所もあるので、`nil` を返したほうが現実的と思います。
21
+ `completionHandler: `で与えるクロージャは、引数として `String?`型が与えられます。処理内で正しい標高が得られなかった場合には `"0.0"` を返すようになっていましたが、実際は標高 `0.0` m という場所もあるので、`nil` を返したほうが現実的と思います。
22
22
 
23
23
 
24
24
 
@@ -30,59 +30,39 @@
30
30
 
31
31
 
32
32
 
33
+ - Controller
34
+
33
35
  ```Swift
34
36
 
35
- func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
36
-
37
- getElevation(locations, completionHandler: { (elevation) in
37
+     model.getElevation(locations, completionHandler: { (elevationString) in
38
-
39
- if let elevation = elevation {
40
-
41
- // 標高が得られたときの処理
42
-
43
- self.afterElevation = elevation
44
-
45
- self.measuringElevation()
46
-
47
- self.elevationString = String(format: "%.2fm", self.gotElevation)
48
-
49
- self.beforeElevation = self.afterElevation
50
-
51
- } else {
52
-
53
- // 何らかの原因で標高が取れなかった場合
54
-
55
- self.elevationString = "0.0"
56
-
57
- }
58
-
59
-
60
38
 
61
39
  DispatchQueue.main.async {
62
40
 
41
+ // ここでは View Controller にラベルに表示しているが、他のクラスのプロパティに書き込む場合でも考え方は同じ
42
+
63
- self.customView.elevation = self.elevationString
43
+ //self.customView.elevation = elevationString
44
+
45
+ self.altitudeLabel.text = elevationString ?? "取得できませんでした"
64
46
 
65
47
  }
66
48
 
67
49
  }, errorHandler: { (error) in
68
50
 
69
- print(error?.localizedDescription)
51
+ print(error?.localizedDescription ?? "エラー情報なし")
70
52
 
71
53
  })
72
54
 
73
- }
55
+ ```
74
56
 
75
-
76
57
 
77
- func getElevation(_ Locations: [CLLocation], completionHandler: @escaping (Double?) -> Void, errorHandler: (Error?) -> Void ) {
78
58
 
79
-
59
+ - Model
80
60
 
81
- self.locations = Locations
61
+ ```Swift
82
62
 
83
-
63
+ func getElevation(_ locations: [CLLocation], completionHandler: @escaping (String?) -> Void, errorHandler: @escaping (Error?) -> Void ) {
84
64
 
85
- guard let coordinate = Locations.first?.coordinate else {
65
+ guard let coordinate = locations.first?.coordinate else {
86
66
 
87
67
  // 終了処理のクロージャに nil を渡す
88
68
 
@@ -94,9 +74,7 @@
94
74
 
95
75
 
96
76
 
97
- // userLocaton = coordinate で良いのでは?
98
-
99
- userLocation = CLLocationCoordinate2DMake(coordinate.latitude, coordinate.longitude)
77
+ let userLocation = CLLocationCoordinate2DMake(coordinate.latitude, coordinate.longitude)
100
78
 
101
79
 
102
80
 
@@ -104,21 +82,45 @@
104
82
 
105
83
 
106
84
 
107
- if let userLocation = userLocation {
85
+ let lonUrl = "&lon=" + userLocation.longitude.description
108
86
 
109
- let lonUrl = "&lon=" + userLocation.longitude.description
87
+ let latUrl = "&lat=" + userLocation.latitude.description
110
88
 
111
- let latUrl = "&lat=" + userLocation.latitude.description
89
+ let outtypeUrl = "&outtype=JSON"
112
90
 
91
+
92
+
113
- let outtypeUrl = "&outtype=JSON"
93
+ let listUrl = baseUrl + lonUrl + latUrl + outtypeUrl
94
+
95
+ print(listUrl)
96
+
97
+ guard let url = URL(string: listUrl) else {
98
+
99
+ // 終了処理のクロージャに nil を渡す
100
+
101
+ completionHandler(nil)
102
+
103
+ return
104
+
105
+ }
106
+
107
+
108
+
109
+ let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
110
+
111
+ if error != nil {
112
+
113
+ // エラー処理のクロージャに error を渡す
114
+
115
+ errorHandler(error)
116
+
117
+ return
118
+
119
+ }
114
120
 
115
121
 
116
122
 
117
- let listUrl = baseUrl + lonUrl + latUrl + outtypeUrl
118
-
119
-
120
-
121
- guard let url = URL(string: listUrl) else {
123
+ guard let data = data else {
122
124
 
123
125
  // 終了処理のクロージャに nil を渡す
124
126
 
@@ -130,47 +132,39 @@
130
132
 
131
133
 
132
134
 
133
- let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
135
+ // error を返せるように、do-try-catch で処理する
134
136
 
135
- if error != nil {
137
+ do {
136
138
 
137
- // エラー処理のクロージャに error を渡す
139
+ let json = try JSONDecoder().decode(ElevationData.self, from: data)
138
140
 
139
- errorHandler(error)
140
141
 
141
- return
142
142
 
143
- // print(error!.localizedDescription)
143
+ // 以下の処理は全体の文脈がわからないので、とりあえずコメントアウト
144
144
 
145
+ // self.afterElevation = json.elevation
146
+
145
- }
147
+ // self.measuringElevation()
148
+
149
+ // self.beforeElevation = self.afterElevation
146
150
 
147
151
 
148
152
 
149
- guard let data = data else {
153
+ // JSON で得られた座標を直接処理
150
154
 
151
- // 終了処理のクロージャに nil を渡す
152
-
153
- completionHandler(nil)
155
+ self.gotElevation = json.elevation
154
-
155
- return
156
-
157
- }
158
156
 
159
157
 
160
158
 
161
- // error を返せるように、do-try-catch で処理する
159
+ let elevationString = String(format: "%.2fm", self.gotElevation)
162
160
 
163
- do {
164
161
 
165
- let json = try JSONDecoder().decode(ElevationData.self, from: data)
166
162
 
167
- completionHandler(json.elevation)
163
+ completionHandler(elevationString)
168
164
 
169
- } catch {
165
+ } catch {
170
166
 
171
- errorHandler(error)
167
+ errorHandler(error)
172
-
173
- }
174
168
 
175
169
  }
176
170