20190529のiOSに関する記事は7件です。

ReactNative実行時のエラー解決メモ

react nativeを0.57.3から0.59.8に上げて実行したらエラーがたくさん出たので解決方法をメモしておきます。

android

Could not get unknown property 'mergeResourcesProvider' for object of type com.android.build.gradle.internal.api.ApplicationVariantImpl.

https://github.com/wix/react-native-navigation/issues/4757#issuecomment-468133753
上記コメント通りに対応すれば解決しました。
metro.config.jsファイルを作成するとありますが、0.59の正式リリースで必要なくなったみたいなので作らなくて大丈夫です。
また、バージョンはいきなり全て最新にすると依存関係でハマったりするので、メジャーバージョンを少しづつあげていく方がいいと思います。

Execution failed for task ':app:processDebugResources'.
 Android resource linking failed

  /Users/<USER_NAME>/.gradle/caches/transforms-1/files-1.1/appcompat-v7-28.0.0.aar/6f6de8a5350930056a96225b06ab7a16/res/values-v28/values-v28.xml:9:5-12:13: AAPT: error: resource android:attr/dialogCornerRadius not found.
  /Users/<USER_NAME>/<PROJECT_NAME>/android/app/build/intermediates/incremental/mergeDebugResources/merged.dir/values-v28/values-v28.xml:11: AAPT: error: resource android:attr/dialogCornerRadius not found.
  /Users/<USER_NAME>/.gradle/caches/transforms-1/files-1.1/drawee-1.10.0.aar/603dac14beab9d235ac515ad5b5f1fe7/res/values/values.xml:3:5-58:857: AAPT: error: resource android:attr/fontVariationSettings not found.
  /Users/<USER_NAME>/.gradle/caches/transforms-1/files-1.1/drawee-1.10.0.aar/603dac14beab9d235ac515ad5b5f1fe7/res/values/values.xml:3:5-58:857: AAPT: error: resource android:attr/ttcIndex not found.

  error: failed linking references. 

ググってみるとcompileSdkVersionを上げると解決するらしい。27だったので28にして実行したら解決した。
ちなみにtargetSdkVersionも上げると別のエラーがいろいろと…
いつか上げなきゃいけない日が来ますが今はとりあえず動けばいいのでそのままで。

androidはとりあえず上記のエラーだけで起動しました。動作確認も特に問題なく一安心。

iOS

iOSは色々と厄介で何度もキャッシュ消したりして時間がかかりました。

** BUILD FAILED **

The following commands produced analyzer issues:
        Analyze /Users/<USER_NAME>/<PROJECT_NAME>/node_modules/react-native/ReactCommon/jsi/jsi.cpp normal x86_64
        Analyze /Users/<USER_NAME>/<PROJECT_NAME>/node_modules/react-native/ReactCommon/yoga/yoga/Yoga.cpp normal x86_64
        Analyze Base/RCTModuleMethod.mm normal x86_64
(3 commands with analyzer issues)

The following build commands failed:
        CompileC /Users/<USER_NAME>/<PROJECT_NAME>/ios/build/CRIA/Build/Intermediates.noindex/RNFirebase.build/Debug-iphonesimulator/RNFirebase.build/Objects-normal/x86_64/RNFirebaseMessaging.o RNFirebase/messaging/RNFirebaseMessaging.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
(1 failure)

iOSだとエラーで止まると最後にこのようなログが出てきますが、ここには具体的なエラーの内容が書かれてないのでそのままググってもあまり意味ないです。
大量のログを少しずつ遡ってerror catchあるいは1 error generated.と出力されている直前の行にエラーの中身が出力されています。

pch was compiled with module cache path ~ , but the path is currently ~

ios/Build/PROJECT_NAME/ModuleCache.noindexフォルダを削除すると解決。

'folly/Portability.h' file not found

https://github.com/facebook/react-native/issues/24192#issuecomment-479497777
ここのpod 'Folly'以下をPodFileに追加してpod installする。
それでもエラーが出る場合はios/PodsフォルダとPodfile.lockを削除して再度pod installする。

info ** BUILD SUCCEEDED **

info Installing build/CRIA/Build/Products/Debug-iphonesimulator/<APP_NAME>.app
An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):
Failed to install the requested application
An application bundle was not found at the provided path.
Provide a valid path to the desired application bundle.
Print: Entry, ":CFBundleIdentifier", Does Not Exist
error Command failed: /usr/libexec/PlistBuddy -c Print:CFBundleIdentifier build/CRIA/Build/Products/Debug-iphonesimulator/<APP_NAME>.app/Info.plist
Print: Entry, ":CFBundleIdentifier", Does Not Exist
. Run CLI with --verbose flag for more details.
error Command failed with exit code 1.

