20200121のiOSに関する記事は5件です。

【小ネタ】環境ごとにアプリアイコンを変更する方法【iOS】

モチベーション

「どれを確認すればよい?」
検証端末に複数の環境のアプリをいれて動作確認をするというのはよくある話だと思いますが、確認する役割の人や確認依頼者がわかりやすいようにする。

  • 確認者がアプリ名は分けているけど時々間違う
  • 依頼者がスキーマ分けているのに時々間違う

というようなことを防ぐ。

対象

  • 開発フェーズがある程度進んでしまっている *1
  • Build Configで環境分けはしている
  • リソース管理ライブラリは使ってない
  • Assets.xcassetsファイルでアイコン定義をしている

対象がちょっと限られていますが、同時に付け加えられるなら可能です。
新規開発の場合はリソース管理系のライブラリの導入を検討した方が良いかと思います。

*1 リソース管理ライブラリなどを適用するには総合的に現実的ではないという状況

やりかた

1.Assets.xcassetsに必要な環境のイメージセットをNew iOS APP iconを選択し追加

New iOS App icon
スクリーンショット 2020-01-21 22.30.52.png

追加後
スクリーンショット 2020-01-21 22.31.40.png

2.追加したイメージセットに環境ごとのアイコンをセット。Dev用、Qa用など
〜省略〜

3.Build SettingsAsset Catalog App Icon Set Name/Asset Catalog App Icon Set Nameに対応したイメージセット名を記載。
スクリーンショット 2020-01-21 22.33.58.png

以上で完成。

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

【swift】画像を指定した比率でトリミングする【TOCropViewController】

使用するライブラリ

TOCropViewController

ユーザーがUIImageオブジェクトの一部を切り取ることができるiOS用のView Controllerを提供します。

目標

1.ボタンをタップすると、UIImagePickerControllerが起動する。
2.トリミングしたい画像をpickし、UIImagePickerを閉じる。
3.配置したUIImageViewと同じ大きさにトリミングできるCropViewControllerが起動する。
4.トリミングを完了すると、UIImageViewにトリミング編集した画像が反映する。

(実装例)
20200121_214939.GIF

サンプルコード

//imageViewをストーリーボード上に配置(適当なsize)
@IBOutlet weak var imageView: UIImageView!

var image:UIImage?

//ボタンを押すとimagePickerが表示され、トリミングしたい画像を選ぶ。
@IBAction func presentCropViewButton(_ sender: UIButton) {

        setImagePicker()
}

func setImagePicker(){

        let picker = UIImagePickerController()
        picker.sourceType = .photoLibrary
        picker.delegate = self
        present(picker, animated: true, completion: nil)
}

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {

        guard let pickerImage = (info[UIImagePickerController.InfoKey.originalImage] as? UIImage) else { return }

        //CropViewControllerを初期化する。pickerImageを指定する。
        let cropController = CropViewController(croppingStyle: .default, image: pickerImage)

        cropController.delegate = self

        //AspectRatioのサイズをimageViewのサイズに合わせる。
        cropController.customAspectRatio = imageView.frame.size

        //今回は使わない、余計なボタン等を非表示にする。
        cropController.aspectRatioPickerButtonHidden = true
        cropController.resetAspectRatioEnabled = false
        cropController.rotateButtonsHidden = true

        //cropBoxのサイズを固定する。
        cropController.cropView.cropBoxResizeEnabled = false

        //pickerを閉じたら、cropControllerを表示する。
        picker.dismiss(animated: true) {

            self.present(cropController, animated: true, completion: nil)
        }
}

func cropViewController(_ cropViewController: CropViewController, didCropToCircularImage image: UIImage, withRect cropRect: CGRect, angle: Int) {
        //トリミング編集が終えたら、呼び出される。
        updateImageViewWithImage(image, fromCropViewController: cropViewController)
}

func updateImageViewWithImage(_ image: UIImage, fromCropViewController cropViewController: CropViewController) {
        //トリミングした画像をimageViewのimageに代入する。
        self.imageView.image = image

        cropViewController.dismiss(animated: true, completion: nil)
}

参考文献

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

【SwiftUI】@Environmentを支える機能

基本的な記法

sample.swift
@Enviroment(.\accessibilityEnabled) var accessibilityEnabled

関数定義

