Swift 3でカスタム通知をどのように作成しますか?


回答:


32

このためのプロトコルを使用することもできます

protocol NotificationName {
    var name: Notification.Name { get }
}

extension RawRepresentable where RawValue == String, Self: NotificationName {
    var name: Notification.Name {
        get {
            return Notification.Name(self.rawValue)
        }
    }
}

次に、通知名をenum任意の場所として定義します。例えば:

class MyClass {
    enum Notifications: String, NotificationName {
        case myNotification
    }
}

そしてそれを

NotificationCenter.default.post(name: Notifications.myNotification.name, object: nil)

このようにして、通知名は財団から切り離されNotification.Nameます。また、実装がNotification.Name変更された場合にのみ、プロトコルを変更する必要があります。


これは、私が最初にそれが機能するはずだと思っていた方法です-通知は列挙型である必要があります。トリックをありがとう!
hexdreamer 2017

問題ない!プロパティをプロトコルに準拠する列挙型にのみ追加NotificationNameできるように、コードを編集して拡張機能のname準拠を追加しました。
halil_g 2017

:厳密に等価でなく、IMOより論理的には、このようNotificationName(代わりのRawRepresentable)の拡張を定義することができますextension NotificationName where Self: RawRepresentable, Self.RawValue == String {
jlj

386

それを達成するためのよりクリーンな(私は思う)方法があります

extension Notification.Name {

    static let onSelectedSkin = Notification.Name("on-selected-skin")
}

そして、あなたはこのようにそれを使うことができます

NotificationCenter.default.post(name: .onSelectedSkin, object: selectedSkin)

2
上記のコードを使用しています。これは静的プロパティです。
Cesar Varela

3
とてもきれいで、私はそれがたくさん好きです
トム・ウォルターズ

10
extension NSNotification.Name の代わりにextension Notification.Name 。それ以外の場合のSwift 3の苦情'Notification' is ambiguous for type lookup in this context
lluisgh

9
文字列にタイプミスを付け、タイプされた通知名の値を示すことに対する私の賛成票を得る:P
Dorian Roy

10
これはWWDC 2016セッション207にアップルが提案された方法であることは注目に値するかもしれないdeveloper.apple.com/videos/play/wwdc2016/207
レオン

36

Notification.postは次のように定義されます。

public func post(name aName: NSNotification.Name, object anObject: AnyObject?)

Objective-Cでは、通知名はプレーンなNSStringです。Swiftでは、NSNotification.Nameとして定義されています。

NSNotification.Nameは次のように定義されます。

public struct Name : RawRepresentable, Equatable, Hashable, Comparable {
    public init(_ rawValue: String)
    public init(rawValue: String)
}

私はそれがEnumであり、利点がないように見えるいくつかのカスタム構造体ではないので、これは奇妙なことです。

NSNotification.Nameの通知にはタイプエイリアスがあります。

public typealias Name = NSNotification.Name

混乱する部分は、通知とNSNotificationの両方がSwiftに存在することです

したがって、独自のカスタム通知を定義するには、次のようにします。

public class MyClass {
    static let myNotification = Notification.Name("myNotification")
}

それを呼び出すには:

NotificationCenter.default().post(name: MyClass.myNotification, object: self)

3
いい答えだ。いくつかのコメント:私はそれが列挙型であると期待するので、これは一種の奇妙なことです —列挙型は閉じたセットです。Notification.Name列挙型の場合、新しい通知を定義することはできません。新しいメンバーの追加を許可する必要がある、その他の列挙型のような型には構造体を使用します。(迅速な進化の提案を参照してください。)
リクスター

2
紛らわしいのは、NotificationとNSNotificationの両方がSwiftに存在するNotificationということです— 値型(構造体)であるため、Swiftの値(im)の可変性に関するセマンティクスの恩恵を受けることができます。一般的に、ファンデーションタイプはSwift 3で「NS」を削除しますが、新しいファンデーション値タイプの1つがそれを置き換えるために存在する場合、古い参照タイプが(「NS」名を維持して)留まり、いつでも使用できます。参照セマンティクスまたはそれをサブクラス化する必要があります。提案を参照してください。
rickster

明確にしましょう:エラーのように、通知名は列挙型である必要があります。独自のエラー列挙型を定義して、それらをErrorTypeに準拠させることができます。
hexdreamer 2016年

1
真実— Appleは少なくとも理論的にはNotoficationName(またはそのようなもの)をプロトコルにして、それに準拠するタイプを作成することができたはずです。私は知らないが、彼らがそうしなかった理由はおそらくあります...おそらくObjCブリッジングと関係があるのでしょうか?より良い解決策が見つかった場合は、バグを報告してください(オープンソースに対しては、Foundation Swiftが公開されています)。
リクスター

2
あなたはおそらく小文字で始まるべきであるという点で正しいでしょう。
hexdreamer 2016


11

NSNotification.Nameにカスタム初期化子を追加できます

extension NSNotification.Name {
    enum Notifications: String {
        case foo, bar
    }
    init(_ value: Notifications) {
        self = NSNotification.Name(value.rawValue)
    }
}

使用法:

NotificationCenter.default.post(name: Notification.Name(.foo), object: nil)

1
Swift 3.0.2の小文字の「列挙型」と「init(_ type:type)」
Jalakoo

@Jalakoo caseenum自体ではなく、enum内のs のみを小文字にする必要があります。タイプ名は大文字で、列挙型はタイプです。
マンメール

9

@CesarVarelaが提案したものと同様の別のオプションを提案するかもしれません。

extension Notification.Name {
    static var notificationName: Notification.Name {
        return .init("notificationName")
    }
}

これにより、通知を簡単に投稿および購読できます。

NotificationCenter.default.post(Notification(name: .notificationName))

これがお役に立てば幸いです。


4

私はあちこちで物事を混ぜて自分の実装を行い、これが最も便利だと思いました。興味のある人のために共有する:

public extension Notification {
    public class MyApp {
        public static let Something = Notification.Name("Notification.MyApp.Something")
    }
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(self.onSomethingChange(notification:)),
                                               name: Notification.MyApp.Something,
                                               object: nil)
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    @IBAction func btnTapped(_ sender: UIButton) {
        NotificationCenter.default.post(name: Notification.MyApp.Something,
                                      object: self,
                                    userInfo: [Notification.MyApp.Something:"foo"])
    }

    func onSomethingChange(notification:NSNotification) {
        print("notification received")
        let userInfo = notification.userInfo!
        let key = Notification.MyApp.Something 
        let something = userInfo[key]! as! String //Yes, this works :)
        print(something)
    }
}


