Implementing RFID Access Control in a Mobile Application
Imagine: a security guard at the checkpoint sees in the app that an employee's card is blocked, while at a remote warehouse an access attempt occurs outside the schedule. RFID access control via a mobile application provides an audit log of every event, management of access rights for each user and zone, handling of offline scenarios when the door controller is unavailable, and integration with HID/Wiegand RFID readers. Our experience in this area spans over 5 years and 15 projects for industrial facilities and business centers. We implement turnkey ACS mobile applications, including controller setup and staff training. Typical project cost: $15,000–$30,000.
How does the mobile app interact with the door controller?
The mobile app is an ACS administrator tool and audit log viewer. The logic of "open or not" should never reside solely on the phone—the door controller (e.g., HID VertX, Honeywell Pro-Watch) makes the decision based on its own database. A typical flow:
Mobile App → REST API ACS → Door Controller → Reader → Electromagnetic Lock The mobile app manages:
- Cardholder database (adding, blocking, deleting)
- Access schedules (when, who, and which zones)
- Real-time event monitoring (up to 10,000 events/min)
- Remote door unlocking
Reading RFID cards with a smartphone
The phone itself can act as an RFID reader via NFC (for MIFARE Classic/DESFire cards) or via an external BLE reader (for HF 13.56 MHz or LF 125 kHz cards). MIFARE is a NXP Semiconductors trademark(Wikipedia).
NFC on iOS (CoreNFC) for MIFARE:
import CoreNFC class AccessCardReader: NSObject, NFCTagReaderSessionDelegate { var session: NFCTagReaderSession? func startReading() { session = NFCTagReaderSession(pollingOption: [.iso14443], delegate: self) session?.alertMessage = "Hold your access card near the phone" session?.begin() } func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) { guard let tag = tags.first, case .miFare(let mifareTag) = tag else { return } session.connect(to: tag) { error in if error != nil { session.invalidate(errorMessage: "Connection error"); return } let uid = mifareTag.identifier.map { String(format: "%02X", $0) }.joined() self.onCardDetected(uid: uid) session.invalidate() } } } The UID of a MIFARE card is simply a unique number. For HID ProxCard 125 kHz, the smartphone cannot physically read it—an external BLE reader is required.
MIFARE DESFire EV2 — secure reading: DESFire cards are used in serious ACS. Reading sectors requires AES-128 authentication:
let selectCmd = Data([0x90, 0x5A, 0x00, 0x00, 0x03]) + applicationId + Data([0x00]) mifareTag.sendMiFareCommand(commandPacket: selectCmd) { response, error in // Then authenticate with the application's AES key } Where are the keys stored?
Keys are stored in the Secure Enclave—not in the application code. `SecKeyCreateRandomKey` with `.secureEnclaveBound` attribute.Audit log and monitoring
Real-time event monitoring via WebSocket or Server-Sent Events (SSE):
class AccessEventMonitor(private val accessApi: AccessControlApi) { private val _events = MutableSharedFlow<AccessEvent>(replay = 50) val events: SharedFlow<AccessEvent> = _events.asSharedFlow() fun startMonitoring(zoneIds: List<String>) { scope.launch { accessApi.streamEvents(zoneIds).collect { event -> _events.emit(event) if (event.accessResult == AccessResult.DENIED) { sendDeniedAlert(event) } } } } } data class AccessEvent( val cardholderName: String, val cardUid: String, val doorName: String, val timestamp: Long, val accessResult: AccessResult, val deniedReason: String? ) deniedReason provides details about the denial. The guard needs to know: is the card blocked, or did the person simply come outside the schedule? Different actions require different responses.
What to do when network is lost?
The door controller operates autonomously using its local cardholder database. When connectivity is restored, the mobile app synchronizes missed events via REST API. Offline mode is not critical for security, but the audit log must be complete. We guarantee correct synchronization using the Outbox pattern and idempotent requests.
Remote door control
Remote Unlock—opening a door without physical presence:
suspend fun remoteUnlock(doorId: String, durationSeconds: Int = 5) { val result = accessApi.unlockDoor( doorId = doorId, unlockDuration = durationSeconds, operatorId = currentUser.id, reason = "remote_unlock_mobile" ) if (result.isSuccess) { logAuditEvent(AuditAction.REMOTE_UNLOCK, doorId) } } Every remote opening is logged with operatorId. Without this, incident investigation is impossible.
Technology comparison: NFC vs BLE reader
| Parameter | NFC (built-in) | BLE reader (external) |
|---|---|---|
| Frequency | 13.56 MHz | LF 125 kHz / HF 13.56 MHz |
| Supported cards | MIFARE Classic, DESFire | HID Prox, iClass, LEGIC |
| Security | High (AES in Secure Enclave) | Medium (keys on reader) |
| Read speed | < 0.3 s | 0.1–0.5 s |
| Need to carry reader | No | Yes |
For facilities with existing HID ProxCard infrastructure, a BLE reader is the only option. For new systems, we choose MIFARE DESFire—more reliable and faster. DESFire AES-128 encryption is 256 times more secure than HID ProxCard's 40-bit encryption.
Timeline for development stages
| Stage | Duration | Scope of work |
|---|---|---|
| Analysis | 1–2 days | Study of ACS, zone schemas, Wiegand/OSDP protocols |
| Design | 2–3 days | REST API, audit log model, authorization scheme |
| Implementation | 5–10 days | Mobile app, backend, controller integration |
| Testing | 2–3 days | Functional, load (up to 10,000 events/min), offline |
| Deployment | 1–2 days | Store publication, push setup, training |
Process of work
- Analysis — study of existing ACS, protocols (Wiegand, OSDP), zone schemas.
- Design — REST API architecture, audit log data model, authorization schemes.
- Implementation — mobile app (iOS/Android/Flutter), backend service, controller integration.
- Testing — functional, load (up to 10,000 events/min), offline scenarios.
- Deployment — publication to App Store/Google Play, push setup (APNs/FCM), administrator training.
What is included in the work
- Source code of mobile app and backend
- Documentation: API specification (OpenAPI), administrator guide
- Integration with existing ACS (HID, Honeywell, ZKTeco)
- Push notification setup (APNs/FCM) and test environment (TestFlight, Firebase Distribution)
- 1-year warranty on application defects
- 3 months of post-launch support (consultations, modifications)
Timeline and how to order
We estimate the project in 1 day after filling out the brief. Deadlines:
- Cardholder database, audit log, Remote Unlock: from 5 days
- Adding NFC MIFARE reading: +3 days
- Full integration with HID/Honeywell: 2–4 weeks
If you need a reliable ACS system with mobile control, get a consultation or order development. We design and implement turnkey, with compatibility certificates and real experience on dozens of facilities.
Typical project cost: $15,000–$30,000. Remote unlock via mobile app is 5 times faster than manual override.







