nsnotification in swift #cocoa_kansai

89
EZ-NET 熊⾕友宏 http://ez-net.jp/ 2015.11.28 @ 第64回 Cocoa 勉強会関⻄ NSNotification in Swift

Upload: tomohiro-kumagai

Post on 11-Feb-2017

1.743 views

Category:

Software


3 download

TRANSCRIPT

EZ-NET 熊⾕友宏 http://ez-net.jp/

2015.11.28 @ 第64回 Cocoa 勉強会関⻄

NSNotification in Swift

熊谷友宏EZ-NET http://ez-net.jp/ @es_kumagai

CodePiece

iOS, OS X, Apple Watch アプリ

ソースコードを Twitter と Gist に同時投稿できる。

いつもの電卓計算式も見える電卓アプリ。 watchOS 1 対応

音で再配達ゴッド簡単操作で 再配達の申し込み。

EZ-NET IP PhoneiPhone でひかり電話を使う。 自宅 LAN からの利用専用

CodePiece for OS X勉強会を楽しむアプリ

ソースコードを Twitter と Gist に同時投稿できる勉強会で知見をみんなと共有したい時とかに便利!

https://github.com/EZ-NET/CodePiece

できること

✓ コードを添えて呟ける ✓ 普通の呟きもできる ✓ ハッシュタグを付け忘れずに呟ける

(Pre-Release)

熊谷友宏EZ-NET http://ez-net.jp/ @es_kumagai

横浜 iPhone 開発者勉強会#yidev

勉強会

わいわい・ゆるく、iPhone 開発者のみんなで楽しく過ごすのが目的の会

【 横浜・馬車道 】カジュアル Swift 勉強会

#cswift

ゆるくみんなで Swift を語らえる場を作りたくて始めた会

【 横浜・青葉台 】

第21回を 2015-12-12 に開催 第4回を 2015-12-26 に開催

熊谷友宏EZ-NET http://ez-net.jp/ @es_kumagai

Xcode 5 徹底解説 MOSA

書籍 / 登壇

Xcode 5 の全機能を徹底的に解説した本

OSX/iOS 系の歴史深い有料会員制の勉強会

紙版は絶版、電子書籍は販売中 秀和システム 10x-Eng.com で取扱予定

Xcode 7 でも役立つはず 法人会員も多数

NSNotificationin Swift

概要NSNotification

▶ 何かが起こったことを 通知 で伝える ▶ 通知は 通知センター が管理 ▶ 知りたい通知を通知センターに登録 ▶ 通知があると知らせてくれる

NSNotificationCenter通知センター

POST

OBSERVE 通知センター NSNotificationCenter

Object通知ハンドラー Object

通知ハンドラー

Object

Object

通知を伝達

通知を送信- postNotification:

伝達を要請

伝達を要請

- addObserver: selector: name: object:

A. 伝達を要請 B. 通知を送信 C. 通知を伝達

NSNotification通知

▶ 通知は 名前 で区別される ▶ 通知元の オブジェクト が添えられる ▶ 任意の 追加情報 が付与されることも

通知 NSNotification

name: String

object: AnyObject?

userInfo: [NSObject : AnyObject]?

▶ NSApplicationWillTerminateNotificationアプリが終了するときに通知される

▶ ACAccountStoreDidChangeNotificationアカウントストアが変更されたときに通知される

▶ AVAudioSessionRouteChangeNotification音声の出力先が変化したときに通知される

▶ NSWorkspaceWillSleepNotificationコンピューターがスリープするときに通知される

▶ kReachabilityChangedNotification ネットワーク環境が変化したときに通知される

用意されている通知(ほんの1例)通知

メリット的な特徴

通知センター

▶ 突如発生する状況変化に対応しやすい ▶ 各オブジェクトが自主的に対応可能 ▶ 全体を把握して制御する負担を軽減

デメリット的な特徴▶ 他オブジェクトと足並みを揃えにくい ▶ 誰が通知を受け取るか分からない ▶ 通知の発生場所を掴みにくい

使いどころ通知センター

