思い立ったが吉日!

iOSが好きです。

curl コマンドでAPIからの返り値の確認

curl コマンドでAPIからの返り値の確認

WEB APIからの返り値などの確認には、curlコマンドが便利です。 実装前に、どんな感じで値が返るのか確認したり、パラメータ変えたらどんな感じで返ってくるのか?という状況で使えるコマンドです。

curl URL名

terminal内では見づらいので、返り値をファイルに書き出す * curl のオプションを使う -o ファイル名 シェルのリダイレクトを使う * > ファイル名

例として デスクトップに移動して

$ cd Desktop 
$ curl http://www.example.com/ > example.txt

という感じで叩くとwww.example.comからの返り値が、デスクトップにexample.txtというファイルに書き出されます。

リダイレクト

シェル・スクリプト・リファレンス - 【 リダイレクトとは 】:ITpro

> 出力のリダイレクト(画面からファイル)
>>    出力をファイルに追記する(ファイルの更新)
<    入力のリダイレクト
<<    入力終端文字列を指定する

Ricty Fontの設定

プログラミングフォントで有名なRictyMacにインストースした際に若干はまったので、備忘録として残します。

最初挑戦したのがgithubの公式のインストール方法でインストールして面倒だったのですが、

github.com

homebrewを使えば簡単にインストールできました。

morizyun.github.io

こちらを参考にインストールすると楽でした。

$ brew tap sanemat/font
$ brew install Caskroom/cask/xquartz
$ brew install ricty
なんか色々インストールしてくれます。
cp -f /usr/local/Cellar/ricty/3.2.2/share/fonts/Ricty*.ttf ~/Library/Fonts/

↑がNo such file or directoryエラーを吐き、わかりづらかったです...

要は/usr/local/Cellarの中にあるrictyを~/Library/Fonts/の中にコピーすれば良さげなので、

$ open  /usr/local/Cellar

f:id:watarotten:20160213022802p:plain

でfinderから見に行くとrictyというフォルダがあったので、コピーしてきて、

$ open ~/Library/Fonts/

で ~/Library/Fonts/を開いて

f:id:watarotten:20160213023019p:plain

こんな感じでコピーして

f:id:watarotten:20160213023401p:plain

rictyの中をたどっていくと、ricty/なんかバージョン/share/fontsの中に.ttfファイルがありました。

入れたいRicty~.ttfとRictyDiscord~.ttfをダブルクリックするとfont bookが立ち上がって、

f:id:watarotten:20160213023849p:plain

フォントインストールできます。

$ fc-cache -vf

はいらなかったのかな?

後半、GUIで入れました。

ちなみに、iterm2に設定するときはpreferenceを開いて、

f:id:watarotten:20160213024104p:plain

profile > text > の真ん中あたりにあるchange fontを押すと

f:id:watarotten:20160213024250p:plain

こんな感じで設定できます。

URLSchemeを使った連携の確認にはメモ帳が便利

URLSchemeを使った連携の確認にはメモ帳が便利

URLScheme連携するアプリで、確認する際に実際にアプリで連携するのが一番良いのですが、呼び出し元のアプリがまだ出来てないといった場合はよくあると思います。

safariなどのブラウザにURLSchemeのURLを入力して叩くのでは、打つのが面倒ですし、せっかく打ったURLもすぐ消えてしまいます。

URLSchemeのテストは実機のメモ帳にURLを打っておくのが便利です。

メモ帳はURLを認識してくれるので、書いたURLSChemeをタップするだけで起動してくれますし、消えません。

コピーしてパラメータを変更してっといったパターンのテストもやりやすいです。

それだけの記事でした。

デバッグ用の隠しコマンドを作る

デバッグ用の隠しコマンドを作る

デバッグで開発者以外に内部的な値の変更を確認してもらう時に、ちょっと困りますよね。

閾値変更したんだけど、大量に実行しないと差がわかりづらいとか。

ログを見て確認してもらうわけにはいかないので、内部的にはちゃんと変更してるよ!ということを見て確認してもらうのが一番早いです。

僕は、ロングタップしたら、内部で使用している値がアラートとして表示されるようにしました。

UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longTapped:)];
longPress.minimumPressDuration = 0.5;
[window addGestureRecognizer:tapGestureRecognizer];
-(void)longTapped:(UILongPressGestureRecognizer*)sender
{
     // アラートとかで変数の値などを表示して確認
}

これを使用したいViewControllerとかに追加し、内部的にどの値を使っているかを確認できます。

アプリ内部で、現在読み込んているjsonファイルのバージョン情報などを確認するときなどに使いました。

本番ビルドに含めないように注意は必要ですが。

Singleton

Singletonの実装

  • 内部変数を用いた方法で実装
static TCPState* sharedInstace = nil;

+ (TCPState *)sharedInstance{
    if (!sharedInstace) {
        sharedInstace = [TCPState new];
    }
    return sharedInstace;
}

ただし、シングルトンのインスタンスnilかどうか(初期化済みかどうか)でif分岐するという方法だとスレッドセーフでは無い為、GCDのdispatch_onceを使う方が良いらしい

  • dispatch_once
+ (instancetype)sharedManager {
    static TCPState *_sharedInstance = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _sharedInstance = [[self alloc] init];
    });

    return _sharedInstance;
}

こっちで書くと複数スレッドからの呼び出しに対しても一度しか処理が行われない事が保証される

Storyboardを使ったUITableView

Storyboardを使ったUITableView

