Core Bluetooth: معماری و توسعه BLE در iOS

نویسنده: IT Sectr منتشر شده: 2026-07-16 زمان مطالعه: 10 دقیقه

Core Bluetooth فریم‌ورک اپل برای تعامل با Bluetooth Low Energy در iOS، iPadOS و macOS است. این فریم‌ورک مجموعه کاملی از APIها را برای کار در هر دو نقش BLE ارائه می‌دهد: دستگاه مرکزی (CBCentralManager) برای اسکن و اتصال به دستگاه‌های پیرامونی، و دستگاه پیرامونی (CBPeripheralManager) برای شبیه‌سازی سرور BLE. Core Bluetooth پشته پروتکل BLE را از رادیوی فیزیکی تا پروفایل GATT کاربردی انتزاع می‌کند. طبق Apple Developer، 2026، Core Bluetooth تنها API رسمی اپل برای توسعه BLE است که از BLE 4.0–5.4 با extended advertising، 2M PHY و LE Audio پشتیبانی می‌کند.

نکات کلیدی

  • Core Bluetooth فریم‌ورک سیستمی اپل برای توسعه BLE در iOS، iPadOS و macOS
  • CBCentralManager کلاسی برای اسکن و اتصال به دستگاه‌های پیرامونی BLE از طرف دستگاه مرکزی
  • CBPeripheralManager کلاسی برای ایجاد سرور BLE که سرویس‌ها و ویژگی‌ها را منتشر می‌کند
  • پروفایل GATT مدل سلسله‌مراتبی سرویس‌ها، ویژگی‌ها و توصیفگرها برای تبادل داده
  • حالت‌های پس‌زمینه Core Bluetooth از ارتباط BLE در پس‌زمینه از طریق نمایندگان سیستم و state restoration پشتیبانی می‌کند

Core Bluetooth چیست: معماری و اجزاء

Core Bluetooth پشته BLE را به دو نقش منطقی تعریف‌شده توسط مشخصات Bluetooth SIG تقسیم می‌کند. نقش دستگاه مرکزی (Central) توسط کلاس CBCentralManager نمایش داده می‌شود — اسکن را آغاز می‌کند، اتصالات برقرار می‌کند و لیست CBPeripheral‌های متصل را مدیریت می‌کند. نقش دستگاه پیرامونی (Peripheral) توسط CBPeripheralManager نمایش داده می‌شود — سرویس‌ها و ویژگی‌ها را منتشر می‌کند، به درخواست‌های مرکزی پاسخ می‌دهد و اعلان‌ها ارسال می‌کند. یک نشست iOS می‌تواند همزمان در هر دو نقش روی رادیوهای BLE مختلف کار کند، اما برنامه معمولی از یک نقش استفاده می‌کند.

معماری Core Bluetooth پنج انتزاع کلیدی را شامل می‌شود. CBCentralManager وضعیت آداپتور Bluetooth دستگاه را مدیریت می‌کند: poweredOn (آماده کار)، poweredOff (Bluetooth غیرفعال)، unauthorized (بدون مجوز)، unsupported (BLE در دسترس نیست). CBPeripheral یک دستگاه BLE راه دور را با UUID، نام، RSSI و سلسله‌مراتب GATT آن نمایش می‌دهد. CBService گروه منطقی ویژگی‌ها. CBCharacteristic نقطه داده برای خواندن/نوشتن/اعلان. CBPeripheralManager یک سرور GATT محلی برای شبیه‌سازی دستگاه پیرامونی ایجاد می‌کند.

کلاسنقشروش‌های اصلی
CBCentralManagerدستگاه مرکزیscanForPeripherals, connect, cancelPeripheralConnection, retrievePeripherals
CBPeripheralدستگاه پیرامونی راه دورdiscoverServices, discoverCharacteristics, readValue, writeValue, setNotifyValue
CBPeripheralManagerدستگاه پیرامونی محلیaddService, removeService, startAdvertising, respondToRequest, updateValue
CBCentralمرکزی راه دورmaximumUpdateValueLength, identifier, ancsAuthorized

