WKWebView の WKUserContentController に気をつけろ!

概要

WKUserContentController の add(_ scriptMessageHandler:, name: ) メソッドは scriptMessageHandler を強参照するので気をつけよう と言う話です。

WKWebView で JS のメソッドをフックしたい時は下記のように「WKUserContentController に
hoge メソッドが実行されたら self に教えてね」と言うコードを書きます。

final class WebViewController: UIViewController {

    private let webView = WKWebView()

    override func viewDidLoad() {
        super.viewDidLoad()

        webView.configuration.userContentController.add(self, name: "hoge")
    }
}

しかしのこのコードは循環参照を起こします。

原因

WKUserContentController の add メソッドですが、実は 第一引数を強参照します。
もしこの第一引数が WKWebView への参照を保持していれば循環参照が起きてしまいます。
まさに上記のようなコードです。

対応策

今回は下記のクラスを用意して擬似的に弱参照になるようにして循環参照を回避しました。

final class WeakScriptMessageHandlerReference: NSObject, WKScriptMessageHandler {

    private weak var handler: WKScriptMessageHandler?

    init(to handler: WKScriptMessageHandler) {
        self.handler = handler
    }

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        handler?.userContentController(userContentController, didReceive: message)
    }
}

add メソッド呼び出し部分を変更

let reference = WeakScriptMessageHandlerReference(to: self)
webView.configuration.userContentController.add(reference, name: "hoge")

まとめ

いつまた同じようなことが起きるか油断できないので定期的にメモリ使用量とメモリグラフを確認しましょう。

参考

Apple の公式リファレンス – add(_:name:)
WKWebView causes my view controller to leak – Stackoverflow

「入門 ReactiveSwift 」という発表をした

社内勉強会で「入門 ReactiveSwift」というタイトルで発表をしました。
↓スライド資料です。

なんと 191 ページもあります。

ReactiveSwift はまだまだ日本語の資料が少ないですからせっかくなので公開しました。
もっと使う人が増えればいいのですが…。

機会があれば ReactiveCocoa のことにも触れたり実践的な内容を取り扱ったりしたいです。

ちなみに発表時間は 50 分でした。
くちパッサパサになりましたよ。

Bitrise で XCTest がコケる

Bitrise でビルドは通るけど XCTest がコケるのを直した話を書きます。

encountered an error (Early unexpected exit, operation never finished bootstrapping - no restart will be attempted. Underlying error: Test runner exited before starting test execution.)

こういうエラーが出て困っていました。

結論

Bitrise 上の Carthage のバージョンが古く xcfilelist に対応していなかったが最新版に更新したら直った。

続きを読む

ReactiveSwift と APIKit を組み合わせる

今日は ReactiveSwiftAPIKit を組み合わせてみます。

いきなりですが以下のようなコードになります。

extension Session: ReactiveExtensionsProvider { }

extension Reactive where Base: Session {
    
    func send<Request>(_ request: Request) -> SignalProducer<Request.Response, SessionTaskError> where Request: APIKit.Request {
        return SignalProducer { [weak base] observer, lifetime in
            guard let session = base else {
                observer.sendInterrupted()
                return
            }
            
            let task = session.send(request) { result in
                switch result {
                case .success(let response):
                    observer.send(value: response)
                    observer.sendCompleted()
                case .failure(let sessionTaskError):
                    observer.send(error: sessionTaskError)
                }
            }
            
            lifetime.observeEnded {
                task?.cancel()
            }
        }
    }
}

let request = AppRequest(name: "おとなのおこづかい帳")
Session.shared.reactive.send(request).startWithResult { result in
    switch result {
    case .success(let app):
        print(app.review == "★★★★★")
    case .failure(let sessionTaskError):
        print(sessionTaskError)
    }
}

RxSwift 版を参考にしました。

ReactiveSwift にはエラーの型が指定できるので RxSwift とくらべてエラーハンドリングしやすそうですね。

// RxSwift
func send<Request>(_ request: Request) -> Observable<Request.Response>

// ReactiveSwift
func send<Request>(_ request: Request) -> SignalProducer<Request.Response, SessionTaskError>

プライバシーポリシー

