SIN@SAPPOROWORKSの覚書

C#を中心に、夜な夜な試行錯誤したコードの記録です。

シルバーウィークにXamarin.FormsでFacebookアプリを作成した

【 Xamarin 記事一覧 】

1.#6 JXUGC

10月3日、「第6回 Japan Xamarin User Group Conference 東京 事例スペシャル!」に参加させて頂きました。
第6回 Japan Xamarin User Group Conference 東京 - connpass

また、お時間を少し頂戴し、タイトルのお話しをさせて頂きました。

2.事例スペシャ

当初から、主催の田淵さん(@ytabuchi)に登壇のお誘いを頂いていたのですが、仕事柄、「お話しできるような ”事例” が無い」ということでお断りさせて頂きました。

しかし、いつまでたってもセッション枠は消えず、タイトル空欄の私の行が残っているので・・・いよいよ焦って事例を作ることにしましたw

そういう意味で、火がついたのは、約2週間前です・・・

丁度シルバーウィークで、まとまった時間が取れるので、「Xamarin.Formsを使用して短期間でどれぐらい作れるか」というコンセプトで作業してみました。

3.Facebookアプリ

時間も制限があるので、今回は、以前作成したことのあるWebアプリをネイティブアプリにリニューアルすることにしました。
結果的には、FacebookAPIの仕様が当時から比べて結構変わっており、内容はかなり違ったものになってしまいました。

「仕様どおり」ではなく、「時間どおり」です^^)

※V2.1以降、FQLは非推奨 scopeについてもだいぶ変わってました。

4.結論

内容については、スライドを見て頂きたいのですが・・・ここでは、結論だけ。

あくまで、個人的な感想ですが・・・

  • Xamarin.Formsを使用すると、短期間で結構動くものが作れる。(しかも、iOS,Android,WindowPhone対応)
  • 細かいところに、気が付きすぎると、作業量が大きく膨らむ(予感がする。)

という事で、

「Xamarin.Formsは、頑張りすぎないのが吉」

って事で。

※ぜんぜん、disっている訳ではありません。

【 Xamarin 記事一覧 】

Swift 2.0 で追加された defer について

defer

deferは、Swift 2.0 で新たに追加された構文です。
The Swift Programming Language: Statements
f:id:furuya02:20150823052955p:plain

deferの動作

deferを使用すると、スコープを抜ける際に実行する処理を記述することができます。
超簡単に例を示せば、次のようになります。

func f(){
    defer{
        print("2")
    }
    print("1")

}
// print "1"
// print "2"

deferを複数定義した場合L.I.F.O.で処理されます。

func f(){
    defer{
        print(“1”)
    }
    defer{
        print(“2”)
    }
   defer{
        print(“3”)
   }
}
// print "3"
// print "2"
// print "1"

利用場所

「絶対的有効!」となるような場面がちょっと思いつかなかったのですが、一応例として
仮ですが、下記のような処理があったとして・・・

func MyFunc(){
        
    // ストリームのオープン	
    let st = NSOutputStream(toFileAtPath: path, append: false)
    st!.open()

    // ストーリームの使用
    st!.write(&buffer, maxLength:100)
    st!.write(&buffer, maxLength:100)
    st!.write(&buffer, maxLength:100)
	
    // ストリームのクローズ    
    st!.close()
}

これが、条件による中断があるとなると、常にリソースの解放に注意が必要なります

func MyFunc(){
        
    // ストリームのオープン	
    let st = NSOutputStream(toFileAtPath: path, append: false)
    st!.open()

    st!.write(&buffer, maxLength:100)

    if !status { // 状態が変化していたら処理中断
         st!.close() // ストリームのクローズ 
	return
    }

    st!.write(&buffer, maxLength:100)

    if !status { // 状態が変化していたら処理中断
        st!.close() // ストリームのクローズ 
	return
    }
	
    st!.write(&buffer, maxLength:100)

    // ストリームのクローズ    
    st!.close()
}

そんな時は、リソースの確保と同時に、解放を書いておけばいいのでは無いでしょうか。

