seekpoint

devlog

Respring via Posix_spawn

iOS 8からsystem()がdeprecatedになりました。

error: 'system' is deprecated: first deprecated in iOS 8.0 - Use posix_spawn APIs instead.

で、posix_spawn()使えと。こっちもiOS 2から使えるようなのでこの際バージョン切り分けではなくこっちを使うのがいいでしょう。 といっても設定からRespringするぐらいにしか使わないのでエラーハンドリングとか軒並みNULLで放棄してこんな感じで。

#include <spawn.h> 
...
char *command[] = {
    "killall",
    "SpringBoard",
    NULL
};
posix_spawn(NULL, command[0], NULL, NULL, command, NULL);

Weak Link to Library.

activatorへのリンクをOptionalにしてdlopenエラーが出ないようにする時のフラグ。フレームワークの場合に利用する-weak_frameworkの場合はフルパスは必要ないが、ライブラリの場合はフルパスを指定する必要がある。

XXX_LDFLAGS = -weak_library $(THEOS)/lib/libactivator.dylib

なお、PATHはldがちゃんと/usr/libにしてくれます。

内部でリンクされているかどうかはこんな感じで

if (%c(LAActivator)) {
    // code.
}

Private Function Test on Cycript.

1
2
3
4
5
6
7
i6:~ root# cycript -p profiled
cy# a = dlsym(RTLD_DEFAULT, "MKBDeviceGetGracePeriod")
0x18c9d1b64
cy# b=new Functor(a,'@')
0x18c9d1b64
cy# b()
@{"GracePeriod":2147483647}

IME変換中文字に対応したSwipeShiftCaret 1.6.4

SSC

SwipeShiftCaret v1.6.4はv0.1の頃からの課題だったIME変換中文字の移動に対応しました。(iOS 7+ですが)

なお、変換中の文字付近をタップした際に出現する拡大ビューが表示されている場合は、iOSデフォルトの動作を優先します。(仕様)

UIKitターゲットにおける厳密なApplication判定

全アプリ向けにtweakを作成する時は慣例としてcom.apple.UIKitを指定するが、UIKitフレームワークをリンクしているバイナリは/bin, /sbin, /usr/libexec配下などにもある。

これを厳密にアプリケーションのみをターゲットにするなら下記のコードで%initを行うかどうかを制御すれば良い。

NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
if ([bundlePath hasSuffix:@".app"]) {
    // for application code.
    %init;
}

Available in iOS 2.0 and later.

PostNotification With Object

DarwinNotifyCenter

PreferenceLoaderからtweakの設定変更時の変更通知としてスタンダードになっているCFNotificationCenterのDarwinNotifyCenterですが、これはシステムグローバル用の通知として設計されており、

void CFNotificationCenterPostNotification(CFNotificationCenterRef center,
        CFStringRef name,
        const void *object,
        CFDictionaryRef userInfo,
        Boolean deliverImmediately);

CFNotificationCenterPostNotificationにおける引数object, userinfo, deliverimmediatelyは無視されNULLになります。であれば、有効な引数はnameだけなので、uint32_t notify_post(const char *name);はcenterをDarwinでnameを引数に取りその他をNULLにした便利関数です。

通常であれば何か変更が入ったら全て設定値をロードしなおせば良いが、極々まれにそれでは都合が悪い場合(パフォーマンスとか)があります。何の設定が変更されたかをPreferenceBundleとして得る事はセッターを設定すると得られますが、それを対象のプロセスに伝える方法としてIPCをするのは大げさでちょいと面倒でした。

DistributedCenter

CFNotificationCenterRefには3種類あり、Darwin, Local, Distributedの3種類があります。このうち、DistributedはiOSではimportされない記述になっています。

#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) || TARGET_OS_WIN32
CF_EXPORT CFNotificationCenterRef CFNotificationCenterGetDistributedCenter(void);
#endif

が、iOS 5からいつのまにか内部的には実装されていました。 サンプルコードはdev wikiに書きました。 Cloakyは1.6.3からこれを使用してステータスバーアイコンのON/OFF設定を検出しています。

Tip of Depends in Debian Package

iPhoneとiPod touch向けにはiOS 4.2以上の全てをサポートして、iPadではiOS 4.2以上5.0未満をサポートする時(RestoreTabがこれ)のDependsの書き方。

firmware (>= 4.2), cy+model.iphone | cy+model.ipod | firmware (<< 5.0) 
  • firmware (>= 4.2)
    これは至極一般的ですね。全てのデバイスに対してiOS 4.2以上を要求します。

  • cy+model.iphone | cy+model.ipod | firmware (<< 5.0)
    |のOR演算子でつないでいます。cy+modelはiphone, ipod, ipadそれぞれが存在します。ORは先頭から評価されていくため以下のようになります。

    • iPhoneであればTRUE
    • iPhoneではないが、iPodであればTRUE
    • iPhoneでもなく、iPodでもないが(== iPad)、iOS 5.0未満であればTRUE
    • iPadでiOS 5以上だとFALSE

参考

iOS 7からRocketBootstrapが必要になったAppListの場合

com.rpetrich.rocketbootstrap (>= 1.0) | firmware (<< 7.0)

Sleipnizer v4.1 Overview