なるべくコードを書かずにStoryboardでTableViewが表現されてるソースを見た時、少しハマったのでメモ。

セルがカスタマイズされたテーブルの表示の作成の流れを確認。

まずは、通常通りSingleViewApplicationの作成。

f:id:watarotten:20151126083909p:plain

UITableViewの設置

f:id:watarotten:20151126084147p:plain

UITableViewCellの設置

f:id:watarotten:20151126084335p:plain

設置しただけではセルは反映されません。(わかりやすくするためセルの色を変えてます)

f:id:watarotten:20151126084629p:plain

いつものUITableViewのDelegate等の設定を行うのですが、viewDidLoadではなく、Storyboard上から設定したいと思います。

f:id:watarotten:20151126084951p:plain

ここにDelegateとDataSourceの設定項目があります。

うまく撮影できなかったのですが、DelegateとDataSourceからControl + Drugで黄色アイコンに向かって接続しViewControllerに結びつけます。

f:id:watarotten:20151126085204p:plain

すると、ViewController側ではこのように確認できます。

f:id:watarotten:20151126085403p:plain

次に、セルを設定したいと思います。

CellのContentView内に適当に部品を配置していきます。

Cellの高さはドラッグすれば変更できます。

f:id:watarotten:20151126090939p:plain

カスタムセルのファイルを追加します。

f:id:watarotten:20151126091005p:plain

カスタムセルのひも付けを行います。

f:id:watarotten:20151126091115p:plain

さらに、再利用のためのIdentifierを設定します。

f:id:watarotten:20151126091227p:plain

プロパティをコードに接続しておきます。

f:id:watarotten:20151126092157p:plain

ここからはソースコードになります。

ViewController.h

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController<UITableViewDelegate,UITableViewDataSource>


@end

ViewController.m

#import "ViewController.h"
#import "CustomCell.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}



- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 5;
}

// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"CustomCell";
    CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    
    cell.titleLabel.text = @"グッド";
    cell.commentLabel.text = @"これはイイと言う意味です。";
    
    return cell;
}
@end

これを実行すると

f:id:watarotten:20151126092538p:plain

切れてかっこ悪いですが、このようになります。

調整ついでに、TableHeader,TableFooterを付けたいと思います。

f:id:watarotten:20151126093302p:plain

赤がHeaderView,青がFooter View,ピンクがcellのBoarder Viewになります。

Header,FooterはTableViewの中で、Cellの外に配置

f:id:watarotten:20151126093515p:plain

BoaderViewはContentViewの中に配置されていることに注意してください。

Cellの数を2に減らして、実行すると

f:id:watarotten:20151126093658p:plain

このようになります。

セルが表示されいないって人はこれをチェックするといいかもです。

qiita.com

GCDを使って並列非同期通信

GCDの並列非同期通信

GCDの並列非同期処理を行います。

非同期通信

NSURL *url = [NSURL URLWithString:@"http://www.apple.com"];
    // UI変更用にmainThreadを準備
    dispatch_queue_t main = dispatch_get_main_queue();
    // 並列のディスパッチキューを準備
    dispatch_queue_t sub = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 並列のディスパッチキューで非同期処理
    dispatch_async(sub, ^{
        // 並列非同期で通信メソッドの実行
        [RequestManager sendAsynchronousRequest:url completionHandler:^(NSURLResponse *response, NSError *connectionError) {
            if (connectionError) {
                NSLog(@"nooaonoaaa!1!!!%@",connectionError);
            } else {
                dispatch_async(main, ^{
                    // UIの変更はmainで行う
                    self.textView.text = [NSString stringWithFormat:@"%@",response];
                    
                });
            }
            
        }];
        // この処理は処理通信より先に実行される
       NSLog(@"通信中");
    });

通信メソッド

+ (void) sendAsynchronousRequest:(NSURL*) requestURL completionHandler:(void (^)(NSURLResponse* response, NSError* connectionError))handler
{
    // SessionTaskの準備(resume呼ぶまで通信開始はしないので注意)
    NSURLSessionTask * task = [[NSURLSession sharedSession] dataTaskWithURL:requestURL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error)
    {
        // errorがnullだったら通信成功してるので結果を返す
        if (!error) {
            handler(response, error);
        }
    }];
    // 5秒待つ
    sleep(5);
    // 通信の開始    
    [task resume];
}

これを実行すると(通信成功するのを前提)

まず、通信用のメソッドsendAsynchronousRequestが呼ばれ、通信準備が非同期で行われる。

並列非同期なので、sendAsynchronousRequestの次の処理である、Logを出す処理も別スレッドで開始される。

この時sendAsynchronousRequest内のBlocksの式の部分(if else)が実行されたりはしないので注意。なぜなら、Blocksの式の部分の実行はsendAsynchronousRequest内のhandler()によってるから。

通信メソッド側の処理は、SessionTaskを生成し、5秒待つ処理があるので5秒待つ。

この間にLogは出す処理は終わってる。通信処理メソッド側は、5秒待ち終えたら通信の開始。

Blocks部分は同期的に処理されていくのでdataTaskWithURLからcompletionHandlerをが実行されるまで実行されない。

通信が終了しコールバックを得たら、dataTaskWithURLのBlocks内の式の部分が実行される。

sendAsynchronousRequestにコールバックが送られたらsendAsynchronousRequestのBlocksの式の部分が実行される。

メインスレッドからUIの変更を行う。

用意しておいたtextViewに通信結果が表示される。