20220109のiOSに関する記事は14件です。

Flutter pod install エラー

Exception: Error running pod install Error: CocoaPods's specs repository is too out-of-date to satisfy dependencies. To update the CocoaPods specs, run: pod repo update Exception: Error running pod install iOSビルド時のエラー。dependencies updateすると発生することが多い。 クリーンしてうまくいくときもあれば、そうでない場合もあったが、これやっとけば正解な手順がわかったのでメモ。 手順 ios/Pods/ と ios/Podfile ios/Podfile.lock手動で削除 (sudo arch -x86_64 gem install ffi), arch -x86_64 pod install --repo-update # M1 mac flutter clean flutter run
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[SwiftUI]Firebase Authenticationで認証機能を実装する方法[SPM]

実装する機能 今回はFirebaseを使用して認証機能を実装していく方法をご紹介します。 FirebaseはGoogleが提供するmobile Backend as a ServiceでmBaaS(エムバース)と呼ばれており、データの管理や認証周りを簡単に実装する事ができます。 今回はFirebaseを利用して、アプリへの認証記録やアカウント管理を端末側では行わず、Firebase側に任せた実装を行なっていきます。 環境 ・ macOS: Monterey ・ Xcode: 13.1 ・ SDK Version: iOS 8.10.0 内容 ・Firebaseへの登録 ・FirebaseをSwift Package Managerでインストール方法 ・Firebase Authenticationで認証機能を設定 ・メールアドレスとパスワードによる実装  ・Sign Inの実装  ・Sign Upの実装  ・Sign Outの実装  ・パスワードのリセット(変更方法) 以上です。 認証機能の呼び方について 認証機能の呼び方につきましては [ログイン] - [ログアウト] [サインイン] - [サインアウト] という言葉が用いられていますが、どちらも意味に違いはないそうです。今回は昨今の認証機能として"サインイン"を用いるケースが多いようなので[サインイン] - [サインアウト]で統一いたします。 (余談ですがWindowsも7まではログオン、ログオフという記入があったようですが現在(Win10)はサインイン、サインアウトとなっているそうです) またアカウントを作る作業を[サイン アップ]とさせていただきます。 全体像 下記図のようにメールアドレスとパスワードを入力するとFirebase側に登録されいればサインインを行い、されていなければエラーを返します。 登録されていなければサインアップを行います。サインイン、サインアップのいずれかを行うとサインインの状態になり、サインアウトすることができます。 またパスワードを忘れたり、任意に変更したりすることもできます。これらを以下の図のようなイメージで作っていきたいと思います。(サインアップも同様) Firebaseへの登録 まずFirebaseを利用する準備を行います。Firebaseへの登録は5分もあればできると思います。 (Googleアカウントは必須になりますので持っていない方は作成してください) 登録はコチラから行えます。 使ってみるを選択 プロジェクトを追加を選択(既にプロジェクトがあるとここに表示される) プロジェクトの作成途中にアナリティクスの有効の有無を聞いてきますが、無料でかつ強力なアナリティクスを使用できますので有効にする事をおすすめします。 後は手順通りに進めていけば問題ないです。途中GoogleService-Info.plistをダウンロードする画面が出てきますが、後で使用しますので分かる場所に保存しておく事をおすすめします。 FirebaseをSwift Package Managerでインストール方法 projectより追加していきます。 赤枠部分を選択して追加していきます。 All Sourcesより赤枠の部分にgithub.com/firebase/firebase-ios-sdk.gitを入力してライブラリをインストールしていきます。 公式ドキュメントはコチラ Firebase Authenticationで認証機能を設定 Firebaseで作成したプロジェクトを選択し Authentication→Sign-method→新しいプロバイダを追加→メール/パスワード を選択して追加することで、Firebase側でメールアドレスとパスワードで認証する機能を追加することができます。 下準備はここまでです。 メールアドレスとパスワードによる実装 AppDelegateの追加 最初にAppDelegateを追加していきます。(Xcode12以降プロジェクト開始時にSwiftUIを選択するとAppDelegateが初期で無いので追加しないといけません。) アプリ名App.swiftファイルにimport FirebaseとAppDelegateクラスを以下のように追加します。 import Firebase class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { FirebaseApp.configure() return true } } 続いて同じファイル内にあるstructに@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegateを追加します。 @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } AppDelegateの追加は以上です。 GoogleService-Info.plistの追加 Firebaseでプロジェクトを作成する際GoogleService-Info.plistのダウンロードをしていると思いますのでファイルにドラッグ&ドロップで追加していきます。 SignInの実装 Auth.auth().signIn(withEmail: email, password: password) { user, error in if error == nil { //サインイン成功 } else { if let errorCode = AuthErrorCode(rawValue: error!._code) { switch errorCode { case .invalidEmail: //メールアドレスの形式によるエラー case .weakPassword: //パスワードが脆弱(6文字以下) case .wrongPassword: //パスワードが間違っている case .userNotFound: //ユーザー情報が見つからない(未登録) case .networkError: //通信エラー default:   //その他エラー } } } } caseの後のエラーは補完でも沢山出てきますが必要なものだけ設定します。 SignUpの実装 Auth.auth().createUser(withEmail: email, password: password) { result, error in if error == nil { //サインアップ成功 } else { if let errorCode = AuthErrorCode(rawValue: error!._code) { switch errorCode { case .invalidEmail: //メールアドレスの形式によるエラー case .weakPassword: //パスワードが脆弱(6文字以下) case .emailAlreadyInUse: //メールアドレスが既に登録されている case .networkError: //通信エラー default: //その他エラー } } } } こちらもサインイン同様、必要なエラーハンドリングだけ設定します。 ※サインインやサインアップのエラー処理については公式に詳細が掲載されていますので(こちら)[https://firebase.google.cn/docs/auth/ios/errors?hl=en&skip_cache=true%22]からどうぞ。 SignOutの実装 サインアウトのメソッドにはthrowsが付いているので(エラーを投げる可能性のあるメソッド)do,try,catchを使用して以下のようにします。 do { try Auth.auth().signOut() //サインアウトした時の処理 } catch { //エラー時の処理 } パスワードのリセット 以下のメソッドが成功すると入力したメールアドレスにFirebase AuthenticationのTemplates内にあるパスワードの再設定で設定した内容の文章が送られ、本文にあるリンクよりパスワードがリセットできるようになっている。 Auth.auth().sendPasswordReset(withEmail: email) { error in if error == nil { //成功した時の処理 } else { if let errorCode = AuthErrorCode(rawValue: error!._code) { switch errorCode { case .userNotFound: //ユーザー情報が見つからない(未登録) case .invalidEmail: //メールアドレスの形式によるエラー default: //その他エラー } } } } 最後に これでFirebase Authenticationを利用して  ・Sign Inの実装  ・Sign Upの実装  ・Sign Outの実装  ・パスワードのリセット ができます。誰かの役に立てば幸いです^^
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[iOS初学者向け]UILabelとは?

UILabelとは? ドキュメントの文言をお借りすると、1行以上のテキストを表示するビューのことです。 詳細 ドキュメントに色々書かれていて、何のことかわかりにくいかなと個人的には思ったのですが、要はテキストを表示するビューのことです。 Android的には、TextView的な。 こちらの型のプロパティを宣言し、さまざまなプロパティやメソッドを用いて値をセットし望みのViewにしていきます。 部分的な具体例ですが、下記を参考にしてみて下さい。 private let hogeLabel: UILabel = { let label = UILabel() label.text = "文字をセット" label.font = UIFont.systemFont(ofSize: 16.0) label.textColor = UIColor.red return label }() 終わり 他にも色々なプロパティがあるので、調べながら使うと細かい調整ができそうです。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【SwiftUI】@EnvironmentObjectはシングルトンにした方が良い話

おはこんばんにちは。 気づいたらアプリエンジニアに転身していた和尚です。 今回は私がSwiftUIアプリを作ってきたなかで、ViewのProperty Wrapperである「EnvironmentObject」はシングルトンにした方がいいなと思ったので、記事を書いていきたいと思います。 概要 SwiftUIのViewModelには、「StateObject」や「ObservedObject」といったProperty Wrapperを用いてViewと1対1の構造をとるViewModelと、「EnvironmentObject」という複数のView間で値を共有することのできるグローバルなViewModelの大きく2種類があります。(解説のため、前者を「ViewModel」、後者を「GlobalViewModel」と呼びます) EnvironmentObjectについては詳しくは説明はしませんが、MVVMアーキテクチャを採用する場合はビジネスロジックはこのEnvironmentObjectを含めた全てのViewModelに記載していきます。 基本的にはSwiftUIでアプリを作成する場合、あるViewに対してのビジネスロジックはそのあるViewに紐づいたViewModelに書いていくことになります。ただ複数のViewを跨いで値を共有したい場合はGlobalViewModelを用いることになります。そういった作りをしていく中でも、ViewModelからGlobalViewModelのプロパティを弄ったり、関数を呼び出したい時があります。本件はその課題を解決するための記事となります。 解決したい課題 ViewModelからGlobalViewModelのプロパティを変更したい ViewModelからGlobalViewModelの関数を呼び出したい これまでの自分の解決法 このような場合、自分はGlobalViewModelのインスタンスをViewからViewModelに逐一渡していました。 GlobalViewModel.swift final class GlobalViewModel: ObservableObject { @Published var count: Int = .zero func countUp() { count += 1 } } MainApp.swift @main struct MainAppApp: App { var body: some Scene { WindowGroup { ContentView().environmentObject(GlobalViewModel()) } } } ContentViewModel.swift final class ContentViewModel: ObservableObject { func onTapButton(_ globalViewModel: GlobalViewModel) { globalViewModel.countUp() } } ContentView.swift struct ContentView: View { @EnvironmentObject private var globalViewModel: GlobalViewModel @StateObject private var viewModel = ContentViewModel() var body: some View { VStack { Text("\(globalViewModel.count)") Button(action: { viewModel.onTapButton(globalViewModel) }) { Text("BUTTON") } } } } この実装方法の場合引数が多くなってしまい大変なのと、そもそもこのGlobalViewModelという存在自体が擬似シングルトンのような動きをしているため、だったら最初からシングルトンにしてしまえば良いのではないかと考えた結果以下のような実装に落ち着きました。 GlobalViewModelをシングルトンにした書き方 GlobalViewModel.swift final class GlobalViewModel: ObservableObject { static let shared: GlobalViewModel = .init() // シングルトンクラスへ private init() {} @Published var count: Int = .zero func countUp() { count += 1 } } MainApp.swift @main struct SampleAppApp: App { var body: some Scene { WindowGroup { ContentView().environmentObject(GlobalViewModel.shared) } } } ContentViewModel.swift final class ContentViewModel: ObservableObject { func onTapButton() { GlobalViewModel.shared.countUp() } } ContentView.swift struct ContentView: View { @EnvironmentObject private var globalViewModel: GlobalViewModel @StateObject private var viewModel = ContentViewModel() var body: some View { VStack { Text("\(globalViewModel.count)") Button(action: viewModel.onTapButton) { Text("BUTTON") } } } } GlobalViewModelのインスタンスをViewから渡す必要がなくなったので、かなりContentViewModelがスッキリしましたね。 注意点 GlobalViewModelをシングルトン化したことで、これまで.sheetや.fullScreenCoverのViewに対しては.environtmentObject()でGlobalViewModelを設定しなければなりませんでしたが、設定しなくてもプロパティが共有されるようになりました。 またこれまでGlobalViewModelは.environmentObject()で設定したViewより下の階層のViewでのみ適用されていましたが、それも全部で共有されるようになるので扱いには注意が必要です。 後書き いかがだったでしょうか。 既存のシステムにこの修正を加える場合は注意書きに書いたものを考慮する必要がありますが、ViewModel内からGlobalViewModelに直接アクセスできるためViewおよびViewModelがとてもスッキリするのでオススメです。 ではでは!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[iOS初学者向け]UIViewとは

UIViewとは iOSアプリにおいて、長方形の画面全体を管理するユーザーインターフェースのことです。 他のビューオブジェクトを、この範囲内でレンダリングします。 ドキュメント曰く使い方としては、インスタンス化して固定の背景色を表示させたり、継承させたサブクラスでカスタムさせたビューを作成するようです。 役割 UIKitなどのフレームワークを用いて、ビューオブジェクトを描画 サブビュー達のサイズを調整 サブビューの親ビューは一つだけです。 継承元にUIResponderがあるため、イベント処理ができる 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iPadだけでアプリ開発をした話

2021/12/16 個人的には衝撃を受ける出来事がありました。 iPad版のSwift Playgroundsで遂に、iOSアプリが開発できるようになりました!! 当時の自分のツイートからも興奮が分かると思います。 https://twitter.com/mtj_j/status/1471269185951256577?s=21 このアップデートで、 iPad上でSwiftファイルのコンパイルと実行 より実用的なコード補完 SwiftUIのライブプレビュー Swift PMのライブラリの読み込みのサポート などなどかなり本格的な機能が導入されました。 一方でデータベースか使えない(これは正しくなくて実は使える)など、まだまだXcodeを普段から使ってるエンジニアからすればおもちゃ感が拭えません。 ですがこれはかなり重要なアップデートだと思っています。 普段から業務でmacを触ってるエンジニアはXcodeでこれからもアプリ開発をすればいいですが、例えば自分のPCを持ってないけどiPadは持ってるという学生といった人がアプリ開発をスタートできるようになりました。 このアップデートはiOSアプリ開発へのハード的な側面でハードルを大きく下げるものです。 しかしデータベースが簡単には使えない、などまだまだ技術的なハードルもあります。 それならせめて、普段からアプリ開発をしている人間がある程度の知見をためて共有していこう、と思い「iPadだけでアプリ開発をする」という縛りでアプリ開発をスタートしました。 AppStoreでリリースすることまでできた今、結論を言うと、大変だけどiPadだけでアプリ開発は不可能じゃないです。 この記事ではiPadでアプリ開発をしたときに見つけたtipsや良かった点、辛かった点を説明します。 作ったアプリ iPadだけでアプリ開発をするのでせっかくならiPadにフォーカスしたアプリにしようと思い、PencilKitを使った絵日記アプリにしました。 シンプル絵日記 このアプリは全てiPadで作られています。 macは1度も触りませんでした。 iPadでのアプリ開発 使ったアプリ 今回使ったアプリは次のとおりです。 Swift Playgrounds Affinity Designer App Store Connect Safari たったこれだけです。 お気づきかもしれませんが、Affinity Designerを使ってアイコンやAppStoreのスクショもiPadで作りました。 DB問題 iPadでデータベースが使えない問題があります。 というのもCoreDataのテーブルを定義するために必要な.xcdatamodeldファイルをSwift Playgroundsでは編集できないからです。 正直データベースなしでアプリ開発はかなり厳しいと思って、僕も他のSQLiteのライブラリやRealmが使えないかと調べたのですが良い方法がなく諦めていました。 しかし、実はこれは解決することができます。 僕も知らなかったのですが、CoreDataは手間をかければ.xcdatamodeldファイルなしで扱えるらしいです。 そしてその手間を省いてくれる、CoreStoreというライブラリを見つけました。 このライブラリの簡単な使い方についてはこちらの記事に書きましたので、そちらを参考にしてください。 CoreStoreを使えばiPadでデータベースが使えない問題は解決できます。 Xcodeを上回るPencilKitとの相性の良さ これはびっくりしました。 iOSが提供しているPencilKitを使えばメモアプリのようなお絵かきができるUIを3行で実現できます。 この3行というのはAppleが言っていることでして、実際には微調整が必要だったりします。 その時にSwiftUIのPreviewを使えば、なんと、Preview上でApple Pencilを使ったデバッグができます!! 今までのPencilKitを使ったデバッグはシミュレータでは動作確認できずに実機が必須で、macでビルドしてiPadで実行して、動作確認をする必要がありました。 しかし、Swift Playgroundsはコードを変更したその瞬間に右のPreview画面に反映され、そのままApple Pencilでタッチして動作確認できます。 これは正直業務レベルでApple Pencilを使っている人におすすめしたいくらい開発体験が良かったです。 ここがつらいよ iPadでアプリ開発 あんまりネガキャンはしたくないのですが、やはり業務でアプリ開発をしている人間からすると不満は出てきます。 詳しくは書きませんが、列挙しておきます。もしこのあたりが気になる人はそれはもうmacを買ってXcodeでアプリ開発をする良いタイミングですのでぜひ! デバッガーが動かない。変数の確認にはprintデバッグをするしかない ウィジェットやSiriをサポートするためのapp extensionを使えない gitが使えない テストができない おわりに iPadでアプリ開発をした話について書きました。 正直、iPadだけでアプリを作り切るのはまだまだ大変だと感じましたが、macを買ってアプリ開発をする勇気がない人が、アプリ開発を体験するのにはとても良いと思いました。 そしてその気になれば僕がやったように、iPadだけでアプリを開発してリリースしきることができるので、みなさんぜひトライしてみてください!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CoreDataのラッパーライブラリ CoreStore入門

CoreDataを扱ったライブラリとしてCoreStoreというライブラリがあります。 スター数も3,000を超えていたりと、比較的注目されているライブラリのようですが日本語で扱った記事がなかったので、まずは簡単な使い方について記事を書こうと思います。 CoreStoreとは CoreDataを扱いやすくするライブラリです。 SwiftUIやCombineのサポートはもちろん、thread safeだったり.xcdatamodeldファイルなしで、スキーマが定義できたりします。 自分は諸事情で.xcdatamodeldファイルを使わずにCoreDataを扱いたかったので今回CoreStoreを触りました。 インストール方法 Swift PMで簡単にインストールできます。 CocoaPodsやCarthageも用意されているみたいですが試してません。 使い方 テーブルの定義 CoreStoreでは.xcdatamodeldファイルを用いて使用することも出来ますが、.swiftファイルのみでも使えます。 class Person: CoreStoreObject { @Field.Stored("name") var name: String = "" } こんな感じで保存したいプロパティをpropertyWrapperを使って定義します。 また、上で使った以外にも、@Field.Relationshipリレーショナルな定義もできます。 コード上でテーブルの定義ができるのはいいですね。 スキーマ定義とDataStackの初期化 さてさて、テーブルを定義したので次はスキーマの定義をします。 アプリの初期化時に以下のコードを実行します。 let schema = CoreStoreSchema( modelVersion: "V1", entities: [ Entity<Person>("Person") ] ) CoreStoreDefaults.dataStack = DataStack(schema) try! CoreStoreDefaults.dataStack.addStorageAndWait(SQLiteStore()) 上のコードについて簡単に説明します。 DataStackはデータの操作をするクラスでして、それの初期化をしています。 初期化の際にはスキーマを渡す必要があり、それを渡しています。 そして、アプリの中では基本的にCoreStoreDefaults.dataStackを用いてアクセスするのでそこにセットしています。 最後にストレージのセットアップをしています。 Read 一番シンプルなデータの読み込みは次の通りです。 let objects = try? CoreStoreDefaults.dataStack.fetchAll(From<Person>()) クエリなんかも書けます。 KeyPathでtype-safeにかけるのがいいですね try? CoreStoreDefaults.dataStack.fetchAll( From<Person>() .where(\.$name == "matsuji") ) Create / Update DBに対する書き込みは全てperformメソッドの中で行います。 CoreStoreDefaults.dataStack.perform(asynchronous: { transition -> Person in let person = transition.create(Into<Person>()) person = "matsuji" return person }) { result in switch result { case .success(let person): print("success") case .failure(let error): print("error") } } 特徴的なのは、performの中でobjectを生成して、書き込み、その結果をクロージャーで受け取っているところです。 performの中で生成されたオブジェクトは書き込みのためだけに生成して使われる必要があり、クロージャーの外から参照してはいけません。 performの中で生成したオブジェクトを他で使いたい場合、completion handlerの中のresultの中身を使う必要があります。 また、既存のデータに対して書き込みを行うときはedit()を用いて、トランザクションのためのオブジェクトに変更する必要があります。 let person: Person CoreStoreDefaults.dataStack.perform(asynchronous: { transition -> Person in let person = transition.edit(person) person = "updated name" return person }) { _ in } Delete データの削除もperform内で行います。 let person: Person CoreStoreDefaults.dataStack.perform(asynchronous: { transition -> Person in transition.delete(person) }) { _ in } おわりに 今回はCoreStoreの簡単な導入を紹介しました。 CoreStoreは使いやすく良いライブラリですので、皆さん一度試してみてください。 今回は省略しましたが、CombineやSwiftUIに対しての機能も用意されているので興味があればそちらもみてみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flutter ios実機ビルドができない Could not run build/ios/iphoneos/Runner.app

実行 % flutter devices connected devices: iPhone (mobile) • XXXXX-XXXXXXXXXXX (deviceID) • ios • iOS 15.1.1 % flutter run -d XXXXX-XXXXXXXXXXX エラー Xcode build done. Could not run build/ios/iphoneos/Runner.app on XXXXX-XXXXXXXXXXX 対応 下記ディレクトリ削除 % rm -rf ~/flutter/bin/cache/artifacts/ 参考 Flutter SDKの仕組み
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flutter ios実機ビルドができない

実行 % flutter devices connected devices: iPhone (mobile) • XXXXX-XXXXXXXXXXX (deviceID) • ios • iOS 15.1.1 % flutter run -d XXXXX-XXXXXXXXXXX エラー Xcode build done. Could not run build/ios/iphoneos/Runner.app on XXXXX-XXXXXXXXXXX 対応 下記ディレクトリ削除 % rm -rf ~/flutter/bin/cache/artifacts/ 参考 Flutter SDKの仕組み
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cloudflare Pages で iOS Safari から mp4 が再生できない

TL;DR iOS Safari は mp4 などのメディアコンテンツを HTTP 範囲リクエスト(206 Partial Content) で受け取らないと再生してくれませんが Cloudflare Pages は HTTP 範囲リクエスト に対応してないため 200 OK で返してきます(response header に Accept-Ranges があり、値が none 以外であれば、そのサーバーは HTTP 範囲リクエストに対応してることになります)。 その response を Service Worker でインターセプトして206に書き換えて解決します。 Nuxt × Workbox の場合 当方は普段 Nuxt × Workbox を利用しているためその場合の利用法になります(それ以外の環境の方は参考程度に御覧ください) nuxt.config.js export default { ... workbox: { cachingExtensions: '~/plugins/workbox-range-request.js', }, } /plugins/workbox-range-request.js // メディアファイルのrangeRequest対応 const myPlugin = { async fetchDidSucceed({ request, response }) { if (response && response.status !== 206 && request.headers.has('range')) { response = await workbox.rangeRequests.createPartialResponse(request, response) } return response }, } workbox.routing.registerRoute( /\.(mp3|ogg|mp4|webm)/, new workbox.strategies.CacheFirst({ cacheName: 'media-file-cache', plugins: [ new workbox.rangeRequests.RangeRequestsPlugin(), myPlugin, ], }), 'GET' ) 本来 Cloudflare に限らず、 Service Worker を利用したサイトの動画を iOS で再生する際は range request に対応する必要がありますが、 workbox の RangeRequestsPlugin では キャッシュされたレスポンス のみのコントロールになってしまいます。 今回の Cloudflare の場合は キャッシュされる前のオリジンレスポンス のコントロールも必要になりますので、 (workboxの場合は) fetchDidSucceed のタイミングで最適化するコードを入れました。 Service Worker 以外の解決法 確認してませんが、Cloudflare Workers を利用して 上記の createPartialResponse 関数内と同様の処理を施せば対応可能かと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

暗い場所で撮影された動画をくっきりあかるくする 機械学習で数行のコードで

暗い場所で撮影された動画を、明るくする方法です かんたんに機械学習モデルをつかって実行します。 暗くて不明瞭な動画から情報をとりたい 個人のスマホだけでなく、夜間の車載カメラや監視カメラなど、暗くて不明瞭な動画だけど、そこから情報をとりたい場合などあると思います。 動画用の軽量明るさ強化モデルStableLLVEがすごい StableLLVEという2021年に発表されたモデルは、軽量で高速ながら、きれいに明るくしてくれます。 これで動画を処理すれば、明瞭な動画が得られます。 動画への使用方法 CoreMLモデルを使ってiPhoneで実行してみます。 CoreML-Modelsからモデルをダウンロード。 初期化。 guard let model = try? VNCoreMLModel(for: StablLLVEImage(configuration: MLModelConfiguration()).model) else { return nil } let request = VNCoreMLRequest(model: model) SemanticImageで動画にモデルをアプライ。 SemanticImage().applyProcessingOnVideo(videoURL: url, { ciImage in let handler = VNImageRequestHandler(ciImage: ciImage, options: [:]) try? handler.perform([request]) guard let result = self!.animeRequest!.results?.first as? VNPixelBufferObservation else { return ciImage } let newImage = CIImage(cvPixelBuffer: result.pixelBuffer) return newImage }, { err, editedVideoURL in // handle editedVideo in here. } 動画の再生時間の2〜3倍程度の時間で処理できます。 処理が高速なので、スマホのカメラフィードをモデルに与えれば、リアルタイムで明るくすることもできます。 今回はスマホでやってみましたが、もちろん、PythonとOpenCVでオリジナルモデルを実行することもできます。 決定的瞬間を逃さずに モバイル用のモデルを使うことでスマホでも使えますし、モデル自体が軽量なのでさまざまな状況で使えます。 暗闇にさまたげられずに、必要な瞬間を取得できると良いですね。 ? フリーランスエンジニアです。 お仕事のご相談こちらまで rockyshikoku@gmail.com Core MLやARKitを使ったアプリを作っています。 機械学習/AR関連の情報を発信しています。 Twitter Medium
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Swift]シングルトンを実装してみよう

シングルトンとは シングルトンパターンとはインスタンスが1個しか生成されないことを保証するものです。 ではどんな時に使うのか 実際に使う目的としてはインスタンスが1個しか生成されないことを保証したいときに使うのですが、ではどのような時に1個しかインスタンスを生成するべきなのでしょうか? 目的はいくつかあって、、 ・情報(値)を共通化したいのに使用するクラスで都度、インスタンスを生成するのが手間 →メモリを節約したい ・たくさんのインスタンスからアクセスされると困る時 などと書きましたが、ざっとまとめると、 インスタンスを一回一回生成する処理を書いていたらコードも冗長だし、値も都度リセットされてしまうから同じ値をいろんなところで使うには適してないよねっていう時に使うという認識で間違いないと思います。 実際に実装してみよう public class Hoge { init() {} private var hoge: [String: Any] = ["name": "Luffy", "age": 19] } こちらのクラスにシングルトンを実装していきましょう。 ①継承されないためのfinal シングルトンは継承されてしまっては効果を発揮しません。 そのため、継承を防ぐためのfinalをクラスにつけてあげる必要があります。 (継承されていないのを明示するためにも) final public class Hoge { } ②初期化(init)をprivate化 シングルトンであるためには使用する時に直接インスタンス化をさせないようにするためにinitをprivate化してあげます。 final public class Hoge { private init() {} } ③sharedというプロパティを実装 外部からのアクセスはこのsharedというプロパティを介して行うことにするために public static let shared = Hoge() と書きHogeクラスのインスタンスを割り当てます。 final public class Hoge {       public static let shared = Hoge() private init() {} } staticはインスタンスを共通化できます。 staticの特徴として、複数のスレッドが一度にstaticプロパティにアクセスした時インスタンスが複数回生成されることを防ぐことができます。 ここまでがシングルトンの真髄になります。 ④実際にメソッドを実装する シングルトンパターンのクラスに実際にメソッドを加えてみましょう。 final public class Hoge { public static let shared = Hoge() private init() {} public func keyString(forkey key: String) -> String? { return hoge[key] as? String } } 先程のHogeクラスのhogeに格納されている値にアクセスするメソッドです。 ⑤シングルトンを使ってアクセスする 先程定義したsharedを窓口にしてメソッドにアクセスしてみます。 print(Hoge.shared.keyString(forkey: "name")) // Optional("Luffy") print(Hoge.shared.keyString(forkey: "age")) // Optional(19) 参考記事 本記事はこちらの記事を自分なりにまとめたものです。 分かりやすい記事をありがとうございました! https://ticklecode.com/swiftsingleton/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

flutter_native_splash使ってみた

要約 Splashとは? flutter_native_splashを使ってみる Android実装時の注意点 OS 12の注意点 Flutter2.5以上ではAndroidManifestのmeta dateを消す 感想 参考記事 Splashとは? アプリを立ち上げるときに一瞬表示される画面のことです。 flutter_native_splashを使ってみる pubspec.yamlにライブラリとflutter_native_splashの設定を追加する pubspec.yaml dependencies: flutter_native_splash: ^1.3.3 flutter_native_splash: color: "#FFFFFF" image: images/hogehoge.png android: true ios: true // ローカルに保存されている画像を呼び出せるよにする assets: - images/ Android実装時の注意点 OS 12の注意点 AndroidOS12ではデフォルトでSplashが実装されてます。 デフォルトのSplashを削除することはできないため、 Android実装時の注意点 Flutter2.5からはio.flutter.embedding.android.SplashScreenDrawableは不要なため削除すると以下のLogは表示されなくなります。 A splash screen was provided to Flutter, but this is deprecated. See flutter.dev/go/android-splash-migration for migration steps. AndroidManifest.xml <meta-data android:name="io.flutter.embedding.android.SplashScreenDrawable" android:resource="@drawable/launch_background" /> 感想 とても簡単に画面を作らずにSplashを表示できる。 しかし、SplashにAccessTokenの処理を書きたい、Splashの表示時間を長くしたいなど、場合によってはこのライブラリを使わない可能性もある。 参考記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOS APIでは RSA/ECB/OAEPwithSHA-256andMGF1Padding の暗号化・復号ができない

iOS の標準APIでは、Java の RSA-OAEP の "RSA/ECB/OAEPwithSHA-256andMGF1Padding" の暗号化・復号ができません。SHA-512も同様。 iOS API と Java の OAEP の違い OAEP は RSA で使われる平文パディングの手法で、アルゴリズムとしては2つのダイジェスト(ハッシュ)アルゴリズムを使います。 メッセージダイジェスト MGF(Mask Generation Function) ダイジェスト iOS の標準API と Java API では OAEP の仕様が微妙に違います。 Java の RSA/ECB/OAEPwithSHA-256andMGF1Padding: メッセージダイジェスト=SHA-256, MGF1ダイジェスト=SHA-1 iOS の SecurityAlgorithm.rsaEncryptionOAEPSHA256: メッセージダイジェスト=SHA-256, MGF1ダイジェスト=SHA-256 メッセージダイジェストは同じなのですが、MGF1ダイジェストが違うため、互換性がありません。 MGF1ダイジェストのほうは普通 SHA-1 で十分なはずなのですが、iOS ではなぜかメッセージダイジェストに合わせて変わってしまう様子。 このため、iOS 標準API では、Java の "RSA/ECB/OAEPwithSHA-256andMGF1Padding" に対応することは不可能です。 Java 側を iOS に合わせる Java 側では、メインダイジェストと MGF1 ダイジェストを個別に指定することができるので、iOS 側に合わせることができます。 Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); cipher.init(Cipher.DECRYPT_MODE, privKey, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT)); 自前で OAEP を実装する iOS 側では、OAEP Padding のみを実装し、あとは rsaEncryptionRaw で RSA 暗号化・復号を実行する、ということで MGF1 ダイジェストをあわせることができます。 OAEP の実装自体はそれほど難しくありません。OAEP を実装したものを RsaOaepPadding.swift として公開しています。 これを使うと、RSA/ECB/OAEPwithSHA-256andMGF1Padding 相当の暗号化は以下のように実装できます。 let plainData = "TEST TEXT".data(using: .utf8)! // main digest SHA256, MGF1 digest SHA1 で RsaOAEPPadding クラスを生成 let padding = RsaOAEPPadding(mainDigest: OAEPDigest.SHA256, mgf1Digest: OAEPDigest.SHA1) // OAEP Padding を計算 (RSA鍵長は 2048bit = 256byte前提) let padded = try! padding.pad(plain: plainData, blockSize: 256); // raw で暗号化 guard let cipherData = SecKeyCreateEncryptedData(publicKey, SecKeyAlgorithm.rsaEncryptionRaw, padded as CFData, &error) else { // Error処理 } 復号は以下のようになります。 guard let decryptedBlock = SecKeyCreateDecryptedData(privKey, SecKeyAlgorithm.rsaEncryptionRaw, cipherData as CFData, &error) else { // Error 処理 } // OAEP Padding 解除 let decrypted = try! padding.unpad(padded: decryptedBlock as Data) let decryptedString = String(data: decrypted, encoding: .utf8)! 検証用テストプログラム 検証用のプログラムおよびライブラリを https://github.com/tmurakam/rsa-oaep においておきます。 Apple に言いたいこと SecKeyAlgorithm の説明、不足しすぎじゃね?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む