func MyFunc(){
        
    // ストリームのオープン	
    let st = NSOutputStream(toFileAtPath: path, append: false)
    st!.open()
    // オープンとセットでクローズ処理を書いてしまう
    defer{
    	st!.close() 
    }
		
    st!.write(&buffer, maxLength:100)

    if !status { return }

    st!.write(&buffer, maxLength:100)

    if !status { return }
	
    st!.write(&buffer, maxLength:100)
}

コードがスッキリするという意味ではなく、間違いが混入しにくくなるという意味で・・・
事後、条件分岐がより複雑になっても、リソース解放の処理に気を使う必要は無くなると思います。

ツッコミを恐れずに言ってしまうと、メソッドのデストラクタ?
「デストラクタ」・・・ちょっと懐かしい・・・

参考資料

https://medium.com/the-traveled-ios-developers-guide/swift-defer-bcef70de8767
The defer keyword in Swift 2: try/finally done right – Hacking with Swift

SimpleNumpadを利用した、拡張コントロールの作成(Swift)

SimpleNumpad

UITextField では、コントロールにフォーカスが入った時にキーボードが自動的に出現させることができます。今回は、このように使用できる数値入力コントロールを作成してみたいと思います。

入力の際に出現させるキーボードは、札幌のnotoroidさんが制作されたSimpleNumpad を使用させて頂きました。

なお、SimpleNumpadの詳しい利用方法については、
[iOS][Objective-C] 数値入力をベンリでクールにしてくれる SimpleNumpad | Developers.IO
で紹介されています。

プロジェクトの作成とSimpleNumpadの導入

SimpleNumpadは、CocoaPodsでプロジェクトに組み込むことができます。

CocoaPodsを利用した、プロジェクト作成の手順は、下記のとおりです。
(1) Single View Application でプロジェクトを新規作成(プロジェクト名を、SampleSinpleNumpadとした)
(2) プロジェクトフォルダに移動して、下記の内容でPodfileを作成

pod 'SimpleNumpad', :git => 'https://github.com/notoroid/SimpleNumpad.git'

(3) $pod install を実行
(4) 生成されたプロジェクト名.xcworkspace を開く
(5) プロジェクトフォルダ内で、下記の内容でBridgingHeader.hを作成

#ifndef SampleSimpleNumpad_BridgingHeader_h
#define SampleSimpleNumpad_BridgingHeader_h

#import <SimpleNumpad/SimpleNumpad.h>

#endif

(6) Build Settingの Swift Compiller - Code Generation で Objective-C Bridging Header にファイル名を追加する
f:id:furuya02:20150819020346p:plain

拡張コントロール(UINumField)の作成

今回の拡張コントロールは、UILabelを継承して作成しました。
コントロールは、storyboardからも使用できるように@IBDesignable属性を設定し、SimpleNumpadのデリゲートクラスとなれるようIDPNumpadViewControllerDelegateも追加しています。

import UIKit

@IBDesignable
class UINumField: UILabel , IDPNumpadViewControllerDelegate{
   private var vc:IDPNumpadViewController?
}

初期化処理

まずは、initialize()を定義して、コントロールの見た目と、タッチイベントを処理できるようにしました(1)。
この初期化処理は、実行時に init(coder aDecoder: NSCoder) から呼び出すようにしたのですが(2)、この init(NSCoder) を定義する場合は、
同時に init(CGRect) も定義(3)しないと、storyboadrでエラーが発生してしまいます。
f:id:furuya02:20150819021849p:plain

なお、この見た目の初期化をstoryboard上でリアルタイムに表示したい場合は、prepareForInterfaceBuilder()から行うことできます(4)。

    func initialize(){ // <= (1)
        // UILabelを Storyboard で置いた時のデフォルト値を削除する
        text="0.0"
        // Labelのフォントのサイズを修正
        font = UIFont.systemFontOfSize(35)
        // Labelがタッチイベントを受け取るように設定する
        userInteractionEnabled = true
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        initialize() //  実行時は、ここを通る <= (2)

        // SimpleNumpadの生成が、Storyboardで処理できないため、ここで初期化する
        vc = IDPNumpadViewController(style:IDPNumpadViewControllerStyle.CalcApp,inputStyle: IDPNumpadViewControllerInputStyle.Number,showNumberDisplay: false)
        vc?.delegate = self // SimpleNumpadのデリゲートクラスを自身とする
        
    }
    // init() イニシャライザを書く場合は、これも同時に定義しないとエラーとなる
    // Failed to update auto layout status interface Builder Cocoa touch
    override init(frame: CGRect) {  // <= (3)
        super.init(frame: frame)
    }
    
    // ここでStoryboard上の見た目を初期化する
    override func prepareForInterfaceBuilder() { // <= (4)
        initialize()
    }


