2015/06/07

CloudantDB

NoSQL系のDBが増えてきて最近 CloudantDB触る機会があって面白いと思ったのでここでメモを書くことにした。 まだまだわからないことだらけなのでこれをまたアップデートしていきたい。

CloudantDBとSQL系DBの違い

  • SQL系:
    一つのDBの中に複数のテーブルがありえる。テーブルの中にデータがある。テーブルにはスキーマが必須なのでデータに型がある。 検索するにはSQL Queryを書いて実行

  • CloudantDB:
    一つのDBの中にJSONドキュメントがある。スキーマがない。それぞれのドキュメントに入っているデータの形式が全然違うことがありえる(しかしJSONである必要がある)。 検索するにはsearch indexが必要。search indexを自分で定義する。クエリーで使えるパラメーターはsearch indexの内容で決まる。そのクエリーはCloudant Queryという。そしてCloudant QueryがとLucene Queryベースだそうです。

登録

まず登録しアカウント名とパスワードを取得し試しに幾つかのDBを作る。


簡単なAPIを叩く

簡単の為にターミナルでcurlを使う。そして、毎回認証が必要だから次の変数を定義する。(本当はcurlの-uオプションで毎回自分のパスワードを入力した方がhistoryに残らなくてセキュリティ的にいいけど簡単のためだから)
CLOUDANT='https://アカウント名:パスワード@アカウント名.cloudant.com'

DBの一覧を取得

これは一番簡単なAPIで上記のアカウント名とパスワードを試せる。
curl ${CLOUDANT}/_all_dbs
["_users","big","myanswers"]

テスト用のデータベースの用意

car_answersというデータベース次の5つのドキュメントを用意する。 Cloudantのコンソルを使って5つのドキュメントを作ることができるが、折角なのでここでAPIで作る
  1. まず、データベースの作成
    curl ${CLOUDANT}/car_answers -X PUT
    
    {"ok":true}
    
  2. 次にアップロードするドキュメントの配列を用意
    json='{
        "docs": [
            {
                "_id": "1",
                "created_at": "2015-05-15 00:00:00",
                "maker": "Mercedes Benz",
                "モデル": "A 180",
                "answers": {
                    "評価": "☆☆☆",
                    "その他": "特になし"
                }
            },
            {
              "_id": "2",
              "created_at": "2015-05-16 00:00:00",
              "maker": "Toyota",
              "モデル": "レクサス",
              "answers": {
                  "評価":"☆☆☆☆",
                  "その他":"よかった"    
              }
            },
            {
              "_id": "3",
              "created_at": "2015-05-17 00:00:00",
              "maker": "Honda",
              "モデル": "フィット",
              "answers": {
                  "評価":"☆☆☆☆☆",
                  "その他":"すぐ購入したいと思います"    
              }
            },
            {
              "_id": "4",
              "created_at": "2015-05-18 00:00:00",
              "maker": "Mercedes Benz",
              "モデル": "E 250 CABRIOLET",
              "answers": {
                  "評価":"☆☆",
                  "その他":"イメージしていたものと少し違った"    
              }
            },
            {
              "_id": "5",
              "other": "他とちがうもの"
            }
        ]
    }'
    
  3. 最後に用意されたJSON配列をバルクAPIで一気にアップロード
    curl ${CLOUDANT}/car_answers/_bulk_docs -X POST -H "Content-Type: application/json" -d "$json"
    
    [{"id":"1","rev":"1-a408ef0c09d5b0aaec96b87e04e049de"},{"id":"2","rev":"1-615734de2db3b4717544f2dfd0e01bec"},{"id":"3","rev":"1-fd7d17addf8149d0c522368e69bda27c"},{"id":"4","rev":"1-d0be948351c84706c5febea1a1622700"},{"id":"5","rev":"1-4d8829cf3843c4d7a0b623bcced3a620"}]
    

ドキュメントの列挙

例えば、car_answersというデータベースにある全てのドキュメントを取得するには_all_docsを使って取れる
curl ${CLOUDANT}/car_answers/_all_docs
{"total_rows":5,"offset":0,"rows":[
{"id":"1","key":"1","value":{"rev":"1-a408ef0c09d5b0aaec96b87e04e049de"}},
{"id":"2","key":"2","value":{"rev":"1-615734de2db3b4717544f2dfd0e01bec"}},
{"id":"3","key":"3","value":{"rev":"1-fd7d17addf8149d0c522368e69bda27c"}},
{"id":"4","key":"4","value":{"rev":"1-d0be948351c84706c5febea1a1622700"}},
{"id":"5","key":"5","value":{"rev":"1-4d8829cf3843c4d7a0b623bcced3a620"}}
]}
デフォルトでは、結果のrowsにidだけ渡される。ドキュメントその物を含んで欲しいときはinclude_docsフラグでできる
curl ${CLOUDANT}/car_answers/_all_docs?include_docs=true
デフォルトでは一つのリクエスト最大取れるドキュメントの数は25でそれを200まで変更することができる
curl ${CLOUDANT}/car_answers/_all_docs?include_docs=true&limit=200
詳細はガイドを参照

ドキュメントをidで検索

なんだかの方法でドキュメントのidさえわかっていればそのドキュメントを簡単に取得できる。例えば、car_answersidが3のドキュメントを取る:
curl ${CLOUDANT}/car_answers/3
{"_id":"3","_rev":"1-fd7d17addf8149d0c522368e69bda27c","created_at":"2015-05-17 00:00:00","maker":"Honda","モデル":"フィット","answers":{"評価":"☆☆☆☆☆","その他":"すぐ購入したいと思います"}}

ドキュメントを条件を指定して検索

インデックスとは

いきなりインデックスが出ると思うかもしれないけど、条件を指定して検索を実現するのは検索インデックスである。データベースに入っているドキュメントは全て同じフォーマットである制約がないから検索インデックスで検索で見つかりたい見つかりたくないドキュメントをフィルタリングすることができる。
検索インデックスは必ずデザインドキュメントの中にある。デザインドキュメントの中に複数の検索インデックスがありえる。

インデックスの作成

まずはCloudantDBのコンソルで作る。car_answersというデータベースを選択しAll Design Docsの➕ボタンを選択しNew Search Indexを選ぶ。そしてCreate Search Indexという画面が表示される。


Create Search Indexの入力フィルドの説明

  • Save to Design Document
    どこのデザインドキュメントに保存するかを選択できるプルダウンメニューがあるが、一個もないときにデザインドキュメント作成用の入力フィルドも表示される。全てのデザインドキュメントが"_design/"で始まるのでそれは固定となっている
  • Index name
    検索インデックスの名前
  • Search index function
    これはインデックス化するに使う関数。入力としてはド(現在のデータベースの)キュメントが渡される。出力はないけどインデックス化したいドキュメントとそのパラメーターをindex(...)でインデックスに入れる。
    function (doc) {
        index("brand", doc.maker);
    }
    
    後で様々な例でもっと説明したいと思う。
  • Analyzer
    インデックスに入れたテキスト(この場合はdoc.maker)をどういう風に区切って扱うかを決めるのはアナライザーだ。多分Tokenizerという言い方をする方がすぐわかりやすい
    例えばドキュメント1を処理した場合は"Mercedes Benz"というストリングがインデックスされる。"Mercedes Benz"を一つのストリングとして扱う("Keyword")か英語の文法を使って単語で分けるか日本語の文法で分けるかUnicode Text Segmentationアルゴリズム("Standard")で分けるかなど。アナライザーによって検索でドキュメントがヒットするかしないか変わる。より詳細は[Cloudant/For Developers/Search Indexes/Analyzers]を参照。
    関数ごとに一個のアナライザーを使う場合はSingle。インデックス化されるパラメーターごとにアナライザーを使う場合はMultipleを選択。

インデックスを使って検索

作ったインデックス関数で検索してみよう。
curl ${CLOUDANT}/car_answers/_design/search/_search/car?q=brand:Honda
{"total_rows":1,"bookmark":"g2wAAAABaANkAB5kYmNvcmVAZGI4LmlibTAwOS5jbG91ZGFudC5uZXRsAAAAAmJgAAAAYn____9qaAJGP9OjegAAAABhAGo","rows":[{"id":"3","order":[0.3068528175354004,0],"fields":{}}]}
URL部意味
 car_answers データベースの名前
 _design/search デザインドキュメントのID
 _search 検索時に使う決まり
 car 検索インデックスの名前
 q CloudantQuery用のパラメーター
 brand:"Honda" CloudantQueryそのもの

他に試してみる
curl ${CLOUDANT}/car_answers/_design/search/_search/car?q=brand:Mercedes%20Benz
{"total_rows":2,"bookmark":"g1AAAACOeJzLYWBgYMpgTmGQS0lKzi9KdUhJMtXLTMo1MLDUS87JL01JzCvRy0styQGpy2MBkgwNQOr____zszKY3OznnJMDiSUyoJphQcCMBxAz_qOakQUAaqAqIg","rows":[{"id":"1","order":[0.028130024671554565,0],"fields":{}},{"id":"4","order":[0.028130024671554565,0],"fields":{}}]}
見つかった!ドキュメント1と4が返ってきている!
_searchでもinclude_docsオプションも使えるしlimitも使える。しかしbashなので&などをエンコードしないといけないしその次にURLエンコードしないといけないしまだ使っていないけどCloudantQueryの中でエスケープしないといけないものもあるのでこれから-vオプションを使う。実際に行われたリクエストがわかるから。
curl -v ${CLOUDANT}/car_answers/_design/search/_search/car?q=brand:Mercedes%20Benz\&limit=1\&include_docs=true
> GET /car_answers/_design/search/_search/car?q=brand:Mercedes%20Benz&limit=1&include_docs=true HTTP/1.1
...
{"total_rows":2,"bookmark":"g2wAAAABaANkAB5kYmNvcmVAZGI1LmlibTAwOS5jbG91ZGFudC5uZXRsAAAAAm4EAAAAAIBuBAD___-famgCRj-czh4AAAAAYQBq","rows":[{"id":"1","order":[0.028130024671554565,0],"fields":{},"doc":{"_id":"1","_rev":"1-a408ef0c09d5b0aaec96b87e04e049de","created_at":"2015-05-15 00:00:00","maker":"Mercedes Benz","モデル":"A 180","answers":{"評価":"☆☆☆","その他":"特になし"}}}]}
定義された検索インデックス関数ではこれ以上大したものができないのでもうすこし拡張していく。