▶ 状況変化を受動的に受けたいときに使う ▶ 自発的な状況確認で充分なら使わない ▶ 伝えたい相手が明確なときは使わない ▶ 継続的な任務の遂行に使うと効果的

具体例通知センター

メソッド デリゲート 通知

Timeline View Controller

SNS Controller

View Controller

Tweet Text

Tweet Button

NSApplication

内容の変更を報告

アカウントの検証

NSAppDelegate

ビューの表示

ボタンの有効化

自動更新の開始最新データを取得

SNS の利用準備

スリープからの復帰を通知

自動更新の再開最新データを取得

初期化を実行

起動完了を報告

変更内容を処理

自動更新の中断

スリープ状態へ移行を通知

通知センターを使う

ふたつの観点通知センターを使う

A. OBSERVE … 通知の監視 B. POST … 通知の送信

OBSERVE通知の監視

準備

OBSERVE

A. 通知を受け取るメソッドを準備

1. 通知センターを取得 2. 通知の伝達を要請

実施

始末Z. 通知の伝達要請を解除

A. 通知を受け取るメソッドを準備OBSERVE

▶ インスタンスメソッドで実装する ▶ 引数で NSNotification を受け取る

func sampleNotificationReceived( notification: NSNotification) {

// ここに通知を受け取ったときの処理を書く

}

1. 通知センターを取得OBSERVE

▶ NSNotificationCenter のメソッドで取得 ▶ シングルトンなインスタンス

let notificationCenter = NSNotificationCenter.defaultCenter()

2. 通知の伝達を要請OBSERVE

▶ NSNotificationCenter に対して要請 ▶ 伝達先のインスタンスとセレクタを渡す

notificationCenter.addObserver( self, selector: "sampleNotificationReceived:", name: ESSampleNotification, object: nil)

たとえば viewWillAppear で実行

Z. 通知の伝達要請を解除OBSERVE

▶ NSNotificationCenter から登録解除 ▶ 適切なタイミングで必ず実施すること

notificationCenter.removeObserver( self, name: ESSampleNotification, object: nil)

たとえば viewWillDisappear で実行

まとめ通知を監視する

NSNotificationCenterA Observer

addObserver:selector:name:object:

sampleNotificationReceived:

ESSampleNotification

ESSampleNotification

sampleNotificationReceived: ESSampleNotification

removeObserver:name:object:

ESSampleNotification

POST通知の送信

準備

POST

A. 通知の仕方を決める

1. 通知センターを取得 2. 通知を送信

実施

A. 通知の仕方を決めるPOST

▶ 通知名を文字列で決める ▶ object で扱うものを決める

一般に “通知を送信したオブジェクト” ▶ userInfo で扱うものを決める

完全に自由形式

通知 NSNotification

name: String

object: AnyObject?

userInfo: [NSObject : AnyObject]?

1. 通知センターを取得POST

▶ NSNotificationCenter のメソッドで取得 ▶ シングルトンなインスタンス

let notificationCenter = NSNotificationCenter.defaultCenter()

2. 通知の送信POST

▶ NSNotification を作る ▶ 通知センターに送信する

let notification = NSNotification( name: ESSampleNotification, object: self, userInfo: nil)

notificationCenter .postNotification(notification)

まとめ通知を送信する

NSNotificationCenterAn Observer

sampleNotificationReceived:

ESSampleNotification

ESSampleNotification

postNotification:

An Object

An Observer

sampleNotificationReceived:

ESSampleNotification

気になったところin Swift

通知がどんな情報を持っているか判らない気になったところ (1/5)

▶ 通知名は String 型→ できれば型で扱いたい

▶ object は AnyObject? 型→ 具体的な型で扱いたい

▶ userInfo は [NSObject : AnyObject]? 型 → 型が不明瞭な上に自由度が高すぎる

通知 NSNotification

name: String

object: AnyObject?

userInfo: [NSObject : AnyObject]?

notificationCenter.addObserver( self, selector: "sampleNotificationReceived:", name: ESSampleNotification, object: nil)

通知を受けるメソッドの作成が必要気になったところ (2/5)

▶ クロージャーを使いたい ▶ セレクターは使いたくない

let notificationCenter = NSNotificationCenter.defaultCenter()