definition.swift
@propertyWrapper public struct Environment<Value> : DynamicProperty {
    @inlinable public init(_ keyPath: KeyPath<EnvironmentValues, Value>(
    @inlinable public var wrappedValue: Value { get }
}

なぜ @Enviroment のように書けるのか

Swift5.1で追加された@propertyWrapperというattributeを型に付けると、@Environmentのようにアノテーションぽい記述を行うことができます。このマークを付けると、値のget set する際にpropertyWrapperをつけた型を経由することが出来ます。

なぜアノテーションの右に括弧があるのか

@propertyWrapperで指定した型にイニシャライザがある場合、引数はEnvironmentの場合は、KeyPathを引数に取って初期化されているので、@Environment(.\accessibilityEnabled)のようになります。

指定するEnvironmentValuesは、かなり多くのプロパティを有しており、そのキーパスを渡す事でアクセスする仕組みになっています。

Dynamic Propertyとは何か

StateやBindingなども適合しているprotocolで、updateという必須実装の関数を一つだけ持っています。SwiftUIのモジュールに含まれています。

リファレンスを読むと以下のように書いてあります。

ドキュメント

Viewの外部プロパティを更新する格納変数

概要

Viewは、Viewのbodyを再計算する前にこれらのプロパティに値を提供します。

内容をまとめると「Dynamic Propertyとは、View外部のプロパティを更新するためのstored propertyで、bodyの中身を再計算するより前の段階でViewによって値が与えられる」となります。

このケースで考えると、@Enviroment(.\accessibilityEnabled) var accessibilityEnabled のvar accessibilityEnabledの部分でstoredPropertyとして扱われていますね。説明を読むとこの環境値はbodyが計算される前に与えられているようなので、「Environmentの値の初期値がタイミングによっては取れない〜」みたいなことはなさそうですね。

参考

SwiftUI徹底入門

apple doc

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

iOSアプリ、Adhoc配信でもプッシュ通知は飛ばせます!飛ばないときのトラブルシューティング

はじめに

今回、社内用にAdhocでプッシュ通知を伴うアプリをリリースしました。
結構証明書周りや署名、ビルド方法などが難しく、「Adhocではできない」と書かれたブログ記事も多くあったため、
ここに確認すると幸せになれる項目を書きます。
結論としては、Adhocでもプッシュ通知飛ばせます。
順不同に並べるので、不安なところを参考にしていただけたら幸いです。

前提

  • サーバサイドからFCMへプッシュを投げて、iOSで通知を受け取る流れ
  • すでにWebのプッシュ通知は実装済みで、サーバサイド→FCMへの連携はできている状態
  • xcode Version 11.2.1
  • enterprise apple developer program ではなく、apple developer program に登録しています

ビルドする target の内容をチェック

  • TARGETS の Signing & Capabilities タブで、Background Modes の Remote notifications にチェックがついていること
  • Push Notifications が選択されていること
  • TARGETS の Build Settings タブで、各種設定ファイルが本番用になっていること

プロビジョニングファイルのチェック

ipaファイルを生成

  1. Window > Organizer > Atchices で対象のアプリを選択し、「Distribute App」
  2. 「Ad Hoc」を選択、rebuild from bitcode
  3. 「Code Signing Identity」で、公開用の「Production」プロビジョニングファイルを選択
  4. ※Adhoc配信でもここで公開用のプロダクションを選択していること

ipaのzipを展開し、署名状況を確認

$ unzip xxxxx.ipa
$ codesign -d --entitlements :~ Payload/xxxxx.app

ここで表示される環境がdevelopmentではなく、productionになっていればOK

ビルドの種類を確認

Product > Scheme > Edit Scheme で、Runメニューの build Configuration が Release になっていればOK

SSL証明書を確認

Macのキーチェーンアクセス > 自分の証明書
本番では「Apple Push Services」という証明書をサーバ側で設定する必要があります。
private keyではなく、public keyから書き出す必要があるので注意。
xcode上の署名には、「iphone Distribution」の証明書を設定します。(Developerになっていないことを確認)

デバイストークンが正しいかどうか

// APNsへプッシュ通知の利用の登録が完了すると呼び出される関数です
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let tokenBytes = deviceToken.map { (byte: UInt8) in String(format: "%02.2hhx", byte) }
        NSLog("Device token: \(tokenBytes.joined())")
}

上記のようにトークンを取得。
これで生のデバイストークンが取れるので、このトークンを元にProductでプッシュが飛ばせるかをチェック。

私はこれを使わせていただきました。
https://github.com/noodlewerk/NWPusher
「Push Certificate」→[bundleid](sandbox|Production) を選択
Adhocなので 「Should use sandbox environment」のチェックを外し、「Push」を押下
→これで通知が飛んだらサーバー(今回はFCM)への設定に問題がありそう

GoogleService-Info.plist が正しいかチェック

  • 本番でプロジェクトを分けている場合、読み込んである内容が本番環境のプロジェクトであることをチェック

FCMに設定しているAPNs証明書をチェック

  • FCMの管理画面 > 設定 > クラウドメッセージング > APNs 証明書 ※APNsに認証キーを使っている場合は本番のキーになっているかを確認
  • 開発環境と本番環境でCSRが同じでないこと

参考

https://developer.apple.com/documentation/usernotifications/registering_your_app_with_apns
https://firebase.google.com/docs/cloud-messaging/ios/client?hl=ja
https://www.amazon.co.jp/iPhone-Android-%E3%80%8C%E9%80%9A%E7%9F%A5%E6%A9%9F%E8%83%BD%E3%80%8D%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E5%BE%B9%E5%BA%95%E3%82%AC%E3%82%A4%E3%83%89-%E7%A5%9E%E5%8E%9F-%E5%81%A5%E4%B8%80-ebook/dp/B00H539T4S
https://qiita.com/tyshgc/items/1e4ba87def4dbe94cd9d

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

UIAlertController の title と message のパターン

毎回 title と message のどっちを設定するとどうなるかを忘れるので表にしてみました

style = actionSheet のときに title あり/message = niltitle = nil/message あり のパターンで後者の方がフォントが太いあたりが見どころです

UIAlertController.Style.alert

not empty message empty message nil message
not empty title alertTextText alertTextEmpty alertTextNil
empty title alertEmptyText alertEmptyEmpty alertEmptyNil
nil title alertNilText alertNilEmpty alertNilNil

UIAlertController.Style.actionSheet

not empty message empty message nil message
not empty title actionSheetTextText actionSheetTextEmpty actionSheetTextNil
empty title actionSheetEmptyText actionSheetEmptyEmpty actionSheetEmptyNil
nil title actionSheetNilText actionSheetNilEmpty actionSheetNilNil

リポジトリ

https://github.com/funnything/AlertPattern

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