プロパティ

続いて、コントロールにフォーカスがある時と無い時に、背景色を変化させるようにし、この色を変更できるようにしました。
@IBInspectable属性を指定することで、storyboardから、プロパティ値の変更が可能になります。

// フォーカスがない時の背景色
    @IBInspectable var offColor: UIColor = .lightGrayColor(){
        didSet{ //storyboardでリアルタイムに表示するため
            backgroundColor = offColor
        }
    }
    // フォーカスがある時の背景色
    @IBInspectable var onColor: UIColor = .clearColor() {
        didSet{ //storyboardでリアルタイムに表示するため
            backgroundColor = onColor
        }
    }

フォーカス時の処理

コントロールがフォーカスを受け取った際の処理は、次のようになります。
ステータスと背景色を変更し、SimpleNumpadをモーダルビューとして表示しています。
なお、このモーダルビューは、この拡張コントロールが置かれている親のViewからポップアップする必要があるので、親のレスポンダーを検索するparentViewControllerを定義しました(6)。

// コントロールがタッチされた際のイベント処理
    override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
        if isBusy {
            return // 既に入力中の場合は処理なし
        }
        // ステータスを「入力中」に変更
        isBusy = true
        // 背景色を変更
        backgroundColor = onColor
        // 親のビューコントローラのモーダルビューとして、vc(SimpleNumpad)を開く
        parentViewController!.presentViewController(self.vc!, animated: true, completion: {})
    }
    
    // 親となるUIViewControllerを検索(取得)する
    var parentViewController: UIViewController? { // <= (6)
        var parentResponder: UIResponder? = self
        while parentResponder != nil {
            parentResponder = parentResponder!.nextResponder()
            if let viewController = parentResponder as? UIViewController {
                return viewController
            }
        }
        return nil
    }

キー入力時の処理

キー入力時の処理は、デリゲートで委譲されたメソッドで処理します。
Enterが押された場合は、モーダルビューを閉じると同時に、背景色を戻して、フォーカスが外れたことを表現しています(7)。

// SimpleNumpadの内部の値が変化した際のイベント処理
    func numpadViewControllerDidUpdate(numpadViewController: IDPNumpadViewController!) {
        self.text = "\(vc!.value)"
    }
    // Enterが押された時のイベント処理
    func numpadViewControllerDidEnter(numpadViewController: IDPNumpadViewController!) { // <= (7)
        // モーダルビューを閉じる
        parentViewController!.dismissViewControllerAnimated(true, completion: {})
        // ステータスを変更
        isBusy = false
       // 背景色を変更
        backgroundColor = offColor
    }

コントロールの使用

それでは、作成した拡張コントロールを使用してみます。
UILabelをStoryboardに配置します。
f:id:furuya02:20150819020450p:plain
続いて、Custom Class で UINumField を選択します
f:id:furuya02:20150819020459p:plain:w500
あとは、@IBInspectable属性を指定されたプロパティを変更します。
f:id:furuya02:20150819025332p:plain:w500

利用に際しての作業は、以上です。

実行している様子は、次のとおりです。
f:id:furuya02:20150819025549g:plain

しかし、SimpleNumpad いいっすね〜

コードは、下記に置きました。github.com

まとめ

今回は、SimpleNumpadを利用させて頂いて、拡張コントロールを作成してみました。
コントロールに関するdelegate処理などを、全部コントロールクラスに詰め込むことで、利用は非常にシンプルになると思います。

参考資料

[iOS][Objective-C] 数値入力をベンリでクールにしてくれる SimpleNumpad | Developers.IO
notoroid/SimpleNumpad · GitHub

Swift 2.0 で追加されたguard の有効利用

最近、Swift書き始めたのですが、C#には無い「オプショナル型」が結構気に入ってます。
長く辛かった、nilとの戦いに勝てそうな気がしてます。

guard

guardは、Swift 2.0 で新たに追加された構文です。
The Swift Programming Language: Statements
f:id:furuya02:20150815101451p:plain

一瞬 if と何が違うのか?って感じですが、私なりに有効な利用場面を結論付けてみました。

if let

if , let のセットで、オプショナル型をアンラップして処理する定型は、次のような感じでしょうか

func sample(num:Int?){
    if let n = num {
        print(n) // numが有効だった場合のメイン処理
    }
}

if の中にメイン処理を詰め込む感じです。
アンラップされた n も、かっこの中だけで有効なので、利にかなってます。

しかし、文脈的に、パラメータが無効だった場合に、さっさと処理をやめて、そのあとでじっくりメイン処理をしたいという場面もあるはずです。
この場合、Swift1.2までなら、こんな感じになるのでしょうか

// if let はどこへ行った
func sample(num:Int?){
    if num == nil {
        return
    }
    // ここからメイン処理
    print(num!)  // <=ここで!とかが登場してしまう
}
// どうしてもアンラップしてから書きたい場合?
func sample(num:Int?){
    if num == nil {
        return
    }
    // ここからメイン処理
    let n = num!
    print(n)
}
// さすがにこれは無いな・・・
func sample(num:Int?){
    if let n == num {
    
	}else{
	    return
	}
    // ここからメイン処理
    let n = num!
    print(n)
}

どうも美しくない

guard let else

そこで guard 登場
先の文脈は、次のように書けます。

func sample(num:Int?){
    guard let n = num else{
        return
    }
    // ここからメイン処理
    print(n) // <= アンラップされた n が、ここで使用できている
}

ありですね。

PICで波動砲を打ってみる

波動砲(はどうほう)

波動砲(はどうほう)は、宇宙戦艦ヤマトに搭載されている、超強力な大砲です。(正式名称は「艦首波動砲」だそうです)
波動砲 - Wikipedia

そう、エネルギー充填中に、砲口がどんどん赤く明るくなって、120%充填が完了した後、「打て!」の号令でドカンと打つやつ(わかる人にしか伝わらない・・・)

今回は、この光る部分を、LEDで表現してみました。

回路

色々検索すると、タイマーとコンデンサーで調整された回路が、公開されていますが・・・コンデンサの容量とか考えるのが面倒なので、PICでプログラムしました。
f:id:furuya02:20150814084554p:plain
電源は、乾電池3本(4.5V)を想定しています。PICのデータシートによると、電源入力は4.0~5.5Vって事なので、レギュレータ(安定化)とか省略しちゃってます。
予熱(赤色LED)は、CCPによるPWMで表現し、白色LEDは、単純にデジタルポートでON/OFFしているだけです。
また、クロックも、4Mで充分なので、PIC内臓のものを使用しています。

※PIC16F819は、PIC16F84とかの定番じゃないため、公開されている情報等が少ないせいか、性能は、84より全然上なのに値段は安いというコスパ大なPICです。
f:id:furuya02:20150814084648p:plain

プロググラム

#include<16F819.h>

//INTRC_IO//内部クロックを使用してクロック用ピンをIOとして使用できるようにする設定
//「NOMCLR」を追加すると、「リセット端子を使用しない」設定となり、ICD2のMCLRを外さなくてもPICが動作する

#fuses INTRC_IO,NOPUT, NOWDT, NOLVP, NOBROWNOUT,NOPROTECT//,NOMCLR
#use delay(CLOCK=8000000)//4MHz

