- 投稿日:2020-04-26T21:38:09+09:00
Apple Watchのセットアップとアプリ開発(Swift 5.1)
Apple Watchアプリの開発方法
MacでSwiftを使って、AppleWatchアプリを作成しました。
この記事ではアップルウォッチのセットアップ方法と、Xcodeでのプロジェクト作成から、ウォッチにインストール完了するまでの手順を紹介します。
アプリの内容やSwiftのコーディング内容については触れていません。開発環境
Mac mini (Late 2014) macOS Mojave Ver.10.14.6
Xcode Ver.11.3.1(11C504)
Swift Ver.5.1
Apple Watch Ver.6.1.3(17S811)
iPhone 7 Ver.13.4.1プロジェクトの作成
Xcodeを起動する。
プロジェクトのテンプレートにwatch OS、アプリケーションはWatch Appを選択する。
言語はSwift、UIはStoryBoardを選択。その他、必要事項を設定する。
ディレクトリを選択して『Create』をクリックするとプロジェクトが作成される。
Finderで確認すると、指定した場所にプロジェクトのファイルがたくさん作られたことが分かる。
ウォッチのセットアップ
セットアップには、母艦としてiPhoneが必要となる。iPhpneにウォッチアプリをインストールする。
App Storeでキーワード「ウォッチ」で検索するとアプリが見付かる。
アプリをインストールして実行すると、以下の画面が起動する。
ウォッチを近付けて「ペアリングを開始」をタップする。カメラが起動するので、ウォッチ上で動いている画像をキャプチャする。認識するとペアリングが行われる。
ペアリングが完了すると、いくつかの設定項目を聞かれる。
それらを入力したら、ハード面の設定は完了。
ウォッチ側では以下の画面が表示される。
デジタルクラウン(時計の竜頭みたいな回せるボタン)を押すと初期画面が表示される。唐突に「もう予定なし」と表示されている。本気で日本語に対応するつもりはないようだ。
今回はwebアプリの開発を想定しており、母艦のiPhoneがなくてもウォッチ単体でweb通信を行えるようにしたい。そのための設定を行う。設定→Wi-Fiを開く。ウォッチが直接通信したいWi-Fiネットワーク名が表示されており、「iPhoneが使用できないときは、このWi-Fiを使用する」となっていることを確認。
実行許可設定
アプリの開発を行う前に、実行許可を与える手順が必要となる。プロジェクト側のセキュリティ設定と、ウォッチ側の許可設定を行う。
野良アプリ実行許可
プロジェクトを野良アプリ(AppleStore公式ではないアプリ)として実行可能にする。Info.plistに設定項目を追加する。
Information Property Listの直下に「+」をクリックして新規項目「App Transport Security Settings」を追加する。
追加した項目の左側の三角をクリックして下向きにし、直下に「Allow Arbitrary Loads」を追加する。
初期値は「NO」になっているので、「YES」に変更する。
開発チームの設定
Xcode → preferenceでプロジェクトとApple IDをひも付け、開発チームを設定する。
ペアリング済の母艦iPhoneをmacとUSB接続し、デバッグを実行する。
ウォッチ側にデベロッパに対する信頼確認が表示される。「信頼する」をタップする。
ところが、iPhoneとウォッチを眺めていても何も起こらない。Xcode上では、「Finished running」となっているが。。。そこで、ウォッチのアプリ一覧画面を調べてみる。
すると、アイコンがたくさん並んでいるところに、デフォルトのクモの巣のようなアイコンが一つ増えている。これがインストールされたアプリで、タップすると実行することができた。
以上、Apple Watchアプリの開発手順でした。
- 投稿日:2020-04-26T20:46:43+09:00
[Swift5]Realm Swift - サンプル#2
iOS で
Realm Swift
を 利用し、サンプルを作成してみました。前回
サンプル
※ Source: RealmManagerAccess.swift
Ream 定義
RealmManagerAccess.swiftimport RealmSwift class Student: Object, Codable, NSCopying { @objc dynamic var studentId = "" @objc dynamic var name = "" @objc dynamic var age = 0 override static func primaryKey() -> String? { return "studentId" }[JSON]
{ "name": "jane", "age": 12, "studentId": "1" }レコードの追加
//guard let data = dataStudentStr.data(using: .utf8) else {...} let obj = try JSONDecoder().decode(Student.self, from: data) try manager.add(obj: obj)レコードの更新
try manager.update(obj: student) { student.name = "test" student.age = 20 print("2.update => findFirst updateStudent:" + student.description ) }レコードの削除
//guard let primaryKey = manager.getPrimaryKey() else {...} try manager.deleteWithQuery(query: "\(primaryKey) == '2' ")レコード検索
let student = manager.findByPrimaryKey(key: "1")
- 投稿日:2020-04-26T17:18:10+09:00
関数に出てくる『 _ 』って何?
基本的に関数を呼び出すには引数ラベルが必要
_ が用いられるまでの流れを追っていこうと思います。
例として、縦の長さと横の長さとして整数型の2つの引数を持ち、面積として2数の積を出力する関数を考えます。
最も基本的な書き方はfunc area(width:Int, height:Int) -> Int { return width * height }そして呼び出す時には
area(width:3, height:4)としなければなりません。
ここでarea(3,4)とするとMissing argument labels 'width:height:' in call
と怒られてしまいます。関数名(引数,引数)とするためには
なんとかしてarea(3,4)の形で呼び出したい...
func area(_ width:Int, _ height:Int) -> Int { return width * height }のように_ を用いることで
area(3,4)で呼び出せるようになります。
ワイルドカードと呼ばれる_には他にも使い方があるようなので、調べて行こうと思います。
- 投稿日:2020-04-26T12:35:26+09:00
【Swift】Enumのassociated Valueがあるときのパターンマッチで単純にif文使ったらエラー出た
Enumのassociated Value、便利なことに気づいたのでよく使うようになったんですが、
この前ちょっと不便だなと思うことがあったので、ストーリー形式で書いてみます。〜associated Value使う前〜
Twitterライクなアプリの開発に入ってたとして、下記みたいなStructがありました。
import Foundation struct Followee { enum Status { case follow case blocked case mute } var status: Status = .follow // ホントは別のところでセットするけどわかりやすく var canAppearTimeline: Bool { if status == .follow { return true } else { return false } } }フォローしてるアカウントの投稿をタイムラインに表示するかどうかを、
canAppearTimeline
で判定しています。
現状、フォローしてるアカウントのステータスは、フォロー中・被ブロック・ミュート中の三つで、フォロー中のときだけ表示します。
シンプルですね。「タイムラインで表示する優先順位づけできひん?」
ここで仕様変更が入って、
「タイムラインで表示する優先順位づけできひん?」
という要望が入りました。
「なんかミュートまでは行かへんけど、この人タイムラインに出過ぎてうっとうしいわ〜ってときあるやろ?」
「嫌いまではいかへんけど、毎日食べると胃もたれする料理みたいな」(?)
「そういうとき、表示頻度を下げる機能あったら、嬉しいよな」というわけで、仕様変更することになりました。
Statusを追加
とりあえずStatusをひとつ追加することにしました。
enum Status { case follow case blocked case mute case showLess(priority: Int) // New! }ロジック
こう考えました。
「うーん、めんどくさいな……どないしよ……」
「もうええわ。優先順位の初期値1入れて、タイムラインにあらわれるたびに1ずつ増やしてって、10になったら表示にしたろ!」
「とりあえず表示頻度減ったらええやろ」機能としてはどうかと思いますが、ロジックは単純になりそうですね。
ところが
ところが、Statusを変更した段階で、
Protocol 'Equatable' requires that 'Followee.Status' conform to 'Equatable'
というエラーが出るようになりました。var canAppearTimeline: Bool { if status == .follow { return true } else { return false } }「な、何もしてないのにif文が通らなくなった!」
associated Valueがあるとなぜifのパターンマッチができないのか
「Equatableに従ってないと怒られるんなら、従わせればええんか……?」
と思って、Statusをこうしてみました。enum Status: Equatable { case follow case blocked case mute case showLess(priority: Int) }これをやると、showLess以外のケースであればコンパイル通るようになります。
しかし……var canAppearTimeline: Bool { if status == .showLess(_) { // Missing argument label 'priority:' in call // showLessのときのロジックを書く } }こんな書き方はできません。
var canAppearTimeline: Bool { if status == .showLess(priority: 1) { // showLessのときのロジックを書く } }これなら通ります。
「えっ、これ1から10まで書かないとアカンの?!」
var canAppearTimeline: Bool { if status == .showLess(priority: 1) || status == .showLess(priority: 2) || status == .showLess(priority: 3) || status == .showLess(priority: 4) || status == .showLess(priority: 5) || status == .showLess(priority: 6) || status == .showLess(priority: 7) || status == .showLess(priority: 8) || status == .showLess(priority: 9) || status == .showLess(priority: 10) { // showLessのときのロジックを書く } }「こうやな!」
これなら通りますが、
「すまん!やっぱpriorityもっと細かくしてくれ!1〜100にしよう!」
と言われた瞬間に「アアアアアアアアアアアアアアア」となってしまいますね。
そう。Associated Valueは値を保存してくれるので、マジメにパターン分けすると、
Associated Valueのとりうる値分のパターンがあるんですね〜対処法
対処法は二つあります。
まずswitch文を使う方法。var canAppearTimeline: Bool { switch status { case .follow: // 処理 case .blocked: // 処理 case .mute: // 処理 case .showLess(priority: let priority): // 処理 } }これはEnum使うときのパターンマッチの王道ですね。
ただ状況によっては、もっとif文ぽく書きたいときもあると思います。
たとえばEnumが10ケースあるけど、ある1ケースに該当するときだけ処理を変えて、あとの9パターンは何もしない、みたいなときとか。
そんなときにSwitch文書くのはちょっと大袈裟ですよね。そんなときのために
if case
という構文があります。if case .showLess(let priority) = status { if priority < maxPriority { increment() return false } else { reset() return true } }こんな感じで書けます。
case .xxx
のあとに=でつないで、Enumが来るっていうちょっと気持ち悪い構文ですが、
まあ元々Switch文を書かなきゃいけないところの省略形だと思えば多少は……いやそうでもないですか。完成形
import Foundation struct Followee { enum Status { case follow case blocked case mute case showLess(priority: Int) mutating func countUp() { if case .showLess(let priority) = self { self = .showLess(priority: priority + 1) } } mutating func resetPriority() { if case .showLess(_) = self { self = .showLess(priority: 1) } } } var status: Status = .follow let maxPriority = 10 var canAppearTimeline: Bool { if case .follow = status { return true } else if case .showLess(let priority) = status { if priority < maxPriority { increment() // 直接countUp()呼んだら怒られたので迂回している return false } else { reset() return true } } return false } } func increment() { followee.status.countUp() } func reset() { followee.status.resetPriority() } var followee = Followee() followee.status = .showLess(priority: 1) (1...10).forEach { _ in print(followee.canAppearTimeline)} print(followee.status)出力false false false false false false false false false true showLess(priority: 1)余談
この記事書くために調べてたら知ったんですが、
switch productBarcode { case .upc(let numberSystem, let manufacturer, let product, let check): print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).") case .qrCode(let productCode): print("QR code: \(productCode).") } // Prints "QR code: ABCDEFGHIJKLMNOP."これを、
switch productBarcode { case let .upc(numberSystem, manufacturer, product, check): print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") case let .qrCode(productCode): print("QR code: \(productCode).") } // Prints "QR code: ABCDEFGHIJKLMNOP."こんな書き方もできるんですね。へえ〜って感じです。
あとrawValueとassociated Valueは一緒に使えないとか、便利な分色々制約あるんだな〜と思いました。
Enumの持ってる単純性を損なっちゃうデメリットもありますね。参考
- 投稿日:2020-04-26T02:14:50+09:00
【Swift】Main.StoryBoardを消して新しいStoryBoardでスタートさせるときの留意点
シミュレーターがずっと真っ暗
毎度おなじみ初心者応援記事(未来の自分への備忘録)です。
一緒に一つずつクリアしましょう。今回はxcodeで新規プロジェクトをcreateしたときに、Main.Storyboardを消して、自作のStoryboardを起動してみようと初心者から一歩進んだ方向けかと思います。
まぁ今更感もあるかもしれませんが、探すのに苦労した自分が忘れないようにしたいのです。
アウトプット大事。iOS12以前ではIs Initial View Controllerという項目にチェックをつけるだけで自作のStoryboardに切替られたはずでした。
しかしながらiOS13からiPadで同一アプリを複数画面で起動できるようにしたとかしてないとかっていう頭がいいのか悪いのかわからない使い方ができる(?)ようになった関係で、いろいろと仕様変更があったようです。
詳しくは@omochimetaru様のiOS13のSceneDelegate周りのアプリの起動シーケンスで書かれてます。まぁ僕のような初心者には何度読み返しても仕様が変わったんだなということしか理解できないのですが、この為に一手間加えないといつまでもシミュレート起動できないことになりました。
いつまでもログに「Failed to instantiate the default view controller for UIMainStoryboardFile 'Main' - perhaps the designated entry point is not set?」と表示されていろいろいじってみても治らず、頭を捻っている方への処方箋となれば幸いです。なお、今回はStoryboardを使って行きます。コードだけで書かれる方には非対応ですので、ご了承ください。
脱・Main.Storyboard、脱・超初心者
早速行きましょう
さっさとMain.Storyboardを削除します。
↓のような選択が表示されますが、Move To Trashでいいと思います。これは表面上は消えるけど、プロジェクトフォルダには残す?残さない?という問いで、今回はMainに完全に消えてもらいます。
消した後に、初期画面は認証画面にしようと思いAuth.StoryboardというStoryboardを作成しました。
このまま一度Runさせようとすると、もはや起動しません。
まずはIs Initial View Controllerにチェックを入れてください。
(画像は使い回し)TARGETSの変更
さて、ここで勘の良い方はTARGETS内にMain Interfaceがあることに気づいたはずです。
でもこれは空白でも動きました。今回は一応Auth.Storyboardを選択しておきます。詳しいかた教えてください。でもRunしても起動しません。
真犯人はInfo.plist
犯人はこいつだ!
ということでInfo.plist内の「Main storyboard file base name」といういかにもなプロパティがありました。こいつを直しましょう
はい。
起動しません!
なんとApplication Scene Manifestというプロパティの中に真犯人がいました。
こいつがMainのままや!というわけでこいつを「Auth」にしたら起動できるはずです。
お疲れ様でした。
この記事が誰かの手助けになれば、恐悦至極にございます。