通知センターを都度取得する必要がある気になったところ (3/5)

▶ 手続き的な事前準備をしたくない ▶ コードをもっとスリムに書きたい

notificationCenter.addObserver( self, selector: "sampleNotificationReceived:", name: ESSampleNotification, object: nil)

通知センター主体のコーディングになる気になったところ (4/5)

▶ 通知センターを意識しないで書きたい ▶ Notification と Observer に着目したい

notificationCenter .postNotification(notification)

notificationCenter.removeObserver( self, name: ESSampleNotification, object: nil)

監視の解除を手動で実施しないといけない気になったところ (5/5)

▶ 後始末が必要なのは原始的? ▶ 監視解除を書くのに煩わされたくない

これらを解消するとどんなコードになるんだろう…

ESNotificationin Swift

https://github.com/EZ-NET/ESNotification

構想ESNotification

▶ 通知は文字列ではなく型で区別 ▶ 通知センターを意識せずに使える ▶ 通知時の処理をクロージャーで書ける ▶ 監視解除が自動で行われる ▶ 既存の通知センターと互換

https://github.com/EZ-NET/ESNotification

大まかな構成ESNotification

NotificationManager

An NotificationObject

Notification

class observeBy(_:handler:)post

NSNotificationCenter

_notificationHandlers

_rawNotificationObserver

NSNotification

ESNotification を使う

ESNotification

in Swift

POST通知の送信

ESNotification

POST

ESNotification

従来▶ 通知の仕方を決める

▶ 通知センターを取得 ▶ 通知を送信

準備A. 通知型を設計

1. 通知を送信実施

class LocationChangedNotification : Notification { var x: Int var y: Int

}

A. 通知型を設計POST

▶ 名前の間違いや衝突をコンパイラで回避可能 ▶ 扱う情報を明確に持てる

ESNotification

LocationChangedNotification(x:10, y:3).post()

1. 通知を送信POST

▶ 通知センターの取得が不要 ▶ 通知を主体に考えられる

ESNotification

OBSERVE通知の監視

ESNotification

1. 目的の通知を監視

準備✓ なし

実施

OBSERVE

ESNotification

従来▶ 通知を受け取るための

メソッドを準備

▶ 通知センターを取得 ▶ 通知の伝達を要請 ▶ 通知の伝達要請を解除

LocationChangedNotification .observeBy(self) { notification in

}

1. 目的の通知を監視

▶ 通知時の処理をクロージャーで指定 ▶ 文字列によるセレクター指定が不要

OBSERVE

ESNotification

RELEASE監視の解除

ESNotification

監視の解除方針RELEASE

▶ NotificationManager はObserver を weak で管理

▶ Observer が解放されると関連する監視を解除

▶ 明示的な監視解除も可能にする

ESNotification

let handle = LocationChangedNotification .observeBy(self) { notification in … }

// 不要になったタイミングでハンドルをリリース handle.release()

監視を明示的に解除する場合RELEASE

▶ 監視登録時にハンドルを取得しておく ▶ ハンドルに対して release を実行する

ESNotification

NotificationManager.release(observer: self)

関連する監視を全て解除する場合RELEASE

▶ NotificationManager を使う ▶ release メソッドに observer を渡す

ESNotification

通知センターとの互換性compatibility

ESNotification

notificationCenter.addObserver( self, selector: "locationChangedNotificationReceived:", name: LocationChangedNotification.notificationIdentifier, object: nil)

func locationChangedNotificationReceived(n: NSNotification) { let notification = n.object as! LocationChangedNotification }

通知型を通知センターで監視できる通知センターとの互換性

▶ 通知名には notificationIdentifier を使う ▶ 受信した通知は object で渡される

ESNotification

NamedNotification.observe( ESSampleLegacyNotification, by: self) { notification in

}

名前付き通知を監視できる

▶ NamedNotification を監視する ▶ 通知センターからの通知も取得可能

通知センターとの互換性

ESNotification

NamedNotification(ESSampleNotification, object: self, userInfo: nil).post()

名前付き通知を送信できる通知センターとの互換性

