- 投稿日:2019-07-28T21:46:32+09:00
SwiftでAES暗号化/復号化
環境
- Xcode 10.3
- Swift 5.0.1
AES/CBC/PKCS7Padding してみる
IDZSwiftCommonCrypto というフレームワークを利用する。
CommonCrypto
の Swift ラッパー。CocoaPods または Carthage が利用できる。 CocoaPods の場合、自分の環境ではREADMEに記載されているようにバージョン番号を併記するとうまくいかず、バージョン番号ははずした。
platform :ios, '9.0' target 'HogeApp' do use_frameworks! pod 'IDZSwiftCommonCrypto' endimport IDZSwiftCommonCrypto class CryptUtil { // 暗号化 class func encrypt(plainText: String, key: String, iv: String) -> [UInt8] { let cryptor = Cryptor(operation: .encrypt, algorithm: .aes, options: .PKCS7Padding, key: key, iv: iv) if let cipheredText = cryptor.update(plainText)?.final() { return cipheredText } return [] } // 復号化 class func decrypt(encryptedData: [UInt8], key: String, iv: String) -> String? { let cryptor = Cryptor(operation: .decrypt, algorithm: .aes, options: .PKCS7Padding, key: key, iv: iv) if let decryptedBytes = cryptor.update(encrypted)?.final() { return String(bytes: decryptedBytes, encoding: .utf8) } return nil } }検索すると CryptoSwift がよくかかるが、こちらはちょっと細工しないとパフォーマンスが出ないのと、もともと開発者自身の学習用だったという事もあって、
IDZSwiftCommonCrypto
のほうを利用してみた。ちなみに PKCS5Padding と PKCS7Padding は互換性があるという事で、 PKCS5Padding したデータも問題なく扱う事ができた。
SHA256 してみる
これも
IDZSwiftCommonCrypto
でできる。extension String { func sha256() -> [UInt8] { return Digest(algorithm: .sha256).update(self)?.final() ?? [] } }[UInt8] を hex string に変換する
ダンプする時によく使うので。
extension Collection where Element == UInt8 { var hexa: String { return map{ String(format: "%02X", $0) }.joined() } }参考
- iosdevzone/IDZSwiftCommonCrypto: A wrapper for Apple's Common Crypto library written in Swift.
- krzyzanowskim/CryptoSwift: CryptoSwift is a growing collection of standard and secure cryptographic algorithms implemented in Swift
- CommonCryptoのSwift製ラッパー IDZSwiftCommonCrypto を使ってみる - タコさんブログ
- CryptoSwiftでAES暗号 (AES-256-CBC) - タコさんブログ
- CryptoSwift: Cryptography You Can Do
- 投稿日:2019-07-28T21:00:43+09:00
[Swift エラー備忘録] No such module ‘RealmSwift’
RealmSwiftのインポート時に出てくるこのエラーに2日も泣かされましたのでその対処法を残しておきたいと思います。
もうQiitaにもクリーン→ビルドという対処法は載っているのですが、私の場合それでも一向に解決しませんでしたが、英語の記事を探していたら一発で解決しました。
バージョン
Xcode 10.2.1
Swift version 5.0.1
Realm 3.17.1
RealmSwiftのインストールはCocoaPodsで行いました。今回その過程は省略します。
Podファイルをインストール後→Product→Schme→New Schme
TargetをPods-プロジェクト名→RealmSwiftに変更
あとはクリーンしてビルドするだけです。
まとめ
英語は勉強しよう。します。
- 投稿日:2019-07-28T16:43:30+09:00
Swift:Timer.scheduledTimerとDipatchQueue.main.asyncAfterの細かい違い
n秒後に〇〇の処理を行うというようなプログラムを書く際,
Timer.scheduledTimer
やDipatchQueue.main.asyncAfter
を使うことになると思いますが,この2つには処理タイミングにズレがあるようです.
Timer.scheduledTimer
はn秒後に(他の処理に割り込んででも)正確に処理が行われるDipatchQueue.main.asyncAfter
は最低n秒待ってから処理が行われるつまり,Threadを指定して正確にn秒後にある処理をさせたい場合は,
Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { (t) in DispatchQueue.main.async { // 処理 } }という風にTimerの中でThreadを指定してやる必要がありそうです.
- 投稿日:2019-07-28T11:47:52+09:00
アプリ画面に流れる文字列(ティッカー、マーキー)を表示する
本記事は「デジタルサイネージアプリ「Sign!」(iOS版)とその実装機能の紹介」の子記事です。
目的
WKWebViewに流れる文字列(ティッカー、マーキーなどと呼ばれます)を表示するための、WKWebViewに読み込ませるHTML文字列を紹介します。
手順としては、まず、流れる文字列を実現するHTML文字列(String)を紹介し、その後、WKWebViewでその文字列を読み込む方法を紹介します。開発・実行環境
- 開発環境:macOS、Xcode(9~10)、Swift(4~5)
- 実行環境:iOS 11以上
コード
まずはHTML文字列を定義するコードを紹介します。ほとんどがCSSの定義です。コード中に、ポイントを解説しています。
ViewController.swiftlet TICKER_HTML = """ <html lang="ja"> <head> <style> /* フォント種類、サイズ、色、背景色などはお好きに設定してください */ * { /* すべてのHTMタグの余白を除きます */ margin: 0; padding: 0; } html { /* Safariには、画面の向き(縦横)が変化したときに、文字サイズを適度に調整する機能がありますが、それを抑制します */ -webkit-text-size-adjust: 100%; } .box { /* 流れる文字列を表示する範囲を画面いっぱいに設定します */ width: 100vw; height: 100vh; /* 流れる文字列を画面の縦方向中央に表示するための設定です */ display: flex; align-items: center; } /* 流れる文字列を収めるpタグです */ p { /* 文字列が画面幅より長くても途中で途切れないように、インラインボックスとして表示します */ display: inline-block; /* 文字列が途中で改行しないようにします */ white-space: nowrap; /* 画面の横幅分の余白を文字列の前に取ります。これにより、画面右端から文字列が流れ始めます */ padding-left: 100vw; /* 実際に文字列を流すためのアニメーション設定です。「%d」には、後ほど、流れる速さ(秒数)が入ります */ animation: marquee %ds linear infinite; } /* 実際に文字列を流すための設定です */ @keyframes marquee { from { /* 文字列の左端が画面の左端に揃っているところから流れ始めますが、先の設定で文字列の左端に100%のpaddingが入っていますので、あたかも画面の右端から文字列が現れるように見えます。なお「%%」は「%」のことです(Stringの中で%を表現するには%%と書きます) */ transform: translate(0%%); } to { /* 文字列の右端が画面の左端に来るまで流します */ transform: translate(-100%%); } } </style> </head> <body> <div class="box"> %@ /* この「%@」に、後ほど、流したい文字列が入ります */ </div> </body> </html> """以上のHTML文字列には、2箇所、値を入れるべき箇所があります。文字列が流れる秒数を指定するところ(%d)と、実際に流す文字列を指定するところ(%@)です。これらの値をString(format:)で指定しながら、WKWebViewで文字列をHTMLとして読み込むメソッド loadHTMLString() で読み込みます。
ViewController.swift@IBOutlet weak var webview: WKWebView! override func viewDidLoad() { super.viewDidLoad() self.webview.loadHTMLString( String(format: TICKER_HTML, 10, "この文字列が流れます") // 任意の秒数(ここでは10)と文字列を指定しています , baseURL: nil) }以上です。
- 投稿日:2019-07-28T11:39:33+09:00
ScrollViewについて個人的メモ
スクロール幅取得
override func scrollViewDidScroll(scrollView: UIScrollView) { contentOffset = scrollView.contentOffset }RxSwift
Rx-1.swifttableView.rx.contentOffset //contentOffsetが変化したとき実行 //$0がスクロール幅 .map { hogehogeTransition($0.y) } //スクロール幅を何かしらに変換したいときはここ .subscribe(onNext: { hugahugaFunction($0.y) //スクロール幅を用いた何かしらの処理 }) .disposed(by: disposeBag)このパターンもあるらしい
違いはあとで追記Rx-2.swifttableView.rx.didScroll .withLatestFrom(tableView.rx.contentOffset) .map { hogehogeTransition($0.y) } .subscribe(onNext: { hugahugaFunction($0.y) }) .disposed(by: disposeBag)Scrollのパラメータ
こんがらがりがちな3つが図でまとまっている
contentSize , contentOffset , frame.size
・UIScrollView(UITableView)のスクロール量を計算するときに確認する図 - Qiita
- 投稿日:2019-07-28T11:27:02+09:00
iPadでアクションシートが開かない問題に対処する
本記事は「デジタルサイネージアプリ「Sign!」(iOS版)とその実装機能の紹介」の子記事です。
目的
iPadでアクションシート(.actionSheet)タイプのダイアログ(UIAlertController)が開かない場合の対処方法を説明します。
開発・実行環境
- 開発環境:macOS、Xcode(9~10)、Swift(4~5)
- 実行環境:iOS 11以上
コード
別の記事「ダイアログを表示する」で、ダイアログ(UIAlertController)を生成する際、「preferredStyle」に「.actionSheet」を選択できることを書きましたが、iPadでアクションシート(.actionSheet)を開くには、present()を実行する前に、1行加える必要があります。
ViewController.swiftoverride func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let dialog = UIAlertController(title: "Dialog Title", message: "Message is here", preferredStyle: .actionSheet) dialog.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) dialog.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) dialog.popoverPresentationController?.sourceView = self.view // この行を追加 self.present(dialog, animated: true, completion: nil) }iPadの場合、アクションシートはポップアップメニュー的に扱われますが、ポップアップする対象のビュー(.sourceView)を指定する必要があり、上記「この行」が、それに該当します。この例では、ViewControllerのviewを指定しています。
ちなみに、ダイアログは以下のようになります。
iPad画面の左上に表示されます。ステータスバーに食い込んでいるのはご愛嬌(サンプルプログラムのViewのレイアウト指定をサボりました)。
ちなみに、「Cancel」は表示されません。ダイアログの外をタップすると、「Cancel」が選択されたものとして処理されます。以上です。
- 投稿日:2019-07-28T11:20:53+09:00
ダイアログを表示する
本記事は「デジタルサイネージアプリ「Sign!」(iOS版)とその実装機能の紹介」の子記事です。
目的
複数の選択肢を持つダイアログ(UIAlertController)を開き、特定の選択肢を選んだときに、何らかの処理を行うためのコードを紹介します。
開発・実行環境
- 開発環境:macOS、Xcode(9~10)、Swift(4~5)
- 実行環境:iOS 11以上
コード
まず、コード例と、実際に表示されるダイアログのイメージを紹介します。
便宜上、ViewControllerのviewDidAppeared()でダイアログを生成し、表示させます(アプリを起動するといきなりダイアログが開きます)。ViewController.swiftoverride func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) // ダイアログ(AlertControllerのインスタンス)を生成します // titleには、ダイアログの表題として表示される文字列を指定します // messageには、ダイアログの説明として表示される文字列を指定します let dialog = UIAlertController(title: "Dialog Title", message: "Message is here", preferredStyle: .alert) // 選択肢(ボタン)を2つ(OKとCancel)追加します // titleには、選択肢として表示される文字列を指定します // styleには、通常は「.default」、キャンセルなど操作を無効にするものは「.cancel」、削除など注意して選択すべきものは「.destructive」を指定します dialog.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) dialog.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) // 生成したダイアログを実際に表示します self.present(dialog, animated: true, completion: nil) }なお、ダイアログを生成する行の「preferredStyle」に「.actionSheet」を指定すると、iPhoneの場合は、以下のようなメニューが画面下からせり上がるように表示されます。用途に合わせて使い分けてください。
ところで、このコードのままでは、「OK」や「Cancel」を選んでも、単にダイアログが閉じるだけで、何の処理もされません。選択肢をタップすると何らかの処理をする場合は、addAction()のhandlerをnilにせず、コードを書く必要があります。
ここでは、「OK」をタップすると、画面の色がランダムに変わるようにしてみます(実用性はありませんが)。
※「OK」に該当するaddAction()のみ記述しています。ViewController.swiftdialog.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in self.view.backgroundColor = UIColor( red: CGFloat(Float.random(in: 0.0 ... 1.0)), green: CGFloat(Float.random(in: 0.0 ... 1.0)), blue: CGFloat(Float.random(in: 0.0 ... 1.0)), alpha: 1.0) }))以上です。
- 投稿日:2019-07-28T11:11:16+09:00
Image Literal, Color Literalの活用法
うまく補完してくれないとき
Color Literal
の場合は特にcolor...
と途中までタイプした時点で補完機能によって
Color Literal
が候補に上がってくれないと非常に使いにくいが
参照が入れ子になっていて、候補に上がってくれない時before.swift//imageFromColorは自作関数 //color litral...とタイプしていっても補完してくれないときある let hogehogeBackImage = UIImage.imageFromColor(color: #colorLiteral(hogehuga))after.swift//参照が入れ子でなければうまく補完してくれる let backColor = #colorLiteral(hugahuga) //参照するか、右辺だけコピペする let hogehogeBackImage = UIImage.imageFromColor(color: backColor)alphaを動的にいじりたいとき
一回Literalにするとパネル表示になるので、パラメータを動的に変化させたいときどうすんの?となる
(そもそもLiteralは見た目をVC側で見ながらやりたいものなので、rgbも変化させたいものには入れたくなくなりがちでは?)
→でもalpha
ぐらいはいじりたいときある、、、let backAlpha = hugahuga //ここ計算式にすれば動的に変化させられる let backColor = #colorLiteral(hugahuga).withAlphaComponent(backAlpha)参考記事
・Xcode 8 の新機能!画像と色のリテラル、画像のコード補完 | DevelopersIO
あとで追記
- 投稿日:2019-07-28T11:11:01+09:00
HTTP Webページを読み込めるようにする
本記事は「デジタルサイネージアプリ「Sign!」(iOS版)とその実装機能の紹介」の子記事です。
目的
WKWebViewで「HTTP」でアクセスするWebページを読み込むための設定を紹介します。
開発・実行環境
- 開発環境:macOS、Xcode(9~10)、Swift(4~5)
- 実行環境:iOS 11以上
設定
Xcodeのナビゲータエリア上部のフォルダアイコン(一番左)をクリックし、表示されるツリーの中のプロジェクトルート(一番上)をクリックします。さらに、エディタエリア上部の「Info」をクリックし、「Custom iOS Target Properties」のうちのいずれかの項目にマウスカーソルを合わせると表示される「+」マークをクリックします。
開いたプルダウンから「App Transport Security Settings」を選びます。
さらに、追加された「App Transport Security Settings」にマウスカーソルを合わせると表示される「+」マークをクリックします。
開いたプルダウンから「Allow Arbitrary Loads in Web Content」を選びます。
以上です。
- 投稿日:2019-07-28T07:19:45+09:00
3x3のCIVectorを作る
CIFilterのkernelに渡すときとか作る必要がある。
let points: [CGFloat] = [ 1,0,0, 0,1,0, 0,0,1, ] let vector = CIVector(values: points, count: points.count)
- 投稿日:2019-07-28T00:15:27+09:00
大手銀行のAPIを使ったiOSアプリを作ってみる
三菱UFJ銀行が提供する銀行APIを使用した簡易iOSアプリを作成してみる。
公式ページが発行するAPIキーを用いて、「個人API 口座」のテスト用データをJSON形式で取得。各種データ項目を配列に格納する。以下、MUFG APIポータルサイト(公式)。
https://innovation.mufg.jp/api/実行環境
【Xcode】Version 10.3
【Swift】Version 5.0.1事前準備
1.MUFG APIポータルサイトから銀行APIページへ移動。アカウントを作成する。
2.「APIキー発行」タブより”APIキー発行登録”ボタンを押下。アプリ情報を登録。
3.発行されるクライアントIDを控えておく。(後ほど使用)実装
自身のクライアントIDやシーケンスNoをheadersとして定義する。
併せて、JSONデータを格納する為の配列を宣言。
※x-btmu-seq-noの例 : "x-btmu-seq-no": "20191231-aseyoshi12345678"ViewController.swiftlet headers = [ "x-ibm-client-id": "<自身のアプリのクライアントID>", "x-btmu-seq-no": "<YYYYMMDD-[16桁の任意の英数字]>", "accept": "application/json" ] var accountIdList: [String] = [] var accountNameList: [String] = [] var accountNameKanaList: [String] = [] var accountNoList: [String] = [] var accountTypeCodeList: [String] = [] var accountTypeDetailCodeList: [String] = [] var accountTypeNameList: [String] = [] var branchNameList: [String] = [] var branchNoList: [String] = [] var currencyCodeList: [String] = [] var primaryAccountFlagList: [String] = []更にviewDidLoad()の中を以下の通り実装。
ViewController.swiftlet request = NSMutableURLRequest(url: NSURL(string: "https://developer.api.bk.mufg.jp/btmu/retail/trial/v1/accounts/")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error) } else { let httpResponse = response as? HTTPURLResponse var lecturerData: Data = data! do { let json = try JSONSerialization.jsonObject(with: lecturerData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String:Any] for (k,v) in json { let arr = v as! NSArray for dic in arr { let d = dic as! NSDictionary self.accountIdList.append(d["accountId"] as? String ?? "") self.accountNameList.append(d["accountName"] as? String ?? "") self.accountNameKanaList.append(d["accountNameKana"] as? String ?? "") self.accountNoList.append(d["accountNo"] as? String ?? "") self.accountTypeCodeList.append(d["accountTypeCode"] as? String ?? "") self.accountTypeDetailCodeList.append(d["accountTypeDetailCode"] as? String ?? "") self.accountTypeNameList.append(d["accountTypeName"] as? String ?? "") self.branchNameList.append(d["branchName"] as? String ?? "") self.branchNoList.append(d["branchNo"] as? String ?? "") self.currencyCodeList.append(d["currencyCode"] as? String ?? "") let Temp = d["primaryAccountFlag"] as? Int ?? 0 self.primaryAccountFlagList.append(String(Temp)) } } } catch { print(error) } } }) dataTask.resume()これで各配列にString型のテスト用データが格納されるようになった。
UICollectionViewに格納すれば以下のように口座情報を一覧化出来る。