ポジティブ丸メガネ

3年目エンジニアです。

Admobの取得をviewDidLoad()に置いてると「this application is modifying the auto layout engine...」と出る件の対処法@Swift

以前Admobを導入してみた記事を書きました。
seiya-orz.hatenablog.com

テスト運用していると、たまにWebThreadからExceptionを受け取って、アプリが止まることがありました。
そのエラーの内容は「This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release.」リリースした時にも起こるかもよ、とか言われてます。

このエラーは、要するに、バックグラウンドスレッドからレイアウトいじっちゃダメですよ、という話。

ただ、自分で書いている部分にレイアウトをいじっている部分は見当たらなく、なんとなくAdmobのせいなのではないか、と思い始めました。とくに、bannerViewに広告を挿しこんでいるのはバックグラウンドな感じがしましたし。

ということで調べてみると、たぶんこの記事が該当します。
groups.google.com

ということで解決策。

dispatch_async(dispatch_get_main_queue(), {
   //広告部分
   self.bannerView.adSize = kGADAdSizeSmartBannerPortrait
   self.bannerView.adUnitID = self.AdMobID  // テスト用ID
   self.bannerView.rootViewController = self
   let admobRequest:GADRequest = GADRequest()
   if self.AdMobTest {
      if self.SimulatorTest {
          admobRequest.testDevices = [kGADSimulatorID]
      } else {
          admobRequest.testDevices = [self.TEST_DEVICE_ID]
      }
   }
   self.bannerView.loadRequest(admobRequest)
})

このdispatch_async()内に広告取得にあたるコードを書き込むことで解決できるようです。今のところExceptionは発生していません。

Admobの公式に書いといてほしかった。もしかしたら書いてるのか?

おわり。

マカーの学生がEclipseからAndroid Studioに移行してアプリをリリースした話。

背景

1年半ほど前に、WindowsPCとEclipseを使ってAndroidアプリをリリースしていた。しかし、1年前に作業機をMacに変更したので、リリースしたアプリはほったらかしだった。ちなみにこれ(結局新しくリリースしたやつ、後述)。
ぷれれこ:録音機能付きプレゼンタイマー - Google Play の Android アプリ

今回なんとなく更新する気になった。そういえば以前自分で研究室後輩たちに向けてAndroid Studioがキテるから使えっていうプレゼンをしていたのを思い出した。今見るとクソみたいなプレゼンだ。

www.slideshare.net

せっかくなのでEclipseからAndroid Studioに乗り換えて、アプリをアップデートしよう。

Android Studio

Android Studio と SDK Tools のダウンロード | Android Developers
これ。なんか昔使ってみようと思ったことがあったはずなんだけど、そのときはメジャーバージョンがまだ出てなかったから使わなかったような気がする。今は2.0のベータ版が出てるみたいなので、時代の流れを感じる。

なんかよくわからんが、Gradleというのを使うらしい。ライブラリの管理とかをEclipseではGUIでポチポチやってたけどそれをコードで書いている印象(あってる?)。なにやらリリース版とデバッグ版とか異なる機能を持つAPKも作成できるらしい、すごい。

とりあえずダウンロードしてインストール。数回クリックしただけで終わった。
f:id:seiya-orz:20151222132334p:plain
なんでもうUpdateのお知らせ来てんだよ、最新版入れたんじゃねーのかよとか思いましたが、今回はスルー。

さてEclipseからプロジェクトを移行しよう。なんか色々外部ライブラリとか使ってるので、Import機能使うとバグりそうな気がした。

ということで、新規でプロジェクト作って順番に書き換えていくスタイルを採用。

Blank Activityでプロジェクト作って、元のプロジェクトからsrcやらresやらをドラッグアンドドロップすればなんかいい感じになった。

ただ、外部の.soファイルを使ってたんだけど、それがうまく読み込めてない現象が発生した。具体的には、System.loadLibrary()を読んでるとこで落ちてる。調べてみるとAndroid Studioでは、「app/src/main/jniLibs」に放り込む必要があるらしいが、放り込んでも落ちる。ふむ、もうちょっと調べてみるとjniLibs/armeabiに放り込む必要があったらしい。なんじゃこりゃ。とにかくやってみたらうまく動いたのでまあいいか。

移行で詰まったのはこれくらい。あとはIDEの使い方に慣れないといけない。

ということでコードをちょこちょこいじってアプリを修正。リリースできる段階に至った。

リリース

上のメニューのBuild/Generate Signed Apk... というところからリリース用APKを作成できるとあったので、やってみた。
qiita.com

前やった時のKeystoreファイルもちゃんと残してあるし、いけるだろ!・・・完全フラグでした。

なんかエラー出てて、作れない。AliasかPasswordが違うようだ。うーむ、わからん。Aliasはもう絶対わからん。無理だ。詰んだ。

しかたない、アプリの公開中止して、新しいアプリとしてリリースするしか無い!ということで、あたらしくKeystoreファイル作ってリリースしなおしました。くそう。今回はちゃんとAliasとPassword覚えました、大丈夫。

ということで、既存のユーザ様(100人くらいしかいないけど)には申し訳ないが、新しいアプリとして出したので、こちらをお使いいただきたい。ごめんなさい。
ぷれれこ:録音機能付きプレゼンタイマー - Google Play の Android アプリ

これからは、暇があればAndroidでもアプリを作ろう。せっかく環境整えたし。やっぱりiOSよりAndroidのほうが慣れてるからレイアウトを組んだりコードを書くのに詰まらないんだよなー。

おわり。

SwiftのCoredata(データベース)のマイグレーションで少し苦労した。

作成しているアプリでCoredataのAttributeを増やす必要が出てきました。こういった場合、単純にAttributeを増やしてアプリを実行すると、アプリはクラッシュしてしまいます。ですのでCoredataのマイグレーションを行う必要がありますが、少し詰まったので忘れないように残しておきます。

Androidアプリを作成していた頃に、マイグレーションを行った時は非常にめんどうだったので、結局DBを消して無理やり解決していました。今回もそうすれば動くには動くのですが、自分ですでにアプリをそこそこ使っていて、DBをリセットされるのはちょっと残念だったので頑張ってみることにしました。

こちらのサイトを参考にして、行いました。
Core DataでDBのMigrationを行う。 |
こちらのサイトの通りにやっていた(つもり)のですが、下記エラーがでて動きません。

「The managed object model version used to open the persistent store is incompatible with the one that was used to create the persistent store」

ふむ。結構いろいろ調べてみたのですが、大体はDB消してもう一回やれよという強引な解決策がほとんどでした。

もう一度AppDelegateをよくよく見てみると、間違い発見!
try coordinator.addPersistentStoreWithType()のoptionの引数がnilになっていました。元々初期値はnilだったので、治すのを忘れていました。せっかくoptions作ったのに参照してないとかアホすぎでしたね。一応下記に該当部分を載せておきますね。

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
        // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
        // Create the coordinator and store
        let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
        let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
        var failureReason = "There was an error creating or loading the application's saved data."
        
        let options = [NSMigratePersistentStoresAutomaticallyOption: true,NSInferMappingModelAutomaticallyOption: true]
        
        do {
            try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: options)
        } catch {
            // Report any error we got.
            var dict = [String: AnyObject]()
            dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
            dict[NSLocalizedFailureReasonErrorKey] = failureReason

            dict[NSUnderlyingErrorKey] = error as NSError
            let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
            // Replace this with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
            abort()
        }
        
        return coordinator
    }()

まあともかくこれでアプリを実行すると無事マイグレーションが行われていたので、一件落着。