├── .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 | NewCover 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 | ![GitHub release (latest by date)](https://img.shields.io/github/v/release/1998code/SwiftNFC?color=g&label=STABLE&style=for-the-badge) 13 | ![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/1998code/SwiftNFC?color=green&include_prereleases&label=BETA&style=for-the-badge) 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 | CleanShot 2023-01-25 at 12 23 13@2x 37 | 38 | 2. Add ```Near Field Communication Tag Reading``` (aka NFC) into the Project's Combilities. 39 | CleanShot 2023-01-25 at 12 30 23@2x 40 | 41 | 3. Add NFC Privacy into ```Info.plist``` 42 | CleanShot 2023-01-25 at 18 35 58@2x 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 | --------------------------------------------------------------------------------