2

これは参考です

// Add observer:
NotificationCenter.default.addObserver(self,
    selector: #selector(notificationCallback),
    name: MyClass.myNotification,
    object: nil)

    // Post notification:
    let userInfo = ["foo": 1, "bar": "baz"] as [String: Any]
    NotificationCenter.default.post(name: MyClass.myNotification,
        object: nil,
        userInfo: userInfo)

1

enumを使用する利点は、名前が正しいことをコンパイラーにチェックさせることです。潜在的な問題を減らし、リファクタリングを容易にします。

通知名に引用符付き文字列の代わりに列挙型を使用したい人のために、このコードはトリックを行います:

enum MyNotification: String {
    case somethingHappened
    case somethingElseHappened
    case anotherNotification
    case oneMore
}

extension NotificationCenter {
    func add(observer: Any, selector: Selector, 
             notification: MyNotification, object: Any? = nil) {
        addObserver(observer, selector: selector, 
                    name: Notification.Name(notification.rawValue),
                    object: object)
    }
    func post(notification: MyNotification, 
              object: Any? = nil, userInfo: [AnyHashable: Any]? = nil) {
        post(name: NSNotification.Name(rawValue: notification.rawValue), 
             object: object, userInfo: userInfo)
    }
}

その後、次のように使用できます。

NotificationCenter.default.post(.somethingHappened)

質問とは関係ありませんが、引用符で囲まれた文字列を入力しないようにするために、ストーリーボードセグエでも同じことができます。

enum StoryboardSegue: String {
    case toHere
    case toThere
    case unwindToX
}

extension UIViewController {
    func perform(segue: StoryboardSegue) {
        performSegue(withIdentifier: segue.rawValue, sender: self)
    }
}

次に、ビューコントローラで次のように呼び出します。

perform(segue: .unwindToX)

> NotificationCenter.default.post(.somethingHappened)これはエラーをスローします。拡張に追加したメソッドは、より多くの引数を受け入れます。

0

文字列のみのカスタム通知を使用する場合、クラスを拡張する理由はありませんが、 String

    extension String {
        var notificationName : Notification.Name{
            return Notification.Name.init(self)
        }
    }

0

@CesarVarelaの答えは良いですが、コードを少しきれいにするために、あなたは以下を行うことができます:

extension Notification.Name {
    typealias Name = Notification.Name

    static let onSelectedSkin = Name("on-selected-skin")
    static let onFoo = Name("on-foo")
}

0

Objective-CとSwiftの両方を同時に使用するプロジェクトでこれをきれいに機能させたい場合は、Objective-Cで通知を作成する方が簡単であることがわかりました。

.m / .hファイルを作成します。

//CustomNotifications.h
#import <Foundation/Foundation.h>

// Add all notifications here
extern const NSNotificationName yourNotificationName;
//CustomNotifications.m
#import "CustomNotifications.h"

// Add their string values here
const NSNotificationName yourNotificationName = @"your_notification_as_string";

あなたのMyProject-Bridging-Header.h(プロジェクトにちなんで名付けられた)でそれらをSwiftに公開します。

#import "CustomNotifications.h"

次のようにObjective-Cで通知を使用します。

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(yourMethod:) name:yourNotificationName:nil];

そして、Swift(5)では次のようになります。

NotificationCenter.default.addObserver(self, selector: #selector(yourMethod(sender:)), name: .yourNotificationName, object: nil)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.