SwiftUIで郵便番号検索APIを使う
読んでほしい人
- SwiftUIを勉強している人
- 郵便番号検索APIを使ってみたい人
補足情報
こちらのサイトのAPIを使って今回はアプリを作っていこうと思います。
記事の内容
SwiftUIでAPIを使ったアプリの学習をしていて、アウトプットのために色々作ってみたいなと思い入門レベルの郵便番号検索ができるアプリを作ってみました。
今回はこんな構造のJSONに合わせて、structを定義します。
{
"message": null,
"results": [
{
"address1": "東京都",
"address2": "三鷹市",
"address3": "",
"kana1": "トウキョウト",
"kana2": "ミタカシ",
"kana3": "",
"prefcode": "13",
"zipcode": "1810000"
}
],
"status": 200
}
モデルを作成する。構造体をネストして、JSONがresults: [{}]
ネストしている構造に合わせて、2個作成する。
struct ZipCode: Codable {
var message: String?
var results: [Result]
var status: Int
}
struct Result: Codable {
var address1: String
var address2: String
var address3: String
var kana1: String
var kana2: String
var kana3: String
var prefcode: String
var zipcode: String
}
View側に今回は、ロジックも書いてます。練習用のアプリということで、メソッドをクラスに書いて分けるのまではやってないです。検索Formがあって、郵便番号-
なしの7桁ではなかったら、エラーメッセージを表示する正規表現を使っております。入力してボタンを押すと、検索をするメソッドが実行されます。
struct ContentView: View {
@State private var zipCode = ""
@State private var results = [Result]()
@State private var errorMessage = ""
var body: some View {
VStack {
TextField("郵便番号を入力してください", text: $zipCode)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
Button(action: {
if validate(zipCode: zipCode) {
fetchResults()
}
}) {
Text("検索")
}
if !errorMessage.isEmpty {
Text(errorMessage)
.foregroundColor(.red)
}
List(results, id: \.zipcode) { result in
VStack(alignment: .leading) {
Text("郵便番号: \(result.zipcode + result.prefcode)")
Text("都道府県コード: \(result.prefcode)")
Text("住所カナ: \(result.kana1 + result.kana2 + result.kana3)")
Text("住所: \(result.address1 + result.address2 + result.address3)")
}
}
}
}
func validate(zipCode: String) -> Bool {
let zipCodePattern = "^[0-9]{3}-?[0-9]{4}$"
let zipCodePredicate = NSPredicate(format: "SELF MATCHES %@", zipCodePattern)
let isValid = zipCodePredicate.evaluate(with: zipCode)
if !isValid {
errorMessage = "郵便番号は-なしで7桁で入力してください!"
} else {
errorMessage = ""
}
return isValid
}
func fetchResults() {
guard let url = URL(string: "https://zipcloud.ibsnet.co.jp/api/search?zipcode=\(zipCode)") else {
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let decodedData = try JSONDecoder().decode(ZipCode.self, from: data)
DispatchQueue.main.async {
self.results = decodedData.results
}
} catch {
print("Failed to decode JSON: \(error.localizedDescription)")
}
}
}.resume()
}
}
使うときは、東京都の三鷹市なら1810000
で目黒区なら、1520000
といった感じで入力します。こんな感じですね。もし入力が正しくないとエラーが表示されます!
こちらが全体のコード
import SwiftUI
struct ZipCode: Codable {
var message: String?
var results: [Result]
var status: Int
}
struct Result: Codable {
var address1: String
var address2: String
var address3: String
var kana1: String
var kana2: String
var kana3: String
var prefcode: String
var zipcode: String
}
struct ContentView: View {
@State private var zipCode = ""
@State private var results = [Result]()
@State private var errorMessage = ""
var body: some View {
VStack {
TextField("郵便番号を入力してください", text: $zipCode)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
Button(action: {
if validate(zipCode: zipCode) {
fetchResults()
}
}) {
Text("検索")
}
if !errorMessage.isEmpty {
Text(errorMessage)
.foregroundColor(.red)
}
List(results, id: \.zipcode) { result in
VStack(alignment: .leading) {
Text("郵便番号: \(result.zipcode + result.prefcode)")
Text("都道府県コード: \(result.prefcode)")
Text("住所カナ: \(result.kana1 + result.kana2 + result.kana3)")
Text("住所: \(result.address1 + result.address2 + result.address3)")
}
}
}
}
func validate(zipCode: String) -> Bool {
let zipCodePattern = "^[0-9]{3}-?[0-9]{4}$"
let zipCodePredicate = NSPredicate(format: "SELF MATCHES %@", zipCodePattern)
let isValid = zipCodePredicate.evaluate(with: zipCode)
if !isValid {
errorMessage = "郵便番号は-なしで7桁で入力してください!"
} else {
errorMessage = ""
}
return isValid
}
func fetchResults() {
guard let url = URL(string: "https://zipcloud.ibsnet.co.jp/api/search?zipcode=\(zipCode)") else {
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let decodedData = try JSONDecoder().decode(ZipCode.self, from: data)
DispatchQueue.main.async {
self.results = decodedData.results
}
} catch {
print("Failed to decode JSON: \(error.localizedDescription)")
}
}
}.resume()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
最後に
今回は、郵便番号検索アプリを作ってAPIの使い方について学習してみました。ご興味あるかたは参考にして使ってみてください。
Codableについてですが、どんなものかというと
Codable
A type that can convert itself into and out of an external representation.
コード化可能
それ自身を外部表現に変換したり、外部表現から変換したりできる型。
typealias Codable = Decodable & Encodable
Discussion
Codable is a type alias for the Encodable and Decodable protocols. When you use Codable as a type or a generic constraint, it matches any type that conforms to both protocols.
ディスカッション
Codableは、EncodableおよびDecodableプロトコルの型エイリアスです。Codableを型またはジェネリック制約として使用すると、両方のプロトコルに適合するすべての型にマッチします。
今回は、モデルでJSONのデータを扱うときに、エンコードとデコードする機能を提供してくれているプロトコルのようです。構造体はこのプロトコル(お決まりごとに)に準拠(従うってこと)で、郵便番号検索APIのJSONのデータをテキストと数値に変換してくれます。
Discussion