20211124のSwiftに関する記事は6件です。

swift 整数値 Int() と 配列 Array() の抽出

Intは整数値を定義するのに用い、Arrayは配列を組織する。 class ViewController: UIViewController { var c = Int() var elem = NSArray() override func viewDidLoad(){ elem = [ “Name1”, “name2”, “name3”, “name4” ] var btn1 = UIButton() btn1.addTarget(self, action: #selector(button1(button1:)), for: .touchUpInside) } @objc func button1(button1: UIButton){ C += 1 Object() } Object(){    if(c <= elem.count - 1){ let name = elem[c] } else { let name = elem[0] } let label = UILabel() label.text = String(name) } } 上の文字列は、ボタンを押すたび、に配列に置かれた、名前が変わり、ラベルに表示されることを記している。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

最小実装でモデルのIDをタイプセーフに表現する

実際のアプリを作るとき、多数のモデルが存在することになると思いますが、 APIから取得したデータはIDというプロパティを持つことが多いと思います。 それらはStringやIntなどの型で表現されると思いますが、 メソッドの引数名によっては誤ったモデルのIDを入れてしまうヒューマンエラーが起こる可能性があります。 それをSwiftの型を用いて解決したいと思います。 ヒューマンエラーの例 fetchLikesというメソッドはStringのidを引数に取るため、LikeでもUserでもidがStringで宣言されているので、引数にLikeのIDを入れることができてしまいます。 final class LikeRepository { /// UserのIDをもとにUserのお気に入り一覧を取得する func fetchLikes(id: String) -> [Like] { ... } } struct User { var id: String } struct Like { var id: String } let user = User(id: "id") let repository = LikeRepository() // 正しい使い方 repository.fetchLikes(id: user.id) let like = Like(id: "id") // 誤った使い方でもコンパイルできてしまうので、問題に気づけない repository.fetchLikes(id: like.id) 解決策 ユーティリティを実装する protocol Identifiable { typealias ID = Identifier<Self> var id: Identifier<Self> { get } } struct Identifier<Object>: Hashable { var rawValue: String } /// User.IDなどのIdentifierをStringで初期化可能にする extension Identifier: ExpressibleByStringLiteral { init(stringLiteral value: StringLiteralType) { self = Identifier(rawValue: value) } } 使い方 struct User: Identifiable, Equatable { var id: ID } struct Like: Identifiable, Equatable { var id: ID } final class LikeRepository { func fetchLikes(id: User.ID) -> [Like] { // id.rawValueでStringにアクセスできる ... } } let user = User(id: "id") let repository = LikeRepository() // 正しい使い方 repository.fetchLikes(id: user.id) let like = Like(id: "id") // エラーになりコンパイルできないので、ミスに気づける // Cannot convert value of type 'Like.ID' (aka 'Identifier<Like>') to expected argument type 'User.ID' (aka 'Identifier<User>') repository.fetchLikes(id: like.id) 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift button action と function の 連結

最初にUIButtonのイニシャライゼーションの指定から。 var btn1 = UIButton() これを、viewDidLoad(){} の中に書くと、画面がロードされたときのみ、{}内だけで有効なイニシャライズ=初期化。下記は、クラス全体で有効なイニシャライズ。 class ViewController: UIViewController { var btn1 = UIButton() override func viewDidLoad(){ btn1.frame = CGRect(x: 左端からの位置, y: 上からの位置, width: 幅, height: 高さ) btn1.setTitleColor(.gray, for: .normal) btn1.backgroundColor = .black btn1.textAlignment = .left btn1.addTarget(self, action: #selector(button1(button1;)), for: .touchUpInside) self.view.addSubview(btn1) } @objc button1(button1: UIButton){ btn1.backgroundColor = .purple object() } func object(){ // New event start. } } このようにしてロードした直後のボタンの位置決めと、タップイベントの作成が開始され、ファンクション化されて連結されていく。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【iOS】アプリ内でシステムログを取得したい

この記事はNTTコムウェア Advent Calendar 2021 1日目の記事です。 初日からかなりニッチな内容ですが、iOSでシステムログを取得する方法について考えます。 ベータテストあるある そんなこと言われても解析のしようがない せめてログをください... という光景を社内で見たことはないでしょうか。 実際アプリをリリースする前にシステムログを取得してもらうのは容易ではないため、 ログをもらうのを諦めているケースは多いと思います。 今までのログ取得方法 iOSでシステムログを確認する方法としては Macにつないで、XCode経由で取得 Macにつないで、Console.app経由で取得 端末の電源ボタンと音量ボタンを長押ししてiPhone内にsysdiagnoseログを出力して、Macに繋いでファイルを取得して何らかの方法で送信してもらう 等の方法がありますが、 開発者ならまだしもテストに協力してもらっている方々にMac用意してXcodeインストールして...はハードルが高すぎます。 iOS15以降のログ取得方法 実は最近(2021年8月)にリリースされたiOS15Beta版からアプリ内でシステムログが取得できるようになりました。 参考) Apple Developer Documentation OSLogStore Class https://developer.apple.com/documentation/oslog/oslogstore この機能を利用してアプリ内にバグ報告機能を実装しておけば、簡単にテスターからログを送信してもらうことができそうです。 サンプルコード if #available(iOS 15.0, *) { do { let store = try OSLogStore.init(scope: .currentProcessIdentifier) // 1時間前までのログを取得 let position = store.position(date: Date().addingTimeInterval(-3600)) // 時間の降順で取得 let enumerator = try store.__entriesEnumerator(options: [.reverse], position: position, predicate: nil) enumerator.forEach { element in let message = element as? OSLogEntry dump(message.debugDescription) dump(message?.composedMessage) } } catch { dump(error) } } else { // iOS14以下の処理 } 実際に取得できたログの一部 2021-11-19 11:39:01:Using ~iphone resources 2021-11-19 11:39:01:Initializing connection 2021-11-19 11:39:01:Removing all cached process handles 2021-11-19 11:39:01:Sending handshake request attempt #1 to server 2021-11-19 11:39:01:Creating connection to com.apple.runningboard 2021-11-19 11:39:01:Received message from server: async_didChangeInheritances:completion: 2021-11-19 11:39:01:didChangeInheritances: <RBSInheritanceChangeSet| gained:{( <RBSInheritance| environment:(none) name:com.apple.frontboard.visibility origID:6452-6464-44> )} lost:(null)> 2021-11-19 11:39:01:Handshake succeeded 2021-11-19 11:39:01:Identity resolved as application<com.xxxx.yyyy> 2021-11-19 11:39:02:No persisted cache on this platform. 2021-11-19 11:39:02:Deactivation reason added: 10; deactivation reasons: 0 -> 1024; animating application lifecycle event: 0 2021-11-19 11:39:02:assertion failed: 20G165 19A339: libxpc.dylib + 55577 [68F6BE69-6DB2-3A18-A053-2C03A9D2A10A]: 0x7d 2021-11-19 11:39:02:activating monitor for service com.apple.frontboard.open 2021-11-19 11:39:02:activating monitor for service com.apple.frontboard.workspace-service 2021-11-19 11:39:02:FBSWorkspace connecting to endpoint : <BSServiceConnectionEndpoint: 0x600000a84210; target: com.apple.frontboard.systemappservices; service: com.apple.frontboard.workspace-service> 2021-11-19 11:39:02:FBSWorkspace registering source: <FBSWorkspaceScenesClient: 0x600002ff8070> 2021-11-19 11:39:02:FBSWorkspace connected to endpoint : <BSServiceConnectionEndpoint: 0x600000a84210; target: com.apple.frontboard.systemappservices; service: com.apple.frontboard.workspace-service> 2021-11-19 11:39:02:Added observer for process assertions expiration warning: <_RBSExpirationWarningClient: 0x6000004d1040> 2021-11-19 11:39:02:Lookup of 'AppleLocale' from current preferences failed (app preferences do not contain the key; falling back to generation via preferred languages) 2021-11-19 11:39:02:CFLocaleCopyCurrent() constructing a locale identifier from preferred languages without a set country code 2021-11-19 11:39:02:[FBSScene] [sceneID:com.xxxx.yyyy-default] Created client agent: <UIApplicationSceneClientAgent: 0x6000006ec3c0> 2021-11-19 11:39:02:Language lookup at CFBundle 0x7fcee7a04cf0 </Users/xxxx/Library/Developer/CoreSimulator/Devices/xxxxxx/data/Containers/Bundle/Application/yyyyyy/dummy.app> (executable, loaded) Localizations : [Base, en] Dev language : en User prefs : [en, ja-US] Main bundle : [<null>] Allow mixed : 0 Result : [en] 2021-11-19 11:39:02:Resource lookup at CFBundle 0x7fcee7a04cf0 </Users/xxxx/Library/Developer/CoreSimulator/Devices/xxxxxx/data/Containers/Bundle/Application/yyyyyy/dummy.app> (executable, loaded) Request : InfoPlist type: strings Result : None 動作的に不完全な部分がある アプリ起動後からのログしか実際は取得できていない ログ取得期間、例えば60分前〜のログを取得、を指定できるパラメータが存在しているが、実際はパラメータを指定しても効果が無い まだできたてほやほやの機能ということもあって今後の改善に期待です。 おわりに アプリ内でシステムログが取得できる、という事実だけでも非常に有用ですのでお困りの方は試してみてはいかがでしょうか。 参考 https://developer.apple.com/documentation/oslog/oslogstore https://developer.apple.com/forums/thread/650843 https://developer.apple.com/forums/thread/677068
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

機械学習で必殺技分類(動作分類モデル作成からiOSでの推論実行まで:失敗編)

動画を機械学習して、動作の種類を判定する GUIで機械学習ができるCreateML(Macで実行可能)。 動作の分類(Action Clasification)もできます。 実は僕は今回学習に失敗したのですが、作業フローはひととおり書いたので、公開しちゃいます。 【こういうのが作りたかった】 手順(データセット準備) 1、動画を集める。 分類したい動作の動画を集めます。 今回はこのようなアクションを用意してみました。     ピースサインなど静止的な動きだと画像分類で十分なので、動画シーケンスに意味のある動きにしました。 集める動画の推奨事項 ・一つのアクションに50個以上の動画を集める ・アプリで使うフレームレート以上の動画を集める(トレーニング時に解析する動画のフレームレートを指定する必要がある。アプリで実行する際も、このフレームレートがを基準に解析する) ・カメラは固定 ・うつすのは一人 ・全身を映す ・明るい環境で ・ぶかぶかすぎる服は適さない ・背景に溶け込みすぎている服の色も適さない ・アプリでいろんな角度からキャプチャする場合は、データセットもいろんな角度のものを用意する ・一つの動画に、一つのアクションの複数回の動作が入っている場合、動作の間の静止や間隔は最小限に抑える。 ・静止が入る場合は、その部分をトリミングするか、注釈つきのデータセットにする。 ・ネガティブケースを入れる(アプリ実行時に起こり得る他の動作。例えば、筋トレ前後でフレームに出入りする歩行シーンを入れる。) ・静止ケースを入れる(筋トレ前後で動かずに座っている状態など。) 2、データを整理する(フォルダ分け、もしくはアノテーション・ファイルをつける) CreateMLに与えるデータをフォルダ分けしてデータセットにします。 フォルダ分けには二つの方法があります。 構成方法1、アクションごとにビデオをフォルダ分けする 「一つの動画に一つのアクション分類」になるように、きちんと動画をトリミングしている場合にこの方法が使えます。 構成方法2、注釈ファイルをつける 一つのビデオに複数のアクションが含まれる場合は、csvやjsonでアノテーションファイル(注釈ファイル)を作ります。 方法1、は一つの動画に一つの動作を隙間なく撮影するもしくはトリミング編集するなど、留意が必要です。 ビデオを細かく撮影したり、後でトリミングするのはけっこう手間なため、僕の個人的おすすめは方法2、です。 アノテーションファイルの例 jsonでは以下のようにアクションごとに「ファイル名、ラベル、時間」キーの辞書の配列を作ります。 時間の形式は、秒数の整数もしくは小数が認識されます。 annotations.json [ { "video" : "killMove1.mov", "label" : "Nmehameha", "start" : 0, "end" : 3.1 }, { "video" : "killMove1.mov", "label" : "Nkankousappou", "start" : 3.2, "end" : 6 }, { "video" : "killMove2.mov", "label" : "Negative", "start" : 0, "end" : 3.7 }, { "video" : "killMove2.mov", "label" : "Ki", "start" : 3.8, "end" : 6.5. } ] 注意:公式ドキュメントには、String形式の分:秒でも、時間:分:秒.コンマ秒でも認識されるとありますが、これはなぜか認識されません。 // これらのJson形式は認識されないので注意 [ { "video" : "specialMoves.mov", "label" : "Nmehameha", "start" : "8:3.2", "end" : "8:4.5" }, { "video" : "specialMoves.mov", "label" : "Ki", "start" : "1:59:5.8", "end" : "1:59:7" }, { "video" : "specialMoves.mov", "label" : "Nkankousappou", "start" : "1:59:14", "end" : "1:59:15.1" } ] 手順(学習) 3、CreateMLでActionClassificationを開く XcodeをControlクリックしてOpenDeveloperToolからCreateMLを開いて以下を選択。 4、学習データを指定 「Training Data」の項目から学習データのフォルダを選択します。 注意:JSONを読み込ませる場合は、JSONファイルを直接選択する必要があります。フォルダ選択では認識しません。 検証データは学習データと別に用意してもいいし、しなくてもOKです。指定しない場合はcreateMLがトレーニングデータの一部を検証データとしてより分けてくれます。 5、学習の設定をする 以下の設定をします。 パラメーター Iterations 計算ステップ数。CreateMLがデータセットから自動で設定してくれます。 増やしたり減らしたりもできます。 Frame Rate アプリで想定するフレームレート。 データセットはこの数値以上のフレームレートで用意する必要があります。 action duration: アクション1回に想定される時間を設定します。 これによってcreateMLはアクション分析のフレーム間隔を認識できます。 Augmentations データを水増しできます。 アクションの左右がどちらでもいい場合は、Horizontal Flip(画像を水平にひっくり返して学習データを増やす)でデータを2倍に拡張できます。 6、学習を開始する 「Run」ボタンで学習を開始します。 自動で指定した計算回数まで学習します。 手順(学習結果) 7、手持ちの動画でモデルをためしてみる CreateMLのPreviewタブをクリックし、動画ファイルをドロップすると、作成したモデルを試せます。 8、CoreMLモデルを取得する CreateMLのOutputタブをクリックし、Getをクリックすると、作成したモデルのCoreMLファイルが取得できます。 手順(モバイルでの推論) 9、CoreMLファイルをアプリにバンドルする 取得したCoreMLモデルをアプリで使うには、アプリにCoreMLファイルをドロップしてバンドルします。 以下のプロセスで、Visionフレームワークを使ってカメラフィードから人体の特徴ポイントを取得し、CoreMLフレームワークでモデルを実行します。 10、人体ポーズの特徴ポイントを取得する まずは Vision で人体ポーズの特徴ポイントを検出し、その特徴ポイントをモデルに与えます。 VisionのVNDetectHumanBodyPoseRequestで人体のキーポイントを取得します。 do { let request = try VNDetectHumanBodyPoseRequest() let handler = try VNImageRequestHandler(cvPixelBuffer: capturedImage) // AVFoundationやARKitなどで、カメラフィードから取得したフレームのPixelBufferを与える try handler.perform([request]) guard let poses = request.results as? [VNRecognizedPointsObservation] else {return} // これが1フレーム分の人体の特徴ポイント poseWindow.append(poses.first) } catch let error { print(error) } //※ カメラからリアルタイムでフレームを与える場合、計算コストからカメラ・プレビューをブロックすることがあるので、グローバルキューで実行しましょう。 上記は、1フレーム分の人体の特徴ポイントです。 動画解析ですので、特徴ポイントを必要フレーム分ためて、モデルにあたえます。 60 フレーム分が必要な場合は、以下のように配列にします。 private lazy var posesWindow:[VNRecognizedPointsObservation] = { // 特徴ポイントの配列を作る var window = [VNRecognizedPointsObservation]() window.reserveCapacity(predictionWindowSize) // 必要なフレーム分のスペースを確保 return window }() { didSet { if posesWindow.count == predictionWindowSize { // 必要なフレーム枚数がたまったら、推論を実行 do { try makePrediction() } catch let error { print(error) } } } } var predictionWindowSize: Int = 60 11、特徴ポイントをマルチアレイ(多次元配列)に変換する 必要なフレーム分の人体の特徴ポイントがたまったら、モデルのインプット用にマルチアレイにしてからモデルにあたえます。 【特徴ポイント × XY座標の信頼度 × 必要フレーム数】の3次元配列マルチアレイにします。 この変換には Vision のメソッドをつかいます。 VNRecognizedPointsObservationを【特徴ポイント × XY座標の信頼度】のマルチアレイに変換してくれるkeypointsMultiArrayメソッドです。 // func makePrediction() throws { let poseMultiArrays: [MLMultiArray] = try posesWindow.map { person in guard let person = person else { return makeZeroedMultiArray() // ポーズリクエストの結果がnil(人が映っていない)場合は、[1,3,18]が全て0値のマルチアレイを入れる } return try person.keypointsMultiArray() // 1フレーム分の人体ポイントを、ポイントと信頼度の配列にしてくれる Vision のメソッド } let modelInput = MLMultiArray(concatenating: poseMultiArrays, axis: 0, dataType: .float) // モデルに必要なフレーム量の配列にまとめる 人体が映っていない時は、全ての値が0のマルチアレイをモデルに与えます。 ゼロ・マルチアレイの作り方はこちら。 private func makeZeroedMultiArray() -> MLMultiArray { let shape:[Int] = [1,3,18] guard let array = try? MLMultiArray(shape: shape as [NSNumber], dataType: .double) else { fatalError("Creating a multiarray with \(shape) shouldn't fail.") } guard let pointer = try? UnsafeMutableBufferPointer<Double>(array) else { fatalError("Unable to initialize multiarray with zeros.") } pointer.initialize(repeating: 0.0) return array } 12、インプットをモデルにあたえる(推論実行) 取得した指定フレーム分のマルチアレイをモデルに与えます。 CoreMLフレームワークを使います。 // func makePrediction() throws { // let poseMultiArrays: [MLMultiArray] = try posesWindow.map { person in // ............ 上記 // ............ // let modelInput = MLMultiArray(concatenating: poseMultiArrays, axis: 0, dataType: .float) let prediction = try classifier.prediction(poses: modelInput) posesWindow.removeAll() // 次の推論のために、ためたフレームを空にする print(prediction.label) // 最も信頼度の高い動作のラベル print(prediction.labelProbabilities[prediction.label]) // 信頼度 与えた動画(必要フレーム分の特徴ポイントの配列)に対しての推論結果がラベルと信頼度として返ってきます。 Nmehameha Optional(0.728015124797821) 13、失敗談 僕が試した時は、データ数が少なかったせいか、不正解のラベルがつくことも多かったです。 リアルタイムで動作解析をして、AR効果をつけたかったのですが。 ちなみに、GifのAR効果は別の方法を用いて作成しました。ARKitのボディ・スケルトンの認識です。 ARKitで両手のポイントを取得し、「手が体の中央からどれくらい離れているか」と「両手の距離で必殺技の体勢をしているか」を判定しました。 機会があれば、CreateMLのシーケンシャルな動作解析を再チャレンジしたいところです。 ? フリーランスエンジニアです。 お仕事のご相談こちらまで rockyshikoku@gmail.com Core MLやARKitを使ったアプリを作っています。 機械学習/AR関連の情報を発信しています。 Twitter Medium
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】LaunchScreenの待機時間を設定する方法

SwiftでLaunchScreenの待機時間を設定する方法。 解決コード AppDelegateの関数の中身にコードを加えるだけ。 class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. sleep(UInt32(5)) //この部分を追加 この場合は5秒待機。 return true } } AppDelegateの関数にsleep(UInt32(秒数))を追加するだけです。 秒数は少数でも可能です。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む