はじめに
初心者が学習とポートフォリオをかねて作成しました。
作成し始めて1〜2ヶ月程度ですので、リファクタリングやコーディング等のアドバイスあればいただきたいです。
アプリ概要
サラリーマンなら今日傘が必要かを一瞬で確認できるアプリがあれば便利だなと思い作成。
ボタンを押す
↓
GPSから今いる場所の緯度・経度を割り出す
↓
緯度・経度から逆ジオコーディング
↓
今いる都道府県を割り出す
↓
お天気APIから今いる都道府県の18時間先の降水確率を取得
↓
取得した降水確率の最大値をピックアップ
↓
傘が必要かそうでないかを表示
すでにそのようなアプリがリリースされていましたが、僕が望んでいたアプリとは違いましたので、ポートフォリオ作成もかねて作成してみました。
- インプット
- ---
- **読書**
- ・絶対に挫折しないiPhoneアプリ開発「超」入門編 ・たった2日でマスターできるiPhoneアプリ開発集中講座
- **Udemy**
- ・iOS12:Learn to Code & Build Real iOS 12 Apps in Swift 4.2 概念などをイメージ図などで教えてくれたので、理解しやすかった。
・【iOS14対応】未経験者がiPhoneアプリ開発者になるための全て iOS Boot Camp
iOSは随時更新されていくので、新しい知識を常にキャッチアップしなければならないとおもった。
- **アウトプット**
- --- **Qiita**
多くの方がQiitaにソースコードを載せてくれてたおかげでここまで作成することができたため、
同じような境遇の人たちの役に立てばと思い作成した。 - --- **Qiita**
- ・絶対に挫折しないiPhoneアプリ開発「超」入門編 ・たった2日でマスターできるiPhoneアプリ開発集中講座
動作環境
対象 | バージョン |
---|---|
iOS | 14.0 |
macOS | Catalina 10.15.7 |
Xcode | 12.0 |
Swift | 5.3 |
使用したお天気API
OpenWeatherMap
Podをインストールする
今回は
"Alaomofire”と”SwiftyJSON”をインストールしました。
pod "Alamofire"
pod "SwiftyJSON"
Main.storyboadを作成する
今回はHomeVCとUmbrellaVCを作成しました。
アプリ起動後にHomeVCへ移行→「Location Information」ボタンタップ→UmbrellaVCへ移行
①labelには、逆ジオコーディングで取得した現在地の都道府県を呼び出し
②labelには、お天気APIから現在地の降水確率を呼び出し
③labelには、降水確率から傘が必要か不必要か判断
実装する
まずはお天気APIで取得するデータのmodelを作成しておく。
CityModel
import Foundation
struct cityModel:Decodable{
var list: [List]
struct List:Decodable {
var pop:Double
}
}
ここに記載する構造体は取得するAPIデータによって異なると思います。
今回使用したお天気APIの「OpenWeather」では、上記のような取得をしました。
各labelに変数を代入しておく。
UmbrellaVC
import UIKit
import CoreLocation
class UmbrellaVC: UIViewController {
@IBOutlet weak var label1: UILabel!
var locationText:String = "大阪府"
@IBOutlet weak var label2: UILabel!
var popText:Int = 0
@IBOutlet weak var label3: UILabel!
var umbrellaJudgement:String = "傘が必要"
override func viewDidLoad() {
super.viewDidLoad()
label1.text = locationText
label2.text = "\(popText)" + "%"
label3.text = umbrellaJudgement
self.label3.layer.borderWidth = 2.0
self.label3.layer.borderColor = UIColor.black.cgColor
self.label3.layer.cornerRadius = 20
//降水確率によって出てくる文字の色を変えて一目でわかるようにする
if popText >= 30 && popText < 70{
label3.textColor = .orange
}else if popText >= 70{
label3.textColor = .red
}
}
}
UmbrellaVCで設定した変数に代入する。
HomeVC
import UIKit
import CoreLocation
import Alamofire
import SwiftyJSON
class HomeVC: UIViewController,CLLocationManagerDelegate {
//各変数へ初期値を入れておく
var latitudeNow: Double = 39.0000
var longitudeNow: Double = 140.0000
var locationManager: CLLocationManager!
var administrativeArea:String = ""
var locationNow: String = ""
private var citymodel: cityModel?
var doubleOfMaximumPop:Double = 100.0
var maxPop:Int = 30
var Judge:String = ""
override func viewDidLoad() {
super.viewDidLoad()
//image viewのレイアウト設定
umbrellaImage.image = UIImage(named:"umbrellaImage")
umbrellaImage.layer.cornerRadius = 10
//locationManagerをviewDidload時に呼び出す(位置情報を更新する)
locationManagerDidChangeAuthorization(CLLocationManager())
//天気取得関数の呼び出し
getWeatherData()
}
// ボタンを押した際に位置情報を取得する
@IBAction func buttonTapped(_ sender: Any) {
//ボタンを押すとlocationManagerの更新をやめる
stopLocationManager()
}
// ボタンを押すとsegueに移行し、UmbrellaVC内のLabel1に都道府県を記載する
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "toUmbrellaVC") {
let locationNow: UmbrellaVC = (segue.destination as? UmbrellaVC)!
locationNow.locationText = administrativeArea
let popNow:UmbrellaVC = (segue.destination as? UmbrellaVC)!
popNow.popText = Int(maxPop)
let umbJud:UmbrellaVC = (segue.destination as? UmbrellaVC)!
umbJud.umbrellaJudgement = Judge
}
}
//ロケーションマネージャー@iOS 14 位置情報の更新
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
locationManager = CLLocationManager()
let status = manager.authorizationStatus
switch status {
case .authorizedAlways, .authorizedWhenInUse:
locationManager.delegate = self
locationManager.startUpdatingLocation()
case .notDetermined, .denied, .restricted:
showAlert()
default:print("未処理")
}
}
//locationManagerの情報更新をやめる
func stopLocationManager(){
locationManager.stopUpdatingLocation()
}
//アラートを表示する関数
func showAlert(){
let alertTitle = "位置情報取得が許可されていません。"
let alertMessage = "設定アプリの「プライバシー > 位置情報サービス」から変更してください。"
let alert: UIAlertController = UIAlertController(
title: alertTitle, message: alertMessage, preferredStyle: UIAlertController.Style.alert
)
//OKボタン
let defaultAction: UIAlertAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)
//UIAlertControllerにActionを追加
alert.addAction(defaultAction)
present(alert, animated: true, completion: nil)
}
//位置情報が更新された際、位置情報を格納する関数
//位置情報が更新されないとlocation managerは起動しない※重要
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.first
let latitude = location!.coordinate.latitude
let longitude = location!.coordinate.longitude
//位置情報を格納する
self.latitudeNow = Double(latitude)
self.longitudeNow = Double(longitude)
//位置情報を取得後、逆ジオコーディングし、都道府県を割り出す
let locationA = CLLocation(latitude: latitudeNow, longitude: longitudeNow)
let geocoder: CLGeocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(locationA) { [self] (placemarks, error) in
if let placemark = placemarks?.first {
self.administrativeArea = placemark.administrativeArea!
} else {
self.administrativeArea = "bb"
}
}
}
//天気予報APIを用いて18時間後までの最大降水確率を取得する
//OpenWeatherMapでは1日の降水確率取得が有料であり、3時間ごとの降水確率取得は無料のためそちらを使用(朝、出勤前にアプリを使うことを想定したため、問題ないと判断)
private func getWeatherData() {
let id = "API ID を入力"
let baseUrl = "http://api.openweathermap.org/data/2.5/forecast?lat=" + "\(latitudeNow)" + "&lon=" + "\(longitudeNow)" + "&exclude=daily&lang=ja&cnt=6&.pop&appid=" + "\(id)"
AF.request(baseUrl, method: .get).responseJSON { [self] response in
guard let data = response.data else {
return
}
do {
let citymodel = try JSONDecoder().decode(cityModel.self, from: data)
//APIのデータをリスト表示する
let popNumber = citymodel.list.map{ $0.pop }
//リスト内のmaxデータを取得する
var doubleOfMaximumPop = popNumber.max()
//maxデータのパーセンテージ表示に変換する
let maxPop = doubleOfMaximumPop! * 100
//データがあるかどうかを判断する
if doubleOfMaximumPop == nil{
print(Error.self)
}else {
//データがあれば、
if doubleOfMaximumPop != nil{
//maxデータを取得する
doubleOfMaximumPop = self.doubleOfMaximumPop
}else {
//同じ数字であれば、その中のひとつをピックアップする
doubleOfMaximumPop = popNumber[0]
}
}
//maxPopへgetweather関数で取得した数値を変数へ代入する
self.maxPop = Int(maxPop)
//maxPopによって傘が必要かの判断をし、判断した文をJudgeへ代入する。
if self.maxPop <= 30 {
self.Judge = "⛅️傘は不要です⛅️"
}else if self.maxPop >= 70 {
self.Judge = "☔️傘が必要です☔️"
}else {
self.Judge = "☂️折り畳み傘を持っていれば安心☂️"
}
}catch let error {
print("Error:\(error)")
}
}
}
}
苦労した点
・LocationManagerで位置情報を更新→逆ジオコーディングしデータを取得するという流れを理解していなかったため、データは取得できているが、変数に代入されないなどの不具合が発生した。
参考サイト一覧
iOS14でのCore Location変更点
関数内の変数を関数外の変数へ代入する
[Codaleについて備忘録]
(https://qiita.com/s_emoto/items/deda5abcb0adc2217e86)