├── .github
├── FUNDING.yml
└── workflows
│ └── swift.yml
├── .gitignore
├── .swiftpm
└── xcode
│ ├── package.xcworkspace
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ └── xcschemes
│ └── SwiftNFC.xcscheme
├── Demo
├── NFC Read-Write.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── NFC Read-Write
│ ├── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ └── icon.png
│ │ └── Contents.json
│ ├── ContentView.swift
│ ├── Localizable.xcstrings
│ ├── NFC_Read_Write.entitlements
│ ├── NFC_Read_WriteApp.swift
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ └── Views
│ │ ├── DraggableInterfaceView.swift
│ │ ├── NFCActionView.swift
│ │ ├── NFCEditorView.swift
│ │ └── NFCOptionsView.swift
└── NFC-Read-Write-Info.plist
├── LICENSE
├── Package.swift
├── README.md
├── Sources
└── SwiftNFC
│ ├── Localizable.xcstrings
│ └── SwiftNFC.swift
└── Tests
└── SwiftNFCTests
└── SwiftNFCTests.swift
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: 1998code
4 |
--------------------------------------------------------------------------------
/.github/workflows/swift.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Swift project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift
3 |
4 | name: Swift
5 |
6 | on:
7 | push:
8 | branches: [ "main" ]
9 | pull_request:
10 | branches: [ "main" ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: macos-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v3
19 | - name: Build
20 | run: swift build -v
21 | # - name: Run tests
22 | # run: swift test -v
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | DerivedData/
7 | .swiftpm/config/registries.json
8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
9 | .netrc
10 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcshareddata/xcschemes/SwiftNFC.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
9 |
10 |
16 |
22 |
23 |
24 |
25 |
26 |
32 |
33 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
60 |
62 |
63 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/Demo/NFC Read-Write.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 54;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 3A7CA69729616DAB00363E59 /* NFC_Read_WriteApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7CA69629616DAB00363E59 /* NFC_Read_WriteApp.swift */; };
11 | 3A7CA69929616DAB00363E59 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7CA69829616DAB00363E59 /* ContentView.swift */; };
12 | 3A7CA69B29616DAC00363E59 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3A7CA69A29616DAC00363E59 /* Assets.xcassets */; };
13 | 3A7CA69F29616DAC00363E59 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3A7CA69E29616DAC00363E59 /* Preview Assets.xcassets */; };
14 | 3A828E182DF5B63F006055AF /* DraggableInterfaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A828E132DF5B63F006055AF /* DraggableInterfaceView.swift */; };
15 | 3A828E192DF5B63F006055AF /* NFCEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A828E152DF5B63F006055AF /* NFCEditorView.swift */; };
16 | 3A828E1A2DF5B63F006055AF /* NFCOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A828E162DF5B63F006055AF /* NFCOptionsView.swift */; };
17 | 3A828E1B2DF5B63F006055AF /* NFCActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A828E142DF5B63F006055AF /* NFCActionView.swift */; };
18 | 3AAAD2492C52C43D00AAE46E /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 3AAAD2482C52C43D00AAE46E /* Localizable.xcstrings */; };
19 | 3ADDA132296B35640096CC30 /* SwiftNFC in Frameworks */ = {isa = PBXBuildFile; productRef = 3ADDA131296B35640096CC30 /* SwiftNFC */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXFileReference section */
23 | 3A7CA69329616DAB00363E59 /* NFC Read-Write.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "NFC Read-Write.app"; sourceTree = BUILT_PRODUCTS_DIR; };
24 | 3A7CA69629616DAB00363E59 /* NFC_Read_WriteApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFC_Read_WriteApp.swift; sourceTree = ""; };
25 | 3A7CA69829616DAB00363E59 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
26 | 3A7CA69A29616DAC00363E59 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
27 | 3A7CA69C29616DAC00363E59 /* NFC_Read_Write.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NFC_Read_Write.entitlements; sourceTree = ""; };
28 | 3A7CA69E29616DAC00363E59 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
29 | 3A828E132DF5B63F006055AF /* DraggableInterfaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggableInterfaceView.swift; sourceTree = ""; };
30 | 3A828E142DF5B63F006055AF /* NFCActionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFCActionView.swift; sourceTree = ""; };
31 | 3A828E152DF5B63F006055AF /* NFCEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFCEditorView.swift; sourceTree = ""; };
32 | 3A828E162DF5B63F006055AF /* NFCOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NFCOptionsView.swift; sourceTree = ""; };
33 | 3AAAD2482C52C43D00AAE46E /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; };
34 | 3ADDA12F296B35430096CC30 /* SwiftNFC */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = SwiftNFC; path = ..; sourceTree = ""; };
35 | 3AFEBDBB2969B836005BEFED /* NFC-Read-Write-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "NFC-Read-Write-Info.plist"; sourceTree = SOURCE_ROOT; };
36 | /* End PBXFileReference section */
37 |
38 | /* Begin PBXFrameworksBuildPhase section */
39 | 3A7CA69029616DAB00363E59 /* Frameworks */ = {
40 | isa = PBXFrameworksBuildPhase;
41 | buildActionMask = 2147483647;
42 | files = (
43 | 3ADDA132296B35640096CC30 /* SwiftNFC in Frameworks */,
44 | );
45 | runOnlyForDeploymentPostprocessing = 0;
46 | };
47 | /* End PBXFrameworksBuildPhase section */
48 |
49 | /* Begin PBXGroup section */
50 | 3A7CA68A29616DAB00363E59 = {
51 | isa = PBXGroup;
52 | children = (
53 | 3ADDA12D296B35100096CC30 /* Packages */,
54 | 3A7CA69529616DAB00363E59 /* NFC Read-Write */,
55 | 3A7CA69429616DAB00363E59 /* Products */,
56 | 3ADDA130296B35640096CC30 /* Frameworks */,
57 | );
58 | sourceTree = "";
59 | };
60 | 3A7CA69429616DAB00363E59 /* Products */ = {
61 | isa = PBXGroup;
62 | children = (
63 | 3A7CA69329616DAB00363E59 /* NFC Read-Write.app */,
64 | );
65 | name = Products;
66 | sourceTree = "";
67 | };
68 | 3A7CA69529616DAB00363E59 /* NFC Read-Write */ = {
69 | isa = PBXGroup;
70 | children = (
71 | 3AFEBDBB2969B836005BEFED /* NFC-Read-Write-Info.plist */,
72 | 3A7CA69629616DAB00363E59 /* NFC_Read_WriteApp.swift */,
73 | 3A7CA69829616DAB00363E59 /* ContentView.swift */,
74 | 3A828E172DF5B63F006055AF /* Views */,
75 | 3AAAD2482C52C43D00AAE46E /* Localizable.xcstrings */,
76 | 3A7CA69A29616DAC00363E59 /* Assets.xcassets */,
77 | 3A7CA69C29616DAC00363E59 /* NFC_Read_Write.entitlements */,
78 | 3A7CA69D29616DAC00363E59 /* Preview Content */,
79 | );
80 | path = "NFC Read-Write";
81 | sourceTree = "";
82 | };
83 | 3A7CA69D29616DAC00363E59 /* Preview Content */ = {
84 | isa = PBXGroup;
85 | children = (
86 | 3A7CA69E29616DAC00363E59 /* Preview Assets.xcassets */,
87 | );
88 | path = "Preview Content";
89 | sourceTree = "";
90 | };
91 | 3A828E172DF5B63F006055AF /* Views */ = {
92 | isa = PBXGroup;
93 | children = (
94 | 3A828E132DF5B63F006055AF /* DraggableInterfaceView.swift */,
95 | 3A828E142DF5B63F006055AF /* NFCActionView.swift */,
96 | 3A828E152DF5B63F006055AF /* NFCEditorView.swift */,
97 | 3A828E162DF5B63F006055AF /* NFCOptionsView.swift */,
98 | );
99 | path = Views;
100 | sourceTree = "";
101 | };
102 | 3ADDA12D296B35100096CC30 /* Packages */ = {
103 | isa = PBXGroup;
104 | children = (
105 | 3ADDA12F296B35430096CC30 /* SwiftNFC */,
106 | );
107 | name = Packages;
108 | sourceTree = "";
109 | };
110 | 3ADDA130296B35640096CC30 /* Frameworks */ = {
111 | isa = PBXGroup;
112 | children = (
113 | );
114 | name = Frameworks;
115 | sourceTree = "";
116 | };
117 | /* End PBXGroup section */
118 |
119 | /* Begin PBXNativeTarget section */
120 | 3A7CA69229616DAB00363E59 /* NFC Read-Write */ = {
121 | isa = PBXNativeTarget;
122 | buildConfigurationList = 3A7CA6A229616DAC00363E59 /* Build configuration list for PBXNativeTarget "NFC Read-Write" */;
123 | buildPhases = (
124 | 3A7CA68F29616DAB00363E59 /* Sources */,
125 | 3A7CA69029616DAB00363E59 /* Frameworks */,
126 | 3A7CA69129616DAB00363E59 /* Resources */,
127 | );
128 | buildRules = (
129 | );
130 | dependencies = (
131 | );
132 | name = "NFC Read-Write";
133 | packageProductDependencies = (
134 | 3ADDA131296B35640096CC30 /* SwiftNFC */,
135 | );
136 | productName = "NFC Read-Write";
137 | productReference = 3A7CA69329616DAB00363E59 /* NFC Read-Write.app */;
138 | productType = "com.apple.product-type.application";
139 | };
140 | /* End PBXNativeTarget section */
141 |
142 | /* Begin PBXProject section */
143 | 3A7CA68B29616DAB00363E59 /* Project object */ = {
144 | isa = PBXProject;
145 | attributes = {
146 | BuildIndependentTargetsInParallel = 1;
147 | LastSwiftUpdateCheck = 1420;
148 | LastUpgradeCheck = 1420;
149 | TargetAttributes = {
150 | 3A7CA69229616DAB00363E59 = {
151 | CreatedOnToolsVersion = 14.2;
152 | };
153 | };
154 | };
155 | buildConfigurationList = 3A7CA68E29616DAB00363E59 /* Build configuration list for PBXProject "NFC Read-Write" */;
156 | compatibilityVersion = "Xcode 8.0";
157 | developmentRegion = en;
158 | hasScannedForEncodings = 0;
159 | knownRegions = (
160 | en,
161 | Base,
162 | "zh-HK",
163 | );
164 | mainGroup = 3A7CA68A29616DAB00363E59;
165 | packageReferences = (
166 | );
167 | productRefGroup = 3A7CA69429616DAB00363E59 /* Products */;
168 | projectDirPath = "";
169 | projectRoot = "";
170 | targets = (
171 | 3A7CA69229616DAB00363E59 /* NFC Read-Write */,
172 | );
173 | };
174 | /* End PBXProject section */
175 |
176 | /* Begin PBXResourcesBuildPhase section */
177 | 3A7CA69129616DAB00363E59 /* Resources */ = {
178 | isa = PBXResourcesBuildPhase;
179 | buildActionMask = 2147483647;
180 | files = (
181 | 3A7CA69F29616DAC00363E59 /* Preview Assets.xcassets in Resources */,
182 | 3A7CA69B29616DAC00363E59 /* Assets.xcassets in Resources */,
183 | 3AAAD2492C52C43D00AAE46E /* Localizable.xcstrings in Resources */,
184 | );
185 | runOnlyForDeploymentPostprocessing = 0;
186 | };
187 | /* End PBXResourcesBuildPhase section */
188 |
189 | /* Begin PBXSourcesBuildPhase section */
190 | 3A7CA68F29616DAB00363E59 /* Sources */ = {
191 | isa = PBXSourcesBuildPhase;
192 | buildActionMask = 2147483647;
193 | files = (
194 | 3A828E182DF5B63F006055AF /* DraggableInterfaceView.swift in Sources */,
195 | 3A828E192DF5B63F006055AF /* NFCEditorView.swift in Sources */,
196 | 3A828E1A2DF5B63F006055AF /* NFCOptionsView.swift in Sources */,
197 | 3A828E1B2DF5B63F006055AF /* NFCActionView.swift in Sources */,
198 | 3A7CA69929616DAB00363E59 /* ContentView.swift in Sources */,
199 | 3A7CA69729616DAB00363E59 /* NFC_Read_WriteApp.swift in Sources */,
200 | );
201 | runOnlyForDeploymentPostprocessing = 0;
202 | };
203 | /* End PBXSourcesBuildPhase section */
204 |
205 | /* Begin XCBuildConfiguration section */
206 | 3A7CA6A029616DAC00363E59 /* Debug */ = {
207 | isa = XCBuildConfiguration;
208 | buildSettings = {
209 | ALWAYS_SEARCH_USER_PATHS = NO;
210 | CLANG_ANALYZER_NONNULL = YES;
211 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
212 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
213 | CLANG_ENABLE_MODULES = YES;
214 | CLANG_ENABLE_OBJC_ARC = YES;
215 | CLANG_ENABLE_OBJC_WEAK = YES;
216 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
217 | CLANG_WARN_BOOL_CONVERSION = YES;
218 | CLANG_WARN_COMMA = YES;
219 | CLANG_WARN_CONSTANT_CONVERSION = YES;
220 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
221 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
222 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
223 | CLANG_WARN_EMPTY_BODY = YES;
224 | CLANG_WARN_ENUM_CONVERSION = YES;
225 | CLANG_WARN_INFINITE_RECURSION = YES;
226 | CLANG_WARN_INT_CONVERSION = YES;
227 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
228 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
229 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
230 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
231 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
232 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
233 | CLANG_WARN_STRICT_PROTOTYPES = YES;
234 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
235 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
236 | CLANG_WARN_UNREACHABLE_CODE = YES;
237 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
238 | COPY_PHASE_STRIP = NO;
239 | DEBUG_INFORMATION_FORMAT = dwarf;
240 | ENABLE_STRICT_OBJC_MSGSEND = YES;
241 | ENABLE_TESTABILITY = YES;
242 | GCC_C_LANGUAGE_STANDARD = gnu11;
243 | GCC_DYNAMIC_NO_PIC = NO;
244 | GCC_NO_COMMON_BLOCKS = YES;
245 | GCC_OPTIMIZATION_LEVEL = 0;
246 | GCC_PREPROCESSOR_DEFINITIONS = (
247 | "DEBUG=1",
248 | "$(inherited)",
249 | );
250 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
251 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
252 | GCC_WARN_UNDECLARED_SELECTOR = YES;
253 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
254 | GCC_WARN_UNUSED_FUNCTION = YES;
255 | GCC_WARN_UNUSED_VARIABLE = YES;
256 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
257 | MTL_FAST_MATH = YES;
258 | ONLY_ACTIVE_ARCH = YES;
259 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
260 | SWIFT_EMIT_LOC_STRINGS = YES;
261 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
262 | };
263 | name = Debug;
264 | };
265 | 3A7CA6A129616DAC00363E59 /* Release */ = {
266 | isa = XCBuildConfiguration;
267 | buildSettings = {
268 | ALWAYS_SEARCH_USER_PATHS = NO;
269 | CLANG_ANALYZER_NONNULL = YES;
270 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
271 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
272 | CLANG_ENABLE_MODULES = YES;
273 | CLANG_ENABLE_OBJC_ARC = YES;
274 | CLANG_ENABLE_OBJC_WEAK = YES;
275 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
276 | CLANG_WARN_BOOL_CONVERSION = YES;
277 | CLANG_WARN_COMMA = YES;
278 | CLANG_WARN_CONSTANT_CONVERSION = YES;
279 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
280 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
281 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
282 | CLANG_WARN_EMPTY_BODY = YES;
283 | CLANG_WARN_ENUM_CONVERSION = YES;
284 | CLANG_WARN_INFINITE_RECURSION = YES;
285 | CLANG_WARN_INT_CONVERSION = YES;
286 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
287 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
288 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
289 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
290 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
291 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
292 | CLANG_WARN_STRICT_PROTOTYPES = YES;
293 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
294 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
295 | CLANG_WARN_UNREACHABLE_CODE = YES;
296 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
297 | COPY_PHASE_STRIP = NO;
298 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
299 | ENABLE_NS_ASSERTIONS = NO;
300 | ENABLE_STRICT_OBJC_MSGSEND = YES;
301 | GCC_C_LANGUAGE_STANDARD = gnu11;
302 | GCC_NO_COMMON_BLOCKS = YES;
303 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
304 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
305 | GCC_WARN_UNDECLARED_SELECTOR = YES;
306 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
307 | GCC_WARN_UNUSED_FUNCTION = YES;
308 | GCC_WARN_UNUSED_VARIABLE = YES;
309 | MTL_ENABLE_DEBUG_INFO = NO;
310 | MTL_FAST_MATH = YES;
311 | SWIFT_COMPILATION_MODE = wholemodule;
312 | SWIFT_EMIT_LOC_STRINGS = YES;
313 | SWIFT_OPTIMIZATION_LEVEL = "-O";
314 | };
315 | name = Release;
316 | };
317 | 3A7CA6A329616DAC00363E59 /* Debug */ = {
318 | isa = XCBuildConfiguration;
319 | buildSettings = {
320 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
321 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
322 | CODE_SIGN_ENTITLEMENTS = "NFC Read-Write/NFC_Read_Write.entitlements";
323 | CODE_SIGN_STYLE = Automatic;
324 | CURRENT_PROJECT_VERSION = 1;
325 | DEVELOPMENT_ASSET_PATHS = "\"NFC Read-Write/Preview Content\"";
326 | DEVELOPMENT_TEAM = "";
327 | ENABLE_HARDENED_RUNTIME = YES;
328 | ENABLE_PREVIEWS = YES;
329 | GENERATE_INFOPLIST_FILE = YES;
330 | INFOPLIST_FILE = "NFC-Read-Write-Info.plist";
331 | INFOPLIST_KEY_NFCReaderUsageDescription = "Use NFC to read and write data.";
332 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
333 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
334 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
335 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
336 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
337 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
338 | "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault;
339 | "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
340 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
341 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
342 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
343 | LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
344 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
345 | LOCALIZATION_EXPORT_SUPPORTED = YES;
346 | MACOSX_DEPLOYMENT_TARGET = 11.0;
347 | MARKETING_VERSION = 1.0;
348 | PRODUCT_BUNDLE_IDENTIFIER = "sample.NFC-Read-Write";
349 | PRODUCT_NAME = "$(TARGET_NAME)";
350 | SDKROOT = auto;
351 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
352 | SUPPORTS_MACCATALYST = NO;
353 | SWIFT_EMIT_LOC_STRINGS = YES;
354 | SWIFT_VERSION = 5.0;
355 | TARGETED_DEVICE_FAMILY = 1;
356 | };
357 | name = Debug;
358 | };
359 | 3A7CA6A429616DAC00363E59 /* Release */ = {
360 | isa = XCBuildConfiguration;
361 | buildSettings = {
362 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
363 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
364 | CODE_SIGN_ENTITLEMENTS = "NFC Read-Write/NFC_Read_Write.entitlements";
365 | CODE_SIGN_STYLE = Automatic;
366 | CURRENT_PROJECT_VERSION = 1;
367 | DEVELOPMENT_ASSET_PATHS = "\"NFC Read-Write/Preview Content\"";
368 | DEVELOPMENT_TEAM = "";
369 | ENABLE_HARDENED_RUNTIME = YES;
370 | ENABLE_PREVIEWS = YES;
371 | GENERATE_INFOPLIST_FILE = YES;
372 | INFOPLIST_FILE = "NFC-Read-Write-Info.plist";
373 | INFOPLIST_KEY_NFCReaderUsageDescription = "Use NFC to read and write data.";
374 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
375 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
376 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
377 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
378 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
379 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
380 | "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault;
381 | "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
382 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
383 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
384 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
385 | LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
386 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
387 | LOCALIZATION_EXPORT_SUPPORTED = YES;
388 | MACOSX_DEPLOYMENT_TARGET = 11.0;
389 | MARKETING_VERSION = 1.0;
390 | PRODUCT_BUNDLE_IDENTIFIER = "sample.NFC-Read-Write";
391 | PRODUCT_NAME = "$(TARGET_NAME)";
392 | SDKROOT = auto;
393 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
394 | SUPPORTS_MACCATALYST = NO;
395 | SWIFT_EMIT_LOC_STRINGS = YES;
396 | SWIFT_VERSION = 5.0;
397 | TARGETED_DEVICE_FAMILY = 1;
398 | };
399 | name = Release;
400 | };
401 | /* End XCBuildConfiguration section */
402 |
403 | /* Begin XCConfigurationList section */
404 | 3A7CA68E29616DAB00363E59 /* Build configuration list for PBXProject "NFC Read-Write" */ = {
405 | isa = XCConfigurationList;
406 | buildConfigurations = (
407 | 3A7CA6A029616DAC00363E59 /* Debug */,
408 | 3A7CA6A129616DAC00363E59 /* Release */,
409 | );
410 | defaultConfigurationIsVisible = 0;
411 | defaultConfigurationName = Release;
412 | };
413 | 3A7CA6A229616DAC00363E59 /* Build configuration list for PBXNativeTarget "NFC Read-Write" */ = {
414 | isa = XCConfigurationList;
415 | buildConfigurations = (
416 | 3A7CA6A329616DAC00363E59 /* Debug */,
417 | 3A7CA6A429616DAC00363E59 /* Release */,
418 | );
419 | defaultConfigurationIsVisible = 0;
420 | defaultConfigurationName = Release;
421 | };
422 | /* End XCConfigurationList section */
423 |
424 | /* Begin XCSwiftPackageProductDependency section */
425 | 3ADDA131296B35640096CC30 /* SwiftNFC */ = {
426 | isa = XCSwiftPackageProductDependency;
427 | productName = SwiftNFC;
428 | };
429 | /* End XCSwiftPackageProductDependency section */
430 | };
431 | rootObject = 3A7CA68B29616DAB00363E59 /* Project object */;
432 | }
433 |
--------------------------------------------------------------------------------
/Demo/NFC Read-Write.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Demo/NFC Read-Write.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Demo/NFC Read-Write/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "display-p3",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.292",
9 | "green" : "0.342",
10 | "red" : "0.136"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "display-p3",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0.432",
27 | "green" : "0.845",
28 | "red" : "0.136"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Demo/NFC Read-Write/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "icon.png",
5 | "idiom" : "universal",
6 | "platform" : "ios",
7 | "size" : "1024x1024"
8 | },
9 | {
10 | "idiom" : "mac",
11 | "scale" : "1x",
12 | "size" : "16x16"
13 | },
14 | {
15 | "idiom" : "mac",
16 | "scale" : "2x",
17 | "size" : "16x16"
18 | },
19 | {
20 | "idiom" : "mac",
21 | "scale" : "1x",
22 | "size" : "32x32"
23 | },
24 | {
25 | "idiom" : "mac",
26 | "scale" : "2x",
27 | "size" : "32x32"
28 | },
29 | {
30 | "idiom" : "mac",
31 | "scale" : "1x",
32 | "size" : "128x128"
33 | },
34 | {
35 | "idiom" : "mac",
36 | "scale" : "2x",
37 | "size" : "128x128"
38 | },
39 | {
40 | "idiom" : "mac",
41 | "scale" : "1x",
42 | "size" : "256x256"
43 | },
44 | {
45 | "idiom" : "mac",
46 | "scale" : "2x",
47 | "size" : "256x256"
48 | },
49 | {
50 | "idiom" : "mac",
51 | "scale" : "1x",
52 | "size" : "512x512"
53 | },
54 | {
55 | "idiom" : "mac",
56 | "scale" : "2x",
57 | "size" : "512x512"
58 | }
59 | ],
60 | "info" : {
61 | "author" : "xcode",
62 | "version" : 1
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Demo/NFC Read-Write/Assets.xcassets/AppIcon.appiconset/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1998code/SwiftNFC/b94009ca3a8c59d87b86bf02fc352eb061945747/Demo/NFC Read-Write/Assets.xcassets/AppIcon.appiconset/icon.png
--------------------------------------------------------------------------------
/Demo/NFC Read-Write/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Demo/NFC Read-Write/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // NFC Read-Write
4 | //
5 | // Created by Ming on 1/1/2023.
6 | //
7 |
8 | import SwiftUI
9 | import SwiftNFC
10 |
11 | struct ContentView: View {
12 | // MARK: - NFC Components
13 | @ObservedObject var nfcReader = NFCReader()
14 | @ObservedObject var nfcWriter = NFCWriter()
15 |
16 | // MARK: - UI State
17 | @State private var keyboardVisible: Bool = false
18 |
19 | var body: some View {
20 | DraggableInterfaceView {
21 | // Top Content - Message Editor
22 | if #available(iOS 16.0, *) {
23 | NFCMessageEditor(nfcReader: nfcReader)
24 | .scrollContentBackground(.hidden)
25 | } else {
26 | NFCMessageEditor(nfcReader: nfcReader)
27 | }
28 | } bottomContent: {
29 | // Bottom Content - Raw Data Editor + Actions
30 | VStack(spacing: 0) {
31 | if #available(iOS 16.0, *) {
32 | NFCRawDataEditor(nfcReader: nfcReader)
33 | .scrollContentBackground(.hidden)
34 | } else {
35 | NFCRawDataEditor(nfcReader: nfcReader)
36 | }
37 |
38 | Divider()
39 |
40 | NFCActionView(
41 | onRead: { nfcReader.read() },
42 | onWrite: {
43 | nfcWriter.msg = nfcReader.msg
44 | nfcWriter.write()
45 | }
46 | )
47 | .frame(height: 75)
48 | }
49 | } dragContent: {
50 | // Drag Content - Options
51 | NFCOptionsView(
52 | nfcWriter: nfcWriter,
53 | keyboardVisible: $keyboardVisible
54 | )
55 | }
56 | .ignoresSafeArea(.all)
57 | .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { _ in
58 | keyboardVisible = true
59 | }
60 | .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in
61 | keyboardVisible = false
62 | }
63 | .onTapGesture(count: 2) {
64 | // Double tap to hide keyboard
65 | UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
66 | }
67 | }
68 | }
69 |
70 | #Preview {
71 | ContentView()
72 | .preferredColorScheme(.dark)
73 | }
74 |
--------------------------------------------------------------------------------
/Demo/NFC Read-Write/Localizable.xcstrings:
--------------------------------------------------------------------------------
1 | {
2 | "sourceLanguage" : "en",
3 | "strings" : {
4 | "Close Keyboard" : {
5 | "localizations" : {
6 | "zh-HK" : {
7 | "stringUnit" : {
8 | "state" : "translated",
9 | "value" : "關閉鍵盤"
10 | }
11 | }
12 | }
13 | },
14 | "Link" : {
15 | "localizations" : {
16 | "zh-HK" : {
17 | "stringUnit" : {
18 | "state" : "translated",
19 | "value" : "連結"
20 | }
21 | }
22 | }
23 | },
24 | "Read NFC" : {
25 | "localizations" : {
26 | "zh-HK" : {
27 | "stringUnit" : {
28 | "state" : "translated",
29 | "value" : "讀取 NFC"
30 | }
31 | }
32 | }
33 | },
34 | "Text" : {
35 | "localizations" : {
36 | "zh-HK" : {
37 | "stringUnit" : {
38 | "state" : "translated",
39 | "value" : "文字"
40 | }
41 | }
42 | }
43 | },
44 | "Type Picker" : {
45 | "localizations" : {
46 | "zh-HK" : {
47 | "stringUnit" : {
48 | "state" : "translated",
49 | "value" : "類型選擇"
50 | }
51 | }
52 | }
53 | },
54 | "Write NFC" : {
55 | "localizations" : {
56 | "zh-HK" : {
57 | "stringUnit" : {
58 | "state" : "translated",
59 | "value" : "寫入 NFC"
60 | }
61 | }
62 | }
63 | }
64 | },
65 | "version" : "1.0"
66 | }
--------------------------------------------------------------------------------
/Demo/NFC Read-Write/NFC_Read_Write.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.developer.nfc.readersession.formats
6 |
7 | TAG
8 |
9 | com.apple.security.app-sandbox
10 |
11 | com.apple.security.files.user-selected.read-only
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Demo/NFC Read-Write/NFC_Read_WriteApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NFC_Read_WriteApp.swift
3 | // NFC Read-Write
4 | //
5 | // Created by Ming on 1/1/2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct NFC_Read_WriteApp: App {
12 | @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
13 | var body: some Scene {
14 | WindowGroup {
15 | ContentView()
16 | }
17 | }
18 | }
19 |
20 | class AppDelegate: UIResponder, UIApplicationDelegate {
21 | var window: UIWindow?
22 |
23 | func application(_ application: UIApplication,
24 | continue userActivity: NSUserActivity,
25 | restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
26 | guard userActivity.activityType == NSUserActivityTypeBrowsingWeb else {
27 | return false
28 | }
29 |
30 | // Confirm that the NSUserActivity object contains a valid NDEF message.
31 | let ndefMessage = userActivity.ndefMessagePayload
32 | guard !ndefMessage.records.isEmpty,
33 | ndefMessage.records[0].typeNameFormat != .empty else {
34 | return false
35 | }
36 |
37 | return true
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Demo/NFC Read-Write/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Demo/NFC Read-Write/Views/DraggableInterfaceView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DraggableInterfaceView.swift
3 | // NFC Read-Write
4 | //
5 | // Created by Ming on 6/8/2025.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct DraggableInterfaceView: View {
11 | let topContent: TopContent
12 | let bottomContent: BottomContent
13 | let dragContent: DragContent
14 |
15 | @State private var topHeight: CGFloat = 0
16 | @State private var isDragging = false
17 |
18 | init(
19 | @ViewBuilder topContent: () -> TopContent,
20 | @ViewBuilder bottomContent: () -> BottomContent,
21 | @ViewBuilder dragContent: () -> DragContent
22 | ) {
23 | self.topContent = topContent()
24 | self.bottomContent = bottomContent()
25 | self.dragContent = dragContent()
26 | }
27 |
28 | var body: some View {
29 | GeometryReader { geometry in
30 | let initialHeight = geometry.size.height / 2
31 |
32 | VStack(spacing: 0) {
33 | // Top section
34 | VStack {
35 | topContent
36 | }
37 | .frame(height: topHeight)
38 | .frame(maxWidth: .infinity)
39 | .background(
40 | LinearGradient(
41 | gradient: Gradient(colors: [Color.accentColor.opacity(0.35), Color.clear]),
42 | startPoint: .top,
43 | endPoint: .bottom
44 | )
45 | )
46 |
47 | // Draggable divider
48 | VStack {
49 | Rectangle()
50 | .fill(isDragging ? Color.primary : Color.gray.opacity(0.25))
51 | .frame(width: 75, height: 4)
52 | .cornerRadius(25)
53 |
54 | dragContent
55 | .padding(.top, 4)
56 | }
57 | .frame(height: 60)
58 | .padding(.vertical, 5)
59 | .gesture(
60 | DragGesture()
61 | .onChanged { value in
62 | isDragging = true
63 | let newHeight = topHeight + value.translation.height
64 | let minHeight: CGFloat = 150
65 | let maxHeight = geometry.size.height - 200 - 60
66 | topHeight = max(minHeight, min(maxHeight, newHeight))
67 | }
68 | .onEnded { _ in
69 | isDragging = false
70 | }
71 | )
72 |
73 | // Bottom section
74 | VStack(spacing: 0) {
75 | bottomContent
76 | }
77 | .frame(maxWidth: .infinity, maxHeight: .infinity)
78 | .background(
79 | LinearGradient(
80 | gradient: Gradient(colors: [Color.clear, Color.red.opacity(0.35)]),
81 | startPoint: .top,
82 | endPoint: .bottom
83 | )
84 | )
85 | }
86 | .onAppear {
87 | if topHeight == 0 {
88 | topHeight = initialHeight - 100
89 | }
90 | }
91 | }
92 | }
93 | }
94 |
95 | #Preview {
96 | DraggableInterfaceView {
97 | Text("Top Content")
98 | .font(.title)
99 | .padding()
100 | } bottomContent: {
101 | Text("Bottom Content")
102 | .font(.title)
103 | .padding()
104 | } dragContent: {
105 | Text("Drag Options")
106 | .font(.caption)
107 | }
108 | .ignoresSafeArea(.all)
109 | .preferredColorScheme(.dark)
110 | }
111 |
--------------------------------------------------------------------------------
/Demo/NFC Read-Write/Views/NFCActionView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NFCActionView.swift
3 | // NFC Read-Write
4 | //
5 | // Created by Ming on 6/8/2025.
6 | //
7 |
8 | import SwiftUI
9 | import SwiftNFC
10 |
11 | struct NFCActionView: View {
12 | let onRead: () -> Void
13 | let onWrite: () -> Void
14 |
15 | var body: some View {
16 | HStack(spacing: 0) {
17 | Button(action: onRead) {
18 | Spacer()
19 | Label("Read NFC", systemImage: "wave.3.left.circle.fill")
20 | Spacer()
21 | }
22 | .tint(.white)
23 |
24 | Divider()
25 |
26 | Button(action: onWrite) {
27 | Spacer()
28 | Label("Write NFC", systemImage: "wave.3.left.circle.fill")
29 | Spacer()
30 | }
31 | .tint(.white)
32 | }
33 | }
34 | }
35 |
36 | #Preview {
37 | NFCActionView(
38 | onRead: { print("Read tapped") },
39 | onWrite: { print("Write tapped") }
40 | )
41 | .frame(height: 75)
42 | .background(Color.gray.opacity(0.2))
43 | }
44 |
--------------------------------------------------------------------------------
/Demo/NFC Read-Write/Views/NFCEditorView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NFCEditorView.swift
3 | // NFC Read-Write
4 | //
5 | // Created by Ming on 6/8/2025.
6 | //
7 |
8 | import SwiftUI
9 | import SwiftNFC
10 |
11 | struct NFCEditorView: View {
12 | @Binding var text: String
13 | let placeholder: String
14 | var font: Font = .body
15 |
16 | var body: some View {
17 | TextEditor(text: $text)
18 | .font(font)
19 | .padding(15)
20 | }
21 | }
22 |
23 | struct NFCMessageEditor: View {
24 | @ObservedObject var nfcReader: NFCReader
25 |
26 | var body: some View {
27 | NFCEditorView(text: $nfcReader.msg, placeholder: "Scan to read or Edit here to write...", font: .title)
28 | }
29 | }
30 |
31 | struct NFCRawDataEditor: View {
32 | @ObservedObject var nfcReader: NFCReader
33 |
34 | var body: some View {
35 | NFCEditorView(text: $nfcReader.raw, placeholder: "Raw Data available after scan.")
36 | }
37 | }
38 |
39 | #Preview {
40 | NFCEditorView(text: .constant("Sample text"), placeholder: "Enter text here...")
41 | }
42 |
--------------------------------------------------------------------------------
/Demo/NFC Read-Write/Views/NFCOptionsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NFCOptionsView.swift
3 | // NFC Read-Write
4 | //
5 | // Created by Ming on 6/8/2025.
6 | //
7 |
8 | import SwiftUI
9 | import SwiftNFC
10 |
11 | struct NFCOptionsView: View {
12 | @ObservedObject var nfcWriter: NFCWriter
13 | @AppStorage("type") private var type = "T"
14 | @Binding var keyboardVisible: Bool
15 | @State private var animationOffset: CGFloat = 0
16 |
17 | var body: some View {
18 | HStack {
19 | Text("SwiftNFC")
20 | .font(.headline)
21 | .fontWeight(.heavy)
22 | .foregroundStyle(
23 | LinearGradient(
24 | gradient: Gradient(colors: [
25 | Color.accentColor.opacity(0.8),
26 | Color.white.opacity(0.8),
27 | Color.red.opacity(0.8),
28 | ]),
29 | startPoint: UnitPoint(x: animationOffset - 0.3, y: 0.5),
30 | endPoint: UnitPoint(x: animationOffset + 0.3, y: 0.5)
31 | )
32 | )
33 | .onAppear {
34 | startShimmerAnimation()
35 | }
36 |
37 | Spacer()
38 |
39 | if keyboardVisible {
40 | Button(action: {
41 | UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
42 | }) {
43 | Image(systemName: "keyboard.chevron.compact.down")
44 | }.tint(.secondary)
45 | }
46 |
47 | Spacer()
48 |
49 | Picker(selection: $type, label: Text("Type Picker")) {
50 | Text("Text").tag("T")
51 | Text("Link").tag("U")
52 | }
53 | .onAppear {
54 | nfcWriter.type = type
55 | }
56 | .onChange(of: type) { newType in
57 | nfcWriter.type = newType
58 | }
59 | }
60 | .padding(.horizontal, 20)
61 | }
62 |
63 | private func startShimmerAnimation() {
64 | withAnimation(
65 | Animation.linear(duration: 2.0)
66 | .repeatForever(autoreverses: false)
67 | ) {
68 | animationOffset = 1.3
69 | }
70 | }
71 | }
72 |
73 | #Preview {
74 | NFCOptionsView(
75 | nfcWriter: NFCWriter(),
76 | keyboardVisible: .constant(true)
77 | ).preferredColorScheme(.dark)
78 | }
79 |
--------------------------------------------------------------------------------
/Demo/NFC-Read-Write-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIBackgroundModes
6 |
7 | nearby-interaction
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 MING
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: 5.9
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 | //
4 | // Created by Ming on 9/1/2023.
5 | //
6 |
7 | import PackageDescription
8 |
9 | let package = Package(
10 | name: "SwiftNFC",
11 | defaultLocalization: "en",
12 | platforms: [
13 | .iOS(.v15)
14 | ],
15 | products: [
16 | .library(
17 | name: "SwiftNFC",
18 | targets: ["SwiftNFC"]),
19 | ],
20 | dependencies: [
21 | ],
22 | targets: [
23 | .target(
24 | name: "SwiftNFC",
25 | dependencies: [])
26 | ]
27 | )
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SwiftNFC 📱🏷️ - Powerful Read+Write Content
2 | ### Accelerated by Apple SwiftUI & Backed with CoreNFC
3 |
4 |
5 |
6 | ## Aims
7 | Provide a super-easy way for Apple Developers to Read and Write NFC Tags on SwiftUI.
8 |
9 |
10 |
11 | ## Version
12 | 
13 | 
14 |
15 |
16 |
17 | ## Environment
18 |
19 | ### Xcode Local
20 | Tested on | Latest | Compatible
21 | --------- | ------ | ----------
22 | iOS | 16 | > 14
23 |
24 | *Apple Watch is well-known not supported. iPad / Mac is compatible with CoreNFC but there is no hardware to support this feature. 😂*
25 |
26 | ### Xcode Cloud ☁️
27 | Compatible ✅
28 |
29 | *Xcode Cloud requires Apple Developer Program membership.*
30 |
31 | ## Guide
32 | [Full Tutorial](https://post.1998.media/how-to-create-a-simple-nfc-app-with-swiftui/)
33 |
34 | ## Preparation
35 | 1. Add to your project via Package Manager.
36 |
37 |
38 | 2. Add ```Near Field Communication Tag Reading``` (aka NFC) into the Project's Combilities.
39 |
40 |
41 | 3. Add NFC Privacy into ```Info.plist```
42 |
43 |
44 | ## Basic Usage
45 |
46 | 1. Import first.
47 | ```swift
48 | import SwiftNFC
49 | ```
50 |
51 | 2. Add ObservedObject before ```body``` or any ```some View```.
52 |
53 | ### Read
54 | ```swift
55 | @ObservedObject var NFCR = NFCReader()
56 | ```
57 |
58 | ### Write
59 | ```swift
60 | @ObservedObject var NFCW = NFCWriter()
61 | ```
62 |
63 | ### Functions
64 | ```swift
65 | func read() {
66 | NFCR.read()
67 | }
68 | func write() {
69 | NFCW.msg = NFCR.msg
70 | NFCW.write()
71 | }
72 | ```
73 |
74 | ## Demo
75 | Path: `./Demo` (Xcode Project in SwiftUI)
76 |
77 | ## License
78 | MIT
79 |
80 | ## FAQ
81 | Q1. How can I contribute to the project?
82 | A1. Simply pull a request, and someone will review your code. If everything is okay, your changes will be merged and reflected in the next minor version.
83 | Q2. Can I use it in Educational (includ. Student's Homework, Class's demo) or NGO or Commerical Project?
84 | A2. YES. This project is under license of MIT. Feel free to use it :)
85 |
--------------------------------------------------------------------------------
/Sources/SwiftNFC/Localizable.xcstrings:
--------------------------------------------------------------------------------
1 | {
2 | "sourceLanguage" : "en",
3 | "strings" : {
4 | "Hold your iPhone near the tag." : {
5 | "extractionState" : "manual",
6 | "localizations" : {
7 | "zh-HK" : {
8 | "stringUnit" : {
9 | "state" : "translated",
10 | "value" : "將 iPhone 靠近 NFC 標籤。"
11 | }
12 | }
13 | }
14 | },
15 | "Raw Data available after scan." : {
16 | "localizations" : {
17 | "zh-HK" : {
18 | "stringUnit" : {
19 | "state" : "translated",
20 | "value" : "掃描後可獲得原始資料。"
21 | }
22 | }
23 | }
24 | },
25 | "Scan to read or Edit here to write..." : {
26 | "localizations" : {
27 | "zh-HK" : {
28 | "stringUnit" : {
29 | "state" : "translated",
30 | "value" : "掃描以閱讀或在此處編輯以寫入..."
31 | }
32 | }
33 | }
34 | }
35 | },
36 | "version" : "1.0"
37 | }
--------------------------------------------------------------------------------
/Sources/SwiftNFC/SwiftNFC.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import CoreNFC
3 |
4 | @available(iOS 15.0, *)
5 | public class NFCReader: NSObject, ObservableObject, NFCNDEFReaderSessionDelegate {
6 |
7 | public var startAlert = String(localized: "Hold your iPhone near the tag.", bundle: .module)
8 | public var endAlert = ""
9 | public var msg = String(localized: "Scan to read or Edit here to write...", bundle: .module)
10 | public var raw = String(localized: "Raw Data available after scan.", bundle: .module)
11 |
12 | public var session: NFCNDEFReaderSession?
13 |
14 | public func read() {
15 | guard NFCNDEFReaderSession.readingAvailable else {
16 | print("Error")
17 | return
18 | }
19 | session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: true)
20 | session?.alertMessage = self.startAlert
21 | session?.begin()
22 | }
23 |
24 | public func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
25 | DispatchQueue.main.async {
26 | self.msg = messages.map {
27 | $0.records.map {
28 | String(decoding: $0.payload, as: UTF8.self)
29 | }.joined(separator: "\n")
30 | }.joined(separator: " ")
31 |
32 | self.raw = messages.map {
33 | $0.records.map {
34 | "\($0.typeNameFormat) \(String(decoding:$0.type, as: UTF8.self)) \(String(decoding:$0.identifier, as: UTF8.self)) \(String(decoding: $0.payload, as: UTF8.self))"
35 | }.joined(separator: "\n")
36 | }.joined(separator: " ")
37 |
38 |
39 | session.alertMessage = self.endAlert != "" ? self.endAlert : "Read \(messages.count) NDEF Messages, and \(messages[0].records.count) Records."
40 | }
41 | }
42 |
43 | public func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) {
44 | }
45 |
46 | public func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
47 | print("Session did invalidate with error: \(error)")
48 | self.session = nil
49 | }
50 | }
51 |
52 | public class NFCWriter: NSObject, ObservableObject, NFCNDEFReaderSessionDelegate {
53 |
54 | public var startAlert = String(localized: "Hold your iPhone near the tag.", bundle: .module)
55 | public var endAlert = ""
56 | public var msg = ""
57 | public var type = "T"
58 |
59 | public var session: NFCNDEFReaderSession?
60 |
61 | public func write() {
62 | guard NFCNDEFReaderSession.readingAvailable else {
63 | print("Error")
64 | return
65 | }
66 | session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: true)
67 | session?.alertMessage = self.startAlert
68 | session?.begin()
69 | }
70 |
71 | public func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
72 | }
73 |
74 | public func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
75 | if tags.count > 1 {
76 | let retryInterval = DispatchTimeInterval.milliseconds(500)
77 | session.alertMessage = "Detected more than 1 tag. Please try again."
78 | DispatchQueue.global().asyncAfter(deadline: .now() + retryInterval, execute: {
79 | session.restartPolling()
80 | })
81 | return
82 | }
83 |
84 | let tag = tags.first!
85 | session.connect(to: tag, completionHandler: { (error: Error?) in
86 | if nil != error {
87 | session.alertMessage = "Unable to connect to tag."
88 | session.invalidate()
89 | return
90 | }
91 |
92 | tag.queryNDEFStatus(completionHandler: { (ndefStatus: NFCNDEFStatus, capacity: Int, error: Error?) in
93 | guard error == nil else {
94 | session.alertMessage = "Unable to query the status of tag."
95 | session.invalidate()
96 | return
97 | }
98 |
99 | switch ndefStatus {
100 | case .notSupported:
101 | session.alertMessage = "Tag is not NDEF compliant."
102 | session.invalidate()
103 | case .readOnly:
104 | session.alertMessage = "Read only tag detected."
105 | session.invalidate()
106 | case .readWrite:
107 | let payload: NFCNDEFPayload?
108 | if self.type == "T" {
109 | payload = NFCNDEFPayload.init(
110 | format: .nfcWellKnown,
111 | type: Data("\(self.type)".utf8),
112 | identifier: Data(),
113 | payload: Data("\(self.msg)".utf8)
114 | )
115 | } else {
116 | payload = NFCNDEFPayload.wellKnownTypeURIPayload(string: "\(self.msg)")
117 | }
118 | let message = NFCNDEFMessage(records: [payload].compactMap({ $0 }))
119 | tag.writeNDEF(message, completionHandler: { (error: Error?) in
120 | if nil != error {
121 | session.alertMessage = "Write to tag fail: \(error!)"
122 | } else {
123 | session.alertMessage = self.endAlert != "" ? self.endAlert : "Write \(self.msg) to tag successful."
124 | }
125 | session.invalidate()
126 | })
127 | @unknown default:
128 | session.alertMessage = "Unknown tag status."
129 | session.invalidate()
130 | }
131 | })
132 | })
133 | }
134 |
135 | public func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) {
136 | }
137 |
138 | public func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
139 | print("Session did invalidate with error: \(error)")
140 | self.session = nil
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/Tests/SwiftNFCTests/SwiftNFCTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import SwiftNFC
3 |
4 | final class SwiftNFCTests: XCTestCase {
5 | func testExample() throws {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | XCTAssertEqual(SwiftNFC().text, "Hello, World!")
10 | }
11 | }
12 |
--------------------------------------------------------------------------------