iOSエンジニアのつぶやき

毎朝8:30に iOS 関連の技術について1つぶやいています。まれに釣りについてつぶやく可能性があります。

【Android】Installation error: INSTALL_PARSE_FAILED_MANIFEST_MALFORMED 対処法

絶賛開発中のアプリで、API 19エミュレータでテストをしようと、実行すると下記のようなエラーが発生しました。

Installation error: INSTALL_PARSE_FAILED_MANIFEST_MALFORMED

ちょっと初見のエラーだったので、ググってみると何やら怪しい回答が、、

stackoverflow.com

どうやら package name が大文字の場合 一部のGoogleサービスが機能しない 可能性があるそうです。そういえば、エミュレータのダイアログにも サービスが使用できません 的なエラーが表示されていた気がするので修正してみます。

解決法

大文字だった package name を全て小文字に修正して、クリーンしてから再度ビルドすると正常にインストールができるようになりました🚀 applicationId も変更する必要があり、Firebase の設定ファイルを入れ替えるのが面倒でした😢 パッケージ名の変更手順は下記を参考にしたので、割愛します。

qiita.com

package を作成するときはくれぐれも小文字を使用するように気をつけましょう🤺 という感じで今日も以上になります。

参考

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com

【Swift】Realmでテストを行う

今日は、Swift ので単体テストを行う際に Realm のデータベースを入れ替えてテストする方法を紹介したいと思います🦅

それではやっていく

まずは、テストのデータを格納するための .realm ファイルを作成し保存します。また、簡単なテストデータ作るときは Realm Studio が便利なのでダウンロードしておきましょう。Realm Studio については前回の記事でも少し書いたので、ぜひ覗いて見てください。

yamato8010.hatenablog.com

今回は下記のように mock.realm という名前で、テストバンドルの中に保存して見ました。

f:id:yum_fishing:20210102231809p:plain

次に上記で作成した mock.realmFinder なり Terminal なりで開き、Realm Studio を立ち上げます。GUI で必要なデータをポチポチ保存します(CSV などでもインポートできます)。

最後にテストで使用する Realm を下記のように定義すれば完了です。

class MockRealmStore {
    lazy var realm: Realm = {
        let testRealmFilePath = Bundle(for: type(of: self)).path(forResource: "mock", ofType: "realm")
        let testRealmFileUrl = URL(fileURLWithPath: testRealmFilePath!)
        return try! Realm(configuration: Realm.Configuration(fileURL: testRealmFileUrl, schemaVersion: 4))
    }()

    func get<T>(type: T.Type, id: String) -> T? where T : Object {
        if let object = realm.object(ofType: type, forPrimaryKey: id) {
            return object
        }
        return nil
    }
}

Bundle(for: type(of: self)) で、そのクラスが属している Bundle (ここではTest Bundle) を取得し、mock.realm のパスから Realm.Configuration を初期化しています。schemaVersion は、カラムの追加など変更の回数で適所書き換える必要があるのでご注意ください。うまく、schemaVersion をハンドリングできるやり方があればまた記事にしたいと思います🧑‍🔧

参考

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com

【Android】特定のActivityでStatus Barのテキストカラーを変更する

機能に引き続き今日も Android 関連の小ネタです👷‍♀️

方法

Activity に仕様している styleandroid:windowLightStatusBar 属性を追加し、true にすることで Status bar に配置してあるアイテムの色を Gray にすることができます。逆に Status Bar のカラーを暗めにしている場合は、false に設定することでアイテムは White になります。下記が style のサンプルになります。

    <style name="AppLauncherTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimaryDark">@color/fideeWhite</item>
        <item name="android:windowLightStatusBar" tools:targetApi="23">true</item>
    </style>

windowLightStatusBar はターゲット API23 以降で使用できるそうなので、tools:targetApi でターゲット API を指定する必要があるそうです。

ちなみに、tools ネームスペースはタグのルートに設定することで使用できるようになります。今回の場合は、style.xml なので <resources> タグ内に配置します。

<resources xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" >

参考

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com

【Android】特定のActivityのAction Barを消す方法

今日はタイトルの通り、Android で特定の ActivityAction Bar を消す方法を紹介します。

方法

やり方は簡単で、アプリの AndroidManifest に記述している Activity の設定で、android:themeNoActionBar を追加します。下記が MainActivityNoActionBar を設定した場合のコードになります。

        <activity
            android:name=".MainActivity"
            android:theme="@style/Theme.AppCompat.Light.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