より複雑なインデックスを使って検索

同じデザインドキュメントの中で新しい検索インデックスcar2を作る
function (doc) {
    if (typeof doc.maker === 'undefined') {
        return;
    }
    if (doc.created_at) {
        index("created_at", doc.created_at, {"store": true});
    }
    if (doc["モデル"]) {
        index("model", doc["モデル"], {"store": true});
    }
    if (doc.answers && doc.answers["評価"] && doc.answers["その他"]) {
        var stars = doc.answers["評価"];
        index("stars", stars, {"store": true});
        index("stars_num", (stars.match(/☆/g) || []).length);
        index("comment", doc.answers["その他"], {"store": true, "index": false});
    }
}
index関数の第三パラメーターはオプションだったので使っていなかったけど、4つのキーがあって次の表で簡単にまとめてみた。詳細は[Cloudant/For Developers/Search Indexes/Options]を参照
キー意味
 store 検索結果に値を含むかどうか。デフォルトではfalse
 index 検索インデックスに入れるかどうか。デフォルトではtrue
 facet faceting機能をオンにするかどうか。デフォルトではfalse。今回は使わない
 boost 検索結果の中で優先度を上げる係数。デフォルトでは1.0。今回は使わない

いろいろと検索してみる

まずは({"store": true}を付けた)モデルで検索すると
curl -v ${CLOUDANT}/car_answers/_design/search/_search/car2?q=model:CABRIOLET
> GET /car_answers/_design/search/_search/car2?q=model:CABRIOLET HTTP/1.1
...
{"total_rows":1,"bookmark":"g2wAAAABaANkAB5kYmNvcmVAZGI4LmlibTAwOS5jbG91ZGFudC5uZXRsAAAAAm4EAAAAAOBuBAD_____amgCRj_Do3oAAAAAYQBq","rows":[{"id":"4","order":[0.1534264087677002,0],"fields":{"stars":"★★","model":"E 250 CABRIOLET","comment":"イメージしていたものと少し違った","created_at":"2015-05-18 00:00:00"}}]}
今までと違ってより詳細な情報を取得していることがわかる。それはstore:trueのお陰。これでinclude_docsを使わずに欲しい情報だけを取ることができる。

TODO:
* より複雑な関数を使った検索index:falseなどについてかく
* より複雑なCloudantQueryで検索 ranges, [], (), AND, OR, などについてかく
* CloudantQueryでエスケープ \/など


2015/05/17

Cloudant basics

Find a document by id: I want to look for an design document which name is 'search'. So the id of the document would be '_design/search' Search the document using the primary index with a parameter:
$ curl -u accountame:password 'accountname.cloudant.com/qdef/_all_docs?key="_design/search"&include_docs=true'
{"total_rows":5,"offset":3,"rows":[
{"id":"_design/search","key":"_design/search","value":{"rev":"14-1826c1455da23e06b926e9a29ac9ec63"},"doc":{"_id":"_design/search","_rev":"14-1826c1455da23e06b926e9a29ac9ec63","views":{"names":{"map":"function(doc) {...}"}}}}
]}
Search the document with its id directly:
$ curl -u accountame:password 'accountname.cloudant.com/qdef/_design/search'

{"_id":"_design/search","_rev":"14-1826c1455da23e06b926e9a29ac9ec63","views":{"names":{"map":"function(doc) {...}"}},"language":"javascript","indexes":{"doc":{"analyzer":"standard","index":"function (doc) {...}"}}}

Useful Links

Lucene stuff
http://lucene.apache.org/core/4_3_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#Overview
Escaping Lucene Characters using Javascript Regex
http://stackoverflow.com/questions/26431958/escaping-lucene-characters-using-javascript-regex
CouchDB API documentation (Basis of Cloudant)
http://wiki.apache.org/couchdb/HTTP_Document_API
View collation (selecting stuff using views)
http://docs.couchdb.org/en/latest/couchapp/views/collation.html
Cloudant library for nodejs on Github
https://github.com/cloudant/nodejs-cloudant#Cloudantdbgetname-callback
Search with complex keys (this is too advanced for me probably)
https://developer.ibm.com/answers/questions/173495/bluemix-cloudant-nano-how-to-search-on-complex-arr.html
Find documents within a range (me)
https://developer.ibm.com/answers/questions/191004/how-to-search-a-string-whose-value-is-between-2015.html
URL encoder/decoder - Useful tool for working with non ascii strings in below tutorials
http://meyerweb.com/eric/tools/dencoder/

Official IBM Cloudant sites

Cloudant API Documentation. (With very simple examples at the right)
https://docs.cloudant.com/api.html#index-functions
Cloudant.com - Primary Indexes (_all_docs)
https://cloudant.com/for-developers/all_docs/
Cloudant.com - Secondary Indexes (views)
https://cloudant.com/for-developers/views/
Cloudant.com - Search Indexes (Queries)
https://cloudant.com/for-developers/search/
Cloudant.com - Examples. Full Text indexing is quite easy to understand
https://cloudant.com/for-developers/examples/
https://cloudant.com/blog/search-faceting-from-scratch-2/
Cloudant.com - Authentication
https://cloudant.com/for-developers/faq/auth/
Cloudant.com - Introducing Cloudant Query
https://cloudant.com/blog/introducing-cloudant-query/


2015/04/06

Curl + Charles

As this blog post, when using Charles for debugging requests done by cURL:

When using libCurl (programmatically):

Set CURLOPT_PROXY and CURLOPT_PROXYPORT as below:
curl_setopt($ch, CURLOPT_PROXY, "127.0.0.1");
curl_setopt($ch, CURLOPT_PROXYPORT, 8888);

// And to avoid certificate trust errors...
// WARNING: Do not use this in production environments
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

When using curl from the terminal

Option --proxy or its short for -x can be used
curl --proxy 127.0.0.1:8888 'http://my.host.com/api/path?param=value'
For example:
curl \
  -X GET \
  -H 'Authorization: Basic MYTOKEN' \
  --proxy '127.0.0.1:8888' \
  -v \
  'http://my.host.com/api/path?param=value'


2015/03/22

Software development is an engineering discipline

I don't usually post to write thoughts but very objective and technical things however this time is kind of an exception.

On O'reilly Software Architecture - Glenn Vanderburg of LivingSocial on why software development is an engineering discipline.

I couldn't agree more.
Maths don't define an engineering, they are there to economically aid or demonstrate a particular hypothesis. They were introduced after the engineering itself. In Software development it is (economically and timely speaking) cheap to build the product so demonstrating things with maths is not really a must

Software engineering is pragmatic and iterative as many other engineering science. However it is immature.

This cartoon and what follows right after it is what I think is the best moment in the session.


Why is this so funny? It is funny because it is absurd. No body will ever do that.

Why is this so funny? It is funny because it is absurd - No body will ever do that.

Yeah, software developers will never do that ...


2015/02/11

Swift stuff

I started with Swift recently. So this is going to be the place I put all the gotchas, tricks and everything including complains
  • Private functions are private:
    This:
    self.addTarget(self, action: Selector("valueChanged"), forControlEvents:.ValueChanged)
    
    private func valueChanged() { ... }
    
    Leads to: unrecognized selector sent to instance the function must not be private.
    func valueChanged() { ... }
    
  • Downcasting:
    These two are basically equal. (cell is an UITableCellView). In objc there is no problem accessing nil stuff. It is a NOP, has good and bad sides. In swift it must be explicitly declared and accessing nils leads to crashes.
    // objc
    [[cell viewWithTag:102] setText:@”Something”];
    
    //or something syntaxly closer to below swift code
    ((UILabel *)[cell viewWithTag:102]).text = @”Something”;
    
    // swift
    (cell.viewWithTag(102) as? UILabel)?.text = “Something”
    
    // to refer `label` more than once create a variable and access its methods if not nil
    if let label = cell.viewWithTag(102) as? UILabel {
        label.text = “Something”
    }
    


2015/02/03

Change computer name

Just after installing a new version of OS X you just realized you don't like the name of your computer?. No problem just go to Preferences > Sharing and change it:

But then you go to Terminal.app and realized that your prompt still has the old name :(. That is because the default terminal prompt uses the hostname. Doing above things does not change the hostname.

You could change the prompt by modifying $PS1 variable but that will not solve the problem as the kernel hostname would still have the old name.

Change the Hostname

To change it we use sysctl kern.hostname=NEW_NAME
$ hostname
Ignacio-no-MacBook-Pro.local

$ sudo sysctl kern.hostname=IgnacioMBP
kern.hostname: Ignacio-no-MacBook-Pro.local -> IgnacioMBP

$ hostname
Ignacio-MBP.local
To confirm current value we use hostname command. Now we have to restart the terminal so the new hostname is read again populating $PS1 as intended :)


