├── Examples
├── iOS Example
│ ├── iOS Example
│ │ ├── Assets.xcassets
│ │ │ ├── Contents.json
│ │ │ ├── AccentColor.colorset
│ │ │ │ └── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── Preview Content
│ │ │ └── Preview Assets.xcassets
│ │ │ │ └── Contents.json
│ │ ├── iOS_ExampleApp.swift
│ │ ├── ContentView.swift
│ │ ├── Util.swift
│ │ └── AppDelegate.swift
│ └── iOS Example.xcodeproj
│ │ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ ├── xcshareddata
│ │ └── xcschemes
│ │ │ └── iOS Example.xcscheme
│ │ └── project.pbxproj
└── TypedNotification_iOS
│ └── TypedNotification_iOS
│ ├── Assets.xcassets
│ ├── Contents.json
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── TypedNotification_iOSApp.swift
│ └── ContentView.swift
├── Sources
├── TypedNotificationsMacro
│ ├── TypedNotificationsMacros.swift
│ └── NotificationMacro.swift
└── TypedNotifications
│ ├── NotificagtionMacro.swift
│ ├── TypedNotificationCenter+Combine.swift
│ ├── UIKit
│ ├── UIApplication.swift
│ ├── UIScene.swift
│ └── UIResponder.swift
│ ├── NotificationCenter+Extensions.swift
│ ├── TypedNotification.swift
│ ├── TypedNotificationDefinition.swift
│ ├── TypedNotificationCenter.swift
│ ├── TypedNotifications.docc
│ └── TypedNotifications.md
│ └── CoreData
│ └── NSManagedObjectContext.swift
├── TypedNotification.xcworkspace
├── xcshareddata
│ ├── IDEWorkspaceChecks.plist
│ └── swiftpm
│ │ └── Package.resolved
└── contents.xcworkspacedata
├── .github
└── workflows
│ ├── test.yml
│ └── docc.yml
├── LICENSE
├── Package.swift
├── Package@swift-5.9.swift
├── Tests
├── TypedNotificationsMacroTests
│ └── NotificationMacroTest.swift
└── TypedNotificationsTests
│ └── TypedNotificationsTests.swift
├── .swiftpm
└── xcode
│ └── xcshareddata
│ └── xcschemes
│ ├── TypedNotificationsTests.xcscheme
│ └── TypedNotifications.xcscheme
├── README.md
└── .gitignore
/Examples/iOS Example/iOS Example/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Examples/TypedNotification_iOS/TypedNotification_iOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Examples/iOS Example/iOS Example/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Examples/TypedNotification_iOS/TypedNotification_iOS/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Examples/iOS Example/iOS Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/iOS Example/iOS Example/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Examples/TypedNotification_iOS/TypedNotification_iOS/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Sources/TypedNotificationsMacro/TypedNotificationsMacros.swift:
--------------------------------------------------------------------------------
1 | import SwiftCompilerPlugin
2 | import SwiftSyntaxMacros
3 |
4 | @main
5 | struct TypedNotificationsMacroPlugin: CompilerPlugin {
6 | let providingMacros: [Macro.Type] = [
7 | NotificationMacro.self
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/Examples/iOS Example/iOS Example/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/TypedNotification.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Examples/iOS Example/iOS Example/iOS_ExampleApp.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | @main
4 | struct iOS_ExampleApp: App {
5 |
6 | @UIApplicationDelegateAdaptor(AppDelegate.self)
7 | var appDelegate
8 |
9 | var body: some Scene {
10 | WindowGroup {
11 | ContentView()
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Examples/TypedNotification_iOS/TypedNotification_iOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Examples/iOS Example/iOS Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Examples/TypedNotification_iOS/TypedNotification_iOS/TypedNotification_iOSApp.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | @main
4 | struct TypedNotification_iOSApp: App {
5 |
6 | @UIApplicationDelegateAdaptor(AppDelegate.self)
7 | var appDelegate
8 |
9 | var body: some Scene {
10 | WindowGroup {
11 | ContentView()
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Examples/TypedNotification_iOS/TypedNotification_iOS/ContentView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct ContentView: View {
4 | var body: some View {
5 | VStack {
6 | Image(systemName: "globe")
7 | .imageScale(.large)
8 | .foregroundStyle(.tint)
9 | Text("Hello, world!")
10 | }
11 | .padding()
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/TypedNotification.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
10 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Examples/iOS Example/iOS Example/ContentView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct ContentView: View {
4 | @State var text = ""
5 |
6 | var body: some View {
7 | VStack {
8 | Image(systemName: "globe")
9 | .imageScale(.large)
10 | .foregroundStyle(.tint)
11 | Text("Hello, world!")
12 | TextField("textfield", text: $text)
13 | }
14 | .padding()
15 | }
16 | }
17 |
18 | #Preview {
19 | ContentView()
20 | }
21 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 |
9 | jobs:
10 | test:
11 | strategy:
12 | matrix:
13 | swift_version:
14 | - "5.9"
15 | - "5.10.0"
16 | - "6.0"
17 | runs-on: macos-latest
18 | steps:
19 | - uses: SwiftyLab/setup-swift@latest
20 | with:
21 | swift-version: ${{ matrix.swift_version }}
22 | - uses: actions/checkout@v4
23 | - name: Run tests
24 | run: swift test
25 |
--------------------------------------------------------------------------------
/Examples/iOS Example/iOS Example/Util.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import TypedNotifications
3 |
4 | func printNotification(_ notification: TypedNotification) {
5 | let text = """
6 | Received Notification:
7 | ├─ name = \(notification.name.rawValue),
8 | ├─ storage = \(notification.storage),
9 | └─ object = \(notification.object.debugDescription)
10 | """
11 | print(text)
12 | }
13 |
14 | func printNotification(_ notification: Notification) {
15 | let userInfo = notification.userInfo
16 | userInfo?.forEach({ key, value in
17 | print("\(key) = \(value)")
18 | })
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/TypedNotifications/NotificagtionMacro.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// A macro for defining a notification whose notification name is attached property name.
4 | @attached(accessor)
5 | public macro Notification() = #externalMacro(module: "TypedNotificationsMacro", type: "NotificationMacro")
6 |
7 | /// A macro for defining a notification.
8 | ///
9 | /// - Parameter name: A name of the notification.
10 | @attached(accessor)
11 | public macro Notification(name: String) = #externalMacro(module: "TypedNotificationsMacro", type: "NotificationMacro")
12 |
13 |
14 | /// A macro for defining a notification.
15 | ///
16 | /// - Parameter name: A name of the notification.
17 | @attached(accessor)
18 | public macro Notification(name: Notification.Name) = #externalMacro(module: "TypedNotificationsMacro", type: "NotificationMacro")
19 |
--------------------------------------------------------------------------------
/Sources/TypedNotifications/TypedNotificationCenter+Combine.swift:
--------------------------------------------------------------------------------
1 | #if canImport(Combine)
2 | import Combine
3 | import Foundation
4 |
5 | extension TypedNotificationCenter {
6 | public func publisher(
7 | for definition: TypedNotificationDefinition,
8 | object: Object? = nil
9 | ) -> some Publisher, Never> {
10 | notificationCenter.publisher(for: definition.name, object: object)
11 | .compactMap { TypedNotification($0, basedOn: definition) }
12 | }
13 | }
14 |
15 | extension NotificationCenter {
16 | public func publisher(
17 | for definition: TypedNotificationDefinition,
18 | object: Object? = nil
19 | ) -> AnyPublisher, Never> {
20 | publisher(for: definition.name, object: object)
21 | .compactMap { TypedNotification($0, basedOn: definition) }
22 | .eraseToAnyPublisher()
23 | }
24 | }
25 | #endif
26 |
--------------------------------------------------------------------------------
/Sources/TypedNotifications/UIKit/UIApplication.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS) || os(visionOS) || os(tvOS) || targetEnvironment(macCatalyst)
2 | import UIKit
3 |
4 | extension UIApplication {
5 | @Notification(name: UIApplication.didBecomeActiveNotification)
6 | public static var didBecomeActiveTypedNotification: TypedNotificationDefinition
7 |
8 | @Notification(name: UIApplication.didEnterBackgroundNotification)
9 | public static var didEnterBackgroundTypedNotification: TypedNotificationDefinition
10 |
11 | @Notification(name: UIApplication.willEnterForegroundNotification)
12 | public static var willEnterForegroundTypedNotification: TypedNotificationDefinition
13 |
14 | @Notification(name: UIApplication.willResignActiveNotification)
15 | public static var willResignActiveTypedNotification: TypedNotificationDefinition
16 |
17 | @Notification(name: UIApplication.willTerminateNotification)
18 | public static var willTerminateTypedNotification: TypedNotificationDefinition
19 | }
20 | #endif
21 |
--------------------------------------------------------------------------------
/.github/workflows/docc.yml:
--------------------------------------------------------------------------------
1 | name: DocC
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | jobs:
8 | DocC:
9 | runs-on: macos-15
10 | env:
11 | DEVELOPER_DIR: "/Applications/Xcode_16.2.app/Contents/Developer"
12 | steps:
13 | - uses: actions/checkout@v4
14 | - name: Build DocC
15 | run: |
16 | swift package --allow-writing-to-directory ./docs generate-documentation \
17 | --target TypedNotifications \
18 | --disable-indexing \
19 | --output-path ./docs \
20 | --transform-for-static-hosting \
21 | --hosting-base-path typed-notifications
22 | - uses: actions/upload-pages-artifact@v3
23 | id: docs
24 | with:
25 | path: docs
26 | DeployDocC:
27 | needs: DocC
28 | permissions:
29 | pages: write
30 | id-token: write
31 | environment:
32 | name: github-pages
33 | url: ${{ steps.deployment.outputs.page_url }}
34 | runs-on: ubuntu-latest
35 | steps:
36 | - name: Deploy to GitHub Pages
37 | id: docs
38 | uses: actions/deploy-pages@v4
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 matsuji
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Sources/TypedNotifications/UIKit/UIScene.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS) || os(visionOS) || os(tvOS) || targetEnvironment(macCatalyst)
2 | import UIKit
3 |
4 | extension UIScene {
5 | @Notification(name: UIScene.willConnectNotification)
6 | public static var willConnectTypedNotification: TypedNotificationDefinition
7 |
8 | @Notification(name: UIScene.didActivateNotification)
9 | public static var didActivateTypedNotification: TypedNotificationDefinition
10 |
11 | @Notification(name: UIScene.didDisconnectNotification)
12 | public static var didDisconnectTypedNotification: TypedNotificationDefinition
13 |
14 | @Notification(name: UIScene.willEnterForegroundNotification)
15 | public static var willEnterForegroundTypedNotification: TypedNotificationDefinition
16 |
17 | @Notification(name: UIScene.willDeactivateNotification)
18 | public static var willDeactivateTypedNotification: TypedNotificationDefinition
19 |
20 | @Notification(name: UIScene.didEnterBackgroundNotification)
21 | public static var didEnterBackgroundTypedNotification: TypedNotificationDefinition
22 | }
23 | #endif
24 |
--------------------------------------------------------------------------------
/TypedNotification.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "swift-docc-plugin",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/apple/swift-docc-plugin",
7 | "state" : {
8 | "revision" : "26ac5758409154cc448d7ab82389c520fa8a8247",
9 | "version" : "1.3.0"
10 | }
11 | },
12 | {
13 | "identity" : "swift-docc-symbolkit",
14 | "kind" : "remoteSourceControl",
15 | "location" : "https://github.com/apple/swift-docc-symbolkit",
16 | "state" : {
17 | "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34",
18 | "version" : "1.0.0"
19 | }
20 | },
21 | {
22 | "identity" : "swift-syntax",
23 | "kind" : "remoteSourceControl",
24 | "location" : "https://github.com/apple/swift-syntax.git",
25 | "state" : {
26 | "revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036",
27 | "version" : "509.0.2"
28 | }
29 | },
30 | {
31 | "identity" : "userinfo-representable",
32 | "kind" : "remoteSourceControl",
33 | "location" : "https://github.com/mtj0928/userinfo-representable",
34 | "state" : {
35 | "revision" : "c7644d3e0c40cd9bf886f978d6916e03166e937c",
36 | "version" : "1.0.0"
37 | }
38 | }
39 | ],
40 | "version" : 2
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/TypedNotifications/NotificationCenter+Extensions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension NotificationCenter {
4 | public func post(
5 | _ definition: TypedNotificationDefinition,
6 | storage: Storage,
7 | object: Object? = nil
8 | ) {
9 | let userInfo = definition.encode(storage)
10 | let notification = Notification(name: definition.name, object: object, userInfo: userInfo)
11 | post(notification)
12 | }
13 |
14 | public func post