وضعیت‌های CBCentralManager همه عملیات BLE را مدیریت می‌کنند. هنگام راه‌اندازی برنامه، centralManagerDidUpdateState با وضعیت فعلی Bluetooth فراخوانی می‌شود. اگر وضعیت .poweredOn نباشد، هرگونه فراخوانی BLE توسط سیستم نادیده گرفته می‌شود. توسعه‌دهنده باید قبل از هر اسکن و اتصال، state را بررسی کند. انتقال از .poweredOff به .poweredOn هنگام روشن شدن Bluetooth در تنظیمات iOS رخ می‌دهد — نماینده فراخوانی مجدد دریافت می‌کند و برنامه می‌تواند اسکن را از سر بگیرد.

CBCentralManager: اسکن و اتصال دستگاه‌های BLE

CBCentralManager نقطه ورود برای همه عملیات BLE از طرف دستگاه مرکزی است. مقداردهی اولیه یک نماینده (CBCentralManagerDelegate) و یک صف DispatchQueue دریافت می‌کند — توصیه اپل استفاده از main queue برای سادگی یا serial queue برای کارایی. پس از مقداردهی، فریم‌ورک به طور خودکار وضعیت Bluetooth را بررسی کرده و centralManagerDidUpdateState: را فراخوانی می‌کند — اولین نماینده اجباری برای پردازش.

اسکن با متد scanForPeripheralsWithServices:options: آغاز می‌شود. پارامتر اول آرایه‌ای از CBUUID سرویس‌ها برای فیلتر کردن است: اگر UUID سرویس‌های مورد نظر مشخص باشد، ارسال آنها مصرف انرژی و زمان جستجو را کاهش می‌دهد. اگر nil باشد، همه دستگاه‌های BLE در محدوده تشخیص داده می‌شوند. گزینه‌ها شامل .allowDuplicatesKey (تشخیص مجدد یک دستگاه) و .solicitedServiceUUIDsKey (برای سرویس‌های منتشر شده در مرکزی) هستند.

swift
import CoreBluetooth

class BLECentral: NSObject {

    private var centralManager: CBCentralManager!
    private var discoveredPeripherals: [CBPeripheral] = []

    override init() {
        super.init()
        centralManager = CBCentralManager(delegate: self, queue: .main)
    }

    // شروع اسکن BLE
    func startScan() {
        guard centralManager.state == .poweredOn else {
            print("Bluetooth در دسترس نیست")
            return
        }
        // اسکن همه دستگاه‌ها (nil = بدون فیلتر)
        centralManager.scanForPeripherals(withServices: nil,
                                            options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
    }

    // توقف اسکن
    func stopScan() {
        centralManager.stopScan()
    }

    // اتصال به دستگاه انتخاب‌شده
    func connect(to peripheral: CBPeripheral) {
        centralManager.connect(peripheral, options: nil)
    }
}

// MARK: - CBCentralManagerDelegate
extension BLECentral: CBCentralManagerDelegate {

    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        if central.state == .poweredOn {
            startScan()
        }
    }

    func centralManager(_ central: CBCentralManager,
                        didDiscover peripheral: CBPeripheral,
                        advertisementData: [String : Any],
                        rssi: NSNumber) {
        if !discoveredPeripherals.contains(where: { $0.identifier == peripheral.identifier }) {
            discoveredPeripherals.append(peripheral)
            print("Found devices: \(peripheral.name ?? "Unknown"), RSSI: \(rssi)")
        }
    }

    func centralManager(_ central: CBCentralManager,
                        didConnect peripheral: CBPeripheral) {
        print("Connected: \(peripheral.identifier)")
        peripheral.delegate = self
        peripheral.discoverServices(nil)
    }

    func centralManager(_ central: CBCentralManager,
                        didDisconnectPeripheral peripheral: CBPeripheral,
                        error: Error?) {
        print("Disconnected: \(peripheral.identifier)")
    }
}

کلاس BLECentral چرخه کامل اسکن و اتصال دستگاه‌های BLE را نشان می‌دهد. centralManagerDidUpdateState اسکن را زمانی که Bluetooth روشن است آغاز می‌کند. didDiscoverPeripheral دستگاه‌های یافت شده را در آرایه discoveredPeripherals با حذف تکراری بر اساس identifier جمع‌آوری می‌کند. پس از اتصال (didConnect)، بلافاصله کشف سرویس‌ها آغاز می‌شود — این یک مرحله اجباری قبل از هر عملیات GATT است.

