├── .github
└── workflows
│ ├── docc.yml
│ └── test.yml
├── .gitignore
├── .swiftpm
└── xcode
│ └── xcshareddata
│ └── xcschemes
│ ├── TypedNotifications.xcscheme
│ └── TypedNotificationsTests.xcscheme
├── Examples
├── TypedNotification_iOS
│ └── TypedNotification_iOS
│ │ ├── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ │ ├── ContentView.swift
│ │ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ │ └── TypedNotification_iOSApp.swift
└── iOS Example
│ ├── iOS Example.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── iOS Example.xcscheme
│ └── iOS Example
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── ContentView.swift
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── Util.swift
│ └── iOS_ExampleApp.swift
├── LICENSE
├── Package.swift
├── Package@swift-5.9.swift
├── README.md
├── Sources
├── TypedNotifications
│ ├── CoreData
│ │ └── NSManagedObjectContext.swift
│ ├── NotificagtionMacro.swift
│ ├── NotificationCenter+Extensions.swift
│ ├── TypedNotification.swift
│ ├── TypedNotificationCenter+Combine.swift
│ ├── TypedNotificationCenter.swift
│ ├── TypedNotificationDefinition.swift
│ ├── TypedNotifications.docc
│ │ └── TypedNotifications.md
│ └── UIKit
│ │ ├── UIApplication.swift
│ │ ├── UIResponder.swift
│ │ └── UIScene.swift
└── TypedNotificationsMacro
│ ├── NotificationMacro.swift
│ └── TypedNotificationsMacros.swift
├── Tests
├── TypedNotificationsMacroTests
│ └── NotificationMacroTest.swift
└── TypedNotificationsTests
│ └── TypedNotificationsTests.swift
└── TypedNotification.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
├── IDEWorkspaceChecks.plist
└── swiftpm
└── Package.resolved
/.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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Generated by gibo (https://github.com/simonwhitaker/gibo)
2 | ### https://raw.github.com/github/gitignore/4488915eec0b3a45b5c63ead28f286819c0917de/Swift.gitignore
3 |
4 | # Xcode
5 | #
6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
7 |
8 | ## User settings
9 | xcuserdata/
10 |
11 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
12 | *.xcscmblueprint
13 | *.xccheckout
14 |
15 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
16 | build/
17 | DerivedData/
18 | *.moved-aside
19 | *.pbxuser
20 | !default.pbxuser
21 | *.mode1v3
22 | !default.mode1v3
23 | *.mode2v3
24 | !default.mode2v3
25 | *.perspectivev3
26 | !default.perspectivev3
27 |
28 | ## Obj-C/Swift specific
29 | *.hmap
30 |
31 | ## App packaging
32 | *.ipa
33 | *.dSYM.zip
34 | *.dSYM
35 |
36 | ## Playgrounds
37 | timeline.xctimeline
38 | playground.xcworkspace
39 |
40 | # Swift Package Manager
41 | #
42 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
43 | Packages/
44 | # Package.pins
45 | # Package.resolved
46 | # *.xcodeproj
47 | #
48 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
49 | # hence it is not needed unless you have added a package configuration file to your project
50 | .swiftpm/configuration/registries.json
51 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
52 |
53 | .build/
54 |
55 | # CocoaPods
56 | #
57 | # We recommend against adding the Pods directory to your .gitignore. However
58 | # you should judge for yourself, the pros and cons are mentioned at:
59 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
60 | #
61 | # Pods/
62 | #
63 | # Add this line if you want to avoid checking in source code from the Xcode workspace
64 | # *.xcworkspace
65 |
66 | # Carthage
67 | #
68 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
69 | # Carthage/Checkouts
70 |
71 | Carthage/Build/
72 |
73 | # Accio dependency management
74 | Dependencies/
75 | .accio/
76 |
77 | # fastlane
78 | #
79 | # It is recommended to not store the screenshots in the git repo.
80 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
81 | # For more information about the recommended setup visit:
82 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
83 |
84 | fastlane/report.xml
85 | fastlane/Preview.html
86 | fastlane/screenshots/**/*.png
87 | fastlane/test_output
88 |
89 | # Code Injection
90 | #
91 | # After new code Injection tools there's a generated folder /iOSInjectionProject
92 | # https://github.com/johnno1962/injectionforxcode
93 |
94 | iOSInjectionProject/
95 |
96 |
97 | ### https://raw.github.com/github/gitignore/4488915eec0b3a45b5c63ead28f286819c0917de/Global/Xcode.gitignore
98 |
99 | ## User settings
100 | xcuserdata/
101 |
102 | ## Xcode 8 and earlier
103 | *.xcscmblueprint
104 | *.xccheckout
105 |
106 |
107 | ### https://raw.github.com/github/gitignore/4488915eec0b3a45b5c63ead28f286819c0917de/Global/macOS.gitignore
108 |
109 | # General
110 | .DS_Store
111 | .AppleDouble
112 | .LSOverride
113 |
114 | # Icon must end with two \r
115 | Icon
116 |
117 | # Thumbnails
118 | ._*
119 |
120 | # Files that might appear in the root of a volume
121 | .DocumentRevisions-V100
122 | .fseventsd
123 | .Spotlight-V100
124 | .TemporaryItems
125 | .Trashes
126 | .VolumeIcon.icns
127 | .com.apple.timemachine.donotpresent
128 |
129 | # Directories potentially created on remote AFP share
130 | .AppleDB
131 | .AppleDesktop
132 | Network Trash Folder
133 | Temporary Items
134 | .apdisk
135 |
136 |
137 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcshareddata/xcschemes/TypedNotifications.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
42 |
43 |
49 |
50 |
56 |
57 |
58 |
59 |
61 |
62 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcshareddata/xcschemes/TypedNotificationsTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
16 |
18 |
24 |
25 |
26 |
27 |
28 |
38 |
39 |
45 |
46 |
48 |
49 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/TypedNotification_iOS/TypedNotification_iOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/iOS Example/iOS Example.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 187E45FC2B0E23E400BB20DD /* TypedNotifications in Frameworks */ = {isa = PBXBuildFile; productRef = 187E45FB2B0E23E400BB20DD /* TypedNotifications */; };
11 | 18D464C12AAC6B900073F946 /* iOS_ExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18D464C02AAC6B900073F946 /* iOS_ExampleApp.swift */; };
12 | 18D464C32AAC6B900073F946 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18D464C22AAC6B900073F946 /* ContentView.swift */; };
13 | 18D464C52AAC6B910073F946 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 18D464C42AAC6B910073F946 /* Assets.xcassets */; };
14 | 18D464C82AAC6B910073F946 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 18D464C72AAC6B910073F946 /* Preview Assets.xcassets */; };
15 | 18D464D22AAC6BBF0073F946 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18D464D12AAC6BBF0073F946 /* AppDelegate.swift */; };
16 | 18D464D42AACBAA40073F946 /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18D464D32AACBAA40073F946 /* Util.swift */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXFileReference section */
20 | 18D464BD2AAC6B900073F946 /* iOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
21 | 18D464C02AAC6B900073F946 /* iOS_ExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOS_ExampleApp.swift; sourceTree = ""; };
22 | 18D464C22AAC6B900073F946 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
23 | 18D464C42AAC6B910073F946 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
24 | 18D464C72AAC6B910073F946 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
25 | 18D464D12AAC6BBF0073F946 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
26 | 18D464D32AACBAA40073F946 /* Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Util.swift; sourceTree = ""; };
27 | /* End PBXFileReference section */
28 |
29 | /* Begin PBXFrameworksBuildPhase section */
30 | 18D464BA2AAC6B900073F946 /* Frameworks */ = {
31 | isa = PBXFrameworksBuildPhase;
32 | buildActionMask = 2147483647;
33 | files = (
34 | 187E45FC2B0E23E400BB20DD /* TypedNotifications in Frameworks */,
35 | );
36 | runOnlyForDeploymentPostprocessing = 0;
37 | };
38 | /* End PBXFrameworksBuildPhase section */
39 |
40 | /* Begin PBXGroup section */
41 | 18D464B42AAC6B900073F946 = {
42 | isa = PBXGroup;
43 | children = (
44 | 18D464BF2AAC6B900073F946 /* iOS Example */,
45 | 18D464BE2AAC6B900073F946 /* Products */,
46 | 18D464CE2AAC6BA90073F946 /* Frameworks */,
47 | );
48 | sourceTree = "";
49 | };
50 | 18D464BE2AAC6B900073F946 /* Products */ = {
51 | isa = PBXGroup;
52 | children = (
53 | 18D464BD2AAC6B900073F946 /* iOS Example.app */,
54 | );
55 | name = Products;
56 | sourceTree = "";
57 | };
58 | 18D464BF2AAC6B900073F946 /* iOS Example */ = {
59 | isa = PBXGroup;
60 | children = (
61 | 18D464C02AAC6B900073F946 /* iOS_ExampleApp.swift */,
62 | 18D464D12AAC6BBF0073F946 /* AppDelegate.swift */,
63 | 18D464C22AAC6B900073F946 /* ContentView.swift */,
64 | 18D464D32AACBAA40073F946 /* Util.swift */,
65 | 18D464C42AAC6B910073F946 /* Assets.xcassets */,
66 | 18D464C62AAC6B910073F946 /* Preview Content */,
67 | );
68 | path = "iOS Example";
69 | sourceTree = "";
70 | };
71 | 18D464C62AAC6B910073F946 /* Preview Content */ = {
72 | isa = PBXGroup;
73 | children = (
74 | 18D464C72AAC6B910073F946 /* Preview Assets.xcassets */,
75 | );
76 | path = "Preview Content";
77 | sourceTree = "";
78 | };
79 | 18D464CE2AAC6BA90073F946 /* Frameworks */ = {
80 | isa = PBXGroup;
81 | children = (
82 | );
83 | name = Frameworks;
84 | sourceTree = "";
85 | };
86 | /* End PBXGroup section */
87 |
88 | /* Begin PBXNativeTarget section */
89 | 18D464BC2AAC6B900073F946 /* iOS Example */ = {
90 | isa = PBXNativeTarget;
91 | buildConfigurationList = 18D464CB2AAC6B910073F946 /* Build configuration list for PBXNativeTarget "iOS Example" */;
92 | buildPhases = (
93 | 18D464B92AAC6B900073F946 /* Sources */,
94 | 18D464BA2AAC6B900073F946 /* Frameworks */,
95 | 18D464BB2AAC6B900073F946 /* Resources */,
96 | );
97 | buildRules = (
98 | );
99 | dependencies = (
100 | );
101 | name = "iOS Example";
102 | packageProductDependencies = (
103 | 187E45FB2B0E23E400BB20DD /* TypedNotifications */,
104 | );
105 | productName = "iOS Example";
106 | productReference = 18D464BD2AAC6B900073F946 /* iOS Example.app */;
107 | productType = "com.apple.product-type.application";
108 | };
109 | /* End PBXNativeTarget section */
110 |
111 | /* Begin PBXProject section */
112 | 18D464B52AAC6B900073F946 /* Project object */ = {
113 | isa = PBXProject;
114 | attributes = {
115 | BuildIndependentTargetsInParallel = 1;
116 | LastSwiftUpdateCheck = 1500;
117 | LastUpgradeCheck = 1500;
118 | TargetAttributes = {
119 | 18D464BC2AAC6B900073F946 = {
120 | CreatedOnToolsVersion = 15.0;
121 | };
122 | };
123 | };
124 | buildConfigurationList = 18D464B82AAC6B900073F946 /* Build configuration list for PBXProject "iOS Example" */;
125 | compatibilityVersion = "Xcode 14.0";
126 | developmentRegion = en;
127 | hasScannedForEncodings = 0;
128 | knownRegions = (
129 | en,
130 | Base,
131 | );
132 | mainGroup = 18D464B42AAC6B900073F946;
133 | productRefGroup = 18D464BE2AAC6B900073F946 /* Products */;
134 | projectDirPath = "";
135 | projectRoot = "";
136 | targets = (
137 | 18D464BC2AAC6B900073F946 /* iOS Example */,
138 | );
139 | };
140 | /* End PBXProject section */
141 |
142 | /* Begin PBXResourcesBuildPhase section */
143 | 18D464BB2AAC6B900073F946 /* Resources */ = {
144 | isa = PBXResourcesBuildPhase;
145 | buildActionMask = 2147483647;
146 | files = (
147 | 18D464C82AAC6B910073F946 /* Preview Assets.xcassets in Resources */,
148 | 18D464C52AAC6B910073F946 /* Assets.xcassets in Resources */,
149 | );
150 | runOnlyForDeploymentPostprocessing = 0;
151 | };
152 | /* End PBXResourcesBuildPhase section */
153 |
154 | /* Begin PBXSourcesBuildPhase section */
155 | 18D464B92AAC6B900073F946 /* Sources */ = {
156 | isa = PBXSourcesBuildPhase;
157 | buildActionMask = 2147483647;
158 | files = (
159 | 18D464C32AAC6B900073F946 /* ContentView.swift in Sources */,
160 | 18D464C12AAC6B900073F946 /* iOS_ExampleApp.swift in Sources */,
161 | 18D464D22AAC6BBF0073F946 /* AppDelegate.swift in Sources */,
162 | 18D464D42AACBAA40073F946 /* Util.swift in Sources */,
163 | );
164 | runOnlyForDeploymentPostprocessing = 0;
165 | };
166 | /* End PBXSourcesBuildPhase section */
167 |
168 | /* Begin XCBuildConfiguration section */
169 | 18D464C92AAC6B910073F946 /* Debug */ = {
170 | isa = XCBuildConfiguration;
171 | buildSettings = {
172 | ALWAYS_SEARCH_USER_PATHS = NO;
173 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
174 | CLANG_ANALYZER_NONNULL = YES;
175 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
176 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
177 | CLANG_ENABLE_MODULES = YES;
178 | CLANG_ENABLE_OBJC_ARC = YES;
179 | CLANG_ENABLE_OBJC_WEAK = YES;
180 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
181 | CLANG_WARN_BOOL_CONVERSION = YES;
182 | CLANG_WARN_COMMA = YES;
183 | CLANG_WARN_CONSTANT_CONVERSION = YES;
184 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
185 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
186 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
187 | CLANG_WARN_EMPTY_BODY = YES;
188 | CLANG_WARN_ENUM_CONVERSION = YES;
189 | CLANG_WARN_INFINITE_RECURSION = YES;
190 | CLANG_WARN_INT_CONVERSION = YES;
191 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
192 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
193 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
194 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
195 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
196 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
197 | CLANG_WARN_STRICT_PROTOTYPES = YES;
198 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
199 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
200 | CLANG_WARN_UNREACHABLE_CODE = YES;
201 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
202 | COPY_PHASE_STRIP = NO;
203 | DEBUG_INFORMATION_FORMAT = dwarf;
204 | ENABLE_STRICT_OBJC_MSGSEND = YES;
205 | ENABLE_TESTABILITY = YES;
206 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
207 | GCC_C_LANGUAGE_STANDARD = gnu17;
208 | GCC_DYNAMIC_NO_PIC = NO;
209 | GCC_NO_COMMON_BLOCKS = YES;
210 | GCC_OPTIMIZATION_LEVEL = 0;
211 | GCC_PREPROCESSOR_DEFINITIONS = (
212 | "DEBUG=1",
213 | "$(inherited)",
214 | );
215 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
216 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
217 | GCC_WARN_UNDECLARED_SELECTOR = YES;
218 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
219 | GCC_WARN_UNUSED_FUNCTION = YES;
220 | GCC_WARN_UNUSED_VARIABLE = YES;
221 | IPHONEOS_DEPLOYMENT_TARGET = 17.0;
222 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
223 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
224 | MTL_FAST_MATH = YES;
225 | ONLY_ACTIVE_ARCH = YES;
226 | SDKROOT = iphoneos;
227 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
228 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
229 | };
230 | name = Debug;
231 | };
232 | 18D464CA2AAC6B910073F946 /* Release */ = {
233 | isa = XCBuildConfiguration;
234 | buildSettings = {
235 | ALWAYS_SEARCH_USER_PATHS = NO;
236 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
237 | CLANG_ANALYZER_NONNULL = YES;
238 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
240 | CLANG_ENABLE_MODULES = YES;
241 | CLANG_ENABLE_OBJC_ARC = YES;
242 | CLANG_ENABLE_OBJC_WEAK = YES;
243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
244 | CLANG_WARN_BOOL_CONVERSION = YES;
245 | CLANG_WARN_COMMA = YES;
246 | CLANG_WARN_CONSTANT_CONVERSION = YES;
247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
249 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
250 | CLANG_WARN_EMPTY_BODY = YES;
251 | CLANG_WARN_ENUM_CONVERSION = YES;
252 | CLANG_WARN_INFINITE_RECURSION = YES;
253 | CLANG_WARN_INT_CONVERSION = YES;
254 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
255 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
256 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
257 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
258 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
259 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
260 | CLANG_WARN_STRICT_PROTOTYPES = YES;
261 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
262 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
263 | CLANG_WARN_UNREACHABLE_CODE = YES;
264 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
265 | COPY_PHASE_STRIP = NO;
266 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
267 | ENABLE_NS_ASSERTIONS = NO;
268 | ENABLE_STRICT_OBJC_MSGSEND = YES;
269 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
270 | GCC_C_LANGUAGE_STANDARD = gnu17;
271 | GCC_NO_COMMON_BLOCKS = YES;
272 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
273 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
274 | GCC_WARN_UNDECLARED_SELECTOR = YES;
275 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
276 | GCC_WARN_UNUSED_FUNCTION = YES;
277 | GCC_WARN_UNUSED_VARIABLE = YES;
278 | IPHONEOS_DEPLOYMENT_TARGET = 17.0;
279 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
280 | MTL_ENABLE_DEBUG_INFO = NO;
281 | MTL_FAST_MATH = YES;
282 | SDKROOT = iphoneos;
283 | SWIFT_COMPILATION_MODE = wholemodule;
284 | VALIDATE_PRODUCT = YES;
285 | };
286 | name = Release;
287 | };
288 | 18D464CC2AAC6B910073F946 /* Debug */ = {
289 | isa = XCBuildConfiguration;
290 | buildSettings = {
291 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
292 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
293 | CODE_SIGN_STYLE = Automatic;
294 | CURRENT_PROJECT_VERSION = 1;
295 | DEVELOPMENT_ASSET_PATHS = "\"iOS Example/Preview Content\"";
296 | DEVELOPMENT_TEAM = 79Q7G522V8;
297 | ENABLE_PREVIEWS = YES;
298 | GENERATE_INFOPLIST_FILE = YES;
299 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
300 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
301 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
302 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
303 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
304 | LD_RUNPATH_SEARCH_PATHS = (
305 | "$(inherited)",
306 | "@executable_path/Frameworks",
307 | );
308 | MARKETING_VERSION = 1.0;
309 | PRODUCT_BUNDLE_IDENTIFIER = "net.matsuji.iOS-Example";
310 | PRODUCT_NAME = "$(TARGET_NAME)";
311 | SWIFT_EMIT_LOC_STRINGS = YES;
312 | SWIFT_VERSION = 5.0;
313 | TARGETED_DEVICE_FAMILY = "1,2";
314 | };
315 | name = Debug;
316 | };
317 | 18D464CD2AAC6B910073F946 /* Release */ = {
318 | isa = XCBuildConfiguration;
319 | buildSettings = {
320 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
321 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
322 | CODE_SIGN_STYLE = Automatic;
323 | CURRENT_PROJECT_VERSION = 1;
324 | DEVELOPMENT_ASSET_PATHS = "\"iOS Example/Preview Content\"";
325 | DEVELOPMENT_TEAM = 79Q7G522V8;
326 | ENABLE_PREVIEWS = YES;
327 | GENERATE_INFOPLIST_FILE = YES;
328 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
329 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
330 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
331 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
332 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
333 | LD_RUNPATH_SEARCH_PATHS = (
334 | "$(inherited)",
335 | "@executable_path/Frameworks",
336 | );
337 | MARKETING_VERSION = 1.0;
338 | PRODUCT_BUNDLE_IDENTIFIER = "net.matsuji.iOS-Example";
339 | PRODUCT_NAME = "$(TARGET_NAME)";
340 | SWIFT_EMIT_LOC_STRINGS = YES;
341 | SWIFT_VERSION = 5.0;
342 | TARGETED_DEVICE_FAMILY = "1,2";
343 | };
344 | name = Release;
345 | };
346 | /* End XCBuildConfiguration section */
347 |
348 | /* Begin XCConfigurationList section */
349 | 18D464B82AAC6B900073F946 /* Build configuration list for PBXProject "iOS Example" */ = {
350 | isa = XCConfigurationList;
351 | buildConfigurations = (
352 | 18D464C92AAC6B910073F946 /* Debug */,
353 | 18D464CA2AAC6B910073F946 /* Release */,
354 | );
355 | defaultConfigurationIsVisible = 0;
356 | defaultConfigurationName = Release;
357 | };
358 | 18D464CB2AAC6B910073F946 /* Build configuration list for PBXNativeTarget "iOS Example" */ = {
359 | isa = XCConfigurationList;
360 | buildConfigurations = (
361 | 18D464CC2AAC6B910073F946 /* Debug */,
362 | 18D464CD2AAC6B910073F946 /* Release */,
363 | );
364 | defaultConfigurationIsVisible = 0;
365 | defaultConfigurationName = Release;
366 | };
367 | /* End XCConfigurationList section */
368 |
369 | /* Begin XCSwiftPackageProductDependency section */
370 | 187E45FB2B0E23E400BB20DD /* TypedNotifications */ = {
371 | isa = XCSwiftPackageProductDependency;
372 | productName = TypedNotifications;
373 | };
374 | /* End XCSwiftPackageProductDependency section */
375 | };
376 | rootObject = 18D464B52AAC6B900073F946 /* Project object */;
377 | }
378 |
--------------------------------------------------------------------------------
/Examples/iOS Example/iOS Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/iOS Example/iOS Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Examples/iOS Example/iOS Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
42 |
44 |
50 |
51 |
52 |
53 |
59 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/Examples/iOS Example/iOS Example/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Combine
3 | import TypedNotifications
4 |
5 | final class AppDelegate: NSObject, UIApplicationDelegate {
6 |
7 | private var cancellable: [AnyCancellable] = []
8 |
9 | override init() {
10 | super.init()
11 |
12 | // MARK: - UIApplication
13 | [
14 | UIApplication.didBecomeActiveTypedNotification,
15 | UIApplication.didEnterBackgroundTypedNotification,
16 | UIApplication.willEnterForegroundTypedNotification,
17 | UIApplication.willResignActiveTypedNotification,
18 | UIApplication.willTerminateTypedNotification
19 | ].forEach { definition in
20 | TypedNotificationCenter.default.publisher(for: definition)
21 | .sink { printNotification($0) }
22 | .store(in: &cancellable)
23 | }
24 |
25 | // MARK: - UIScene
26 | [
27 | UIScene.willConnectTypedNotification,
28 | UIScene.didActivateTypedNotification,
29 | UIScene.didDisconnectTypedNotification,
30 | UIScene.willEnterForegroundTypedNotification,
31 | UIScene.willDeactivateTypedNotification,
32 | UIScene.didEnterBackgroundTypedNotification
33 | ].forEach { definition in
34 | TypedNotificationCenter.default.publisher(for: definition)
35 | .sink { printNotification($0) }
36 | .store(in: &cancellable)
37 | }
38 |
39 | // MARK: - UIResponder
40 | [
41 | UIResponder.keyboardWillShowTypedNotification,
42 | UIResponder.keyboardDidShowTypedNotification,
43 | UIResponder.keyboardWillHideTypedNotification,
44 | UIResponder.keyboardDidHideTypedNotification,
45 | UIResponder.keyboardWillChangeFrameTypedNotification,
46 | UIResponder.keyboardDidChangeFrameTypedNotification
47 | ].forEach { definition in
48 | TypedNotificationCenter.default.publisher(for: definition)
49 | .sink { notification in printNotification(notification) }
50 | .store(in: &cancellable)
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/Examples/iOS Example/iOS Example/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 6.0
2 |
3 | import CompilerPluginSupport
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "typed-notifications",
8 | platforms: [.iOS(.v13), .macOS(.v10_15), .watchOS(.v6), .tvOS(.v13)],
9 | products: [
10 | .library(name: "TypedNotifications", targets: ["TypedNotifications"]),
11 | ],
12 | dependencies: [
13 | .package(url: "https://github.com/apple/swift-syntax", from: "600.0.0"),
14 | .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
15 | .package(url: "https://github.com/mtj0928/userinfo-representable", from: "1.1.1")
16 | ],
17 | targets: [
18 | .target(
19 | name: "TypedNotifications",
20 | dependencies: [
21 | .product(name: "UserInfoRepresentable", package: "userinfo-representable"),
22 | "TypedNotificationsMacro"
23 | ]
24 | ),
25 | .macro(
26 | name: "TypedNotificationsMacro",
27 | dependencies: [
28 | .product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
29 | .product(name: "SwiftCompilerPlugin", package: "swift-syntax")
30 | ]
31 | ),
32 | .testTarget(name: "TypedNotificationsTests", dependencies: ["TypedNotifications"]),
33 | .testTarget(
34 | name: "TypedNotificationsMacroTests",
35 | dependencies: [
36 | "TypedNotificationsMacro",
37 | .product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
38 | ]
39 | )
40 | ]
41 | )
42 |
--------------------------------------------------------------------------------
/Package@swift-5.9.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.9
2 |
3 | import CompilerPluginSupport
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "typed-notifications",
8 | platforms: [.iOS(.v13), .macOS(.v10_15), .watchOS(.v6), .tvOS(.v13)],
9 | products: [
10 | .library(name: "TypedNotifications", targets: ["TypedNotifications"]),
11 | ],
12 | dependencies: [
13 | .package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0"),
14 | .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
15 | .package(url: "https://github.com/mtj0928/userinfo-representable", from: "1.0.2")
16 | ],
17 | targets: [
18 | .target(
19 | name: "TypedNotifications",
20 | dependencies: [
21 | .product(name: "UserInfoRepresentable", package: "userinfo-representable"),
22 | "TypedNotificationsMacro"
23 | ]
24 | ),
25 | .macro(
26 | name: "TypedNotificationsMacro",
27 | dependencies: [
28 | .product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
29 | .product(name: "SwiftCompilerPlugin", package: "swift-syntax")
30 | ]
31 | ),
32 | .testTarget(name: "TypedNotificationsTests", dependencies: ["TypedNotifications"]),
33 | .testTarget(
34 | name: "TypedNotificationsMacroTests",
35 | dependencies: [
36 | "TypedNotificationsMacro",
37 | .product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
38 | ]
39 | )
40 | ]
41 | )
42 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # typed-notifications
2 | This library attaches type-information to NotificationCenter.
3 |
4 | ## Example
5 | You can post and observe notifications in a type-safe manner.
6 | ```swift
7 | NotificationCenter.default
8 | .publisher(for: .userNameUpdate, object: user)
9 | .sink { notification in
10 | // Notifications can be received in a type safe manner.
11 | let storage: UserNameUpdateNotificationStorage = notification.storage
12 | let user: User? = notification.object
13 | // ...
14 | }
15 |
16 | extension TypedNotificationDefinition {
17 | @Notification
18 | static var userNameUpdate: TypedNotificationDefinition
19 | }
20 |
21 | @UserInfoRepresentable
22 | struct UserNameUpdateNotificationStorage {
23 | let oldName: String
24 | let newName: String
25 | }
26 | ```
27 |
28 | ## How to Use
29 | Define a notification and how to encode/decode the userInfo in `TypedNotificationDefinition`.
30 | ```swift
31 | extension TypedNotificationDefinition {
32 | static var userNameWillUpdate: TypedNotificationDefinition {
33 | .init(name: "userWillUpdate") { newName in
34 | ["newName": newName]
35 | } decode: { userInfo in
36 | userInfo?["newName"] as? String ?? ""
37 | }
38 | }
39 | }
40 | ```
41 |
42 | And then, you can post/observe the notifications in type safe manner.
43 | ```swift
44 | // [Post]
45 | // Notifications can be posted in a type safe manner.
46 | let newName: String = ...
47 | let user: User = ...
48 | NotificationCenter.default.post(.userNameWillUpdate, storage: newName, object: user)
49 |
50 | // [Observation]
51 | NotificationCenter.default.publisher(for: .userNameWillUpdate, object: user)
52 | .sink { notification in
53 | // Notifications can be received in a type safe manner.
54 | let newName = notification.storage
55 | let user: User? = notification.object
56 | // ...
57 | }
58 | ```
59 |
60 | ### Notification macro
61 |
62 | You can use `@Notification` macro, if `@UserInforRepresentable` macro is attached to your type.
63 | ```swift
64 | extension TypedNotificationDefinition {
65 | @Notification
66 | static var userNameUpdate: TypedNotificationDefinition
67 | }
68 |
69 | @UserInfoRepresentable
70 | struct UserNameUpdateNotificationStorage {
71 | let oldName: String
72 | let newName: String
73 | }
74 | ```
75 |
76 | ## Pre-defined Notifications
77 | This repository contains frequent system notifications.
78 | - UIKit
79 | - [UIApplication](Sources/TypedNotifications/UIKit/UIApplication.swift)
80 | - [UIScene](Sources/TypedNotifications/UIKit/UIScene.swift)
81 | - [UIResponder](Sources/TypedNotifications/UIKit/UIResponder.swift)
82 | - Core Data
83 | - [NSManagedObjectContext](Sources/TypedNotifications/CoreData/NSManagedObjectContext.swift)
84 |
85 | Your PR adding new notifications is appreciated. Feel free to make a new PR.
86 |
--------------------------------------------------------------------------------
/Sources/TypedNotifications/CoreData/NSManagedObjectContext.swift:
--------------------------------------------------------------------------------
1 | import CoreData
2 | import UserInfoRepresentable
3 |
4 | extension NSManagedObjectContext {
5 | /// A notification that the context is about to save.
6 | @Notification(name: NSManagedObjectContext.willSaveObjectsNotification.rawValue)
7 | public static var willSaveObjectsTypedNotification:
8 | TypedNotificationDefinition
9 |
10 | /// A notification that posts when the context completes a save.
11 | @Notification(name: NSManagedObjectContext.didSaveObjectsNotification.rawValue)
12 | public static var didSaveObjectsTypedNotification:
13 | TypedNotificationDefinition
14 |
15 | /// A notification that posts when the context completes a save.
16 | @Notification(name: NSManagedObjectContext.didChangeObjectsNotification.rawValue)
17 | public static var didChangeObjectsTypedNotification:
18 | TypedNotificationDefinition
19 | }
20 |
21 | public struct NSManagedObjectNotificationUserInfo: UserInfoRepresentable {
22 | /// The set of objects that were inserted into the context.
23 | public var insertedObjects: Set
24 |
25 | /// The set of objects that were updated.
26 | public var updatedObjects: Set
27 |
28 | /// The set of objects that were marked for deletion during the previous event.
29 | public var deletedObjects: Set
30 |
31 | /// The set of objects that were refreshed but were not dirtied in the scope of this context.
32 | public var refreshedObjects: Set
33 |
34 | /// The set of objects that were invalidated.
35 | public var invalidatedObjects: Set
36 |
37 | public init(
38 | insertedObjects: Set,
39 | updatedObjects: Set,
40 | deletedObjects: Set,
41 | refreshedObjects: Set,
42 | invalidatedObjects: Set
43 | ) {
44 | self.insertedObjects = insertedObjects
45 | self.updatedObjects = updatedObjects
46 | self.deletedObjects = deletedObjects
47 | self.refreshedObjects = refreshedObjects
48 | self.invalidatedObjects = invalidatedObjects
49 | }
50 |
51 | public init(userInfo: [AnyHashable : Any]) throws {
52 | insertedObjects = userInfo[NSInsertedObjectsKey] as? Set ?? []
53 | updatedObjects = userInfo[NSUpdatedObjectsKey] as? Set ?? []
54 | deletedObjects = userInfo[NSDeletedObjectsKey] as? Set ?? []
55 | refreshedObjects = userInfo[NSRefreshedObjectsKey] as? Set ?? []
56 | invalidatedObjects = userInfo[NSInvalidatedObjectsKey] as? Set ?? []
57 | }
58 |
59 | public func convertToUserInfo() -> [AnyHashable : Any] {
60 | [
61 | NSInsertedObjectsKey: insertedObjects,
62 | NSUpdatedObjectsKey: updatedObjects,
63 | NSDeletedObjectsKey: deletedObjects,
64 | NSRefreshedObjectsKey: refreshedObjects,
65 | NSInvalidatedObjectsKey: invalidatedObjects,
66 | ]
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/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/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