全Changelogはこちらを見ていただくとして、4.1の目玉について。

  • Multiple Javascript action definition.
    かなり前から要望はいただいていた機能で、JavaScriptの実行アクションをユーザが自由に追加できます。増やした分は「Run JavaScript n」の形で表現されます。

  • onLoad Javascript execution.
    ページのロード完了時に指定したJavaScriptを実行します。前段の機能で複数指定が可能になった為、こちらも複数のJSを指定できます。私はAutoPagerizeのJSを設定してます。

  • Flipswitch can invoke alternate-action-url.
    特有というわけではないが、Flipswitchのalternate-action-urlにSleipnizerの設定を開くPATHを追加しました。CCTogglesのトグルを長押しすると起動できます。地味に便利。

  • Gesture系の設定をまとめて1階層下へ
    “L”, “U”そしてGlitchy GestureとSensitivityをまとめて1階層下へ移動。

その他、バグ修正等。たぶんこの実装を行った事で「Run JavaScript n」じゃなくて名前にしてくれと要望がでると思いますが、いまのところはペンディングで。

AllAroundPullViewの後継、AAPullToRefreshを作成しました

1年ほど前にSleipnizerにPullToによるアクションを搭載するためにAllAroundPullViewを開発し組み込んだ上でOSSで公開していました。

その後iOS 6でApple実装によるUIRefreshControlが提供され、iOS 7で見た目が大幅に変わったためiOS 7向けのSleipnizerにおいてもなんか変えられたらなーと思っていたところに見た目素晴らしいライブラリが公開されました。

それがこちら UzysCircularProgressPullToRefresh

Pinterest風でなかなか良いぞと思ったので、forkして以下のあたりの変更を施して出来上がったのがAAPullToRefreshです!

  • 見つけたバグっぽいのつぶし
  • 4方向同時に追加可能なように修正
  • トリガー状態で裏Viewを発光させる
  • Sleipnizer向けにthresholdプロパティの追加

AAPullToRefresh

パッと見の見た目変化に加えて、指を離してからビューのスクロールが止まるまでとそこからインジケーターがしぼんで消えつつ通常位置までビューが戻るあたりの滑らかなアニメーションがAllAroundPullViewに比べて気持ち良いものになってます。

多分iOS 7向けのSleipnizerに搭載されますのでご期待あれ。

File Extension for Theos Logo.

theosのLogo用拡張子は種類がいくつかあるのでその話。

theosって?

theosはiOS/Mac向けのMakefileフレームワークだ。Makefileからincludeしてあげれば苦行のようなMakefileの記述からある程度解放してくれる。

Logo

どちらかと言えばtheosはビルド過程にこの処理を含ませるためのMakefileという側面が強いが、MobileSubstrate向けのswizzleコードの記述を楽に書けるよう変換も行わせる事ができる。 その際の拡張子が以下だ。

  • .x
  • .xm
  • .xi
  • .xmi

この拡張子の違いについて説明しよう。まず一番一般的な.xmだが、これはLogo変換処理を行い.mmファイルを生成し、Objective-C++としてプリプロセス、コンパイル、リンクを行う。 次に.xはそれのObjective-C版だ。全てに言える事だがLogoの変換処理に違いはない。オリジナルのマクロによってObjc/Objc++で普通にswizzleを書くのと同じ状態へ持っていくのだから至極当然の処理順だ。

最後にそれらにiがついたものだ、これは真っ先にobjc/objc++プリプロセスが行われる。その後にLogo処理、コンパイル、リンクとなる。そこそこコードを書いていると以下のように書きたいと思うこともあるだろう。

#define SHARED_INSTANCE [%c(SBProcess) sharedInstance]

%cマクロはLogo処理向けであるのでプリプロセスに放り込むとエラーになる。このレベルであればobjc_getClassで代用がきくが以下のようなケースはそうもいかない。

%hook UIKBKey 
- (NSString *)displayString {
    NSString *result = %orig();
    return [self.name rangeOfString:@"-Small-"].location != NSNotFound ? [result lowercaseString] : result;
}
%end

%hook UIKBTree
- (NSString *)displayString {
    NSString *result = %orig();
    return [self.name rangeOfString:@"-Small-"].location != NSNotFound ? [result lowercaseString] : result;
%end

処理内容が同じなので変更は一度ですむようにすべきだが、関数にまとめても%orig%hookの外では使用できない。 プリプロセスが先に走る.xi, .xmiではdefineにLogoを書いておく事が可能になるのでdefineでまとめられる。

#define HONOR_CASE \
    NSString *result = %orig(); \
    return [self.name rangeOfString:@"-Small-"].location != NSNotFound ? [result lowercaseString] : result;

%hook UIKBKey
- (NSString *)displayString { HONOR_CASE }
%end

%hook UIKBTree
- (NSString *)displayString { HONOR_CASE }
%end

めでたく修正時には一度の変更ですむようにできた。
コード出典: ShowCase – github/ashikase

まとめ

extension process order
.x Logo –> objc preprocess+compile –> link
.xm Logo –> objc++ preprocess+compile –> link
.xi objc preprocess –> Logo –> objc compile –> link
.xmi objc++ preprocess –> Logo –> objc++ compile –> link

なおこの表はiphonedevwikiにも記述しておいた。