CBPeripheralManager: ایجاد سرور BLE در iOS

CBPeripheralManager کلاسی برای شبیه‌سازی دستگاه پیرامونی BLE در iOS است. برنامه در نقش پیرامونی می‌تواند سرویس‌ها و ویژگی‌های خود را منتشر کند، درخواست‌های خواندن/نوشتن ورودی از دستگاه مرکزی را دریافت کند و اعلان‌ها ارسال کند. CBPeripheralManager برای لوازم جانبی BLE شبیه‌سازی‌شده توسط iPhone استفاده می‌شود: کنترل‌ها، صفحه‌کلیدها، ردیاب‌ها، دروازه‌های IoT.

چرخه زندگی CBPeripheralManager با مقداردهی اولیه و نماینده CBPeripheralManagerDelegate آغاز می‌شود. پس از تأیید poweredOn از طریق peripheralManagerDidUpdateState:، سرویس‌ها منتشر می‌شوند (addService:) و تبلیغات آغاز می‌شود (startAdvertising:). داده‌های تبلیغاتی CBAdvertisementData شامل نام محلی (CBAdvertisementDataLocalNameKey)، UUID سرویس‌ها (CBAdvertisementDataServiceUUIDsKey) و سطح توان (CBAdvertisementDataTxPowerLevelKey) هستند. حداکثر اندازه بسته تبلیغاتی 31 بایت برای BLE 4.0، 251 بایت برای extended advertising BLE 5.0+ است.

swift
// دستگاه پیرامونی BLE در iOS از طریق CBPeripheralManager
class BLEPeripheral: NSObject {

    private var peripheralManager: CBPeripheralManager!

    let serviceUUID = CBUUID(string: "1234")
    let characteristicUUID = CBUUID(string: "5678")

    override init() {
        super.init()
        peripheralManager = CBPeripheralManager(delegate: self, queue: .main)
    }

    // انتشار سرویس با ویژگی
    func setupService() {
        let characteristic = CBMutableCharacteristic(
            type: characteristicUUID,
            properties: [.read, .write, .notify],
            value: nil,
            permissions: [.readable, .writeable]
        )
        let service = CBMutableService(type: serviceUUID, primary: true)
        service.characteristics = [characteristic]
        peripheralManager.add(service)
    }

    // شروع تبلیغات
    func startAdvertising() {
        let advertisementData: [String: Any] = [
            CBAdvertisementDataLocalNameKey: "My BLE Device",
            CBAdvertisementDataServiceUUIDsKey: [serviceUUID]
        ]
        peripheralManager.startAdvertising(advertisementData)
    }
}

// MARK: - CBPeripheralManagerDelegate
extension BLEPeripheral: CBPeripheralManagerDelegate {

    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
        if peripheral.state == .poweredOn {
            setupService()
        }
    }

    func peripheralManager(_ peripheral: CBPeripheralManager,
                        didAdd service: CBService,
                        error: Error?) {
        if error == nil {
            startAdvertising()
        }
    }

    // مدیریت درخواست خواندن
    func peripheralManager(_ peripheral: CBPeripheralManager,
                        didReceiveRead request: CBATTRequest) {
        let data = "CurrentValue".data(using: .utf8)!
        request.value = data
        peripheralManager.respond(to: request, withResult: .success)
    }

    // مدیریت درخواست نوشتن
    func peripheralManager(_ peripheral: CBPeripheralManager,
                        didReceiveWrite requests: [CBATTRequest]) {
        for request in requests {
            if let value = request.value {
                print("Write: \(value)")
            }
        }
        peripheralManager.respond(to: requests.first!, withResult: .success)
    }
}

کلاس BLEPeripheral یک سرور BLE با یک ویژگی که از خواندن، نوشتن و اعلان پشتیبانی می‌کند ایجاد می‌کند. پس از مقداردهی، peripheralManagerDidUpdateState سرویس را از طریق addService: منتشر می‌کند، سپس تبلیغات را از طریق startAdvertising: آغاز می‌کند. مدیریت‌کننده‌های didReceiveRead و didReceiveWrite به درخواست‌های GATT ورودی از دستگاه مرکزی پاسخ می‌دهند. برای ارسال اعلان‌ها از متد updateValue:forCharacteristic:onSubscribedCentrals: استفاده می‌شود.