あー…ここでこのエラーですか…
ネットにある色んな解決方法を大方試してみるも解決せず…

埒が明かないのでXcodeからアプリを実行してみる。(実行するときはxcworkspaceから実行しないとエラーになるので注意)

……何事もなくアプリが起動:joy:
なんでよ…と思いつつ再度コマンドで実行してみるもやっぱりダメ。

この後更に色々試しましたが、結局解決できなかったのでめんどくさいけどXcodeから実行することにしましたorz

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

iOS シミュレータ を オレオレ証明書なサーバに接続する手順

iOS シミュレータで localhostに接続する

(今更ATSかよっと思いますがご愛嬌) info.plistにATS対象除外リストに入れるのが普通のやり方だと思いますが、証明書をシミュレータにインストールして、信頼できるサーバとして登録したほうが早そう。ATSの設定をしてもうまく行かなかったので。ただのリンク集です。

手順

そのほか

ATS調査の時に使ったリンク集がこちら。
https://www.gettoby.com/p/rk8t8x16qrms

TOBYというgoogle chrome 拡張。便利ですねコレ。

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

Optionalの比較

オプショナルの比較

オプショナルの値の比較

オプショナル変数を比較する場合、unwarpする必要がある

 var i: Int?
 if let i = i {
    if i == 0 {
        print("0")
    } else {
        print("0ではない")
    }
 } else {
    print("nil")
 }

もちろんすべてのケースでそれぞれ処理する必要があるなら別だが、1以上の場合のみラベルを表示するとかになると
let isHidden = i > 0
だけで良い

比較2

String?の.countやisEmpty の場合

こちらも基本同じです。

func hoge2(_ val: String?) {
    if val?.count == 0 {
        print("0文字です")
    }
    if val?.isEmpty == true {
        print("0文字です2")
    }
    if varl?.isEmpty == false {
        print("0文字じゃないかnilです")
    }
}
hoge2("1")
print("^^^^ \"1\"の結果 ^^^^")
hoge2("")
print("^^^^ \"\"の結果 ^^^^")
hoge2(nil)
print("^^^^ nilの結果 ^^^^")

結果

文字列がある =  1
^^^^ "1"の結果 ^^^^
0文字です
0文字です2
^^^^ ""の結果 ^^^^
^^^^ nilの結果 ^^^^
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UITableViewのデリゲートメソッドの呼び出しを別のオブジェクトに転送する方法

UITableViewのデリゲートメソッドの呼び出しを tableView.delegate で指定したオブジェクト以外に転送したかったので、その方法を調べました。 1

要件として、既存の class Delegate: NSObject, UITableViewDelegate が存在する状態で、 Delegate で宣言しているデリゲートメソッドはそのメソッドが呼び出され、宣言していないメソッドは AnotherDelegate に転送してほしいということがありました。

一つの方法として、 Delegate 上で全てのデリゲートメソッドを定義しておき、その各メソッド内部で転送先のオブジェクトの同名メソッドを呼び出すというのを考えたのですが、面倒な上に UITableView の一部のデフォルト動作がデリゲートメソッド自体の有無で切り替わるようになっているらしく、この方法だと定義しなくてもいいメソッドのデフォルト動作がうまく作動しないことがわかりました。

そこで、セレクタの呼び出しをそのまま別のオブジェクトに転送しようと考えその方法を検索しました。すると、 NSProxy を利用した方法などが見つかるのですが、残念ながら NSProxy 中で使われている NSInvocation というクラスがSwiftではunavailableとされており、利用できません。

そこで、以下の方法で実装しました。

前提

  • Xcode 10.2.1
  • Swift 5.0.1
  • iOS 12.2

セレクタについての説明は省略します。

TL;DR

コード

final class Delegate: NSObject, UITableViewDelegate {
    /// 転送先のオブジェクト
    private let proxy: NSObject
    init(with proxy: NSObject) {
        self.proxy = proxy
    }

    override func responds(to aSelector: Selector!) -> Bool {
        // `Delegate` のインスタンスか `proxy` がセレクタを処理できるなら `true` を返す
        return Delegate.instancesRespond(to: aSelector) || proxy.responds(to: aSelector)
    }

    override func forwardingTarget(for aSelector: Selector!) -> Any? {
        // `Delegate` のインスタンスが処理できないセレクタは `proxy` に処理させる
        return proxy
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("Selected: \(indexPath)")
    }
}

final class AnotherDelegate: NSObject, UITableViewDelegate {
    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        print("Deselected: \(indexPath)")
    }
}