See also:
MDLog:/sysadmin - How to change the hostname of a Linux system


2015/01/17

Setup Go in Windows

Some notes on how to setup the computer to get started with go language. Very simple notes.
  • Installed it via the msi installer from the downloads page
  • Open the command line (cmd.exe) and check everything is fine
    > go version
    go version go1.4 windows/386
    Go installer sets up GOROOT (although I think it is not needed to define it explicitly since the go tool can find it)
    > echo %GOROOT%
    C:\Go\
    
    We can also check other variables
    > go env GOOS
    windows
    
    > go env GOARCH
    386
    

  • The only environment variable that needs to be set is GOPATH

    Add GOPATH as a new User Environment Variable, its value should be somewhere we are going to put all our go code, also it should not be the same as GOROOT .
    > echo %GOPATH%
    C:\Users\GuillermoIgnacio\Documents\go
    
    As explained in the docs, GOPATH directory needs to have 3 directories in it: src, pkg and bin
    cd %GOPATH%
    mkdir src
    mkdir pkg
    mkdir bin
    
  • Optionally, we could add GOPATH\bin to our PATH so we can run executables easier after each time we use the install command: go install something
Done!. Now I am ready to start with hello world or a little library :)


2015/01/12

Cleaning my Mac

The other day I spent like 4 hours removing unneeded stuff from my PC. It took me this long because I had several old files like unused VirtuaBox kernel extensions (I remember installing them when trying to learn some WebOS programming), numerous useless plugins all over the computer, other kinds of extensions, trash, etc. I googled each one of them to make sure I don't delete important things by mistake.
This is a list of things/places to check so in the future I don't have to google everything again. :)
  1. First, EtreCheck is a tool I like it very much because is not offensive or intrusive as Onyx or similar tools, unlike CleanMyMac 2 it has no ads (CleanMyMac is crap). EtreCheck is free, relatively fast and open source.
    EtreCheck attempts to just report the facts, without making any judgements or recommendations. Debugging is not a task that can be automated. It needs humans, ...
    Just run the app and it will tell what extensions, plugins, etc is loaded in the system. These are good places to start to look for.

  2. Login items Go to Preferences > Users & Groups
    Delete all unneeded items from from the list. Example, Dropbox, Google drive. All those things I don't use anymore. I only use OneDrive now :).

    Also read this: Stackexchange - What is the yellow warning sign in the login items tab....

  3. Daemons and/or agents.
    Look for things like google talk, Adobe ancient stuff, etc. Legacy stuff (prior to 10.4)
    /Library/StartupItems
    /System/Library/StartupItems
    
    Items loaded at Mac starts up time. Run as the root user.
    /Library/LaunchDaemons
    /System/Library/LaunchDaemons
    
    Items loaded when any user logs in. Run as that user.
    /Library/LaunchAgents
    /System/Library/LaunchAgents
    
    Items loaded only when ${USER} user logs in. Run as that user.
    ~/Library/LaunchAgents
    
    Useful commands:
    $ launchctl list
    $ launchctl list | grep -v "com.apple"
    
  4. Kernel extensions This places are quite sensitive. Make sure you don't delete unwanted things from here of the entire OS might break.
    /System/Library/Extensions
    /Library/Extensions
    ~/Library/Extensions
    
    Cnet - Increasing system stability by pruning the kernel extension folder

  5. Internet plugins
    Make sure you have read this article:Unsupported third-party add-ons may cause Safari to unexpectedly quit or have performance issues and don't delete Apple stuff incorrectly.
    /Library/Internet Plug-Ins/
    /Library/Input Methods/
    /Library/InputManagers/
    /Library/ScriptingAdditions
    ~/Library/Internet Plug-Ins/
    ~/Library/Input Methods/
    ~/Library/InputManagers/
    ~/Library/ScriptingAdditions
    


2014/09/30

SIMBL Plugins

What is SIMBL ?

As the wiki says, SIMBL is an application enhancement (InputManager bundle) loader for Mac OS X developed by Mike Solomon. Now a days SIMBL is not maintained anymore but there is a new version of it called EasySIMBL, maintained by Nomura-san.
In short, it is a plugin loader for any Cocoa Application in OSX. So, If you have an app you want to hack (lets say add some extra functionality) to an App and that app does not brings Plugin functionality then EasySIMBL is your option.

First Steps

  1. Download and Install EasySIMBL.
  2. Create an Xcode project
    File > New > Project > OS X > Framework & Library > Bundle
  3. Follow further instructions from "Creating A SIMBL Plugin Bundle" from old SIMBL wiki page.
    Basically, make sure your Info.plist have SIMBLTargetApplications with appropriate values in BundleIdentifier, MaxBundleVersion and MinBundleVersion.

    For the more curious, there is an undocumented parameter: RequiredFrameworks. See details here. It says it has never been used but I personally think it could be useful in cases when your plugin requires a non-standard framework which is embedded in the target app. Using this key load your plugin only if the required frameworks exist.
  4. Start by implement load method as explained in the old wiki.
  5. Once you build your plugin you can move it manually onto EasySIMBL and set the Debug level to "Notice + Info + Debug" to see a bit more of information about the loading process of your plugin
  6. Open Console.app and see "All Messages" now open the target App and check what happens...

Debug

Obviously the process of manually copying the .bundle to ~/Library/Application\ Support/SIMBL/Plugins is tedious. We can do it a bit better:

# Copy the product to SIMBL plugins dir
SIMBL_PLUGIN_DIR="${HOME}/Library/Application Support/SIMBL/Plugins"
rm -rf "${SIMBL_PLUGIN_DIR}/${FULL_PRODUCT_NAME}"
cp -r "${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}" "${SIMBL_PLUGIN_DIR}/${FULL_PRODUCT_NAME}"

However, in the end this is not what we want. We want to be able to run the target app with the plugin and be able to set breakpoints, etc!.

Setting Xcode for plugin development

This step is not specific to EasySIMBL. Basically every plugin in OSX can be developed/debbuged in this way:
  1. Tell Xcode where to place product after build.
    In our case the location is: "~/Library/Application Support/SIMBL/Plugins". Do this by editing CONFIGURATION_BUILD_DIR on your target:
    .
    Note that some apps (that support plugins without the need of EasySIMBL) will require the plugin to be in ~/Library/Application Support/TheApp/Plugins/ or sometimes in /Applications/TheApp/Contents/Plugins. Just make sure you have enough access permissions so Xcode can write there (Hint: use chmod if needed).

    For example iPhoto.app
  2. Tell Xcode what application to start on "Run".
    In my case I just created a example plugin for Writer.App (That awesome app!). So I edit my schema. Product > Scheme > Edit Scheme...
  3. Specific to EasySIMBL. EasySIMBL seems not to load correctly the latest version of the plugin on Sandboxed apps. Nomura-san says he is aware of this issue and it can be work-arounded by un-checking and checking the "Use SIMBL" before each time we run our plugin.
  4. This is basically all. This is my 1 cent and comments are welcome :)


2014/08/08

Recent Posts Widget for Blogger

I got a little bored the standard "Blog Archive" widget. So I searched for a plain list of post on the widget site without luck but I found this useful helperblogger page which provided a script that uses below API to get the entire list of posts parse it and show it nicely.
GET https://www.blogger.com/feeds/blogID/posts/default
(Full documentation of Blogger's API can be found here)
So, what I did was to modify the code a bit for visual purposes and then brought it into my code. Just set below code as an "HTML/JS Widget":
<script type="text/javascript">
var numposts = 20;
var showpostdate = false;
var showpostsummary = false;
var numchars = 100;
function showrecentposts(json) {
    document.write('<ul>');
    for (var i = 0; i < numposts; i++) {
        var entry = json.feed.entry[i];
        var posttitle = entry.title.$t;
        var posturl;
        if (i == json.feed.entry.length) break;
        for (var k = 0; k < entry.link.length; k++) {
            if (entry.link[k].rel == 'alternate') {
                posturl = entry.link[k].href;
                break;
            }
        }
        posttitle = posttitle.link(posturl);
        var readmorelink = "»»";
        readmorelink = readmorelink.link(posturl);
        var postdate = entry.published.$t;
        var cdyear = postdate.substring(0, 4);
        var cdmonth = postdate.substring(5, 7);
        var cdday = postdate.substring(8, 10);
        var monthnames = new Array();
        if (showpostdate == true) {
            monthnames[1] = "Jan";
            monthnames[2] = "Feb";
            monthnames[3] = "Mar";
            monthnames[4] = "Apr";
            monthnames[5] = "May";
            monthnames[6] = "Jun";
            monthnames[7] = "Jul";
            monthnames[8] = "Aug";
            monthnames[9] = "Sep";
            monthnames[10] = "Oct";
            monthnames[11] = "Nov";
            monthnames[12] = "Dec";
        }
        if ("content" in entry) {
            var postcontent = entry.content.$t;
        } else if ("summary" in entry) {
            var postcontent = entry.summary.$t;
        } else {
            var postcontent = "";
        }
        var re = /<\S[^>]*>/g;
        postcontent = postcontent.replace(re, "");
        document.write('<li>');
        document.write(posttitle);
        if (showpostdate == true) {
            document.write(' - ' + cdday + ' ' + monthnames[parseInt(cdmonth, 10)] + ' ' + cdyear);
        }
        if (showpostsummary == true) {
            document.write('');
            if (postcontent.length < numchars) {
                document.write('<i>');
                document.write(postcontent);
                document.write('</i>');
            } else {
                document.write('<i>');
                postcontent = postcontent.substring(0, numchars);
                var quoteEnd = postcontent.lastIndexOf(" ");
                postcontent = postcontent.substring(0, quoteEnd);
                document.write(postcontent + '... ' + readmorelink);
                document.write('</i>');
            }
        }
        document.write('</li>');
    }
    document.write('</ul>');
}
</script>
<script src="http://nacho4d-nacho4d.blogspot.com/feeds/posts/default?orderby=published&alt=json-in-script&callback=showrecentposts">
</script>

My changes

  • Removed all custom ui modifications and put the post in a ul. I added a custom bullet by giving setting the ul an id #archive-list and adding its style in the template
    #archive-list {
     list-style: none;
     margin-left: 0;
     padding-left: 1em;
     text-indent: -1em;
     }
    #archive-list li:before {
     content: "\0BB \020";
     }
    
  • I thought of using feeds/posts/default instead of the full path http://nacho4d-nacho4d.blogspot.com/feeds/posts/default however it does not work on posts pages.