عملیات GATT: خواندن، نوشتن و اعلان‌ها

عملیات GATT (Generic Attribute Profile) اساس تبادل داده در Core Bluetooth است. پس از کشف سرویس‌ها و ویژگی‌ها، دستگاه مرکزی می‌تواند سه نوع عملیات را انجام دهد: خواندن مقدار ویژگی، نوشتن مقدار و اشتراک در اعلان‌ها/نشان‌ها. هر عملیات ناهمزمان است و نتیجه را از طریق نماینده CBPeripheralDelegate مربوطه برمی‌گرداند.

خواندن با فراخوانی readValueForCharacteristic: انجام می‌شود. مقدار در peripheral:didUpdateValueForCharacteristic:error: دریافت می‌شود. مهم: خواندن مقدار فعلی را از دستگاه برمی‌گرداند، نه مقدار ذخیره‌شده را. اگر دستگاه از خواندن پشتیبانی نکند (ویژگی .read)، فراخوانی خطا برمی‌گرداند. برای مقادیر بزرگ (بزرگتر از MTU)، BLE به طور خودکار داده را در سطح GATT قطعه‌بندی و مونتاژ می‌کند.

نوشتن با writeValue:forCharacteristic:type: انجام می‌شود. BLE از دو مدل نوشتن پشتیبانی می‌کند: withResponse (قابل اعتماد، با تأیید) و withoutResponse (سریع، بدون تأیید). ویژگی CBCharacteristic.properties انواع نوشتن موجود را تعیین می‌کند. حداکثر اندازه یک بسته نوشتن توسط MTU محدود می‌شود: 23 بایت برای BLE 4.0 (20 بایت داده مفید + 3 بایت هدر)، تا 247 بایت برای BLE 5.0 با extended MTU (MTU 251).

اعلان‌ها با فراخوانی setNotifyValue:true forCharacteristic: فعال می‌شوند. پس از اشتراک، دستگاه پیرامونی به طور خودکار به‌روزرسانی‌ها را از طریق peripheral:didUpdateValueForCharacteristic: هر زمان که مقدار ویژگی تغییر کند ارسال می‌کند. برای غیرفعال کردن اعلان‌ها، setNotifyValue:false forCharacteristic: فراخوانی می‌شود. Core Bluetooth به طور خودکار توصیفگر CCCD را در دستگاه پیرامونی مدیریت می‌کند.

عملیاتمتدنمایندهنوع انتقال
خواندنreadValueForCharacteristic:didUpdateValueForCharacteristicPolling (درخواست-پاسخ)
نوشتن withResponsewriteValue:forCharacteristic:type:withResponsedidWriteValueForCharacteristicبا تأیید
نوشتن withoutResponsewriteValue:forCharacteristic:type:withoutResponseبدون نمایندهبدون تأیید
اعلانsetNotifyValue:true forCharacteristic:didUpdateNotificationStateForCharacteristic + didUpdateValueForCharacteristicPush از پیرامونی

حالت پس‌زمینه Core Bluetooth و State Restoration

حالت پس‌زمینه Core Bluetooth به برنامه BLE اجازه می‌دهد در هنگام بودن در پس‌زمینه به اسکن، حفظ اتصالات و دریافت اعلان‌ها ادامه دهد. برای فعال‌سازی نیاز است: capability “Uses Bluetooth LE accessories” را در Xcode فعال کنید (Info.plist → Required background modes → App communicates using Core Bluetooth) و کلید „bluetooth-central” را به UIBackgroundModes اضافه کنید. برای نقش پیرامونی — „bluetooth-peripheral”.

State Restoration مکانیزم Core Bluetooth برای بازیابی وضعیت اتصالات BLE پس از راه‌اندازی مجدد برنامه توسط سیستم iOS است. هنگام فعال‌سازی حالت پس‌زمینه و تعیین restoreIdentifier در مقداردهی CBCentralManager یا CBPeripheralManager، iOS وضعیت پشته BLE را هنگام پایان برنامه ذخیره کرده و در راه‌اندازی بعدی آن را بازیابی می‌کند. نماینده centralManager:willRestoreState: یک فرهنگ لغت با CBPeripheral‌های ذخیره‌شده و اتصالات در انتظار دریافت می‌کند.