tableView.delegate = Delegate(with: AnotherDelegate())

適当にセルをタップした時の出力

Selected: [1, 3]
Deselected: [1, 3]
Selected: [1, 1]
Deselected: [1, 1]
Selected: [2, 0]

1. 既存の Delegate に転送先を保存するためのプロパティを作成する

final class Delegate: NSObject, UITableViewDelegate {
    /// 転送先のオブジェクト
    private let proxy: NSObject
    init(with proxy: NSObject) {
        self.proxy = proxy
    }
    /* ... */
}

まず、転送先のオブジェクトを保存するプロパティ proxy を既存の Delegate に追加しておきます。

2. Delegateresponds(to aSelector: Selector!) を実装する

final class Delegate: NSObject, UITableViewDelegate {
    /* ... */
    override func responds(to aSelector: Selector!) -> Bool {
        // `Delegate` のインスタンスか `proxy` がセレクタを処理できるなら `true` を返す
        return Delegate.instancesRespond(to: aSelector) || proxy.responds(to: aSelector)
    }
    /* ... */
}

responds(to aSelector: Selector!) は、 NSObjectProtocol が定義しているメソッドで、あるオブジェクトが指定したセレクタを処理できるかを返します。

今回の要件の場合、

  1. Delegate のインスタンスが aSelector を処理できる
  2. proxyaSelector を処理できる

のいずれか一方が真なら全体としてセレクタを処理できる事になります。

そこでそれぞれの処理の可能性を、

  1. Delegate.instancesRespond(to: aSelector) (ドキュメント) 2
  2. proxy.responds(to: aSelector) (ドキュメント)

で調べています。

3. DelegateforwardingTarget(for aSelector: Selector!) を実装する

final class Delegate: NSObject, UITableViewDelegate {
    /* ... */
    override func forwardingTarget(for aSelector: Selector!) -> Any? {
        // `Delegate` のインスタンスが処理できないセレクタは `proxy` に処理させる
        return proxy
    }
    /* ... */
}

次に、 Delegate が処理できなかったデリゲートメソッドの呼び出しを proxy に転送する必要があります。

Objective-Cのランタイムでは、あるオブジェクトが実装していないセレクタがそのオブジェクトに通知されると、 forwardingTarget(for aSelector: Selector!) というメソッドが呼び出されます。 (本当はもっと色々呼び出されるのですが、今回関係するのはこのメソッドだけです。)

このメソッドは引数(aSelector)に実装していないセレクタが渡され、戻り値でそのセレクタを処理できる別のオブジェクトを返す必要があります。

今回の要件の場合、 Delegate のインスタンスで処理できないセレクタは proxy が処理できるので、無条件に proxy を返しています。

「処理できないセレクタが呼び出された時にこのメソッドが呼ばれるのであれば、 responds(to aSelector: Selector!) を実装する必要はないのではないか」と思われるかもしれませんが、両方実装しないと正常に動作しません( forwardingTarget(for aSelector: Selector!) が呼ばれない)。おそらく UITableView の内部でデリゲートメソッドを呼び出す際に delegate.responds(to aSelector: Selector!) を呼び出し、 true でない場合にはそのデリゲートメソッドの呼び出し自体を省略しているからだと考えられます。

4. 通常通りデリゲートメソッドを実装する

final class Delegate: NSObject, UITableViewDelegate {
    /* ... */
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("Selected: \(indexPath)")
    }
}

final class AnotherDelegate: NSObject, UITableViewDelegate {
    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        print("Deselected: \(indexPath)")
    }
}

tableView.delegate = Delegate(with: AnotherDelegate())

ここまで実装しておけば、あとは通常通りデリゲートメソッドを実装することで呼び出されます。

TL;DRに書いたコードでは、 DelegatetableView(_:didSelectRowAt:) を実装し、 AnotherDelegatetableView(_:didDeselectRowAt:) を実装していますが、どちらも正常に呼び出されています。

デリゲートメソッドの呼び出しの優先度は、 DelegateAnotherDelegate の順です。もし両方のクラスに同じデリゲートメソッドを定義した場合、 Delegate のものが優先的に呼ばれます。

自分はObjective-Cの理解が全然甘いので、もし何か間違ったことを言っていれば教えてください。

参考文献


  1. タイトルにはUITableViewとありますが、UIKitの他のデリゲートでも同じことができます 

  2. 当たり前ですが、ここで Delegate のインスタンスが aSelector を処理できるかを調べるのに self.responds(to: aSelector)super.responds(to: aSelector) を使うことはできません。前者は無限ループになるし、後者はそもそもスーパークラスに定義されているわけではないので意味が通りません。(多分)この記事のような目的のために NSObject.instancesRespond(to:) というクラスメソッドがあるんだと思います。 

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