2014/07/27

Ubuntu server setup

Rackspace stuff... I have rebuilt my system many times, so these are some useful notes to begin with

First time login and another user creation

$ ssh root@ww.xx.yy.zz
$ sudo adduser username

Some additional steps:

$ sudo apt-get update
$ sudo apt-get install git-core curl build-essential openssl libssl-dev

Color $PS1

In ~/.bashrc find and uncomment: force_color_prompt=yes

Install Emacs24

http://blog.be-open.net/emacs/ubuntu-emacs24-install/
add-apt-repository ppa:cassou/emacs
apt-get update
apt-get install emacs24 emacs24-el
A piece of my ~/.emacs file
;; Add ~/.elisp directory to my load path.
;; Not needed since .emacs.d is read by default
(add-to-list 'load-path' "~/.emacs.d")

;; Show trailing white spaces
(setq-default show-trailing-whitespace t)
(set-face-background 'trailing-whitespace "#191970")

;; Adds Other Package manager repositories
(require 'package)
(add-to-list 'package-archives
             '("elpa" . "http://tromey.com/elpa/"))
(add-to-list 'package-archives
             '("marmalade" . "http://marmalade-repo.org/packages/"))
(add-to-list 'package-archives
             '("melpa" . "http://melpa.milkbox.net/packages/"))

Install Go

apt-get install golang

Install Nodejs

I stopped trying to make a web server with node. But these are the steps I used to follow
$ git clone https://github.com/joyent/node.git && cd node
$ git checkout -b v0.8.1 v0.8.1
$ ./configure
$ make
$ sudo make install
$ node -v

# Install npm
$ curl http://npmjs.org/install.sh | sudo sh
$ npm -v

# If perl complains:
# perl: warning: Please check that your locale settings:
#  LANGUAGE = (unset),
#  LC_ALL = (unset),
#  LC_CTYPE = "UTF-8",
#  LANG = "en_US.UTF-8"
#   are supported and installed on your system.
# Do:
$ export LANGUAGE=en_US.UTF-8
$ export LANG=en_US.UTF-8
$ export LC_ALL=en_US.UTF-8
$ locale-gen en_US.UTF-8
# and run it again



2014/01/02

Package xcb-shm was not found in the pkg-config search path

I tried running a hello world program in GTK from the official tutorial page

They basically say:

clang++ `pkg-config --cflags gtk+-2.0` hello.cpp `pkg-config --libs gtk+-2.0` -o hello

This assumes that gtk is installed (brew install gtk+). Also for easiness, they recommend to use pkg-config to get the entire list of paths to compile something that uses gtk.

The problem is that pkg-config complains about "xcv-shm blah blah blah" and it does not work.

The answer from here is to set PKG_CONFIG_PATH appropriately. Apparently is a miss configuration of gtk + x11 + brew.

$ pkg-config --cflags gtk+-2.0
Package xcb-shm was not found in the pkg-config search path.
Perhaps you should add the directory containing `xcb-shm.pc'
to the PKG_CONFIG_PATH environment variable
Package 'xcb-shm', required by 'cairo', not found
PKG_CONFIG_PATH should be set so it reads from Xquartz path so we need to add it to ~/.profile.
$ echo "export PKG_CONFIG_PATH=/opt/X11/lib/pkgconfig" >> ~/.profile

Now it works :)

$ pkg-config --cflags gtk+-2.0
-D_REENTRANT -I/opt/X11/include/cairo -I/opt/X11/include/pixman-1 -I/opt/X11/include/libpng15 -I/opt/X11/include -I/opt/X11/include/libpng15 -I/opt/X11/include -I/opt/X11/include/freetype2 -I/opt/X11/include -I/opt/X11/include/freetype2 -I/opt/X11/include -I/usr/local/Cellar/gtk+/2.24.22/include/gtk-2.0 -I/usr/local/Cellar/gtk+/2.24.22/lib/gtk-2.0/include -I/usr/local/Cellar/pango/1.36.1/include/pango-1.0 -I/usr/local/Cellar/atk/2.10.0/include/atk-1.0 -I/usr/local/Cellar/gdk-pixbuf/2.30.1/include/gdk-pixbuf-2.0 -I/usr/local/Cellar/pango/1.36.1/include/pango-1.0 -I/usr/local/Cellar/harfbuzz/0.9.25/include/harfbuzz -I/usr/local/Cellar/pango/1.36.1/include/pango-1.0 -I/usr/local/Cellar/glib/2.38.2/include/glib-2.0 -I/usr/local/Cellar/glib/2.38.2/lib/glib-2.0/include -I/usr/local/opt/gettext/include 


2013/12/30

LLDB from the command line

Before IDEs and all current eye-candy stuff, everything was done like this:
  • Compile a sample.cpp program with debug symbols
    $ clang++ -g sample.cpp
    
  • Star the debugger
    $ lldb
    
  • Create a target to start the debug session
    $ target create a.out
    
  • Setup your breakpoints
    # breakpoing set --file sample.cpp --line 7
    $ b sample.cpp:7
    
  • List breakpoints
    $ breakpoint list
    
  • Set a condition to a preakpoint (conditional breakpoint).
    Use list to get the breakpoint number and replace it with N.
    $ condition N (int)[[myObj name] isEqualToString:@"Bar"]
    
  • Run the target
    $ run a.out
    
  • Step in
    $ s
    
  • Print the stack trace
    $ st
    
From now do as usual...
More Commands on lldb-gdb table


2013/11/10

clang-format

I've been wanting to use clang-format since a while already. It turns out that installation instructions are kind outdated. I had to read various commits in the clang repository to realize clang-format command it was moved from clang to llvm repository.

2015/01/18 Update: Use brew!

Now clang-format is on brew!. Just do:
brew install clang-format

2014/08/06 Update: Use prebuilds!

Now is even simpler, just download the prebuilds for your system, decompress and use them!
# Download
$ curl -O http://llvm.org/releases/3.4.2/clang+llvm-3.4.2-x86_64-apple-darwin10.9.xz
# Decompress
$ tar xvfJ clang+llvm-3.4.2-x86_64-apple-darwin10.9.xz 
# Locate clang-format
$ ./clang+llvm-3.4.2-x86_64-apple-darwin10.9/bin/clang-format --help
Just one thing. I couldn't find the git hook python script available. Still, it is in the source and it does not need to be compiled, just copy th
# Download only (not the entire llvm project) the repository that contains the hook script
$ svn co http://llvm.org/svn/llvm-project/clang-tools-extra/trunk extra
# Voila!
$ find . name git-clang-format
# Now just copy it somewhere into your $PATH

Old approach: Build llvm


We need to compile LLVM! I wish brew provides something like --with-tools or something like --with-clang-format. For now we have to do the entire thing by ourselves. Some instructions were taken from Clang's getting started page:

Getting LLVM + Clang

$ svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
$ cd llvm/tools
$ svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
$ cd ../..
$ cd llvm/tools/clang/tools
$ svn co http://llvm.org/svn/llvm-project/clang-tools-extra/trunk extra
$ cd ../../../..
$ cd llvm/projects
$ svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt
$ cd ../..

Building LLVM + Clang

$ ./llvm/configure
$ make
In my case, make didn't succeed, it takes forever and it fails somewhere after building clang-format but that is ok. clang-format was built and I wasn't planning to install llvm/clang from source either, I am fine with Xcode/brew stuff :p

Installing clang-format

If it is not clear where makeput all compiled stuff we can always use find:
$ find . -name "clang-format"
./Release+Asserts/bin/clang-format
./tools/clang/tools/clang-format
We need to copy clang-format command from above location to somewhere in our PATH or make an alias. Also we want to install git-clang-format and maybe other scripts.
# copy clang-format command to your $PATH
$ cp ./Release+Asserts/bin/clang-format /somewhere/in/your/PATH

# install (copy) git-clang-format subcommand to your $PATH
$ cd ./tools/clang/tools/clang-format
$ cp git-clang-format ~/.bin

Using clang-format command

Now, lets say we are inside a directory which contains c/cpp/objc sources which we want to reformat. We simply create a .clang-format file which should be of the form:
Key: Value
Key: Value
Key: Value
Key: Value
And then we would run clang-format file.c Some commands to know more about the command and keys available:
# dump all configuration keys
clang-format -dump-config 
clang-format -dump-config  style=webkit

# help
clang-format --help
clang-format --help-list-hidden

# format a file (it will read .clang-format file if available)
clang-format filename.c

# format a file with a explicit style
clang-format -style=google filename.c
Check clang's official style documentation page for more info.

Using it with git

Since we installed git-clang-format file to our PATH, if we are in a git repository, we can do:
git clang-format
And it will reformat all staged code, pretty awesome!

Are you wondering where did clang-format subcommand come from?! Answer: custom git subcommands are simply executables in the PATH and its name should have the "git-" prefix and should not have extension.

Hope this info is helpful to somebody :)



2013/11/09

インターネット接続 制限あり、ウィンドウズタブレットでの件

新しい無線ルーター買って電波が強くなった筈だから楽しみだったのに ウィンドウズタブレットのインターネット接続が「制限あり」??!

いろいろ調べたところアダプターのせいらしい、ドライバーを最新にすべきだそうだ。したけど直らない。

また別のサイトの情報によるとアダプターを無効にしてからまた有効にすると直ることがある。試してみたら実際に直っていた!でも一時的だけ、ちょっとするとまた「制限あり」になった。

「問題のトラブルシューティング」をクリックすると同じく一時的に直る。よく見ると問題解決の後に「アダプターのリセット」というステップがあるからおそらくアダプターを無効にしてから有効にすると同じかと思う。

そこで、このブログの情報を見つけて解決しました!アダプターの設定を変更するのだった!!hiro25hiro07様に感謝感謝!

念のためここに転載。

ネットワークアダプタの設定を変更します。

  1. デバイスマネージャーを開きます。
  2. [ネットワーク アダプター]をダブルクリックします。
  3. [Broadcom 802.11abgn Wireless SDIO Adapter]をダブルクリックします。
  4. プロパティーが表示されますので、[詳細設定]タブをクリックします。
  5. 表示された[詳細設定]タブから   以下の項目の設定変更を行ってください。
    • [20/40 Coexistance]の[Auto]を[Enable]に変更します。
    • [40MHz Intolerant]の[Disabled]を[Enable]に変更します。
    • [802.11n Preamble]の[Auto]を[Mixed Mode]に変更します。
    • [Afterburner]の[Disabled]を[Enable]に変更します。
    • [Bandwidth Capability]の[11a:20/40;11bg:20MHz]を[11a/b/g:20/40MHz]に変更します。
    • 以上で、操作は終了です。


あと、やっぱりドライバーのアップデートした方が良さそうだ、 Acerうんち全然アップデートしないからSony様からパクりました。 とりあえず、 DriverIdenfier.comからv5.93.98.4ダウンロードできた。 93.97.113でもだめでしたし、その一個前もだめだったし。
他のメーカーはちゃんとアップデートバージョンでしているのに、Acerはだしていない。。。ひどい。

参考



2013/10/22

Tar

I wish this was a tar tutorial, however there are plenty of them already. Maybe some people find it useful too, specially when downloading, opening tar balls... :)
A ".tar" file is a collection of files within a single file in an uncompressed form. If the file is a ".tar.gz" ( usually called tarball) or ".tgz" file then it is a collection of files in a compressed form.
To compress a file you could create the tar file with the z option. Or alternatively you could create the file with any other tool and then use gzip to compress it.

Some Tar Options

  • c : create a new archive
  • d : delete from the archive
  • r : append files to the end of the archive
  • t : list contents
  • u : update (append files if newer)
  • x : extract
  • f : file?
  • v : verbose
  • z : gzip

Uncomprezing files:

# target file is a zipped tar ball
$ tar -zxvf file.tar.gz
# target file is simply a tar ball
$ tar -xvf file.tar

Listing contents of the file

$ tar -tvf myfile.tar

Compressing files

# tar contents of folder foo in foo.tar
$ tar -cvvf foo.tar foo/
That is all, maybe I add more options as needed.



2013/07/30

Installing Emacs from the source

This VM I am in is a bit old and somewhow apt-get is not pulling the latest packages... so I need it to get it manually.
As many other open source software built with make: Get a release, from http://ftp.jaist.ac.jp/pub/GNU/emacs/ for example, then compile and install.
$ wget http://ftp.jaist.ac.jp/pub/GNU/emacs/emacs-24.3.tar.gz
$ tar -xzvf emacs-24.3.tar.gz
$ cd emacs-24.3
$ less INSTALL; # Just take a glance of the install notes
$ ./configure; # Check the output is not something weird
$ make; # No errors should appear here
$ sudo make install; # Gets moved to somewhere in the PATH
If in the way perl complains about LANG, etc variables:
 perl: warning: Setting locale failed.
 perl: warning: Please check that your locale settings:
  LANGUAGE = (unset),
  LC_ALL = (unset),
  LANG = "en_US.utf8"
    are supported and installed on your system.
 perl: warning: Falling back to the standard locale ("C").
Follow instructions: Perl warning Setting locale failed in Debian
$ export LANGUAGE=en_US.UTF-8
$ export LANG=en_US.UTF-8
$ export LC_ALL=en_US.UTF-8
$ sudo /usr/sbin/locale-gen en_US.UTF-8
$ sudo /usr/sbin/dpkg-reconfigure locales
# now choose the appropriate language from the list to generate
Finally re-run from the last step, install in this case:
$ sudo make install


2013/07/23

SQL stuff

I am just a beginner in SQL, so I write some trivial stuff here. Lets say I have some datum in my table of logs:
SELECT *, stime - time AS delay FROM logs;
+------+------+-------+-------+
| usid | time | stime | delay |
+------+------+-------+-------+
|    1 |   10 |    10 |     0 |
|    1 |   10 |    12 |     2 |
|    1 |   15 |    17 |     2 |
|    1 |   13 |    15 |     2 |
|    1 |   18 |    21 |     3 |
|    1 |   19 |    22 |     3 |
|    1 |   20 |    25 |     5 |
|    1 |   21 |    26 |     5 |
|    1 |   22 |    26 |     4 |
|    1 |   23 |    26 |     3 |
+------+------+-------+-------+
Where:
  • usid is some random id
  • time is record time in the client side
  • stime is record time in the server side
  • delay is simply the diff of server time and client side


Some exercises:

Count the number of records with a particular delay:
SELECT stime - time AS delay,
       COUNT(*) FROM logs
GROUP BY stime - time;
+-------+----------+
| delay | count(*) |
+-------+----------+
|     0 |        1 |
|     2 |        3 |
|     3 |        3 |
|     4 |        1 |
|     5 |        2 |
+-------+----------+
Too much detail (too granulated), so we want to group 2 and 3 delay into 2, 4 and 5 into 4, etc. To make sure I am doing the right calculations, here is the delay and rounded delay:
SELECT *,
       stime - time AS delay,
       FLOOR((stime - time) / 2) * 2 AS rounded_delay
FROM   logs;
+------+------+-------+-------+---------------+
| stid | time | ctime | delay | rounded_delay |
+------+------+-------+-------+---------------+
|    1 |   10 |    10 |     0 |             0 |
|    1 |   10 |    12 |     2 |             2 |
|    1 |   15 |    17 |     2 |             2 |
|    1 |   13 |    15 |     2 |             2 |
|    1 |   18 |    21 |     3 |             2 |
|    1 |   19 |    22 |     3 |             2 |
|    1 |   20 |    25 |     5 |             4 |
|    1 |   21 |    26 |     5 |             4 |
|    1 |   22 |    26 |     4 |             4 |
|    1 |   23 |    26 |     3 |             2 |
+------+------+-------+-------+---------------+
So now group by the rounded delay (named just "delay" here)
SELECT FLOOR((stime - time) / 2) * 2 AS delay,
       COUNT(*) FROM logs
GROUP BY FLOOR((stime - time) / 2) * 2;
+-------+----------+
| delay | count(*) |
+-------+----------+
|     0 |        1 |
|     2 |        6 |
|     4 |        3 |
+-------+----------+
Add a percentage, I know I have 10 rows in my table so I can do this:
SELECT *,
       c.cnt / 10 AS percentage
FROM (
      SELECT FLOOR((stime - time) / 2) * 2 AS delay,
             COUNT(*) AS cnt
      FROM   logs
      GROUP BY FLOOR((stime - time) / 2) * 2
) c;
+-------+-----+------------+
| delay | cnt | percentage |
+-------+-----+------------+
|     0 |   1 |     0.1000 |
|     2 |   6 |     0.6000 |
|     4 |   3 |     0.3000 |
+-------+-----+------------+
Although, probably is better not to hard-code the count:
SELECT *,
       c.cnt / ( SELECT COUNT(*) FROM logs) AS percentage
FROM (
       SELECT FLOOR((stime - time) / 2) * 2 AS delay,
              COUNT(*) AS cnt
       FROM   logs
       GROUP BY FLOOR((stime - time) / 2) * 2
) c;
+-------+-----+------------+
| delay | cnt | percentage |
+-------+-----+------------+
|     0 |   1 |     0.1000 |
|     2 |   6 |     0.6000 |
|     4 |   3 |     0.3000 |
+-------+-----+------------+
There should be much better ways (better performance) of doing this... I am glad to receive some comments :)


2013/07/22

Converting from to unix time

I write this because I always forget the right formats, options...

BSD Date - Mac OSX

The f option is for input format. The j option is to not try to set the time. The last argument is optional, is the output format and it requires an x as a prefix.
  • Convert to unix time
    $ date -jf "%Y-%m-%d %H:%M:%S %Z" "2013-07-19 00:00:01 JST" "+%s"
    1374159601
    
  • Convert from unix time
    $ date -jf "%s" 1374159601 "+%Y-%m-%d %H:%M:%S %Z"
    2013-07-19 00:00:01 JST
    
    Also:
    $ date -jf "%s" 1374159601
    Fri Jul 19 00:00:01 JST 2013
    


GNU Date - Linux, etc

The -d is for the input date. Format is recognized automatically. The last parameter, same as BSD Date, is the output format (optional). It must be prepended by a +.
  • Convert to unix time
    $ date -d "2013-07-19 00:00:00 JST" "+%s"
    1374159600
    
  • Convert from unix time
    $ date -d @1374159600 "+%Y-%m-%d %H:%M:%S %Z"
    2013-07-19 00:00:00 JST
    
    Also :
    $ date -d @1374159600
    Fri Jul 19 00:00:00 JST 2013
    


2013/03/02

Gollum

Wikis with Gollum

Gollum is a simple wiki system built on top of Git that powers GitHub Wikis. Super easy to starting working in your wiki.

Installation

gem install gollum redcarpet rdiscount
gollum --help

Usage

git clone https://github.com/allending/Kiwi.git
cd Kiwi.wiki/
gollum
Alternatively
git clone https://github.com/allending/Kiwi.git
gollum --page-file-dir Kiwi.wiki/ TestWiki

Editing changes

As documented, changes done directly in the source should be committed to trigger gollum to reflect them

Coda

If coda is your editor then below markdown mode might become handy: Markdown.mode by bobthecow - Github


2012/04/23

Installing GLEW in MacOS X

The old way

The easy way is at the bottom of the post :)
  1. Download the sources from SourceForge:
  2. http://glew.sourceforge.net/

  3. Unzip/Untar the file (Some browsers will do this automatically)

  4. Before compiling ...
  5. Usually you would run `make` but the shell script will fail to find/guess "$system" variable so we need to set it manually (as of in version 1.7.0 [08-26-11]). So open the makefile with some editor and change system to be `darwin` as shown in the image.
  6. Compile, Install and Clean
  7. make
    sudo make install
    make clean
    
    Some harmless warnings appear but ... let them be.

  8. We are done!
  9. Glew's headers are in /usr/include/GL and the dynamic and static libraries are in /usr/lib/ as usual :)

The easy way

  1. Use homebrew and save you all these little hazels ... :)
    brew install glew
    

Links

  1. Installing GLEW in Mac OS X (Leopard) - Julian Villegas
  2. GLEW on Mac OS - 175 CS Forum


2012/03/21

CoreImage and UIKit coordinates

Recently I found a very nice CoreImage tutorial for iOS: Easy face detection with core animation in iOS5 and after downloading the source I found that this quite popular tutorial was teaching something wrong, coordinates system conversions!.

I can't remember how many bugs/problems I had because of not converting the coordinates well or using the incorrect coordinate system when I was at the uni. So I decided to write this little post so my great audience (approx. 6 viewers per day) can benefit from it :)

CoreImage coordinates are different from UIKit

CoreImage Coordinate system


(From CoreImage Programming Guide)


Even-though is not very clear in the image, each image processed has its own coordinate space and its origin in at the left bottom corner of the image. Each images's coordinate systems is device independent.

UIKit Coordinate System


(From View Programming Guide for iOS)


UIViews's frame's origin is at the top left corner and its coordinates are in their superview coordinate space. They are not independent.

Converting coordinates


We should convert CoreImage coordinates to UIKit coordinates system, not the other way around, because chances are that most of your code will be in UIKit coordinates and because other iOS devs will expect {0,0} to be at the top left corner, be nice and don't change everything just because of CoreImage. :)

This is easily done with an affine transform:
Where ui and ci subindexes mean UIKit and CoreImage coordinates respectively and h is the height of the image in regard.

We could do this manually but happily there are a bunch of functions for this task like: CGAffineTransformMakeScale, CGAffineTransformTranslate, CGPointApplyAffineTransform and even CGRectApplyAffineTransform!. Thanks to Apple for making our life easier.

The code


// Create the image and detector
CIImage* image = [CIImage imageWithCGImage:imageView.image.CGImage];
CIDetector* detector = [CIDetector detectorOfType:CIDetectorTypeFace 
                                          context:... options:...];

// CoreImage coordinate system origin is at the bottom left corner
// and UIKit is at the top left corner. So we need to translate
// features positions before drawing them to screen. In order to do
// so we make an affine transform
CGAffineTransform transform = CGAffineTransformMakeScale(1, -1);
transform = CGAffineTransformTranslate(transform,
                                    0, -imageView.bounds.size.height);

// Get features from the image
NSArray* features = [detector featuresInImage:image];
for(CIFaceFeature* faceFeature in features) {

  // Get the face rect: Convert CoreImage to UIKit coordinates
  const CGRect faceRect = CGRectApplyAffineTransform(faceFeature.bounds, transform);

  // create a UIView using the bounds of the face
  UIView* faceView = [[UIView alloc] initWithFrame:faceRect];

  ...

  if(faceFeature.hasLeftEyePosition) {
    
    // Get the left eye position: Convert CoreImage to UIKit coordinates
    const CGPoint leftEyePos = CGPointApplyAffineTransform(faceFeature.leftEyePosition, transform);
    ...

  }

  ...
}


You can download the sample from here and see the result is pretty much the same as the original tutorial. Only this time we didn't scramble with the coordinate system :)

In case, you didn't notice, the original example changes the whole window coordinate system causing its origin to be at the bottom left (like Cocoa in the Mac) hence the imageView appears at the bottom.




2012/03/02

Another twilight theme for Xcode

I just created my ...


err ...


actually just tweaked Twilight theme a bit and named it "Twilight Console" It resembles Twilight but it uses font Monaco size 10 and the most important part (at least to me) is the console is also customized:


If you play with the console very often (gdb, llvm debugger, etc) you might like this theme.

Download TwilightConsole.dvtcolortheme or get if from Github :)




2012/02/08

Shake animation using CAKeyFrameAnimation

Animation that shakes a toolbar (UIToolbar or any generic UIView object)

I put this here so I have a quick and simple example to refer to in the future :)

- (void)shakeToolbar
{
    static BOOL performingAnimation = NO;
    if (!performingAnimation) {
        performingAnimation = YES;

        // Make an image view to animate
        // Probably not necessary when animation only "position"
        // property, but if animating its size, transform or some
        // property that will need its subviews to layout is more
        // efficient to do it like this.
        UIGraphicsBeginImageContext(toolbar.frame.size);
        CGContextRef context = UIGraphicsGetCurrentContext();
        [self.view.layer renderInContext:context];
        UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        UIImageView *imageToAnimate;
        imageToAnimate = [[UIImageView alloc] initWithImage:img];
        imageToAnimate.frame = toolbar.frame;
        
        // Replace the toolbar with the image view temporarily
        [toolbar.superview addSubview:imageToAnimate];
        toolbar.hidden = YES;
        [imageToAnimate release];

        // Build the animation
        CGPoint pos = toolbar.layer.position;
        CGMutablePathRef shakePath = CGPathCreateMutable();
        CGPathMoveToPoint(shakePath, NULL, pos.x, pos.y);
        CGPathAddLineToPoint(shakePath, NULL, pos.x - 5, pos.y);
        CGPathAddLineToPoint(shakePath, NULL, pos.x + 4, pos.y);
        CGPathAddLineToPoint(shakePath, NULL, pos.x - 3, pos.y);
        CGPathAddLineToPoint(shakePath, NULL, pos.x + 2, pos.y);
        CGPathAddLineToPoint(shakePath, NULL, pos.x - 1, pos.y);
        CGPathCloseSubpath(shakePath);
        const float durationOfShake = 0.5;
        CAKeyframeAnimation *shakeAni;
        shakeAni = [CAKeyframeAnimation animationWithKeyPath:
                                                        @"position"];
        shakeAni.path = shakePath;
        shakeAni.duration = durationOfShake;
        CFRelease(shakePath);

        // Perform the animation
        [imageToAnimate.layer addAnimation:shakeAni 
                                    forKey:@"shakeAnimation"];

        // Restore the toolbar to its original state when finished
        // (Probably is better to do this in the delegate method ... )
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 
                                      durationOfShake * NSEC_PER_SEC);
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            toolbar.hidden = NO;
            [imageToAnimate removeFromSuperview];
            performingAnimation = NO;
        });
    }
}


2012/01/30

Customized Blogger search box

I write this here so I have a place to refer so when some good day I decide to change current I don't have to write this again :)

The implementation is far from optimal but I couldn't find a better option than polling until the search box is loaded to fully customize it. Some properties are overwritten by defaults and CSS seems to be not enough. If somebody know how to do this in a cleaner/better way, I want to hear it ( read : write a comment please :) )

The CSS code that will make the search field look nicer and hides the search button:
/* customization of search box - nacho4d (css)*/
.gsc-search-box{
}
table.gsc-search-box td{
 border: 0px;
}
input.gsc-input { /* This is the real search field*/
background-image:url('http://web.me.com/nacho4d/toolbar_find24.png');
background-repeat:no-repeat;
background-position:0% 50%;

border: 1px solid gray;
-webkit-border-radius: 5px ;
-moz-border-radius: 5px ;
border-radius: 5px ;

-moz-box-shadow: inset 0 5px 15px rgba(0,0,0,.15);
-webkit-box-shadow: inset 0 5px 15px rgba(0,0,0,.15);
box-shadow: inset 0 5px 15px rgba(0,0,0,.15);

font-size: 1.2em;
color: gray;
padding-left:30px;
padding-right:7px;
height: 24px;
}
input.gsc-input:focus { 
-moz-box-shadow: inset 0 3px 25px rgba(0,0,0,.0);
-webkit-box-shadow: inset 0 3px 25px rgba(0,0,0,0);
box-shadow: inset 0 3px 25px rgba(0,0,0,0);
color:black;
}
input.gsc-search-button { /* this is the search button */
display:none;
}
/* end of customization of search box - nacho4d (css)*/
Remember to put this somewhere inside <style> tag :)

The js code that will poll looking for the search field (which is loaded dynamically) and set a couple of attributes to do the final touch.
<!-- Start of customization of search box - nacho4d (js) -->
  <script type='text/javascript'>
function fixSearchField() {
  var searchFields = document.getElementsByTagName('input');
  for (var i = 0; i < searchFields.length; i++) {
    var searchField = searchFields[i];
    if (searchField.getAttribute('class', 0) == ' gsc-input'){
      // found it!
      searchField.style.paddingLeft = '30px';
      searchField.style.width = '120px';
      searchField.setAttribute('placeholder', 'Search ...');
      return;
    }
  }
  setTimeout('fixSearchField()', 1000);
}
fixSearchField();
</script>

<!-- End of customization of search box - nacho4d (js) -->

Remember to put this somewhere after </style> :)

I guess this would be much easier if I were hosting my own blog ... just saying.


2012/01/27

scrollViewDidEndScrolling ?

Last time I tried this I think it was in iOS2.x, 3.x ... so instead of looking for an old code I decided to implement this again. I want to know when the text view did end scrolling. I mean when the text end moving (regardless regular dragging or drag + deceleration).


First I created my regular UITextView, set the delegate and implemented the delegate methods:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    if (scrollView.isDecelerating) {
    NSLog(@" ... restarting");
    } else {
    NSLog(@"Scroll will begin");
    }
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(CGPoint *)targetContentOffset
{
    NSLog(@"will end dragging: velocity: %f", velocity.y);
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    if (decelerate) {
    } else {
        NSLog(@"Scroll did end (Dragging)");
    }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    NSLog(@"Scroll did end (Decelerating)");
}

So if I ...

scroll without deceleration I get:

Scroll will begin
will end dragging: velocity: 0.000000
Scroll did end (Dragging)


scroll with deceleration I get:

Scroll will begin
will end dragging: velocity: 0.321089
Scroll did end (Decelerating)


start scrolling with deceleration and while its decelerating do another scrolling without deceleration I get:

Scroll will begin
will end dragging: velocity: 1.094134
... restarting
will end dragging: velocity: 0.000000
Scroll did end (Dragging)


start scrolling with desceleration and while its decelerating do another scrolling with deceleration I get:

Scroll will begin
will end dragging: velocity: 1.995404
... restarting
will end dragging: velocity: 0.442658
Scroll did end (Decelerating)


Everything goes as expected :)


Why do I need scrollViewWillEndDragging:withVelocity:targetContentOffset: ?. This is my little finding: If that method is **not** implement and then I ...

start scrolling with deceleration and while its decelerating do another scrolling without deceleration I get :

Scroll will begin
will end dragging: velocity: 1.094134
... restarting
will end dragging: velocity: 0.000000
Scroll did end (Dragging)
Scroll did end (Decelerating)

Huh?? The textview ended dragging and just after decelerating? Did I find a bug?
So, in order to get notified only once when the text did end scrolling I need to implement scrollViewWillEndDragging:withVelocity:targetContentOffset: ?? ??
I hope I am not missing something.


In the mean time I uploaded a sample project that show the issue here.


2012/01/24

Installing Node.js for the third time

I think this is the third time I want to use node.js in a different computer and I **always** get stuck in the same place.
I know there are several "How do I install node.js" tutorials, blogs, etc.
Here is what it worked for me:

  • In theory you install node using brew: brew install node (or brew upgrade node to upgrade current node installation) and then npm : curl http://npmjs.org/install.sh | sh.
    curl https://npmjs.org/install.sh | sh

  • But in practice, At least me, I always get permission errors, link errors, it looks pretty bad. It is because I need to set adjust my permissions first : sudo chown -R $USER /usr/local.

  • Then, If you got: "Error: The linking step did not complete successfully." finish node installation with brew link node.

  • Now installing npm should work flawless too: curl http://npmjs.org/install.sh | sh

Hope it helps... :)

Other References


2012/01/22

Adding Linkedin Badge to Blogger

I write this here so If I ever decide to change the template of this blog I have a reference to go back and set this again :)

Basically I simply replaced :
<a class='profile-link' expr:href='data:userUrl' rel='author'><data:viewProfileMsg/></a>

with Linkedin's badge. Something like this :)
 <b:if cond='data:aboutme != ""'><dd class='profile-textblock'><data:aboutme/></dd></b:if>
 </dl>
   <a href='http://jp.linkedin.com/pub/guillermo-ignacio-enriquez-gutierrez/31/b53/173'><img id='linkedinImg' border='0' src='http://www.linkedin.com/img/webpromo/btn_myprofile_160x33.png'/></a>
   <!-- <a class='profile-link' expr:href='data:userUrl' rel='author'><data:viewProfileMsg/></a> -->
 </b:if>

optionally, remove the border and shadow of img tag. (Put this somewhere before ]]></b:skin>)
#linkedinImg{
-webkit-box-shadow: none; 
-moz-box-shadow: none; 
box-shadow: none;
border: 0px;
}

In my opinion this looks much better than Google+ :)




2012/01/14

Catching Keyboard Events in iOS


Some notes before starting ...

Things explained here are NOT possible using public APIs so this definitely violates AppStore's rules. However, if you want to take your chances and implement this for the AppStore or perhaps for Jailbroken iPhones ... keep on reading :) I think this time it is harder to get caught since this doesn't need to link against private frameworks or add private headers, etc.

The question is: How?

The short answer

The trick is in accessing GSEventKey struct memory directly and check certain bytes to know the keycode and flags of the key pressed. Below code is almost self explanatory and should be put in your UIApplication subclass.

#define GSEVENT_TYPE 2
#define GSEVENT_FLAGS 12
#define GSEVENTKEY_KEYCODE 15
#define GSEVENT_TYPE_KEYUP 11

NSString *const GSEventKeyUpNotification = @"GSEventKeyUpHackNotification";

- (void)sendEvent:(UIEvent *)event
{
    [super sendEvent:event];

    if ([event respondsToSelector:@selector(_gsEvent)]) {

        // Key events come in form of UIInternalEvents.
        // They contain a GSEvent object which contains 
        // a GSEventRecord among other things

        int *eventMem;
        eventMem = (int *)[event performSelector:@selector(_gsEvent)];
        if (eventMem) {

            // So far we got a GSEvent :)
            
            int eventType = eventMem[GSEVENT_TYPE];
            if (eventType == GSEVENT_TYPE_KEYUP) {
                
                // Now we got a GSEventKey!
                
                // Read flags from GSEvent
                int eventFlags = eventMem[GSEVENT_FLAGS];
                if (eventFlags) { 

                    // This example post notifications only when 
                    // pressed key has Shift, Ctrl, Cmd or Alt flags

                    // Read keycode from GSEventKey
                    int tmp = eventMem[GSEVENTKEY_KEYCODE];
                    UniChar *keycode = (UniChar *)&tmp;

                    // Post notification
                    NSDictionary *inf;
                    inf = [[NSDictionary alloc] initWithObjectsAndKeys:
                      [NSNumber numberWithShort:keycode[0]],
                      @"keycode",
                      [NSNumber numberWithInt:eventFlags], 
                      @"eventFlags",
                      nil];
                    [[NSNotificationCenter defaultCenter] 
                        postNotificationName:GSEventKeyUpNotification 
                                      object:nil
                                    userInfo:userInfo];
                }
            }
        }
    }
}


If you are asking yourself "Where all those #defines come from?", "Application subclass?, I don't have such a thing in code" then please read the long answer, I hope you find if helpful :)

The long answer:

UIEvents are simple wrappers of GSEventRefs, which contain a GSEventRecord struct in it. Usually we only treat UIEvents representing touch events because the UIApplication will not dispatch other events to our views or objects we create. These are UIInternalEvents and can represent accelerometer events, volume events, keyboard events, etc.

  1. In order to intercept all events our application receives we need to override sendEvent: method in our UIApplication subclass.
  2. We need to access the GSEvent and check it is a key event and then check its flags (Shift, Cmd, Ctrl, Alt). Its easy as that, the problem is we don't know how to get the type and flags of GSEvent, it is a private API :(

- GSEvent objects -
So I did the homework and according to Kenny TM in here and here, GSEvents looks like:
typedef struct __GSEvent {
    CFRuntimeBase _base;
    GSEventRecord record;
} GSEvent;
typedef struct __GSEvent* GSEventRef;
typedef struct GSEventRecord {
    GSEventType type; // 0x8 //2
    GSEventSubType subtype;    // 0xC //3
    CGPoint location;     // 0x10 //4
    CGPoint windowLocation;    // 0x18 //6
    int windowContextId;    // 0x20 //8
    uint64_t timestamp;    // 0x24, from mach_absolute_time //9
    GSWindowRef window;    // 0x2C //
    GSEventFlags flags;    // 0x30 //12
    unsigned senderPID;    // 0x34 //13
    CFIndex infoSize; // 0x38 //14
} GSEventRecord;
typedef struct GSEventKey {
 GSEvent _super;
 UniChar keycode, characterIgnoringModifier, character; // 0x38, 0x3A, 0x3C
 short characterSet;  // 0x3E
 Boolean isKeyRepeating; // 0x40
} GSEventKey;
Headers seems to be a bit old (iOS3~4) but things haven't changed so much. What is for sure is that keycode is right next to infoSize. (Isn't this the fun of private APIs?).
Everything we have to do now is to count bytes from the start of the memory of GSEvent
int *eventMem;
eventMem = (int *)[event performSelector:@selector(_gsEvent)];
GSEventType is at 2
GSEventFlags at 12 and
Unichar keycode at index 15.
I do some checks in the way but that is basically all. :)

Yes, Apple might change GSEvent at anytime so if you are a challenger and would like to submit this to the AppStore, at least do do this conditionally:
// I am lazy to do the proper check here in the post :)
[super sendEvent:event];
if ([[[UIDevice currentDevice] systemVersion] intValue] == 5) {
    ...
}

If you use it or tried to, it would be awesome you drop a line in the comments. (^-^)/
I still have not completed a sample for this little hack but in the mean you can check this this gist out where you can find some keycodes and masks.

References


2011/12/16

Share buttons

Added buttons: Finally added tweet, like, share, burp and fart buttons (LOL) to this humble blog in blogger. So much info around there but not all of them are up to date, it was hard to find the correct information.
Got some info from here, here, here and here put the code inside my blog in the footer :)
Which one was the easiest ?
The winner is LinkedIn besides it looks great :) but GooglePlus was almost as easy as the winner.

Which one was the most difficult ... ?
Facebook, Ohh how I hate Facebook!

Twitter is OK :)
<div class='post-footer'>

 <!-- Tweeter -->
 <div style='float:left;padding:4px;'>
   <a class='twitter-share-button' data-count='none' data-lang='en' data-via='nacho4d' expr:data-text='data:post.title' expr:data-url='data:post.url' href='http://twitter.com/share' rel='me'/>
   <b:if cond='data:post.isFirstPost'><script src='http://platform.twitter.com/widgets.js' type='text/javascript'/></b:if>
 </div>
 <!-- Google plus -->
 <div style='float:left;padding:4px;'>
   <g:plusone annotation='none' expr:href='data:post.url' size='medium'/>
 </div>
 <!-- LinkedIn -->
 <div style='float:left;padding:4px;'>
    <b:if cond='data:post.isFirstPost'><script src='http://platform.linkedin.com/in.js' type='text/javascript'/></b:if>
    <script expr:data-url='data:post.url' type='in/share'/>
 </div>
 <!-- Facebook -->
 <div style='float:left;padding:5px;'>
   <a expr:share_url='data:post.url' name='fb_share' rel='nofollow' type='share'/>  
   <b:if cond='data:post.isFirstPost'><script src='http://static.ak.fbcdn.net/connect.php/js/FB.Share' type='text/javascript'/></b:if>
 </div>
 <br/>
 <br/>

Any kind of comments or criticism is welcomed here :)


2011/12/08

CATimingFunction with bezier path


CoreAnimation is awesome! specially because it lets you define a timing function for you animation. But, somehow I always find myself googling for a interactive bezier curve creator site or something that lets me draw the curve.

Really, I don't know how many times I've done the same search and sometimes I can't find the page I used in the past.
So this time I wrote a simple but interactive HTML (using the canvas) that helps me define a bezier path I can use to create a custom timing function for CoreAnimation:
Here is the source:
$ git clone git@github.com:nacho4d/BezierJS.git




2011/08/02

Versions in OSX 10.7 : Half tutorial, half just notes

Foreword
Last week I spent some days implementing one of the new features in MacOSX 10.7 Lion: Versions and autosave.
It was not so straight forward, APIs are too new that there is no samples yet. Documentation is not enough for newbies like me. (hehe)

I thought that watching Apple's session 107 of WWDC 2011 was going to be enough but it turns out that the video/pdfs dont show all the code you need. Besides I couldn't find the sample code specifically for that session!. TextEdit autosave implementation is just too simple, not very educational in this regard.

I think my case is not trivial. Why? Because I am using a NSWindow subclass with NSBorderlessWindowMask and I have to craft close/minimize/maximize buttons, handle window resizing by my own. I even have to add the autosave button by my own and I still don't get the "--Edited" or "--Locked" string right next to the versions button (NSWindowDocumentVersionsButton)

So here are my notes:
(I assume the app is a document-based cocoa application)

First:
The easy part, override +[NSDocument autosaveInPlace] which will enable versions by default
//MyDocument.h
+ (BOOL)autosavesInPlace{
 return YES;
}
Or override +[NSDocument preservesVersions] directly.

Now, without much effort you will get something like this:

Second:

Still easy, implement the following NSWindowDelegate methods. If you are using a subclass of NSWindowController that is a good place, if not then the NSDocument subclass should be fine :)

To customize the size of the window in the version browser:
//Bug? : In NSBorderlessWindowMask windows below method is called but has no effect!
- (NSSize)window:(NSWindow *)aWindow willResizeForVersionBrowserWithMaxPreferredSize:(NSSize)maxPreferredSize maxAllowedSize:(NSSize)maxAllowedSize{
    //Create a custom size for the window entering version browser
    //This is just an example of a size you might use. This is quite big
    float ratio = maxPreferredSize.height/maxPreferredSize.width;
    NSSize winSize = [aWindow frame].size;
    winSize.height = winSize.width * ratio;
    return winSize;
}

To customize the window when entering/exiting version browser
- (void)windowWillEnterVersionBrowser:(NSNotification *)notification
{

    //Suggested by the docs:
    //disable UI elements that will have no effect in version browser.
    //Simplify the UI, as well

    //Example a textView:
    MyWindow *win = (MyWindow *)[notification object];
    [win hideTitlebarAndStatusBar];     //method of MyWindow : simply the window
    [win setUserIntaractionEnabled:YES];//method of MyWindow : disable UI
    [myTextView setEditable:NO];        //disable UI
}

- (void)windowDidEnterVersionBrowser:(NSNotification *)notification
{
    //If needed, do your thing here
}
- (void)windowWillExitVersionBrowser:(NSNotification *)notification
{
    //If needed, do your thing here too
}

- (void)windowDidExitVersionBrowser:(NSNotification *)notification
{
    //Suggested by the docs
    //Set the window to its original settings

    MyWindow *win = (MyWindow *)[notification object];
    [win showTitlebarAndStatusBar];     //method of MyWindow : restore the window
    [win setUserInteractionEnabled:YES];//method of MyWindow : restore UI
    [myTextView setEditable:YES];       //restore UI
}

Third
In fact this step is easy too but not very well documented, I believe.

In normal cases (When using regular windows with non NSBorderLessWindowMask) doing step 1 and 2 is enough. Cocoa will coordinate buttons in the window and resized to not to work while in the version browser.

In case of NSBorderLessWindowMask windows we need to do more.
As suggested by Apple docs UI/input should be disabled and in the window should be simplified to show only relevant information.

Something that is not clear in the docs is that above methods are only called for the current document (The document in the left side of the version browser).
So in order to disable UI in windows of past versions's documents (the documents in the right side of the browser) we need other APIs:

-[NSDocument isInViewingMode] will return YES for all the documents in the right side of the browser. NO for the current document
And -[NSDocument windowForSheet] will return the windows for all these documents.

So something like this should work:
- (NSWindow *)windowForSheet{
    MyWindow *win = (MyWindow *)[super windowForSheet];
    MyDocument *doc = (MyDocument *)[[win windowController] document];
    if ([self isInViewingMode]) {
        //disable UI temporarily
        [win hideTitlebarAndStatusBar];
        [win setUserInteractionEnabled:NO];
     [doc->myTextView setEditable:NO];
    }else{
        [win showTitlebarAndStatusBar];
        [win setUserInteractionEnabled:YES];
        [doc->myTextView setEditable:YES];
    }
    return win;
}

However, in version browser, each document is created in a different thread, so if you are handling big documents it should be much more efficient to above check in the same a method that run in the same thread. (-[NSDocument windowForSheet] is called in current document thread)

So, we better write above content inside windowControllerDidLoadNib: to avoid thread changing. :)

BTW. If you wan't to debug your app's version browser set flag NSDocumentRevisionsDebugMode to YES. Each window/document in the version browser is loaded in one different thread so without this flag is difficult to debug


Fourth:
These are not mandatory but recommended to implement.
Enabling writing/saving asynchronously might improve dramatically application responsiveness, specially when working with big documents.

- (void)autosaveWithImplicitCancellability:(BOOL)autosavingIsImplicitlyCancellable completionHandler:(void (^)(NSError *errorOrNil))completionHandler;
- (BOOL)canAsynchronouslyWriteToURL:(NSURL *)url ofType:(NSString *)typeName forSaveOperation:(NSSaveOperationType)saveOperation;
- (void)continueActivityUsingBlock:(void (^)(void))block;
- (void)continueAsynchronousWorkOnMainThreadUsingBlock:(void (^)(void))block;
- (NSDocument *)duplicateAndReturnError:(NSError **)outError;
etc


Window restoration:
I wish Apple showed this part too in their examples so my life would have been easier but NO! - they like me to suffer! (Well not really...)

Implement these NSWindowDelegate methods too to save/restore some info into/from the coded window.

- (void)window:(NSWindow *)window willEncodeRestorableState:(NSCoder *)state
{
     //any additional info to save 
     //Example:
     [state encodeObject:myObj forKey:@"myObject"];
}
- (void)window:(NSWindow *)window didDecodeRestorableState:(NSCoder *)state
{
     //any additional info to restore
     //Example:
     MyObject *myObj = [state decodeObjectForKey:@"myObject"];
}

Similarly you can override below methods of NSWindow
I am saving the state of the title bar because that is not done by default by Cocoa
- (void)restoreStateWithCoder:(NSCoder *)coder
{
    [super restoreStateWithCoder:coder];
    titleBarHidden = [super decodeBoolForKey:@"titleBarHidden"];
}
- (void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
    [super encodeRestorableStateWithCoder:coder];
    [coder encodeBool:titleBarHidden forKey:@"titleBarHidden"];
}

Basically that is all.
More info can be found in the following links and surely everywhere very soon as Lion and these features becomes more and more popular.

References