swift
// پیکربندی Core Bluetooth با State Restoration
class BLECentralWithRestoration: NSObject {

    let restoreIdentifier = "com.app.blecentral"
    private var centralManager: CBCentralManager!

    override init() {
        super.init()
        let options: [String: Any] = [
            CBCentralManagerOptionRestoreIdentifierKey: restoreIdentifier,
            CBCentralManagerOptionShowPowerAlertKey: true
        ]
        centralManager = CBCentralManager(delegate: self,
                                          queue: nil,
                                          options: options)
    }
}

extension BLECentralWithRestoration: CBCentralManagerDelegate {

    // بازیابی وضعیت پس از راه‌اندازی مجدد
    func centralManager(_ central: CBCentralManager,
                        willRestoreState dict: [String : Any]) {
        if let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey]
            as? [CBPeripheral] {
            for peripheral in peripherals {
                peripheral.delegate = self
                // بازیابی کشف GATT
                peripheral.discoverServices(nil)
            }
        }
    }

    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        if central.state == .poweredOn {
            print("Bluetooth پس از بازیابی آماده است")
        }
    }
}

در پیکربندی BLECentralWithRestoration، کلید CBCentralManagerOptionRestoreIdentifierKey ذخیره‌سازی وضعیت را فعال می‌کند. اگر برنامه توسط iOS پایان یافته باشد (مثلاً به دلیل کمبود حافظه)، در راه‌اندازی بعدی centralManager:willRestoreState: لیست CBPeripheral‌های قبلاً متصل شده را دریافت می‌کند. برنامه نمایندگان را بازیابی کرده و کشف مجدد سرویس‌ها را انجام می‌دهد — کاربر متوجه قطع اتصال نمی‌شود. بدون State Restoration، همه نشست‌های BLE هنگام پایان برنامه از دست می‌روند.

مثال برنامه BLE در Swift: مرکزی و پیرامونی

مثال کامل برنامه BLE در Swift دستگاه مرکزی و پیرامونی را در یک پروژه ترکیب می‌کند. برنامه می‌تواند در دو حالت کار کند: کشف و اتصال به دستگاه‌های BLE (Central) یا شبیه‌سازی لوازم جانبی BLE (Peripheral). در زیر معماری با یک مدیر BLE مشترک که نقش را در شروع انتخاب می‌کند ارائه شده است.

swift
// مدیر جهانی BLE برای مرکزی و پیرامونی
class BLEManager {

    enum Role {
        case central
        case peripheral
    }

    private let role: Role
    private var centralManager: CBCentralManager?
    private var peripheralManager: CBPeripheralManager?
    let advertisedServiceUUID = CBUUID(string: "A001")

    init(role: Role) {
        self.role = role
        switch role {
        case .central:
            centralManager = CBCentralManager(delegate: nil, queue: .main)
        case .peripheral:
            peripheralManager = CBPeripheralManager(delegate: nil, queue: .main)
        }
    }

    // دستگاه مرکزی: اسکن
    func scanForDevices() {
        centralManager?.scanForPeripherals(withServices: nil, options: nil)
    }

    // دستگاه پیرامونی: تبلیغات
    func advertiseService() {
        let data: [String: Any] = [
            CBAdvertisementDataServiceUUIDsKey: [advertisedServiceUUID]
        ]
        peripheralManager?.startAdvertising(data)
    }
}

// استفاده در شروع کار
let isCentral = UserDefaults.standard.bool(forKey: "isCentral")
let manager = BLEManager(role: isCentral ? .central : .peripheral)

if isCentral {
    manager.scanForDevices()
} else {
    manager.advertiseService()
}

مدیر BLEManager نقش را در مقداردهی انتخاب کرده و Manager مربوطه (CBCentralManager یا CBPeripheralManager) را ایجاد می‌کند. پرچم نقش می‌تواند در UserDefaults ذخیره شود یا از طریق سرور پیکربندی منتقل شود. این رویکرد به برنامه BLE اجازه می‌دهد خود را با سناریوی استفاده تطبیق دهد: در نقطه فروش، iPhone به عنوان مرکزی برای اسکن ترمینال‌های پرداخت کار می‌کند، در دروازه IoT — به عنوان پیرامونی برای جمع‌آوری داده از سنسورها.

