- 投稿日:2020-01-07T21:55:02+09:00
IFTTTのリッチ通知から任意のiOSショートカットを起動する
はじめに
iOSのショートカットは、ショートカットのURLスキームを使って外部から任意のショートカットを起動することができます。
ここでは、リッチ通知から任意のショートカットを起動する方法について説明します。前提
- iOSショートカットアプリが必要です。
- また、通知から起動する任意のショートカットを事前に作成する必要があります。
- IFTTTから通知を受信するために、IFTTTアプリのインストールが必要です。
ショートカットのURLスキームの仕様
URLスキームを使用してショートカットを実行するのドキュメントに記載されていますが、以下のURLスキームを開くことで任意のショートカットを起動することができます。
shortcuts://run-shortcut?name=Make%20PDF&input=text&text=soupnameにショートカット名を指定するのですが、URLエンコードが必要です。
また、inputパラメータの値にtextを指定することで、textパラメータの値をショートカットに渡すことができます。
その他、inputパラメータの値にclipboardを指定することもできますが、詳しくは上記のドキュメントをご参照ください。
ただし、ここでは、inputパラメータやtextパラメータの指定はなしで説明します。IFTTT側のリッチ通知の設定
ここでは、IFTTTのアクションの作り方については詳しく説明しません。他の記事をご参照ください。
まずは、IFTTTの
Thisのアクションは適当にWebhooksなどを指定します。
次に、Thatですが、Notificationsを選択後、Send a rich notification from the IFTTT appを選択します。選択後、通知で表示するタイトルやメッセージ内容を適当に設定します。
ここで重要な項目はLink URLです。
この項目に起動したいショートカットのURLスキームを指定します。例えば、
天気という名称のショートカットを起動する場合は、shortcuts://run-shortcut?name=%E5%A4%A9%E6%B0%97を
Link URLに指定します。上記を設定後、Saveボタンを押下したら完了です。
リッチ通知からのショートカットの起動
通知を受信したら自動でショートカットが起動されてほしいとも思うかもしれませんが、残念ながら(セキュリティ上)自動でショートカットは起動されません。
ショートカットを起動するには、受信した通知をタップする必要があります。
通知をタップ後に、URLスキームで指定した名称のショートカットが起動します。おわりに
ショートカット自体は、通知を実際にタップしないと起動されないため、若干煩わしいですね。
また、リッチ通知からショートカット起動して何がうれしいのかはまだよく分かっておりませんが、アイデア次第で色々活用方法はあるのではないでしょうか。
例えば、技術本の新刊情報を通知で知らせて、通知をタップしたらAmazonの購入サイトに遷移すると良いかなとも思います。
今後良い活用方法を模索したいと思います。
- 投稿日:2020-01-07T21:24:12+09:00
急にiOSアプリ開発(未経験)の仕事が来たのでパッケージマネージャ何がいいねんに立ち向かう
背景
タイトルの通りですが、業務でやったことのないiOSアプリ開発の仕事が急に来た。
直近の課題の1つにパッケージマネージャ何使えばいいんだ問題があったのでわからないなりに調べてみた結果と所感をまとめた個人の備忘録です。
間違ってたら優しく指摘ください!(個人ブログも参考にしたので情報古いところあるかも)候補
- CocoaPods
- Carthage
- Swift Package Manager
CocoaPods
特徴
- 中央集権的なパッケージマネージャ
- Specsと呼ばれる巨大なリポジトリの中に全てのパッケージ情報を詰め込んでいる(ので中央集権的)
- podコマンドによりパッケージの登録・更新が可能
- OSSで開発されている。Ruby製(なのでgemでMacへインストールする)
- 公式ページの検索が非常に優秀でライブラリを探しやすいとのこと
メリット
- Webサイトでライブラリを探せる
- Dynamic FrameworksとStatic Librariesの両方に対応(Dynamic Frameworkはアプリ起動時に結合されるもの、Static Frameworkはアプリビルド時に結合されるものらしい)
- 依存するライブラリがさらに依存しているライブラリも解決してくれる
- 依存関係の定義ファイルを見ればどのライブラリにアプリが依存しているか一目でわかる
- 依存ライブラリのアップデートをコマンド1つで検知できる
- pod tryでライブラリのリポジトリ中のサンプルプログラムをすぐに試せる
- 公式で簡単に依存関係を管理できるアプリを出している
- ほぼすべてのライブラリがCocoaPodsをサポートしている
デメリット
- CocoaPodsを入れてから初めて依存関係をインストールとき、長時間待つ必要がある。それは前述の巨大なリポジトリをすべてMacに入れるため。また、pod updateするときも同じだけ待つ必要がある
- 依存ライブラリが使えるようにするために、CocoaPodsは勝手にプロジェクトをいじる。それによって問題が起きてしまう場合が多い
- プロジェクトをビルドするとき全ての依存ライブラリをビルドするのでビルド時間が長い
Carthage
特徴
- 非中央集権的パッケージマネージャ
- CocoaPodsとは違いパッケージが1リポジトリで管理されていなく、別々のリポジトリで個別に管理
- 依存に追加したいgithubのリポジトリを指定してパッケージをインストールする
- Swift製のOSS
メリット
- Dynamic FrameworksとStatic Librariesの両方に対応
- 依存するライブラリがさらに依存しているライブラリも解決してくれる
- 依存関係の定義ファイルを見ればどのライブラリにアプリが依存しているか一目でわかる
- 依存ライブラリのアップデートをコマンド1つで検知できる
- 1度しかライブラリのビルドをしないのでCocoaPodsに比べてビルド時間が速い
- CocoaPodsのように勝手にプロジェクトがいじられることがないので安心
デメリット
- 依存関係を追加するためのステップが多いのでミスしがち
- すべてのライブラリがCarthageをサポートしているわけではない
Swift Package Manager
特徴
- Apple公式で出したパッケージマネージャ
- 対応しているライブラリが少なく、バックエンド用途のライブラリが多いらしい
メリット
- 今後スタンダードになっていくであろうパッケージマネージャらしい
- 依存するライブラリがさらに依存しているライブラリも解決してくれる
- 依存関係の定義ファイルを見ればどのライブラリにアプリが依存しているか一目でわかる
デメリット
- watchOS、tvOSなどサポートしていないOSがある
- 決まったフォルダ構成に従うことを強制される
- 対応するライブラリがまだ少ない
所感
対応しているライブラリがまだまだ少なそうな「Swift Package Manager」は現時点で避けたい。
依存関係の追加が楽だが勝手にプロジェクトをいじられて大変な思いをしそうなCocoaPodsをとるか、やや追加が面倒かつ対応してないライブラリもあるがプロジェクトをいじられない心理的安全性を保てるCarthageをとるかでいうと、個人的にはCarthageに軍配。
ただでさえ初めてのネイティブ開発で依存関係追加の手間を惜しんで、CocoaPodsが何かを壊してXcodeとにらめっこ...は避けたいため。(ある程度形ができたら引き継ぐ予定のメンバーもネイティブ知らない人ばかりなので...)
- 投稿日:2020-01-07T20:06:40+09:00
ExpoのPermissions周りを実機検証してみる
はじめに
Expoでの開発に限らず、iOSアプリでは一部のプライバシーに関わる機能を利用する際、その目的を説明する文言を設定する必要があります。設定した説明文は、ユーザーに権限を確認するためのダイアログに表示されます。
設定が必要な機能については下記のような記事が参考になりました。
iOS のユーザデータにアクセスするための Info.plist への許可設定まとめ
http://neos21.hatenablog.com/entry/2018/06/18/080000[iOS 10] 各種ユーザーデータへアクセスする目的を記述することが必須になるようです
https://dev.classmethod.jp/smartphone/iphone/ios10-privacy-data-purpose-description/Expoにおいてこの説明文(UsageDescription)は
app.jsonのexpo.ios.infoPlistオブジェクトに追加することでカスタマイズできます。
デフォルトだと説明文が存在しない訳ではなく、英語、かつ目的を説明していない(Give ~ permissionという感じの)ため、App Storeに申請する際、下記の記事に書かれているようにリジェクトされてしまいます。【React Native】【Expo】iOSのパーミッション要求ダイアログで審査リジェクトされた話とその対応
https://tech.maricuru.com/entry/2018/07/27/195921私が過去リジェクトされた際には目的がどうのというより、言語を統一しなさいという感じだったと思うので、目的をちゃんと説明しつつアプリが対象としている言語で設定する必要があるようです。
フォトライブラリ(カメラロール)、カメラ、通知、位置情報のパーミッション設定をiOS/Androidの実機で確認してみたので、その流れを説明します。
画面を作成
このようにPermissionを確認するボタンだけを表示するような画面を作ります。
追加するモジュールはこのあたりです
- expo-image-picker
- expo-permissions
- expo-location
- expo-haptics これは全然関係ないけど使ってみたかったので。許可が得られた時に振動をつけてみます。
Permission取得の処理
PermissionButton.jsimport React, { useState } from "react"; import { TouchableOpacity, Text, View, ActivityIndicator, Alert } from "react-native"; import * as Haptics from "expo-haptics"; import { Linking } from "expo"; import * as Permissions from "expo-permissions"; function PermissionButton(props) { const { type, title } = props; const [isLoading, setLoading] = useState(false); const [isGranted, setGranted] = useState(false); return ( <TouchableOpacity style={{ width: "100%", height: 50, marginVertical: 12 }} disabled={isLoading} onPress={async () => { if (isGranted) { props.onPress && props.onPress(); return false; } setLoading(true); const { status } = await Permissions.askAsync(Permissions[type]); setLoading(false); if (status === "granted") { setGranted(true); await Haptics.notificationAsync( Haptics.NotificationFeedbackType.Success ); setTimeout(props.onPress, 500); } else { // ユーザーが意図的にPermissionを不許可にしている場合 // アラートを表示して設定画面に移動する Alert.alert( `${title}が無効になっています`, "設定画面へ移動しますか?", [ { text: "キャンセル", style: "cancel" }, { text: "設定する", onPress: () => { Linking.openURL("app-settings:"); } } ] ); } console.log(type, status); }} > <View style={[ props.style, { position: "relative", width: "100%", height: "100%", justifyContent: "center", alignItems: "center", backgroundColor: isGranted ? "#3cbe8d" : "#3cb3ff", borderRadius: 25 } ]} > {isLoading ? ( <ActivityIndicator color="#ffffff" style={{ position: "absolute", width: "100%", height: "100%", top: 0, left: 15, justifyContent: "center", alignItems: "flex-start" }} /> ) : null} {isGranted ? ( <Text style={{ position: "absolute", width: "100%", height: "100%", lineHeight: 50, top: -2, left: 15, justifyContent: "center", alignItems: "flex-start" }} > 👍 </Text> ) : null} <Text style={{ color: "#ffffff", fontWeight: "bold" }} > {title} </Text> </View> </TouchableOpacity> ); } export default PermissionButton;このように、指定したPermissionをaskするボタンをコンポーネント化しておきました。
画面を作成し、各機能を確認する処理を追加
App.jsimport React from "react"; import { Text, View, Alert } from "react-native"; import PermissionButton from "./PermissionButton"; import * as ImagePicker from "expo-image-picker"; import * as Location from "expo-location"; import { Notifications } from "expo"; export default function App() { return ( <View style={{ flex: 1, padding: 32, backgroundColor: "#fff", alignItems: "center", justifyContent: "center" }} > <Text style={{ fontSize: 24, marginBottom: 16, fontWeight: "bold" }} > パーミッション確認 </Text> <PermissionButton type="CAMERA_ROLL" title="フォトライブラリ(カメラロール)" onPress={async () => { try { const result = await ImagePicker.launchImageLibraryAsync(); console.log(result); } catch (error) { Alert.alert(error.message); } }} /> <PermissionButton type="CAMERA" title="カメラ" onPress={async () => { try { const result = await ImagePicker.launchCameraAsync(); console.log(result); } catch (error) { Alert.alert(error.message); } }} /> <PermissionButton type="NOTIFICATIONS" title="PUSH通知" onPress={async () => { try { const token = await Notifications.getExpoPushTokenAsync(); console.log("token", token); const res = await fetch("https://expo.io/--/api/v2/push/send", { method: "POST", headers: { Accept: "application/json", "Content-type": "application/json" }, body: JSON.stringify({ to: token, title: "通知テスト", body: "ここにbodyが入ります", _displayInForeground: true }) }); console.log(res); } catch (error) { Alert.alert(error.message); } }} /> <PermissionButton type="LOCATION" title="位置情報" onPress={async () => { try { const { coords } = await Location.getCurrentPositionAsync(); Alert.alert( "位置情報の取得に成功しました", `緯度:${coords.latitude}\n経度:${coords.longitude}` ); } catch (error) { Alert.alert(error.message); } }} /> </View> ); }このように画面にボタンを配置して、各機能を確認するだけの処理を実装します。
通知に関しては送信が確認できればいいので、ExpoのAPIを直接叩いています。その際、iOSでもアプリ起動時に通知が表示されるように_displayInForeground: trueをオプションに指定しておきます。
app.jsonの編集app.json{ "expo": { "name": "許可テスト", "slug": "permission-test", "privacy": "public", "sdkVersion": "36.0.0", "platforms": [ "ios", "android", "web" ], "version": "1.0.0", "orientation": "portrait", "icon": "./assets/icon.png", "splash": { "image": "./assets/splash.png", "resizeMode": "contain", "backgroundColor": "#3cb3ff" }, "updates": { "fallbackToCacheTimeout": 0 }, "assetBundlePatterns": [ "**/*" ], "ios": { "supportsTablet": true, "bundleIdentifier": "バンドル名", "infoPlist": { "NSCameraUsageDescription": "[テスト文言]写真をアップロードするためにカメラを使用します", "NSPhotoLibraryUsageDescription": "[テスト文言]アカウント画像をアップロードするためにフォトライブラリを使用します", "NSLocationAlwaysUsageDescription": "[テスト文言]位置情報をアップロードします", "NSLocationUsageDescription": "[テスト文言]位置情報をアップロードします", "NSLocationWhenInUseUsageDescription": "[テスト文言]位置情報をアップロードします" } }, "android": { "package": "パッケージ名", "permissions": [ "ACCESS_COARSE_LOCATION", "ACCESS_FINE_LOCATION", "CAMERA", "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE", "VIBRATE", ] }, "notification": { "icon": "./assets/notificationicon.png", "color": "#3cb3ff" } } }
app.jsonはこんな感じで設定しました。
expo.ios.infoPlistに各UsageDescriptionを、expo.android.permissionsには必要なだけの機能を記載します。
一応expo.notificationでAndroid用の通知アイコンも設定しておきました。(詳しくはこちらの記事)
expo.android.permissionsのVIBRATEは、expo-hapticsを使うので入れています。検証
Android
実機での検証はAndroidの方が圧倒的に楽なので先にやってみます。
Androidの場合も6.0から一部機能でダイアログが表示されますが、この文言は設定できないので、
適切なexpo.android.permissionsがあれば申請の際に気にする必要はありません。
Expo Clientでの確認で十分な気もしつつ、念の為スタンドアロンアプリとしてビルドし実機で確認します。ビルド$ expo build:androidビルド後に表示されたURLからダウンロードし、
端末を繋げてからAndroid SDKのadb installコマンドでAPKファイルのパスを指定してインストールします。インストール$ adb install permission-test-xxxxxx.apk各Permissionを確認してみます。
通知
通知の許可に関してはダイアログが出ず通りました。
問題なく送信され、通知アイコンの設定も上手くいきました。色とアイコン(鍵)の部分が独自デザインです。
以上、Androidはこんな感じです。
iOS
iOSの場合、
ios.infoPlistの内容はExpo Client上では反映されないため、スタンドアロンアプリとしてビルドしてみないと検証できません。
しかし、スタンドアロンアプリを実機で確認する場合、リリースビルドをApp StoreやTestFlight経由でインストールするしか方法がありません(少なくとも公式には)。今回はTestFlightの内部テストで確認してみたいと思います。
途中までは通常のアプリ申請のフローと同じなので、
App Storeへの申請についての記事の
iOS用にビルド(Apple Developer Programにメンバーシップ登録後)
セクションからアプリ(ipaファイル)をApp Store Connectにアップロード
セクションまでと同じ手順でビルドからアップロードまでを行います。Transporterでアップロードする場合はこんな感じに。
そしてアップロードが完了した後、App Store ConnectのTestFlightタブを見るとこんな感じです。
何らかの処理をしているようなので、しばらく待ちます。数十分から一時間ほどかかります。。。
しばらくしてAppleからメールが届くかと思います。もう一度App Store Connectを見ると、米国の輸出コンプライアンスへの確認が求められます。「!」アイコンから進み質問に答えてから、
テスターに自分を招待して、iOS端末にTestFlightをインストールして確認します。
各Permissionを確認してみます。
「
"アプリ名"が〜を求めています」という見出しは端末の言語設定によってiOSが固定で入れている文言です。
その下の部分に、app.jsonで設定した説明文が表示されています。通知
通知の場合は、もともとOSで決められたものが表示されます。
PUSH通知も問題なく届きました。再確認の方法
iOSの許可ダイアログは一回しか表示されず、「設定」から各アプリ・各機能へのアクセスを不許可にしても再度表示することはできません。
再度ダイアログを表示させるには、「設定」→「一般」→「リセット」→「位置情報とプライバシーをリセット」で全てのアプリのプライバシー設定ごとリセットするか、アプリを再インストールするしか方法は無いようです。TestFlightではアプリをアンインストールしてからこのようにすぐ再インストールすることが可能ですので、App Store経由の場合よりは比較的簡単にリセットできるかと思います。
Androidの場合は「設定」→「アプリと通知」→アプリを選択し、「権限」から各機能の許可を外すことができます。
その場合はiOSと違って、リクエストした際には再び許可ダイアログが表示されます。
- 投稿日:2020-01-07T18:18:27+09:00
Swiftで連絡先アプリに連絡先を追加する
はじめに
連絡先アプリから読み込みを行う記事はそこそこありましたが、連絡先を追加する記事はそんなになかったのでメモとして残しておきます。
環境
以下の環境で動作確認を行いました。
- Xcodeのバージョン: Xcode11.3
- Swiftのバージョン: Swift5
下準備
連絡先にアクセスするためにinfo.plistに以下の項目を追加します。
Key Value Privacy - Contacts Usage Description連絡先にアクセスする理由等を記述 これがないとエラーになるので忘れずに追加します。
連絡先へのアクセス許可を確認
アプリが連絡先にアクセスすることを許可してもらう必要があるので、現在の認証状態を確認してそれに応じた対応を行います。
初回の場合はいつものアクセス許可を求めるアラートを表示させたりするのが一般的だと思います。
その際にアラートを出したり、後の連絡先を保存する際に必要になるCNContactStoreをインスタンス化させておきます。import Contacts let contactStore = CNContactStore() switch CNContactStore.authorizationStatus(for: .contacts) { case .notDetermined, .restricted: contactStore.requestAccess(for: .contacts) { granted, error in if let error = error { print(error) } if granted { // アクセスを許可してもらえた時 } else { // アラートからアクセスの許可をしてもらえなかった時 } } case .denied: // 拒否されている場合 case .authorized: // すでにアクセスが許可されている場合 default: // それ以外の場合 }連絡先を追加
それでは本題です。
まずは連絡先のデータベースに保存を行うためのCNSaveRequestと保存する連絡先の情報となるCNMutableContact用意します。import Contacts let request = CNSaveRequest() let contact = CNMutableContact()名前や電話番号、メールアドレスなどの情報を
CNMutableContactに入れていって、最後にCNSaveRequestで保存を行うような流れになります。名前
名字、名前、それぞれのふりがなを指定することができます。
contact.givenName = "名前" contact.familyName = "名字" contact.phoneticGivenName = "なまえ" contact.phoneticFamilyName = "みょうじ"組織
組織名とふりがなを指定できます。
contact.organizationName = "組織名" contact.phoneticOrganizationName = "そしきめい"電話番号
電話番号は複数指定可能です。
CNLabeledValue<CNPhoneNumber>の配列となっていて、初期化に若干苦戦しました。引数
labelにCNLabelPhoneNumberMainのようなCNLabelPhoneNumber...となっている定数を指定すると、デフォルトで用意されている電話番号の項目用のタイトルが上につきます。String?なので任意の文字列を入れることも可能です。contact.phoneNumbers = [ CNLabeledValue<CNPhoneNumber>(label: CNLabelPhoneNumberMain, value: CNPhoneNumber(stringValue: "123-4567-8910")), CNLabeledValue<CNPhoneNumber>(label: "カスタムのラベル", value: CNPhoneNumber(stringValue: "123-4567-8910")) ]メールアドレス
メールアドレスも電話番号と同じく複数指定可能です。
CNLabelEmailAddress...みたいなのはなかったので、CNLabelHomeとCNLabelWorkを指定しています。この辺はなんでもいいのかもしれません。contact.emailAddresses = [ CNLabeledValue<NSString>(label: CNLabelHome, value: NSString(string: "sample@sample.com")), CNLabeledValue<NSString>(label: CNLabelWork, value: NSString(string: "sample@sample.com")) ]URL
これも同じく複数指定可能です。
contact.urlAddresses = [CNLabeledValue<NSString>(label: CNLabelURLAddressHomePage, value: NSString(string: "https://www.apple.com/jp/"))]住所
同じく複数指定可能です。
CNMutablePostalAddressが引数に必要になるのでインスタンス化して住所の情報を代入します。let address = CNMutablePostalAddress() address.country = "日本" address.postalCode = "460-8508" address.state = "愛知県" address.city = "名古屋市" address.street = "中区三の丸3丁目1−1" contact.postalAddresses = [CNLabeledValue<CNPostalAddress>(label: CNLabelHome, value: address)]保存
最後に保存を行います。
CNSaveRequestに保存するCNMutableContactを追加してから保存を行います。
例外を投げるのでtry catchが必要です。request.add(contact, toContainerWithIdentifier: contactStore.defaultContainerIdentifier()) do { try contactStore.execute(request) } catch { print(error) }追加された連絡先はこんな感じに表示されます。
さいごに
他にも
birthdayやrelation、socialProfilesなどを指定することができますが、とりあえずよく使いそうな項目だけ抜粋しました。何か間違っている点などがありましたら指摘してください?♂️
参考にさせていただいた記事
- 投稿日:2020-01-07T18:02:06+09:00
【解決済み】SWIFT_VERSION '5.0' is unsupported, supported versions are: 3.0, 4.0, 4.2の対処法
解決方法
まず最初に書いておきます。
ライブラリを選択して、Swift Language Versionを4.2に選択
説明
pod inittarget "aaa" do use_frameworks! pod "Alamofire" pod "SwiftyJSON" pod "NVActivityIndicatorView" endpod installここでインストールしたライブラリの数だけエラーが出ます。エラー内容が
SWIFT_VERSION '5.0' is unsupported, supported versions are: 3.0, 4.0, 4.2こんな感じ。
書いてあることは5.0はサポートされてないよー。3.0, 4.0, 4.2のどれかにしてねーって言ってます。
swift --versionでswiftのバージョンを調べてみても
swift --version Apple Swift version 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1) Target: x86_64-apple-darwin17.7.0と出てきます。
?
XcodeとSwiftのバージョンが悪いのかなと考えていたのですが、答え見つけました。
英語読めてよかったーーー!
まとめ
初心者はエラーに弱いですが、頑張ってググれば出てきます。頑張りましょう!
- 投稿日:2020-01-07T17:44:44+09:00
【swift5】現在地を取得した天気アプリ
ソースはこちら
https://github.com/sventouz/swift_weatherApp
全体像
①は固定。。。
これは位置情報を取得して今いる場所の地名を取得したかったができなかった。
Google Map APIの逆ジオコーディングを使用することで実現可能なので近日中に仕上げたい。②こちらは今日の日付を取得して表示するだけなのだが、時間がなく割愛。ここもあとで実装する。
③ここは変わる。笑
現在地の天気によって画像が変わる仕様になっている。参考のYOUTUBEの動画のかたが親切にもまとめてくださっているので使うと簡単。④どんな天気かが表示される。
⑤現在地の気温。
アプリの詳細
このアプリは
- Alamofire
- SwiftyJSON
- NVActivityIndicatorView
の3つのライブラリを使用している。
現在地から天気や気温などを取得しているのはOpenWeatherAPIである。
ソース
ソースは以下の通りです。説明が長くなってしまうのでページ下部にあるYoutubeをみていただきたい。
import UIKit import Alamofire import SwiftyJSON import NVActivityIndicatorView import CoreLocation class ViewController: UIViewController, CLLocationManagerDelegate { @IBOutlet weak var locationLabel: UILabel! @IBOutlet weak var dayLabel: UILabel! @IBOutlet weak var conditionImageView: UIImageView! @IBOutlet weak var conditionLabel: UILabel! @IBOutlet weak var temperatureLabel: UILabel! let apiKey = "ここはあなたのAPIkey" var lat = 26.8205 var lon = 30.8024 // loading var activityIndicator: NVActivityIndicatorView! // user location let locationManager = CLLocationManager() override func viewDidLoad() { super.viewDidLoad() let indicatorSize: CGFloat = 70 let indicatorFrame = CGRect(x: (view.frame.width-indicatorSize)/2, y: (view.frame.height-indicatorSize)/2, width: indicatorSize, height: indicatorSize) activityIndicator = NVActivityIndicatorView(frame: indicatorFrame, type: .lineScale, color: UIColor.white, padding: 20.0) activityIndicator.backgroundColor = UIColor.black view.addSubview(activityIndicator) // use popup to check and get location locationManager.requestWhenInUseAuthorization() if (CLLocationManager.locationServicesEnabled()) { locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest locationManager.startUpdatingLocation() } } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { let location = locations[0] print(location) lat = location.coordinate.latitude lon = location.coordinate.longitude Alamofire.request("http://api.openweathermap.org/data/2.5/weather?lat=\(lat)&lon=\(lon)&appid=\(apiKey)&units=metric").responseJSON { response in self.activityIndicator.stopAnimating() if let responseStr = response.result.value { let jsonResponse = JSON(responseStr) let jsonWeather = jsonResponse["weather"].array![0] let jsonTemp = jsonResponse["main"] let iconName = jsonWeather["icon"].stringValue self.locationLabel.text = jsonResponse["name"].stringValue self.conditionImageView.image = UIImage(named: iconName) self.conditionLabel.text = jsonWeather["main"].stringValue self.temperatureLabel.text = "\(Int(round(jsonTemp["temp"].doubleValue)))" } } } }まとめ
今回初めてきちんとAPIから天気を取得して表示することができた。
これで自分好みの天気アプリを作りたい。
天気アプリを作った感じだが、実際には
- APIの使い方
- 位置情報の使い方
- JSONの扱い方
などとその周りも扱えたのがよかった。
参考
今回はこちらの方のものを参考にさせていただきました。
- 投稿日:2020-01-07T11:14:47+09:00
スプレッドシートを使って、iOS開発の効率を上げる
はじめに
アプリ開発の中でデバッグは時間がかかる作業である。
動的に変わる処理のデバッグはさらに時間がかかる。
AndroidとiOS両プラットフォームで展開しているサービスであるとさらに時間がかかる。
その解決手段としてスプレッドシートを使った方法を紹介しますなぜスプレッドシートを使うのか
- 誰でも容易に変更、閲覧ができる
- 簡単なAPIとして扱える
- プラットホーム関係なしに扱える
実践してみる
準備
- シートを作成
- ファイル → ウェブに公開を選択
文言の管理をまとめる xvrh/localize-with-spreadsheet
- スプレッドシートで定義した文言をStringファイルに変換するツール
- Android、iOSの文言の共通管理できる
スプレッドシートの値をアプリで呼び出す t-osawa-009/SpreadsheetClient
- スプレッドシートの値をアプリで呼び出すためのライブラリを自作した物を活用します。
カスタムスキームの定義とデバッグ
- スプレッドシートをカスタムスキームのドキュメントとして使える
- 定義したURLをアプリで取得し、UniversalLink, カスタムスキームを処理するメソッドを呼び出すことでデバッグを簡単に行える
- シートを更新することでUniversalLink, カスタムスキームのパラメータも動的に変更可能
デバッグ用のアカウント管理
- アカウント情報のAPIとして活用できる
注意点
- スプレッドシートをURLを知っていると誰でも情報取得可能。公開されても問題ないもののみ扱う方がいいと思われる
まとめ
- スプレッドシートを使うことで簡単に開発効率を上げられる
- ドキュメントのデータとアプリで実行するデータを一致した値で扱える
環境
Xcode 11.3 Apple Swift version 5.1.3 (swiftlang-1100.0.282.1 clang-1100.0.33.15) Target: x86_64-apple-darwin19.0.0
- 投稿日:2020-01-07T01:01:00+09:00
iOS&Firebase 初めの一歩:iOSアプリのプロジェクトでFirebaseを使ってみる
対象読者
iOSアプリの作成経験はあるが、Firebaseはまだ使った事がない。
ざっくりとした使用感を掴みたい人。
実プロジェクトで使いそうな要素のみを抽出したサンプルアプリを作成してみる。準備
まずはFirebaseにプロジェクトを登録し設定する。
プロジェクト作成
開発用のGoogleアカウントがあれば(無ければ個人のものでも良い)それでFirebase Consoleにログインする。
Firebaseコンソールプロジェクトを作成(プラスマークのタイル)から新規にプロジェクトを作成する。
ちなみにプロジェクトは異なるクライアント環境ごとに作る必要はない。
モバイルアプリで言えばサーバーサイドの環境に当たるので、大抵はAndroidでもiOSでも共通だろう。以下を入力
- プロジェクト名:任意のもの。
- 地域/ロケーション:デフォルトではアメリカ合衆国になっているが、日本でサービスするならばJapanに変更した方
- アナリティクスの地域:日本(リスト最後の方にある)
- Cloud Firestoreのロケーション:asia-northeast1 →これは異なるリージョン間通信が発生すると課金対象となるので、予め最もユーザーが多い地域にしておくのが有利。 →またCloud Firestoreを利用しない場合も予め上記に設定しておいた方が良い。この値は後から変更できないからだ。
- Firebase 向け Google アナリティクスのデータ共有にデフォルトの設定を使用する →基本的にアナリティクスのデータをgoogleに共有するかどうか、という内容。 →特に個人情報を渡すわけではない(あくまでアナリティクスデータのみ)ので、 理由がない限りはチェックしておく。
- 測定管理者間のデータ保護条例に同意。 →上記アナリティクスの共有に同意した場合、こちらにもチェックを付けないとプロジェクトが作成できないようになっている。
少しするとプロジェクトが作成され、コンソールが表示される。
アプリの追加
このプロジェクトを利用するクライアント(=アプリ)を追加する。
用意されているのは、iOS・Android・Web・Unity
iOSを選択する。
- コンソール上部の「アプリにFirebaseを追加しましよう」の下にある「iOS」アイコンをクリック
iOSアプリにFirebaseを追加ダイアログを入力していく
- Xcodeでプロジェクトを作成する。
- アプリの登録
- バンドルID:Xcodeからコピぺ
- ニックネーム:Firebaseコンソール上で識別しやすい名前が良い。アプリ名やiOSが入っていると良いのかな。
- AppStore ID:App Store Connectにある。入れておく。
- 設定ファイルのダウンロード Firebase接続に必要な情報が書かれた設定ファイルを自動生成してくれるので、ダウンロードしてそのままプロジェクトに追加する。
GoogleService-Info- Firebase SDKの追加 cocoa podsでSDKをインストールする。
- Cocoa podsをインストール(既にあればこの手順は不要)
$ cd プロジェクトディレクトリ $ pod init
- Podファイルを編集して保存
$ vi Podfile target 'FirebaseSample2' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks use_frameworks! # Pods for FirebaseSample2 pod 'Firebase/Core' //←この1行を追加 end
インストール実行
$ pod installこれでxcworkspaceファイルができるはずなので、そこからプロジェクトを起動し、ビルドしてみる。
初期化コードの追加
AppDelegateに以下のコードを追加する。import Firebase func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // 追記する FirebaseApp.configure() return true }
- Firebaseをimport
- application(application,didfinishLaunchingWithOptions)メソッドに
FirebaseApp.configure()(Firebaseの初期化処理)を追記
- アプリを実行して確認 初回起動時にSDKからの通信が正しく行われているかをコンソールから確認できるようになっている。 コンソール側でアプリを認識できたらOK
認証機能の追加
ユーザーを識別しないアプリの方が少ないと思うので、大抵は何はともあれ認証しないと始まらない。
Firebaseの認証(Authentication)の方式としては以下が用意されている。
* メール/パスワード
* 電話番号
* Playゲーム
* Game Center
* GitHub
* 匿名この中でも多く使われそうなのは、メール/パスワード、SNS、匿名あたりだろうか。
アプリによっては電話番号も良いソリューションになるだろう。
また、自前システムでの認証などにも対応できるカスタム認証という手法も取れる(ここでは扱わない)準備
準備といっても先ほどの認証方式を設定するだけ。
1. FirebaseコンソールのAuthenticationをクリック
2. ログイン方法タブをクリック
3. 認証方式(ログインプロバイダという)のうち、使うものを有効にする。なお、FacebookやTiwtterを利用する場合、それぞれで開発者登録したうえでアプリの登録が必要となる。
この辺のことは情報も多いので各自探し、以下ドキュメントも参照してほしい。
https://firebase.google.com/docs/auth/?authuser=0また、より詳細な情報を得るにはガイドの参照もお勧めする。
https://firebase.google.com/docs/ios/setupSDKの導入
Firebaseはサービスドメインごとにポッドが別れている。
既に導入しているCoreは必須ライブラリだがそれ以外は個別に指定する必要がある。認証に関して言えば基本はAuhtなので、以下を追記することになる。
pod 'Firebase/Auth'また、今回は扱わないがSNSなど複数のフェデレーションを利用する場合などに便利なUI(ViewControllerサブクラス)を自動生成してくれる仕組みも用意されている。その場合には、さらに
pod 'FirebaseUIを追記する。



