▶ NamedNotification で送信する ▶ NSNotification と同等の通知ができる

ESNotification

LocationChangedNotification .observeBy(self) { notice in

print("Location:\(notice.x),\(notice.y)") }

送信

ESNotification

監視

ESNotification

LocationChangedNotification(x:10, y:3).post()

使い方のまとめ

もう少し手軽に使いたい

ESNotification

Observer を暗黙的に指定したい

ESNotification

🤔

LocationChangedNotification .observeBy( self ) { notification in

… }

理由はここで self を指定することが多いから

▶ 既定の引数で self を指定する方法は? ▶ またはその代替案は?

Observer を暗黙指定したい

ESNotification

self を省略する手はなさそう … 🙁

ESNotification

Observer を主体にしてみる

ESNotification

protocol NotificationObservable : AnyObject {

}

A. Observable プロトコルを作成

▶ 通知監視できることを意味するプロトコル ▶ ここに通知監視に関する機能を備える

Observer を主体にする

ESNotification

extension NotificationObservable {

func observeNotificationNamed( name: String, handler:(NamedNotification)->Void) -> HandlerID

func observeNotification<T:NotificationProtocol>( handler: (T) -> Void) -> HandlerID

func releaseObservingNotifications() }

B. Observable に機能を実装

▶ 通知を監視するための機能をつける ▶ 任意のタイミングで監視解除も可能にする

Observer を主体にする

ESNotification

import ESNotification

extension ViewController : NotificationObservable { }

C. 通知を監視可能にする

▶ NotificationObservable を適用する ▶ 通知監視に関する振る舞いが備わる

Observer を主体にする

ESNotification

self.observeNotification { (notification: LocationChangedNotification) in }

1. 通知を監視する

▶ 自身のメソッドを使って監視を開始 ▶ 引数で監視する通知を指定

Observer を主体にする

ESNotification

self.releaseObservingNotifications

2. 通知監視を解除

▶ 自身のメソッドを使って監視を解除 ▶ 自身が登録した監視すべてが解除される

Observer を主体にする

ESNotification

self.observeNotification { (notice:LocationChangedNotification) in print("Location:\(notice.x),\(notice.y)") }

送信

Observer を主体にする

監視

ESNotification

LocationChangedNotification(x:10, y:3).post()

使い方のまとめ

Observer 主体だと監視する通知を読み取りにくい

ESNotification

🤔

Notification 主体

監視する通知を読み取りにくい

Observer 主体

ESNotification

self.observeNotification { (notification: LocationChangedNotification ) in

}

LocationChangedNotification .observeBy(self) { notification in

}

ここまで読んで やっと分かる

扱う通知が すぐに分かる

通知型の登場位置による差異

やっぱり Notification 主体でself を省略したい

self.observeNotification { (notification: LocationChangedNotification) in

}

LocationChangedNotification.observeBy(self) { notification in

}

ESNotification

でも実現方法が見つからない …

型の表記を少し前に持ってきてみる

ESNotification

extension NotificationObservable {

func observeNotification<T:NotificationProtocol>( handler: (T) -> Void) -> HandlerID

func observeNotification<T:NotificationProtocol>( _ : T.Type, handler: (T) -> Void) -> HandlerID

}

型情報を引数で指定

▶ 型情報を引数で渡す ▶ ただし引数の型情報は使わない

型の表記を少し前に持ってくる

ESNotification

self.observeNotification { (notification: LocationChangedNotification ) in

}

型情報を引数にとらない場合

型の表記を少し前に持ってくる

型情報を引数にとる場合

ESNotification

self.observeNotification( LocationChangedNotification.self ) { notification in

}

ここでようやく 登場した通知型が

視覚的な差異

ほんの少しだけ 前に登場

self.observeNotification( LocationChangedNotification.self) { notification in

}

構文的な特徴型の表記を少し前に持ってくる

ESNotification

通知型が少し前に登場

型推論で 省略できる

この self は 欲しくない …

使うとき

定義func observeNotification<T:NotificationProtocol>( _ : T.Type, handler: (T) -> Void) -> HandlerID }

こちらも self がカッコわるい … 🙁

ESNotification

良い方法ありませんか?スマートに self を省略したい

ESNotification

in Swift使い方のまとめ

ESNotification

ESNotification in Swift

ESNotification

// 通知を定義する final class SampleNotification : Notification { }

class ViewController: NSViewController, NotificationObservable {

override func viewDidLoad() { super.viewDidLoad() // 通知を監視する self.observeNotification { (notification: SampleNotification) in

print("\(notification) received.") } } @IBAction func sendNotification(sender:AnyObject?) { // 通知を送信する SampleNotification().post() } }

シンプルな使用例

ESNotificationin Objective-C

ESNotification

必要な機能の検討ESNotification in Objective-C

▶ NSNotificationCenter をObjective-C で使うのは違和感がない

▶ ESNotification の通知型をObjective-C からも送信したい

ESNotification

NSNotificationCenter を拡張通知に関する機能追加

▶ NSNotificationCenter に機能を追加 ▶ 型拡張で直接実装

extension NSNotificationCenter { func addObserver(observer: AnyObject, selector: Selector, ESNotification: NotificationProtocol.Type, object: AnyObject?) func removeObserver(observer: AnyObject, ESNotification: NotificationProtocol.Type, object: AnyObject?)

func postESNotification(notification: Postable)

} ESNotification

// Swift で定義する場合 class ESSampleNotification : NSObject, Notification {

}

// Objective-C で定義する場合 #import <ESNotification/ESNotification-Swift.h>

@interface ESSampleNotification : NSObject <ESNotification>

@end

Swift または Objective-C での定義方法通知クラスを定義する

▶ Notification プロトコルに準拠させる ▶ Objective-C 互換クラスとして定義

ESNotification

NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter]; ESObjCNotification* notification = [[ESObjCNotification alloc] init];

[notificationCenter postESNotification:notification];

in Objective-C通知を送信する

▶ 通知センターを使って送信 ▶ 従来に近い送信方法(使うメソッドは異なる)

ESNotification

NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter addObserver:self selector:@selector(objcNotificationReceived:) ESNotification:[ESObjCNotification class] object:nil];

in Objective-C

▶ 監視対象を通知型で指定 ▶ 従来に近い送信方法(使うメソッドは異なる)

通知を監視する

ESNotification

NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter removeObserver:self ESNotification:[ESObjCNotification class] object:nil];

in Objective-C

▶ 慣例に倣い、通知解除を手動で実行 ▶ 解除対象を通知型で指定

通知監視を解除する

ESNotification

in Objective-C使い方のまとめ

ESNotification

ESNotification in Objective-C

#import <ESNotification/ESNotification-Swift.h>

// 通知を定義する @interface ESSampleNotification : NSObject <ESNotification> @end

@implementation ESSampleNotification @end

// 通知を使う @implementation ESViewController

- (void)sendNotificationAction:(id)sender { // 通知を送信する NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter]; ESSampleNotification* notification = [[ESSampleNotification alloc] init];

[notificationCenter postESNotification:notification]; }

@end

シンプルな送信例

ESNotification

ESNotification in Objective-C

#import <ESNotification/ESNotification-Swift.h>

@implementation ESViewController

- (void)objcNotificationReceived:(NSNotification*)notification { ESSampleNotification* notice = notification.object; }

- (void)viewWillAppear { [super viewWillAppear]; NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];

[notificationCenter addObserver:self selector:@selector(objcNotificationReceived:) ESNotification:[ESSampleNotification class] object:nil]; } }

- (void)viewWillDisappear { [super viewWillDisappear];

NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];

[notificationCenter removeObserver:self ESNotification:[ESSampleNotification class] object:nil]; }

@end

シンプルな監視例

ESNotification

ESNotificationin Swift & Objective-C

https://github.com/EZ-NET/ESNotification

まとめNSNotification in Swift

▶ NSNotification 概要 ▶ もっと気軽に使いたい

✓ ESNotification in Swift ✓ Swift みたいに気軽に使えるように

▶ Objective-C でも使いたい ✓ ESNotification in Objective-C ✓ 従来と同じ感覚で使えるように

https://github.com/EZ-NET/ESNotification