سوالات متداول

Core Bluetooth چیست؟

Core Bluetooth فریم‌ورک اپل برای توسعه BLE در iOS، iPadOS و macOS است. APIهایی برای کار دستگاه مرکزی (CBCentralManager) و پیرامونی (CBPeripheralManager) ارائه می‌دهد. از BLE 4.0–5.4، extended advertising، 2M PHY و LE Audio پشتیبانی می‌کند. Core Bluetooth تنها API رسمی اپل برای ارتباط BLE است که برای همه برنامه‌های iOS کار با Bluetooth Low Energy الزامی است.

CBCentralManager چه تفاوتی با CBPeripheralManager دارد؟

CBCentralManager کلاسی برای کار در نقش دستگاه مرکزی است: دستگاه‌های پیرامونی BLE را اسکن می‌کند، اتصالات برقرار می‌کند، ویژگی‌ها را می‌خواند و می‌نویسد. CBPeripheralManager کلاسی برای کار در نقش پیرامونی است: سرویس‌ها را منتشر می‌کند، به درخواست‌های خواندن/نوشتن پاسخ می‌دهد و اعلان‌ها ارسال می‌کند. یک iPhone می‌تواند همزمان در دو نقش از طریق نمونه‌های مختلف مدیران کار کند.

چگونه Core Bluetooth را برای کار در پس‌زمینه پیکربندی کنیم؟

برای کار BLE در پس‌زمینه، capability „Uses Bluetooth LE accessories” را در Xcode فعال کنید و کلید „bluetooth-central” را به UIBackgroundModes اضافه کنید. برای نقش پیرامونی — „bluetooth-peripheral”. در مقداردهی مدیر برای State Restoration، restoreIdentifier را مشخص کنید. بدون این تنظیمات، برنامه در پس‌زمینه رویدادهای BLE را دریافت نمی‌کند و اتصالات را از دست می‌دهد.

چرا Core Bluetooth دستگاه‌ها را پیدا نمی‌کند؟

دلایل اصلی: CBCentralManager.state != .poweredOn (Bluetooth غیرفعال یا مجاز نیست)، نماینده تنظیم نشده، دستگاه خارج از محدوده یا بسته‌های تبلیغاتی ارسال نمی‌کند. مجوز NSBluetoothAlwaysUsageDescription را در Info.plist، وضعیت Bluetooth را در centralManagerDidUpdateState بررسی کنید و مطمئن شوید scanForPeripherals فقط در .poweredOn فراخوانی می‌شود.

آیا می‌توان چند CBPeripheral را همزمان متصل کرد؟

بله، Core Bluetooth از اتصال همزمان به چندین دستگاه BLE پشتیبانی می‌کند. هر CBPeripheral به طور مستقل از طریق نماینده خود مدیریت می‌شود. iOS تعداد اتصالات همزمان BLE را در سطح سیستم محدود می‌کند (معمولاً 5–7 برای iPhone). برای سناریوهای 1:N (مثلاً مرکز تناسب اندام با 10 ردیاب)، صف‌بندی و سرویس‌دهی دوره‌ای دستگاه‌های پیرامونی مورد نیاز است.

خلاصه

  • Core Bluetooth فریم‌ورک سیستمی اپل برای توسعه BLE با کلاس‌های CBCentralManager و CBPeripheralManager
  • CBCentralManager اسکن، اتصال و عملیات GATT با دستگاه‌های BLE راه دور را مدیریت می‌کند
  • CBPeripheralManager دستگاه پیرامونی BLE را با انتشار سرویس‌ها و پردازش درخواست‌های ورودی شبیه‌سازی می‌کند
  • پروفایل GATT شامل سرویس‌ها، ویژگی‌ها و توصیفگرها با عملیات خواندن، نوشتن و اعلان است
  • حالت پس‌زمینه نیاز به UIBackgroundModes و restoreIdentifier برای State Restoration دارد
  • MTU BLE اندازه بسته را محدود می‌کند: 23 بایت برای BLE 4.0، تا 251 بایت برای BLE 5.0+ با extended MTU
  • Swift async/await از طریق CheckedContinuation کد BLE ناهمزمان با نمایندگان را ساده می‌کند

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید