アプリのスタートアップ機能を搭載したInfinityTask v1.4

InfinityTaskの1.4をリリースしました。1.4では実験的な機能としてアプリのスタートアップを登録できる機能を追加しました。 「Startup Applications」の項目でアプリをオンにするとRespring後に自動でそのアプリが起動します。 SafariとChromeではこの起動時にOpenTabBackground++が動作するようにしました。

この起動は電話やメールと同様のサイレントな起動をしますので、通常のアプリ起動シーケンスとは異なる起動シーケンスになります。 このためアプリ側の各種初期化を行うメソッドの実装によっては望みの状態まで起動されない事もあります。(特にゲーム系はスタート画面まで行かない事が多いです)

本機能の主眼であるSafari, Chromeにおいては起動されてOTB++が動作する事を確認済みです。

iPadかどうか判別する

[[UIDevice currentDevice] isWildcat]
でもいいんですが、無駄にPrivate API使うのも危険なので(とは言えAppleのコードではかなり使われているのでこれの仕様が変わったり無くなったりする事はほぼ無いんじゃないかとは思いますが)、最近良く見かける下記のやり方の方が良さそう。
static BOOL IsPad(void)
{
    return UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad;
}

これも安心のiOS 3.2+

画面が縦なのか横なのかを判別する

これまで色々なメソッド使ってみたりしましたが、これ以上ない正解に辿りついたのでメモ。

static BOOL IsLandscape(void)
{
    return UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation);
}

ちなみにステータスバーが非表示でも大丈夫みたいです。
Available in iOS 2.0 or higherの安心設計。

resizableImageWithCapInsets:(UIEdgeInsets)capInsets でちょっとハマった話

Capの左右(上下)の合計を画像の幅(高さ)と同じにした時のバグ(iOS 5)

ボタン用の下のような画像を背景に使った時にこんなコードを書いてハマったケース。

[button setBackgroundImage:[bgImage resizableImageWithCapInsets:UIEdgeInsetsMake(20.0f, 22.0f, 20.0f, 22.0f)] forState:UIControlStateNormal];
こう書くとiOS 6ではうまくいくのだけど、iOS 5では通常時は普通の表示だけどボタンを押下すると何か欠ける。

これは元画像の幅44pxに対してUIEdgeInsetsMakeのleftとrightの合計が44pxで同一になっているのが原因だ。左右端が表示されているのは画像の高さが47pxでtop+bottomは40だからだろう。ちなみに同一になるように書いてもiOS 6では丁度境界の1pxを引き伸ばしているのか、いい具合に表示された。丁度にする必要はないので若干数値を減らせばOKだ。
[button setBackgroundImage:[bgImage resizableImageWithCapInsets:UIEdgeInsetsMake(20.0f, 20.0f, 20.0f, 20.0f)] forState:UIControlStateNormal];

Capの左右(上下)で片方だけ0指定した時のバグ(iOS 5)

先ほどの画像のUIActivityViewController呼び出し時みたいなViewの背景に指定している画像は右のような1x44の画像で、これをリサイズさせて背景に指定しているが、上の方にしか適応されていない。コードはこんな感じだ。
_backgroundImageView.image = [bgImage resizableImageWithCapInsets:UIEdgeInsetsMake(43.0f, 0, 0, 0)];
これはCap-topを43にして残りの下1pxを伸ばしてくれると思って書いたコードだ。これの原因はtopに1以上の値を指定したがbottomは0を指定しているためだ。どうやら左右(上下)の数値の組み合わせは0,0の組み合わせでなければ1以上を指定して引き伸ばす範囲を明確にしないとダメらしい。ちなみにこれもiOS 6ではイイカンジに引き伸ばして表示された。解決の為には1以上を指定すればいいので、引き伸ばすピクセルが一番下の1pxではなく下から2px目になってしまうが、以下のようにすれば良い。
_backgroundImageView.image = [bgImage resizableImageWithCapInsets:UIEdgeInsetsMake(42.0f, 0, 1.0f, 0)];

これでiOS 5でもちゃんと引き伸ばされる。

Sleipnizer 3.1が出ました

Sleipnizer 3.1が出ました!

新機能

■Improved FindOnPageアクション

独自のFindOnPageを呼ぶ出すアクションを追加しました。


■Focus SearchBarアクション