本プライバシーポリシーは StudioRookery(以下、「当サイト」とします) の各種サービス(当サイトによる情報提供、各種お問合せの受付等)において、当サイトの利用者(以下、「利用者」とします。)の個人情報もしくはそれに準ずる情報を取り扱う際に、当サイトが遵守する方針を示したものです。

基本方針

当サイトは、個人情報の重要性を認識し、個人情報を保護することが社会的責務であると考え、個人情報に関する法令を遵守し、当サイトで取扱う個人情報の取得、利用、管理を適正に行います。当サイトで収集した情報は、利用目的の範囲内で適切に取り扱います。

適用範囲

本プライバシーポリシーは、当サイトおよび当サイトの各種サービスにおいてのみ適用されます。

個人情報の取得と利用目的

当サイトで取得する訪問者の個人情報と利用目的は下記の通りです。

広告配信

当サイトの各種サービスは広告配信のため AdMob を利用しているものがあります。
AdMob についての詳細はこちらをご参照ください。

利用状況解析

当サイトの各種サービスは利用状況解析のため Google Firebase Analytics および Fabric を利用しているものがあります。

これらのツールの利用によりアプリの操作データや Cookie を収集していますが、トラフィックデータは匿名で収集されており個人を特定するものではありません。

Google Firebase Analytics によって収集されたデータは Googleの プライバシーポリシー に基づいて管理されます。

Fabric によって収集されたデータは Fabric の プライバシーポリシー に基づいて管理されます。

その他

利用者からのお問い合わせ内容や不具合報告などにメールアドレスやアプリのデータなどの個人情報が含まれる場合があります。

個人情報の安全対策

当サイトは、個人情報の正確性及び安全性確保のために、セキュリティに万全の対策を講じています。

ご本人の照会

ユーザーがご本人の個人情報の照会・修正・削除などをご希望される場合には、ご本人であることを確認の上、対応させていただきます。

法令、規範の遵守と見直し

当運営者は、保有する個人情報に関して適用される日本の法令、その他規範を遵守するとともに、本ポリシーの内容を適宜見直し、その改善に努めます。
この方針は、事業内容の変更、法令の改正等により、事前に公表することなく任意の時期に変更される場合があります。

ReactiveSwift もいいぞという話

Swift のリアクティブプログラミングのライブラリといえば RxSwiftReactiveSwift が有名ですよね。

今まで RxSwift しか使ってこなかったのですが ReactiveSwift を使ってみたらとても良かったのでそれについて書きたいと思います。

ReactiveSwift と RxSwift を比較する形で紹介します。

RxSwift やその他 Rx ライブラリをある程度自由に使いこなせるという方を対象に書いています。

続きを読む

classic というミュージックアプリをリリースしました

classic というiOS 6 時代のミュージックアプリっぽい音楽再生アプリをリリースしました。
ダウンロードはこちらからどうぞ。

昔の標準の音楽再生アプリみたいなのを求めている人に強くおすすめしたいです。
といわけでこの度リリースした classic の素晴らしい機能を紹介しますので気に入った方はぜひダウンロードして試してみてください。

classic はこんなアプリ

  • コンセプトは「聴きたい曲をすぐ再生」です。
  • ミュージックライブラリの音楽を再生できます。
  • iOS 専用です。
  • 昔ながらのデザインを踏襲しています。
  • スンナリ、クセなく使えます。
  • 今の標準の音楽再生アプリにウンザリしていて元に戻してほしい、という方におすすめです。

続きを読む

Carthage で SwiftDate を更新したらエラー “Task failed with exit code 65:” が発生する

環境

Version
Xcode 9.2
Carthage 0.29.0
SwiftDate 4.5.1

Carthage 経由でインストールしていた SwiftDate を 4.5.1 へ更新したときに以下のようなエラーが起きました。

*** Building scheme "SwiftDate-iOS" in SwiftDate.xcodeproj
*** Building scheme "SwiftDate" in SwiftDate.xcodeproj
Build Failed
Task failed with exit code 65:
/usr/bin/xcrun xcodebuild -project ...

This usually indicates that project itself failed to compile. Please check the xcodebuild log for more details: ...

続きを読む