参考

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com

【Swift】iPad で UIActivityViewController の表示に気を付ける

iPhoneUIAlertController を表示させたことがある方なら馴染みがあると思いますが、UIAlertControllerUIActivityViewControlleriPad でそのまま表示処理を書いてしまうとランタイムエラーでクラッシュしてしまいます。

筆者も UIActivityViewController の表示で、iPad のための設定を忘れて Apple にリジェクトされてしまったので一応メモを残しておきます。

解決法

popoverPresentationControllersourceViewsourceRect に適当な値を設定することでクラッシュを回避することができます。sourceView では、ポップオーバーを表示させるための View を指定し、sourceRect でポップオーバーを表示する位置を設定しています。ポップオーバーの設定は iPhone では必要ないので、iPad のみで設定するようにしています。

        let uiActivityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
        if UIDevice.current.userInterfaceIdiom == .pad {
            let screenSize = UIScreen.main.bounds
            uiActivityViewController.popoverPresentationController?.sourceView = contextViewController.view
            uiActivityViewController.popoverPresentationController?.sourceRect = CGRect(x:screenSize.size.width/2, y: screenSize.size.height-200, width: 0, height: 0)
        }

ちなみに、ポップオーバーがどんなものかというのは下記の記事がわかりやすいので載せておきます。

【Swift4】UIPopoverPresentationControllerを使ってiPhone/iPad両対応のPopoverを出してみた - Qiita

iOS UIKitによるPopoverの表示およびカスタマイズ - Qiita

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com

【Android】android.content.ActivityNotFoundException の対処法

MainActivity から異なるパッケージにある OnboardActivityIntent を使用して画面遷移しようとしたところ下記のような Error に遭遇していました 👀

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: YamatoOtaka.Fidee.debug, PID: 30741
    java.lang.RuntimeException: Unable to resume activity {YamatoOtaka.Fidee.debug/YamatoOtaka.Fidee.MainActivity}: android.content.ActivityNotFoundException: Unable to find explicit activity class {YamatoOtaka.Fidee.debug/YamatoOtaka.Fidee.HogeActivity}; have you declared this activity in your AndroidManifest.xml?

原因

どうやらエラーメッセージの通り、AndroidManifest に該当の Activity が追加されていない場合に発生する Error のようです。

developer.android.com

上記のように Android Studio のテンプレートを使用すれば Activity が作成される際に AndroidManifest にも Activity が自動的に追加されるそうです。

解決

とりあえず、AndroidManifest に下記のように Activity を追加したところ無事に動作するようになりました。✨ 原因 のセクションでも書いた通り、これらの追加作業を自動的に行ってもらうように、今後 Activity の追加は Android Studio のテンプレートを使用して行おうと思います。

    <activity android:name=".OnboardActivity"></activity>

参考

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com

【Swift】アプリのバージョンを比較する

個人で開発中のアプリで、バージョンに応じて強制アップデートをかける際に、現在のバージョンと必須バージョンの比較をすることがあったのでその方法をメモしておきます✍️

結論

必須のバージョン(requiredVersion)と現在のバージョン(currentVersion)を比較した時に、必須バージョンの方が大きかった場合にハンドリングを行えるようにするサンプルが下記になります。

let requiredVersion = "1.2.0"
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String // 1.0.0

if requiredVersion.compare(currentVersion) == .orderedDescending {
    // 強制アップデート
}

まず Bundle.main.infoDictionary?["CFBundleShortVersionString"] でアプリのバージョンを取得しています。CFBundleShortVersionString は、Bundle Configuration のキーの一つで、アプリのバージョンをバンドルから取得することができます。Bundle Configuration については以前の記事でもちょろっと触れた気がします?が、下記にドキュメントを載せておきます。

developer.apple.com

次に requiredVersion.compare(currentVersion) == .orderedDescending についてですが、compare(_:)NSStringインスタンスメソッドで、メソッド名の通り二つの String 値を比較します。このメソッドの戻り値である ComparisonResult には、下記の三つのケースがあり今回の場合は .orderedDescending を使用することで、左のオペランドが右のオペランドよりも大きい場合に処理を行えるようにしています。

詳しくは下記のドキュメントを参照してみてください。

developer.apple.com

という感じで今日も以上になります。それではまた明日🧑‍🔧

参考

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com