void main(void){
	int c=0;
	int duty=0;
	set_tris_b(0x80);

        //CCPの初期化 
        setup_timer_2(T2_DIV_BY_1,0xFF,1);
	setup_ccp1(CCP_PWM);

	// 赤色LEDをOFF(真っ暗)
        set_pwm1_duty(0);
	
	while(true){
                // 徐々に赤色LEDを明るくする
		for(duty=0;duty<0xFF;duty++){
			delay_ms(50);
			set_pwm1_duty(duty);
		}
                // 白色LEDをON
		output_high(PIN_A1);
		delay_ms(3000); // 3秒待機
                // 赤色・白色LED共にOFF
		set_pwm1_duty(0);
		output_low(PIN_A1);
		delay_ms(3000); // 3秒待機
	}
}

全プロジェクトは、こちらです。github.com

動作確認

動作確認している様子です。
f:id:furuya02:20150814083948j:plain
f:id:furuya02:20150814064136g:plain

やっぱり、波動砲青色LEDを足したほうが、いいかな・・・

色が違うとか、タイミングが変とか、色々ツッコミありそうですが、そこはPICなので、プログラムを変えたらOK!
後は、実装ですが・・・なんか、ここに来て、必要性を全く感じなくなって来てしまった・・・

Windows10でのIDC2のセットアップ 〜Window10(VMWare) + MPLAB + CCSコンパイラ

MPLAB X

Windows10に移行後、ICD2の使用環境を構築した際の覚書です。

ICDを使用するためには、MPLABが必要なのですが、現在では、これは「MPLAB X」となっており、Mac用も配布されています。
MPLAB®X IDE | Microchip Technology Inc.
f:id:furuya02:20150814055332p:plain

しかし、私の所有しているICD2は、一世代前のもので、この「MPLAB X」では対応していません。
今ここで、最新ICE3(約3万円)を購入するのもちょっと辛いので、今回は、旧MPLAB(Windows用のみしか無い)を使用しました。

(旧)MPLAB

f:id:furuya02:20150814062635p:plain
旧MPLABは、下記のページの「アーカイブのダウンロード」からダウンロードできます。
MPLAB®X IDE | Microchip Technology Inc.

今回は、32ビットWindows用の一番新しいバージョン「MPLAB IDE v8.92」をダウンロードしました。
ダウンロードした「MPLAB_IDE_8_92.zip」を解凍して、setup.exeを実行するだけでインストールは完了します。

ICD2のドライバインストール

MPLABをインストールすると、ICD2用のドライバも含まれますので、この時点でICD2を接続します。
C:\Program Files (x86)\Microchip\MPLAB IDE\Drivers64の中のmchpusb.sysとmchpusb64.sysがドライバの本体です。
f:id:furuya02:20150814060430p:plain

正常にドライバーがセットされると、デバイスマネージャーで下記のように確認できます。
f:id:furuya02:20150814060515p:plain

一度間違って、違うドライバがインストールされてしまうと、うまく認識できなくなります。
この場合は、一旦、C:¥Widows¥System32¥drivers¥のmchpusb.sysとmchpusb64.sysを削除して、やり直してみてください。
f:id:furuya02:20150814060821p:plain

Cコンパイラのセットアップ

MPLABには、PIC18以下のCコンパイルが含まれていせん(アセンブラのみ)
そこで私は、CCSコンパイラを別に購入して使用しています。
f:id:furuya02:20150814061244p:plain
インストールは、こちらもsetup.exeをキックするだけです。

インストール後、下記からプラグインをダウンロードしてインストールします。
http://www.ccsinfo.com/downloads/setup_mplab_plugin.exe

後は、プロジェクトのウイザードでインストールしたCコンパイラを使用するように設定するだけです。
f:id:furuya02:20150814061449p:plain

動作確認

PIC16F819を使用して、セットアップ後の動作確認をしてみます。

メニューから「Configure」-「Select Device」で「PIC16F819」を選択します
f:id:furuya02:20150814061715p:plain

接続した感じは、下記のとおりです。
f:id:furuya02:20150814061522j:plain

メニューから「Programmer」-「Select Programmer」で「MPLAB ICD2」を選択し、
続いて「Programmer」-「Connect」で接続を確認します。
また、「Programmer」-「Settings...」-「Status」-「Run Self Test」で「Pass」になっているかどうかを確認します。
f:id:furuya02:20150814061926p:plain


セットアップ完了です。