20190728のSwiftに関する記事は11件です。

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'
end
import 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()
    }
}

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[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
スクリーンショット 2019-07-28 20.32.24.png

TargetをPods-プロジェクト名→RealmSwiftに変更
スクリーンショット 2019-07-28 20.32.56.png

スクリーンショット 2019-07-28 20.33.15.png

あとはクリーンしてビルドするだけです。

まとめ

英語は勉強しよう。します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift:Timer.scheduledTimerとDipatchQueue.main.asyncAfterの細かい違い

n秒後に〇〇の処理を行うというようなプログラムを書く際,Timer.scheduledTimerDipatchQueue.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を指定してやる必要がありそうです.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アプリ画面に流れる文字列(ティッカー、マーキー)を表示する

本記事は「デジタルサイネージアプリ「Sign!」(iOS版)とその実装機能の紹介」の子記事です。

目的

WKWebViewに流れる文字列(ティッカー、マーキーなどと呼ばれます)を表示するための、WKWebViewに読み込ませるHTML文字列を紹介します。
手順としては、まず、流れる文字列を実現するHTML文字列(String)を紹介し、その後、WKWebViewでその文字列を読み込む方法を紹介します。

開発・実行環境

  • 開発環境:macOS、Xcode(9~10)、Swift(4~5)
  • 実行環境:iOS 11以上

コード

まずはHTML文字列を定義するコードを紹介します。ほとんどがCSSの定義です。コード中に、ポイントを解説しています。

ViewController.swift
let 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)
}

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ScrollViewについて個人的メモ

スクロール幅取得

override func scrollViewDidScroll(scrollView: UIScrollView) {
        contentOffset = scrollView.contentOffset
}

RxSwift

Rx-1.swift
         tableView.rx.contentOffset //contentOffsetが変化したとき実行 
             //$0がスクロール幅
            .map { hogehogeTransition($0.y) } //スクロール幅を何かしらに変換したいときはここ
            .subscribe(onNext: {
                hugahugaFunction($0.y) //スクロール幅を用いた何かしらの処理
            })
            .disposed(by: disposeBag)

このパターンもあるらしい
違いはあとで追記

Rx-2.swift
         tableView.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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iPadでアクションシートが開かない問題に対処する

本記事は「デジタルサイネージアプリ「Sign!」(iOS版)とその実装機能の紹介」の子記事です。

目的

iPadでアクションシート(.actionSheet)タイプのダイアログ(UIAlertController)が開かない場合の対処方法を説明します。

開発・実行環境

  • 開発環境:macOS、Xcode(9~10)、Swift(4~5)
  • 実行環境:iOS 11以上

コード

別の記事「ダイアログを表示する」で、ダイアログ(UIAlertController)を生成する際、「preferredStyle」に「.actionSheet」を選択できることを書きましたが、iPadでアクションシート(.actionSheet)を開くには、present()を実行する前に、1行加える必要があります。

ViewController.swift
override 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を指定しています。
ちなみに、ダイアログは以下のようになります。
1

iPad画面の左上に表示されます。ステータスバーに食い込んでいるのはご愛嬌(サンプルプログラムのViewのレイアウト指定をサボりました)。
ちなみに、「Cancel」は表示されません。ダイアログの外をタップすると、「Cancel」が選択されたものとして処理されます。

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ダイアログを表示する

本記事は「デジタルサイネージアプリ「Sign!」(iOS版)とその実装機能の紹介」の子記事です。

目的

複数の選択肢を持つダイアログ(UIAlertController)を開き、特定の選択肢を選んだときに、何らかの処理を行うためのコードを紹介します。

開発・実行環境

  • 開発環境:macOS、Xcode(9~10)、Swift(4~5)
  • 実行環境:iOS 11以上

コード

まず、コード例と、実際に表示されるダイアログのイメージを紹介します。
便宜上、ViewControllerのviewDidAppeared()でダイアログを生成し、表示させます(アプリを起動するといきなりダイアログが開きます)。

ViewController.swift
override 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)
}

実行するとこのようなダイアログが表示されます。
1

なお、ダイアログを生成する行の「preferredStyle」に「.actionSheet」を指定すると、iPhoneの場合は、以下のようなメニューが画面下からせり上がるように表示されます。用途に合わせて使い分けてください。
2

ところで、このコードのままでは、「OK」や「Cancel」を選んでも、単にダイアログが閉じるだけで、何の処理もされません。選択肢をタップすると何らかの処理をする場合は、addAction()のhandlerをnilにせず、コードを書く必要があります。
ここでは、「OK」をタップすると、画面の色がランダムに変わるようにしてみます(実用性はありませんが)。
※「OK」に該当するaddAction()のみ記述しています。

ViewController.swift
dialog.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)
  }))

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

あとで追記

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HTTP Webページを読み込めるようにする

本記事は「デジタルサイネージアプリ「Sign!」(iOS版)とその実装機能の紹介」の子記事です。

目的

WKWebViewで「HTTP」でアクセスするWebページを読み込むための設定を紹介します。

開発・実行環境

  • 開発環境:macOS、Xcode(9~10)、Swift(4~5)
  • 実行環境:iOS 11以上

設定

Xcodeのナビゲータエリア上部のフォルダアイコン(一番左)をクリックし、表示されるツリーの中のプロジェクトルート(一番上)をクリックします。さらに、エディタエリア上部の「Info」をクリックし、「Custom iOS Target Properties」のうちのいずれかの項目にマウスカーソルを合わせると表示される「+」マークをクリックします。
1

開いたプルダウンから「App Transport Security Settings」を選びます。
2

さらに、追加された「App Transport Security Settings」にマウスカーソルを合わせると表示される「+」マークをクリックします。
3

開いたプルダウンから「Allow Arbitrary Loads in Web Content」を選びます。
4

さらに、その値(Value)を「Yes」にします。
5

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

3x3のCIVectorを作る

CIFilterのkernelに渡すときとか作る必要がある。

let points: [CGFloat] = [
 1,0,0,
 0,1,0,
 0,0,1,
]
let vector = CIVector(values: points, count: points.count)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

大手銀行の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.swift
let 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.swift
let 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に格納すれば以下のように口座情報を一覧化出来る。
Simulator Screen Shot - iPhone Xʀ - 2019-07-27 at 16.31.59.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む