Pythonista で QRコードを作ってuiに表示する

はじめに

買ったまま1年ぐらい(もっとかも)使ってなかった Pythonista 3。もったいないので公式ドキュメントをみながらあれこれ試してる。Qiitaはいつも見てるばかりなので、書いてみることにした。

やること - QRコードを作る

iOSアプリとして使いたいのでuiViewを使う。テキストフィールドに変換したい文字列をいれて、ボタンを押すとQRコードを作る。

完成イメージ

IMG_8F7BE818D2D7-1.jpeg

QRコードの作り方

qrcodeというライブラリを使うと簡単にQRコードがつくれる。それをconsoleに出すのは、こうやればいいので簡単。

sample.py
import qrcode

img = qrcode.make('http://flapro.net/') #ここにQRコードにしたい文字列を入れる
show.image()

ui.ImageViewに表示するには、PILからImageに変換する必要がある。
Pythonista公式サイトのフォーラムにやりかたが載っていたので使わせてもらった。

pil2ui.py
# pil <=> ui
def pil2ui(imgIn):
    with io.BytesIO() as bIO:
        imgIn.save(bIO, 'PNG')
        imgOut = ui.Image.from_data(bIO.getvalue())
    del bIO
    return imgOut

完成したソース

qr.py
import ui
import io
from PIL import Image 
import qrcode

def getQR(sender):
    img = pil2ui(qrcode.make(sender.superview['textfield'].text))
    sender.superview['imageView'].image = img

# pil <=> ui
def pil2ui(imgIn):
    with io.BytesIO() as bIO:
        imgIn.save(bIO, 'PNG')
        imgOut = ui.Image.from_data(bIO.getvalue())
    del bIO
    return imgOut

v = ui.load_view()
v.present('sheet')

参考

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

app Extensionの設定周り

AppExtensionについて

iOS8のApp Extensionsをつくってみる(Share 実装編)

App Extensions プログラミングガイドをざっくりまとめた
の記事が分かりやすいです。
簡単にいうと、アプリ間連携の仕組みになります。

概念

gainen.png

ホストとなるアプリとExtensionバイナリをAppGroupでグルーピングして共有ファイル/データへアクセスできるようにします。

手順

前提
・すでにプロジェクトが出来上がっていること前提。
・AppleDeveloperアカウントを持っていること

1.Extensionスキームを追加

extension.png

Targets欄の"+"ボタンを押して、Action Extensionのスキームを追加。

2.本体アプリにバイナリ追加

embedBinary.png

GeneralタブのEmbedBinaryの"+"ボタンをタップし、先ほど追加したExtensionのスキームを追加

3.Entitlementsファイルを設定

entitlements.png

CapabilitiesタブでAppGroupをOnにするとEntitlementsファイルが作成されます。
ホストアプリのスキームとExtensionのスキーム両方に設定。

4.AppGroupを設定

addGroup.png

AddGroupsの"+"ボタンをタップしてGroupを追加します。
ホストアプリのスキームとExtensionのスキーム両方に設定。

5.AppleDeveloperでAppGroupを追加

AppGroupAssignment.png

Group名は先ほどプロジェクトで追加したGroup名

6.ビルドするAppIDとAppGroupをつなげる

appIdGroup.png
ホストアプリとExtension両方に設定。
設定後、プロビジョニングプロファイルを作成し、ダウンロード。

7.Extensionのplist設定

plist.png
BundleDisplayNameとNSExtensionActivationRuleを設定。

参考にした記事

https://www.techotopia.com/index.php/Sharing_Data_Between_a_WatchKit_App_and_the_Containing_iOS_App

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

「」内の文字が省略されるUILabelを作る

ときどき、文字列の中央を省略するのではなく、「」内の文字を省略して表示してほしいという要件が来ることがあります。正攻法でやるとなかなか難しそうなので、こんな方法を考えました。

サンプルコードはこちら。

  • 文頭「 中の文字列 」文末 のように、3つの UILabel に分割し、Stack View等でまとめる
  • 省略したい中央の UILabelContent Compression Resistance Priority を、ほかの2つの UILabel より小さいな値にする

コード上では特別することはありませんが、文全体が動的に変わるような場合は適宜分割し、それぞれのラベルに振り分ける必要があります。

ちなみにもし、一行ではなく複数行でこれをやりたいというケースだった場合...どうしたらいいんでしょうね(笑)

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