検索バーにフォーカスするアクションを追加しました。

■KeepTabBarオプション

タブを残してフルスクリーンにするフルスクリーンオプションを追加しました。

■SmartBannerKillerオプション

こいつを殺せます。

■iOS 6ネイティブのオーバーレイボタンを縦画面でも表示させる

■このオーバーレイボタンに長押しジェスチャー

こいつを縦画面でも表示させます+何故か標準では長押ししても履歴が出ないので、出るようにしました。




■L/R始まりのジェスチャーの安定性を向上

時にジェスチャーが効かない事をなくしました。

■4.2でもインストール可に。

■いくつか不要と思うアクションを廃止

New tweak "InfinityTask"

InfinityTask debut on Cydia Store!

This new tweak feature is...

  • Infinite background task.

  • Add OpenTabBackground++ background task to Safari and Chrome.

  • Add Add to ReadingList background task to SpringBoard for iOS 6+.

  • ■Infinite bgtask and change idle sleep behavior.

    First, this is iOS original task completion and idle sleep behavior mechanism since iOS 4.

    Then below image is InfinityTasktweaked version of one.

    If you lock the device then after 15s, iOS go idle sleepmode for battery life(super power saving mode). Idle sleep mode stoped most HW and SW feature also TCP connection. But many background task required network connection; So system prevent idle sleep mode for background task during it running.

    Although iOS restrict 600sec for battery. InfinityTask unlock that mechanism! You can enable infinite background task one by one apps, that app's background task can prevent your selected time have passed after locking the device. I recommend enable to network connection not required bgtask type apps.

    ■Add OpenTabBackground++ background task to Safari and Chrome.

    Added new background task for Safari and Chrome monitoring clipboard to open url as new tab in background.


    ■Add Add to ReadingList background task to SpringBoard for iOS 6+.

    Said OpenTabBackground++ is background task, so that app must running background. This feature isn't require Safari running in background. When you copy URL to clipboard, automatically add to ReadingList that URL.

    Mac上でiOSバイナリをClassDump

    「あのバージョンのバイナリ(SpringBoardとか)をClassDumpしたいけど稼働してる実機がないなぁ」って時の方法。

    1. vfdecrypt(Intel Mac) をDL。chmod +xして/usr/local/binにでも突っ込むといいでしょう。
    2. DumpしたいバージョンのipswをDL。
    3. ipswをunzipmkdir dmg; unzip hogehoge.ipsw -d dmg; cd dmg
    4. 対象のVFDecrypt keyを調べる。Root Filesystemのやつね。
    5. vfdecrypt -i hogehoge.dmg -k 対象のkey -o output.dmg
    6. output.dmgをマウントしてバイナリをコピる。
    7. class-dumpする。

    以上。

    自動retainされないNSMutableSet

    addObjectしてもretainされないNSMutableSetを作る。使い所はグローバル変数のstatic NSMutableSet *でこれを使って特定クラスのオブジェクトを詰め込んでいって、あるタイミングで全インスタンスに対して何か処理をする為。retainさせないのはreleaseタイミングはデフォルトのままにするため。まぁaddObjectしたらすぐにreleaseするのと大差ないんだけどね(その場合もSEGVには注意しないといけない)。

    CFSetCallBacks assignCallbacks = kCFTypeSetCallBacks;
    assignCallbacks.retain = NULL;
    assignCallbacks.release = NULL;
    NSMutableSet *assignSet = (NSMutableSet *)CFSetCreateMutable(kCFAllocatorDefault, 0, &assignCallbacks);

    これで[assignSet addObject:obj];してもobjのretainCountは増えない。解放されたオブジェクトを触るとSEGVになるので、以下のようにaddするオブジェクトのクラスにおけるdeallocの中で外す。

    - (void)dealloc
    {
            if ([assignSet containsObject:self])
                    [assignSet removeObject:self];
            [super dealloc];  // %orig;
    }
    上記のようにdeallocいじらないといけないので、単一のクラスのオブジェクトを詰め込む場合以外ぐらいに留めておくのがよさそう。

    参考 http://decafish.blog.so-net.ne.jp/2010-10-27-1

    NoLockScreen2 Public beta test

    NoLockScreen

    NoLockScreenご存知ですか?そこそこ古くからあるtweak(CydiaUpdates.netを見る限りは2010/10/05)でアラートがなく、パスコードロックもされていないようであればiPhoneの特徴的なslide to unlockをスキップしてくれる代物で愛用しています。

    が、初版の公開から一切のアップデートが行われておらずiOS5での新しいバナーシステムへの対応は行われていませんでした。さらにiOS6ではアプリが最前面にある状態(SpringBoardではない)でロック後にロックボタンでロックを解除すると、ロック解除後にすぐさまロックされてしまうバグがあります。

    この状態で放置されており、自分的にも欲しいtweakでしたので対応版をつくりました(こんな時オープンソースならフォークするのが楽なのにと思います、なので将来的にはgithubで公開予定です)。

    NoLockScreen2

    上述の問題点である

    • iOS 5+でロックスクリーンにバナー表示があるのに自動アンロックしてしまう対応不足
    • iOS 6でロックされてしまうバグ

    を解消したNoLockScreen2をパブリックベータテストします。iFileでインストールするのが一番楽だと思います。問題なければBigBoss行き。アイコンは四苦八苦してたら@renz0ne23さんが作ってくれました!多謝!

    jp.r-plus.nolockscreen2_0.0.1-11_iphoneos-arm.deb

    依存関係は以下です。

    Depends: mobilesubstrate, preferenceloader, firmware(>= 4.0)
    Conflicts: com.openbrew.nolockscreen

    DicActivity


    ActionMenuで選択文字列を各種辞書やGoogle検索(Safari)に投げ込むDaijirin ActionMenu PluginをiOS 6から使えるようになったUIActivity+UIActivityViewControllerで作り直しました。以下課題というか問題点

    • 表示時に一気に各ActivityClassをalloc initするコストがやはり高いのか、iPhone 4では動作速度にちょっと不満がでるレベル(といっても0.1sぐらいの差ですが)
    • オプション追加する度にアイコンで四苦八苦する
    • アルファ画像しか使えない仕様なので視認性も期待したほど向上していない

    お蔵入りの可能性が高そうですかね

    iOS 6におけるSleipnizerフルスクリーン機能

    ■Portraitモードにおけるoverlayボタン

    v3.1からPortraitモードでもOS標準のoverlayボタンが表示されるように変更します。下記の表はそれを踏まえての遷移表です。

    ■Sleipnizerフルスクリーン遷移表

    Sleipnizer (>= 3.1)のフルスクリーン管理が非常に複雑になってきましたので、まとめておきます。左の状態からアクション後の状態が右側です。on/offの状態はoverlayボタンが出なくて不便なだけなのでならないように作ってます。

    SleipnizeriOS 6 nativeactionSleipnizeriOS 6 native
    offofftweakonon
    nativeoff (on : Relate to native)
    offontweak (Portrait)onon
    tweak (Landscape)offoff
    native (Landscape)
    onontweakoffoff
    native

    ■Relate to native

    Relate to nativeを有効にすると上記の表で1箇所だけ動作が変わります。また、iOS6からのiOS標準のフルスクリーン状態は/var/mobile/Library/Preferences/com.apple.mobilesafari.plistに保存され、Safariの起動時(Suspendから復帰ではない)にplist設定値から状態が復元されるのですが、この際にフルスクリーンがONで保存されていると起動時からフルスクリーンになります。Startup FullScreenが半分入る感じですね。

    ■Keep statusbar

    諸々の整合性からiOS6+ですとKeep statusbarが有効なのはPortraitモードのみになりました。FullScreen for Safariは完全になくしてしまったみたいですが。

    ■iPad

    iPadは標準が入ってこなかったようなので、これまで通りに使えるはずです。はず。

    libinstabanner

    libinstabannerをリリースしました。iOS5から実装されたUIAlertViewによる通知にかわる方法として上部にバナーがでるおなじみのアレですが、tweakから任意に呼び出すには結構な手間がかかる(特にSpringBoardの外からは)のでライブラリを作りました。

    使い方は1通りしかないので難しい事は何もなく、ヘッダーのUsageがもうほんとすべてです。bundleIdentifierの引数はnilチェックしてるのでそこは最低限なにか入れる必要があります。titleはnilにすると自動でDisplayNameが入ります。com.apple.Preferencesを指定して日本語なら「設定」てな感じです。

    bundleIdentifierを自由に指定できるのでこんな感じにすると・・

    [InstaBanner showBannerWithBundleIdentifier:@"eu.heinelt.ifile" title:nil message:@"libinstabanner test"];
    こうなります。タップするとそのアプリを起動できます。
    面倒なコードが1行ですむね!やったね!

    CleanActivity

    iOS 6からUIActivityViewControllerを使用したメニュー導入されました。従来使われていたUIActionSheetより生成にパワーが必要でUIActionSheetよりも起動は遅いですが、表示できるメニュー数が多くて便利ですね。便利なんですが人によっては絶対に使わないようなメニューもあるかと思います。Weiboとか、メールとか、メッセージとか。そこでOS標準提供のActivityをオン・オフ設定できるCleanActivityを作りました。これで私のBylineは2段にまとまってスッキリ!

    設定できるのは標準提供されている以下9種類のActivityです。

  • Twitter
  • Facebook
  • Weibo
  • Message
  • Mail
  • Pasteboard
  • Print
  • Assign to Contact
  • Save to CameraRoll
  • 色々とオフにするとスッキリしたメニューにできます。

    CleanActivityはgithubで公開しています。Licensed under the GPL v3です。アイコンのほうきがGPLだったので。CleanStatusのほうきと同じ画像だと思うのですがアレはちゃんとGPL守ってるのでしょうか。

    FakeClockUp v0.7.1-1

    ここ最近tsSpeed Aをオープンソース実装する事を目指してFakeClockUpをバージョンアップしてきてv0.7.1-1で達成できました。バイナリ・ソースコードともにgithubにあります。ライセンスは踏襲してApache License 2.0です。

    ■実装の雑感

    スライダーの値はfloat型でしか得られないので、スライダーの値をListの値に完全に合わせてあげなければ設定が同期しているようにならないのですが、ここで他のSpecifierの値を設定するにはobjectを引数とするメソッドを使用するので、floatもしくはdoubleからオブジェクトへ変換する際にNSNumberを使って0.100000000の値を[NSNumber numberWithDouble:0.100000000];してみると実際の値は0.099999998753みたいな値になってしまい同期がとれませんでした。なので、最終的にはNSStringとして保存する形になりました。詳しくはソースで。

    Sleipnizerで使っているAllAroundPullViewを公開しました。

    タイトル通り、Sleipnizerで使っているPull-To-Gestureの部分のコードをgithubで公開しました。特徴はSleipnizerで使えるように4方向に対応している事です。branchのsleipnizerがまさに使ってる状態です。

    https://github.com/r-plus/AllAroundPullView

    使い方の詳細はgithubのREADMEを参照。

    chpwnのPullToRefreshViewを元に以下な感じの代物です。

  • 色々と微妙な実装だったので修正(大分不親切な実装だった)
  • 4方向に対応
  • コード実行部分をdelegateからBlocksに変更
  • LabelとLastRefreshDateを削除
  • XcodeのDemoプロジェクトの追加
  • tweakで使えるようにARC非対応!非対応!
  • kCFCoreFoundationVersionNumberのススメ

    iOSのバージョンを判定するのには二通り程あって、UIDeviceから取得する方法

    float iOSVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
    と、kCFCoreFoundationVersionNumberを使うやり方があります。
    kCFCoreFoundationVersionNumber >= 550.52 //550.52はiOS 4.2の値

    ちなみにfloatValue使うと小数点2個は取れないので5.1.1は5.100000になります。kCFCoreFoundationVersionNumberも3項目のバージョンでの値に変更はないため、3項目までちゃんと取る場合はNSNumericSearchできちんと比較する必要があります。

    で、tweakのバージョン判定では2項目までで十分な事が多いのでkCF~の方がオススメです。うっかりspringboardが対象のtweakのconstructorでUIDevice使うと危険なので。UIDeviceを使ってはいませんが、iOS5が出た当初に発現していた画面1/4問題は、例えばLastAppでは以下のバージョン判定の1行が原因で起こっていました。

    isFirmware3x_ = (class_getInstanceMethod(objc_getClass("SBApplication"), @selector(pid)) != NULL);
    これを以下に書き換えることで対処したと思われます。
    isFormware3x_ = (kCFCoreFoundationVersionNumber < 550.32) ? YES : NO; // 550.32はiOS 4.0の数字

    新OSが出た際の確認方法は/System/Library/Frameworks/CoreFoundation.framework/Info.plistのCFBundleVersionから確認できます。iOS5.1.xは690.10ですね。

    2.0からの番号はこのあたりを参考に。chpwn / IconSupport - Firmware.h

    What is Tweak?



    ■What is Tweak?

    「CydiaでよくダウンロードするTweakとは何なのか?」について割りと詳しく解説してみようと思う。

    様々な便利な機能の追加や、細かい変更をOSに施すTweakは単語であるTweakの意味「ちょっとした調整」から来ている。ソフトウェアの既存動作にちょっとした調整を加える目的のソフトウェアをさす。

    では好みの動作をさせるにはどうしたらいいか。
    端的に言えばある処理をフックして好みの処理に置き換える事で実現出来る。

    フックを考えるにあたってまず、そのソフトウェアの動作を考えよう。
    iPhoneひいてはOS Xのソフトウェアは専らObjective-Cで記述されている。
    Objective-Cは非常に動的な言語で、コンパイル時点であまり警告・エラーを出さず実行時にうまいこと動けばOKって感じの言語だ。

    オブジェクト指向言語であり、クラスとメソッドとインスタンス変数があってカプセル化されているように見えるが、C言語が皮をかぶっているだけで中でランタイムがゴリゴリ動いてる印象だ。

    メソッドもC関数に皮を数枚かぶせた感じの実装で、実態はIMP型という関数ポインタだ。

    更にランタイムが非常に強力で、カプセル化されていながら、見ようと思えば何でも見れるようになっていて実にフックしやすい言語なんだ。

    フックの仕方には何種類か方法があって、木下さんの連載を参考にすると
    あたりが挙げられる。詳しくは連載の方が面白いのでそちらを見ていただくとして、簡単で便利なのはIMPの入れ替えだ。
    IMP変数を取得するための関数もランタイムで提供されているため、実に簡単にフックが行える。

    ■例を見てみよう!

    - (BOOL)shouldShowDictationKey;というインスタンスメソッドがUIKeyboardLayoutStarクラスに存在する。これは音声入力キーを表示すべきかどうかYESかNO(BOOL値)で返すメソッドだ。
    iOSの音声入力はSiriが可能な言語と今のところ同じであるので、使用中のキーボードがそれに該当するかどうかを判定してYESかNOを返していると想像がつくと思う。

    このメソッドを書き換えて常にYESやNOを返す処理に変えてしまえば、あのマイクキーが常に出るようになったり、常に表示されなくなる事も想像がつくだろう。 このメソッドをフックするには以下のようなコードで実現できる。
    IMP old, new;
    old = class_getMethodImplementation([UIKeyboardLayoutStar class], @selector(shouldShowDictationKey));
    new = class_getMethodImplementation([UIKeyboardLayoutStar class], @selector(new_shouldShowDictationKey));
    
    IMP tmp;
    tmp = old;
    old = new;
    new = tmp;
    
    オリジナルのメソッドを使う気がなければ最後のところは
    old = new;
    だけで事足りてしまう。 また、method_exchangeImplementationsなんて素敵な関数もあるのでこうも書ける。
    Method old, new;
    old = class_getInstanceMethod([UIKeyboardLayoutStar class], @selector(shouldShowDictationKey));
    new = class_getInstanceMethod([UIKeyboardLayoutStar class], @selector(new_shouldShowDictationKey));
    
    method_exchangeImplementations(old, new);
    いずれにせよ、これらの処理を行えば以後OSやアプリケーションがshouldShowDIctationKeyメソッドを呼び出すと、実際に処理が走るコードは自らが書いたnew_shouldShowDictationKeyメソッドのもととなる。 ちなみにnew_shouldShowDictationKeyは追加しなければならないので、class_addMethod関数で追加できる。
    static BOOL replaced_UIKeyboardLayoutStar_shouldShowDictationKey(UIKeyboardLayoutStar *self, SEL _cmd) { return NO; }
    
    class_addMethod([UIKeyboardLayoutStar class],
        @selector(new_shouldShowDictationKey),
        &replaced_UIKeyboardLayoutStar_shouldShowDictationKey,
        "c@:");
    
    メソッドの実装であるIMPを上書きするだけでも書き方は色々ある。
    Method method = class_getMethodImplementation([UIKeyboardLayoutStar class], @selector(shouldShowDictationKey));
    static BOOL replaced_UIKeyboardLayoutStar_shouldShowDictationKey(UIKeyboardLayoutStar *self, SEL _cmd) { return NO; }
    
    method_setImplementation(method, &replaced_UIKeyboardLayoutStar_shouldShowDictationKey);
    
    さて、これらの処理を適当な関数にまとめて、その関数を呼び出せば晴れてフックが完了するが、元々は存在しない関数だ。一体どうやって呼べばいいのか。
    そもそもこのコードを任意のタイミングで読みこませる(ロード)にはどうしたらいいのか。

    まず、動的なコードのロードはライブラリが同様の形態を持っているため、これを読み込むための環境が配備されている。OS XのサブセットであるiOSではdyld(Linuxならld-linux.so)によって読み込まれる.dylib形式(Linuxで言えば.so、Windowsなら.dll)にコンパイルしてdyldに読み込んでもらえばいい。

    次に関数の呼び出しだがアプリケーション側が呼んでくれるはずもないので、ロードされたタイミングで実行する関数をdyldに指定するオプションがある。これはgccの-initオプションの引数に関数名を記述すればいい。このオプションはDarwin linker(dyldのことだ)に引き渡される。man dyldでマニュアルを読むとこのオプションで指定された関数はdyldによって実行される事が書いてある。
    そこには__attribute__((constructor))が指定されたものも実行する事が記述されている。

    これらをまとめると、このTweakは以下のようなコードになる。

    このコードを動的にロードできるようdylib形式でコンパイルしたファイルがTweakそのものだ。

    この例だとメソッドの置換ではなく上書きにしたため、わりと短いコードですんでいるが、置換するとなると行うべき処理は多くなっていき、フックするメソッドが増えていくととても長いコードになっていく。そこで、このランタイムによる実装の置き換えを便利な関数でまとめてくれたライブラリを提供してくれるパッケージがCydiaにはある。そう、MobileSubstrateだ。

    ■MobileSubstrate

    MobileSubstrateの提供する関数はメソッドの置換や関数の置換、インスタンス変数の取得を行える関数を提供してくれている。
    では上記のコードをMobileSubstrateの一般的な関数を使用した形にすると、以下の様になる。

    種々のTweakが一様にMobileSubstrateをパッケージの依存関係に指定しているのは、MobileSubstrateが提供する関数を使用しているのもあるが、他にも重要な機能をMobileSubstrateは担っている。 ざっと以下のような構成だ。
    • MSHook系関数を提供するライブラリlibsubstrate.dylib
    • TweakをロードするSubstrateLoader.dylib
    • plistによるTweakのロードを制御(SubstrateLoader)
    • DYLD_INSERT_LIBRARIES環境変数へのMobileLoader.dylib(MobileSubstrate->SubstrateInjection->SubstrateBootstrap->SubstrateLoader)の追加
    • DYLD_INSERT_LIBRARIESを元に戻すMobileSafety
    この中で、TweakをロードするSubstrateLoaderが最重要機能といってもいいだろう。
    これはDynamicLibrariesディレクトリ以下のdylibを同名のplistの内容からロードの可否を判定してロードしてくれる。特定のアプリケーションにのみフックを適応させたい場合、このplistに条件を記述しておくことでコード本体にそのif文をかかずにすむようになっている。これがないと各Tweakは自分で自分のコードをロードする為のコードをかかなくてはならなくなってしまう。

    TweakをロードするのがSubstrateLoaderなら、SubstrateLoaderはどのようにロードされているのだろうか。SubstrateLoaderだって勝手に書かれた関数でしかないのだ。これにはDYLD_INSERT_LIBRARIESという環境変数へdylibを指定する事でdyldがロードするdylibの一つに加える事で実現している。

    DYLD_INSERT_LIBRARIESはLinuxでいうところのLD_PRELOADに非常によく似ていて、テストのために一時的に使用するライブラリのバージョンを違うものを指定したい時などに使われるものだ。LD_PRELOADを使ったフックについてはこちらのページ(LD_PRELOADを使ったテスト(C言語編))が非常にわかりやすく、行なっている内容もiOSのTweakと非常に似ている。そんなに長くないので是非読んでみてほしい。

    現在のMobileSubstrateはMobileSubstrate.dylibをDYLD_INSERT_LIBRARIESに追加している。/Library/MobileSubstrate/MobileSubstrate.dylibは/Library/Framework/CydiaSubstrate.framework/Libraries/SubstrateInjection.dylibへのシンボリックリンクとなっていて、SubstrateInjection.dylibは同ディレクトリのSubstrateBootstrap.dylibへのシンボリックリンクとなっている。
    SubstrateBootstrapからSubstrateLoader.dylibが読み込まれ、各種Tweakが読み込まれる流れだ。

    最後にMobileSafetyについて。SpringboardをクラッシュさせるTweakをインストールしてしまった場合、無限にRespringする事態におちいるのを防ぐためにTweakの導火線であるSubstrateLoader.dylibをロードするDYLD_INSERT_LIBRARIES環境変数を元の状態に戻した上でSpringboardを起動する役目を担っている。これが俗にいうSafe Modeだ。Tweakのロードを行わないだけなので、Cydiaからインストールしたフックを行わないアプリケーション(iFileとか)は普通に起動できる。SpringboardのクラッシュまでならMobileSafetyで対応できますが、Rebootループになるようなものには無力なので、最新のMobileSubstrateでは音量アップボタンを押しながら起動する事でTweakはおろかMobileSafetyもロードせずに起動できるようになりました。この場合もやっぱりiFileとかは動かせます。

    ちなみにOS XではSIMBLというLoaderが有名だ。MSHook系の関数を使用する前のようなコードを書く事でOS X向けのTweakも作成できる。

    ■theos

    最後にtheosについて。theosはiOS向けのMakefileプラットフォームで、長ったらしいMakefileを非常に簡素化できます。また、詳細は過去の記事を参照として割愛しますが、Tweak向けにマクロを用意してあり、直感的に記述できるようになります。
    上記の例示コードでいくとtheosを使用するとたった3行で記述できてしまいます。簡単だね!:)

    %hook UIKeyboardLayoutStar
    - (BOOL)shouldShowDictationKey { return NO; }
    %end

    ■終わりに、Jailbreak developerが増える事を願って。

    iOSのTweakのソースコードの多くはgithubで公開されています。
    記事上部で長々と書いたコードもtheosを使えば簡単に書けますので、「ここの動きが気に食わない」と感じるところがあれば、是非チャレンジしてみてはどうでしょうか。私も最初は大学でのC言語の授業(ポインタぐらいまで)程度の知識からスタートしましたので、そんなに難しくはないと思います。
    何より自分の作ったものが動くというのは楽しいものですよ?

    フリックキーボードの音声入力キーを無効にするNoDictation

    iOS5.1からSiriが日本語に対応した事で、日本語キーボードでも音声入力が使えるようになりましたが
    あまりの邪魔っぷりにこのキーを無効にするtweakを作りました。Require: iOS 5.1+です。

    日本語フリックキーボードの場合だけ無効にするバージョンと全てのキーボードで無効にするバージョンを作りました。
     全てのキーボードで無効にするバージョンはBigBoss提出予定です。

    https://github.com/r-plus/NoDictation/downloads

    ちなみにSiriが要らないのならSiriをOFFにすれば純正でこのボタンは無効です。
    iPadは設定から音声入力のON/OFFが純正で可能です。

    Bylineのキャッシュが終わったらLocal Notificationを発行する

    ■BylineTweetFormatter v0.0.3

    BylineTweetFormatterを修正して、Sylfeedのように同期 キャッシュが終わったタイミングでLocal Notificationを出すようにしてみました。
    Local Notificationなのでフォアグラウンドの場合は出ません。
    加えてキャッシュ完了数で判定しているため、1つもキャッシュしない設定や同期の場合通知されません。


    https://github.com/r-plus/BylineTweetFormatter

    DictGoogle v0.3 スワイプで辞書画面を終了

    普段左手持ちで左の親指で全て操作してるのですが、この持ち方だと画面右上がちょっと遠い。
    そこそこの頻度で利用するDefineメニューのDoneボタンを押すのがそろそろ面倒になったので、
    左右どちらかのスワイプで破棄できるようにDictGoogleに機能追加しました。

    モーダルビューの破棄に限定したので、iPadではスワイプしても意味はないです。