{"id":12058,"date":"2026-02-16T15:45:54","date_gmt":"2026-02-16T06:45:54","guid":{"rendered":"https:\/\/www.moonmile.net\/blog\/?p=12058"},"modified":"2026-02-16T15:45:54","modified_gmt":"2026-02-16T06:45:54","slug":"iphoneios%e3%81%a7-ibeacon-%e3%81%a8-en-api-%e5%8f%97%e4%bf%a1%e6%a9%9f%e3%82%92%e4%bd%9c%e3%82%8b","status":"publish","type":"post","link":"http:\/\/www.moonmile.net\/blog\/archives\/12058","title":{"rendered":"iPhone(iOS)\u3067 iBeacon \u3068 EN API \u53d7\u4fe1\u6a5f\u3092\u4f5c\u308b"},"content":{"rendered":"\n<p>Android \u3067 iBeacon\/EN API \u53d7\u4fe1\u6a5f\u3092\u4f5c\u308b\u3053\u3068\u304c\u3067\u304d\u305f\u306e\u3067\u3001\u6b21\u306f iPhone(iOS) \u3067\u4f5c\u3063\u3066\u307f\u307e\u3059\u3002\u5148\u306b\u66f8\u3044\u305f\u901a\u308a\u3001Android \u3068 iOS \u3067\u306f Beacon \u306e\u53d7\u4fe1\u65b9\u6cd5\u304c\u7570\u306a\u308a\u307e\u3059\u3002\u3055\u3089\u306b\u3001iOS \u3067\u306f Scan Window\/Scan Interval \u306e\u7d30\u304b\u3044\u52d5\u4f5c\u3092\u6307\u5b9a\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093\u3002<br>\u3067\u3001Apple \u7248\u3068 Android \u7248\u306e\u4e21\u65b9\u306e\u53d7\u4fe1\u6a5f\u3092\u4f5c\u3063\u305f\u3068\u304d\u306b\u3001\u3069\u3046\u3084\u3089 Android \u306e\u307b\u3046\u304c\u53d7\u4fe1\u983b\u5ea6\u304c\u3044\u307e\u3044\u3061\u306a\u3093\u3067\u3059\u3088\u306d&#8230;\u3068\u3044\u3046\u78ba\u8a8d\u306e\u305f\u3081\u306b iOS \u7248\u3092\u4f5c\u3063\u3066\u6bd4\u8f03\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<p>\u7d50\u8ad6\u304b\u3089\u5148\u306b\u8a00\u3046\u3068\u3001Android \u7248\u306e\u307b\u3046\u306f SCAN_MODE_LOW_POWER \u3067\u52d5\u304b\u3057\u3066\u7701\u96fb\u529b\u5316\u3059\u308b\u3068\u3001iOS \u7248\u3068\u6bd4\u3079\u3066\u76f8\u5f53\u53d7\u4fe1\u983b\u5ea6\u304c\u843d\u3061\u307e\u3059\u3002\u9006\u306b\u8a00\u3048\u3070 iOS \u7248\u306e\u307b\u3046\u304c\u30d0\u30c3\u30c6\u30ea\u30fc\u306e\u6d88\u8017\u304c\u6fc0\u3057\u3044\u3068\u3044\u3046\u3053\u3068\u3067\u3059\u3002\u3053\u306e\u3042\u305f\u308a\u3082\u5f8c\u306b\u78ba\u8a8d\u3057\u305f\u3044\u3067\u3059\u3002SCAN_MODE_LOW_LATENCY \u3067\u52d5\u304b\u3059\u3068\u3001Android \u7248\u306e\u307b\u3046\u3082\u53d7\u4fe1\u983b\u5ea6\u304c\u4e0a\u304c\u308b\u306e\u3067\u3059\u304c\u3001\u3053\u308c\u3082 iOS \u7248\u3068\u6bd4\u3079\u308b\u3068\u3069\u306e\u4f4d\u306e\u983b\u5ea6\u3067\u3044\u3051\u308b\u306e\u304b\u540c\u7a0b\u5ea6\u306a\u306e\u304b\uff1f\u3068\u3044\u3046\u3053\u3068\u3082\u3044\u305a\u308c\u8abf\u3079\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>SwiftUI \u3067\u4f5c\u308b<\/strong><\/h2>\n\n\n\n<p>FolkBears \u672c\u4f53\u306f\u5f93\u6765\u306e storyboard \u3067\u4f5c\u3063\u3066\u3042\u308b\u306e\u3067\u3059\u304c\u3001\u4eca\u306f SwiftUI \u3067\u4f5c\u308b\u306e\u304c\u697d\u306a\u306e\u3067\u3001\u4eca\u56de\u306e\u53d7\u4fe1\u6a5f\u306f SwiftUI \u3067\u4f5c\u3063\u3066\u3044\u304d\u307e\u3059\u3002\u672c\u4f53 FolkBears \u3082 SwiftUI \u5f62\u5f0f\u306b\u79fb\u884c\u4e2d\u3067\u3059\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: swift; title: ; notranslate\" title=\"\">\nstruct ContentView: View {\n    var body: some View {\n        TabView {\n            IBeaconTabView()\n                .tabItem { Label(&quot;iBeacon&quot;, systemImage: &quot;dot.radiowaves.left.and.right&quot;) }\n\n            FolkBearsTabView()\n                .tabItem { Label(&quot;FolkBears&quot;, systemImage: &quot;antenna.radiowaves.left.and.right&quot;) }\n\n            EnApiTabView()\n                .tabItem { Label(&quot;EN API&quot;, systemImage: &quot;waveform.path&quot;) }\n\n            ManufacturerDataTabView()\n                .tabItem { Label(&quot;Mfr Data&quot;, systemImage: &quot;barcode&quot;) }\n        }\n    }\n}\n\n\/\/ MARK: - iBeacon\nprivate struct IBeaconTabView: View {\n    @StateObject private var scanner = BeaconScan()\n    @State private var detectionLog: &#x5B;(id: String, beacon: CLBeacon, date: Date)] = &#x5B;]\n    @State private var summaries: &#x5B;BeaconSummary] = &#x5B;]\n\n    private let windowSeconds: TimeInterval = 5 * 60\n\n    var body: some View {\n        NavigationView {\n            VStack(alignment: .leading, spacing: 16) {\n                HStack {\n                    Text(&quot;\u72b6\u614b: \\(scanner.scanningStatus)&quot;)\n                    Spacer()\n                    Button(scanner.isScanning ? &quot;\u505c\u6b62&quot; : &quot;\u958b\u59cb&quot;) {\n                        scanner.isScanning ? scanner.stopScanning() : scanner.startScanning()\n                    }\n                    .buttonStyle(.borderedProminent)\n                }\n\n                if summaries.isEmpty {\n                    Text(&quot;\u53d7\u4fe1\u3057\u305f iBeacon \u304c\u307e\u3060\u3042\u308a\u307e\u305b\u3093&quot;)\n                        .foregroundStyle(.secondary)\n                        .frame(maxWidth: .infinity, alignment: .leading)\n                } else {\n                    List(summaries) { summary in\n                        BeaconRow(summary: summary)\n                    }\n                    .listStyle(.plain)\n                }\n            }\n            .padding()\n            .navigationTitle(&quot;iBeacon&quot;)\n            .onAppear {\n                scanner.onIBeacon = { beacon, date in\n                    addDetection(beacon, at: date)\n                }\n                if !scanner.isScanning { scanner.startScanning() }\n            }\n            .onDisappear {\n                detectionLog.removeAll()\n                summaries.removeAll()\n            }\n        }\n    }\n\n    private func addDetection(_ beacon: CLBeacon, at date: Date) {\n        let id = &quot;\\(beacon.uuid.uuidString)-\\(beacon.major.intValue)-\\(beacon.minor.intValue)&quot;\n        detectionLog.append((id: id, beacon: beacon, date: date))\n\n        \/\/ 5\u5206\u3088\u308a\u53e4\u3044\u30ed\u30b0\u3092\u524a\u9664\n        detectionLog = detectionLog.filter { date.timeIntervalSince($0.date) &lt;= windowSeconds }\n\n        \/\/ \u96c6\u8a08\n        let grouped = Dictionary(grouping: detectionLog, by: { $0.id })\n        summaries = grouped.values.compactMap { entries in\n            guard let latest = entries.max(by: { $0.date &lt; $1.date }) else { return nil }\n            return BeaconSummary(\n                id: latest.id,\n                uuid: latest.beacon.uuid,\n                major: latest.beacon.major.uint16Value,\n                minor: latest.beacon.minor.uint16Value,\n                rssi: latest.beacon.rssi,\n                accuracy: latest.beacon.accuracy,\n                proximity: latest.beacon.proximity,\n                count: entries.count\n            )\n        }\n        .sorted { $0.count &gt; $1.count }\n    }\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\"><strong>iBeacon \u3092\u53d7\u4fe1\u3059\u308b<\/strong><\/h2>\n\n\n\n<p>iOS \u3067 iBeacon \u3092\u4f7f\u3063\u3066\u8fd1\u63a5\u691c\u51fa\u3059\u308b\u5834\u5408\u306b\u306f\u3001CLBeaconRegion \u3068 CLLocationManager \u306e\u4e21\u65b9\u3092\u4f7f\u3044\u307e\u3059\u3002\u3053\u306e\u3042\u305f\u308a\u306e\u52d5\u304d\u306f Android \u3068\u7570\u306a\u308b\u306e\u3067\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u3082\u3068\u3082\u3068\u3001iBeacon \u306e\u5229\u7528\u304c\u3001\u5e97\u8217\u306a\u3069\u306b\u914d\u7f6e\u3055\u308c\u305f Beacon \u3092\u691c\u51fa\u3059\u308b\uff1d\u5e97\u5185\u306b\u5165\u3063\u305f\u3053\u3068\u3092\u691c\u51fa\u3059\u308b\u3068\u3044\u3046\u7528\u9014\u306b\u306a\u3063\u3066\u3044\u308b\u306e\u3067\u3001Beacon \u691c\u51fa\u306f\u3001\u3042\u308b\u9818\u57df\u306b\u5165\u3063\u305f\u6642\u3001\u3042\u308b\u3044\u306f\u51fa\u305f\u3068\u304d\u306b\u3057\u304b\u30a4\u30d9\u30f3\u30c8\u304c\u767a\u751f\u3057\u307e\u305b\u3093\u3002<\/p>\n\n\n\n<p>\u3053\u306e\u305f\u3081\u306b\u3001\u65e2\u306b Beacon \u306e\u9818\u57df\u306b\u5165\u3063\u3066\u3044\u308b\u6642\u306b\u30a2\u30d7\u30ea\u3092\u7acb\u3061\u4e0a\u3052\u308b\u3068\u30a4\u30d9\u30f3\u30c8\u304c\u767a\u751f\u3057\u307e\u305b\u3093\u3002\u3042\u3089\u304b\u3058\u3081\u3001\u9818\u57df\u5916\u306e\u3068\u3053\u308d\u3067\u30a2\u30d7\u30ea\u3092\u7acb\u3061\u4e0a\u3052\u3066\u3001Beacon \u306e\u9818\u57df\u306b\u5165\u3089\u306a\u304f\u3066\u306f\u3044\u3051\u307e\u305b\u3093\u3002<br>\u4f55\u6545\u3001\u3053\u3093\u306a\u4ed5\u69d8\u306b\u306a\u3063\u3066\u3044\u308b\u306e\u304b\u4e0d\u601d\u8b70\u3067\u3059\u304c\u3001\u305f\u307e\u306b\u300c\u5e97\u5185\u306b\u5165\u308b\u524d\u306b\u30a2\u30d7\u30ea\u3092\u7acb\u3061\u4e0a\u3052\u3066\uff5e\u300d\u3068\u3044\u3046\u30a2\u30ca\u30a6\u30f3\u30b9\u304c\u3042\u308b\u306e\u306f\u3053\u306e\u305f\u3081\u3067\u3057\u3087\u3046\u3002<\/p>\n\n\n\n<p>FolkBears \u306e\u53d7\u4fe1\u6a5f\u3067\u306f\u3001Android \u7248\u306e\u3088\u3046\u306b\u9023\u7d9a\u3057\u3066 Beacon \u3092\u53d7\u4fe1\u3057\u3066\u6b32\u3057\u3044\u306e\u3067\u3001CLLocationManagerDelegate \u306e locationManager(_ manager: CLLocationManager, didRange beacons: [CLBeacon], satisfying beaconConstraint: CLBeaconIdentityConstraint) \u3092\u4f7f\u3063\u3066\u3001\u5b9a\u671f\u7684\u306b\u53d7\u4fe1\u3059\u308b\u3088\u3046\u306b\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: swift; title: ; notranslate\" title=\"\">\nclass BeaconScan: NSObject, ObservableObject {\n    private var locationManager: CLLocationManager\n    private var beaconRegion: CLBeaconRegion?\n    private var beaconConstraint: CLBeaconIdentityConstraint?\n\n    \/\/\/ iBeacon\u691c\u51fa\u6642\u306b\u547c\u3070\u308c\u308b\u30b3\u30fc\u30eb\u30d0\u30c3\u30af\uff08UI\u3067\u96c6\u8a08\u3059\u308b\u305f\u3081\uff09\n    var onIBeacon: ((CLBeacon, Date) -&gt; Void)?\n    \n    @Published var discoveredBeacons: &#x5B;CLBeacon] = &#x5B;]\n    @Published var isScanning = false\n    @Published var scanningStatus = &quot;\u505c\u6b62\u4e2d&quot;\n    \n    \/\/ \u30c7\u30d5\u30a9\u30eb\u30c8\u306eiBeacon\u8a2d\u5b9a\n    private let defaultUUID = UUID(uuidString: &quot;90FA7ABE-FAB6-485E-B700-1A17804CAA13&quot;)!\n    private let defaultIdentifier = &quot;FolkBearsBeacon&quot;\n    \n    override init() {\n        self.locationManager = CLLocationManager()\n        super.init()\n        setupLocationManager()\n    }\n    \n    private func setupLocationManager() {\n        locationManager.delegate = self\n        \/\/ iBeacon\u30ec\u30f3\u30b8\u30f3\u30b0\u306b\u306f\u300c\u3053\u306eApp\u306e\u4f7f\u7528\u4e2d\u300d\u4ee5\u4e0a\u304c\u5fc5\u8981\u3002\u30d0\u30c3\u30af\u30b0\u30e9\u30a6\u30f3\u30c9\u53d7\u4fe1\u3059\u308b\u5834\u5408\u306f Always \u3082\u8981\u6c42\u3059\u308b\u3002\n        if locationManager.authorizationStatus == .notDetermined {\n            locationManager.requestWhenInUseAuthorization()\n        }\n    }\n    \n    func startScanning() {\n        guard !isScanning else { return }\n        \n        \/\/ \u30d3\u30fc\u30b3\u30f3\u30ea\u30fc\u30b8\u30e7\u30f3\u3092\u4f5c\u6210\n        beaconRegion = CLBeaconRegion(\n            uuid: defaultUUID,\n            identifier: defaultIdentifier\n        )\n        beaconConstraint = CLBeaconIdentityConstraint(uuid: defaultUUID)\n        \n        guard let region = beaconRegion else { return }\n        \n        \/\/ \u30ea\u30fc\u30b8\u30e7\u30f3\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u958b\u59cb\n        locationManager.startMonitoring(for: region)\n\n        \/\/ \u3059\u3050\u306b\u30ec\u30f3\u30b8\u30f3\u30b0\u958b\u59cb\uff08\u65e2\u306b\u30ea\u30fc\u30b8\u30e7\u30f3\u5185\u306b\u3044\u308b\u5834\u5408 didEnterRegion \u304c\u6765\u306a\u3044\u3053\u3068\u304c\u3042\u308b\u305f\u3081\uff09\n        if let constraint = beaconConstraint {\n            locationManager.startRangingBeacons(satisfying: constraint)\n        }\n        \n        isScanning = true\n        scanningStatus = &quot;\u30b9\u30ad\u30e3\u30f3\u4e2d...&quot;\n        print(&quot;iBeacon \u30b9\u30ad\u30e3\u30f3\u958b\u59cb&quot;)\n    }\n    \n    func stopScanning() {\n        guard isScanning else { return }\n        \n        if let region = beaconRegion {\n            locationManager.stopMonitoring(for: region)\n        }\n\n        if let constraint = beaconConstraint {\n            locationManager.stopRangingBeacons(satisfying: constraint)\n        }\n        \n        isScanning = false\n        scanningStatus = &quot;\u505c\u6b62\u4e2d&quot;\n        discoveredBeacons.removeAll()\n        print(&quot;iBeacon \u30b9\u30ad\u30e3\u30f3\u505c\u6b62&quot;)\n    }\n}\n\n\/\/ MARK: - CLLocationManagerDelegate\nextension BeaconScan: CLLocationManagerDelegate {\n    func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {\n        guard let beaconRegion = region as? CLBeaconRegion else { return }\n        print(&quot;\u30d3\u30fc\u30b3\u30f3\u30ea\u30fc\u30b8\u30e7\u30f3\u306b\u5165\u308a\u307e\u3057\u305f: \\(beaconRegion.identifier)&quot;)\n        \n        \/\/ \u30ec\u30f3\u30b8\u30f3\u30b0\u958b\u59cb\n        let constraint = beaconConstraint ?? CLBeaconIdentityConstraint(uuid: beaconRegion.uuid)\n        beaconConstraint = constraint\n        locationManager.startRangingBeacons(satisfying: constraint)\n    }\n    \n    func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {\n        guard let beaconRegion = region as? CLBeaconRegion else { return }\n        print(&quot;\u30d3\u30fc\u30b3\u30f3\u30ea\u30fc\u30b8\u30e7\u30f3\u304b\u3089\u51fa\u307e\u3057\u305f: \\(beaconRegion.identifier)&quot;)\n        \n        \/\/ \u30ec\u30f3\u30b8\u30f3\u30b0\u505c\u6b62\n        let constraint = beaconConstraint ?? CLBeaconIdentityConstraint(uuid: beaconRegion.uuid)\n        locationManager.stopRangingBeacons(satisfying: constraint)\n    }\n    \n    func locationManager(_ manager: CLLocationManager, didRange beacons: &#x5B;CLBeacon], satisfying beaconConstraint: CLBeaconIdentityConstraint) {\n        let now = Date()\n        let validBeacons = beacons.filter { $0.proximity != .unknown }\n\n        DispatchQueue.main.async {\n            self.discoveredBeacons = validBeacons\n            self.scanningStatus = &quot;\u691c\u51fa: \\(validBeacons.count)\u500b&quot;\n        }\n\n        for beacon in validBeacons {\n            let majorHex = String(format: &quot;%04X&quot;, beacon.major.uint16Value)\n            let minorHex = String(format: &quot;%04X&quot;, beacon.minor.uint16Value)\n            print(&quot;\u30d3\u30fc\u30b3\u30f3\u691c\u51fa - UUID: \\(beacon.uuid), Major: 0x\\(majorHex), Minor: 0x\\(minorHex), RSSI: \\(beacon.rssi), Distance: \\(String(format: &quot;%.2f&quot;, beacon.accuracy))m&quot;)\n            DispatchQueue.main.async {\n                self.onIBeacon?(beacon, now)\n            }\n        }\n    }\n    \n    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {\n        print(&quot;Location Manager \u30a8\u30e9\u30fc: \\(error.localizedDescription)&quot;)\n        DispatchQueue.main.async {\n            self.scanningStatus = &quot;\u30a8\u30e9\u30fc&quot;\n        }\n    }\n    \n    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {\n        switch status {\n        case .authorizedWhenInUse, .authorizedAlways:\n            print(&quot;\u4f4d\u7f6e\u60c5\u5831\u306e\u4f7f\u7528\u304c\u8a31\u53ef\u3055\u308c\u307e\u3057\u305f&quot;)\n            \/\/ \u6a29\u9650\u53d6\u5f97\u5f8c\u306b\u30b9\u30ad\u30e3\u30f3\u6307\u793a\u304c\u51fa\u3066\u3044\u305f\u5834\u5408\u3001\u30ec\u30f3\u30b8\u30f3\u30b0\u3092\u958b\u59cb\u3057\u3066\u304a\u304f\n            if isScanning {\n                let constraint = beaconConstraint ?? CLBeaconIdentityConstraint(uuid: defaultUUID)\n                beaconConstraint = constraint\n                locationManager.startRangingBeacons(satisfying: constraint)\n            }\n        case .denied, .restricted:\n            print(&quot;\u4f4d\u7f6e\u60c5\u5831\u306e\u4f7f\u7528\u304c\u62d2\u5426\u3055\u308c\u307e\u3057\u305f&quot;)\n            DispatchQueue.main.async {\n                self.scanningStatus = &quot;\u4f4d\u7f6e\u60c5\u5831\u6a29\u9650\u304c\u5fc5\u8981&quot;\n            }\n        case .notDetermined:\n            print(&quot;\u4f4d\u7f6e\u60c5\u5831\u306e\u6a29\u9650\u304c\u672a\u78ba\u5b9a&quot;)\n        @unknown default:\n            break\n        }\n    }\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\"><strong>\u6307\u5b9a\u3057\u305f UUID \u306e iBeacon \u3057\u304b\u691c\u51fa\u3067\u304d\u306a\u3044<\/strong><\/h3>\n\n\n\n<p>Android \u7248\u3067\u306f\u3072\u3068\u307e\u305a iBeacon \u5f62\u5f0f\u306e\u3082\u306e\u3092\u53d7\u4fe1\u3057\u3066\u304b\u3089 UUID \u3092\u30c1\u30a7\u30c3\u30af\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3057\u305f\u304c\u3001iOS \u7248\u3067\u306f\u3001\u3042\u3089\u304b\u3058\u3081\u6307\u5b9a\u3057\u305f UUID \u306e iBeacon \u3057\u304b\u691c\u51fa\u3067\u304d\u307e\u305b\u3093\u3002\u78ba\u304b 10 \u500b\u7a0b\u5ea6\u3057\u304b\u767b\u9332\u3067\u304d\u306a\u3044\u306e\u3067\u3001\u8907\u6570\u306e UUID \u3092\u540c\u6642\u306b\u4f7f\u3046\u3068\u304d\u306f\u3001\u4f55\u3089\u304b\u306e\u5f62\u3067 beaconRegion \u3092\u5207\u308a\u66ff\u3048\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: swift; title: ; notranslate\" title=\"\">\nfunc startScanning() {\n    guard !isScanning else { return }\n    \n    \/\/ \u30d3\u30fc\u30b3\u30f3\u30ea\u30fc\u30b8\u30e7\u30f3\u3092\u4f5c\u6210\n    beaconRegion = CLBeaconRegion(\n        uuid: defaultUUID,\n        identifier: defaultIdentifier\n    )\n    beaconConstraint = CLBeaconIdentityConstraint(uuid: defaultUUID)\n    \n    guard let region = beaconRegion else { return }\n    \n    \/\/ \u30ea\u30fc\u30b8\u30e7\u30f3\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u958b\u59cb\n    locationManager.startMonitoring(for: region)\n\n    \/\/ \u3059\u3050\u306b\u30ec\u30f3\u30b8\u30f3\u30b0\u958b\u59cb\uff08\u65e2\u306b\u30ea\u30fc\u30b8\u30e7\u30f3\u5185\u306b\u3044\u308b\u5834\u5408 didEnterRegion \u304c\u6765\u306a\u3044\u3053\u3068\u304c\u3042\u308b\u305f\u3081\uff09\n    if let constraint = beaconConstraint {\n        locationManager.startRangingBeacons(satisfying: constraint)\n    }\n    \n    isScanning = true\n    scanningStatus = &quot;\u30b9\u30ad\u30e3\u30f3\u4e2d...&quot;\n    print(&quot;iBeacon \u30b9\u30ad\u30e3\u30f3\u958b\u59cb&quot;)\n}\n<\/pre><\/div>\n\n\n<p>\u73fe\u5728\u3001\u56fa\u5b9a\u306e UUID \u3057\u304b\u53d7\u4fe1\u3067\u304d\u306a\u3044\u306e\u3067\u3001\u30a2\u30d7\u30ea\u304b\u3089\u8907\u6570 UUID \u3092\u6307\u5b9a\u3067\u304d\u308b\u3068\u3044\u3044\u3067\u3057\u3087\u3046\u3002\u901a\u5e38\u306f\u3001UUID \u3092\u56fa\u5b9a\u306b\u3057\u3066\u304a\u3044\u3066 major \u3068 minor \u3067\u8b58\u5225\u3059\u308b\u3053\u3068\u304c\u591a\u3044\u3067\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>major \u3068 minor \u3092 ID \u3068\u3057\u3066\u4f7f\u3046<\/strong><\/h3>\n\n\n\n<p>CLLocationManagerDelegate#locationManager \u3067\u53d7\u3051\u53d6\u3063\u305f\u3068\u304d\u306b\u3001CLBeacon \u306e major \u3068 minor \u3092\u53d6\u308a\u51fa\u3059\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002FolkBears \u3067\u306f\u3001major \u3068 minor \u3092\u30ef\u30f3\u30bb\u30c3\u30c8\u306b\u3057\u3066 TempUserID \u3068\u3057\u3066\u4f7f\u3063\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: swift; title: ; notranslate\" title=\"\">\nfunc locationManager(_ manager: CLLocationManager, didRange beacons: &#x5B;CLBeacon], satisfying beaconConstraint: CLBeaconIdentityConstraint) {\n    let now = Date()\n    let validBeacons = beacons.filter { $0.proximity != .unknown }\n\n    DispatchQueue.main.async {\n        self.discoveredBeacons = validBeacons\n        self.scanningStatus = &quot;\u691c\u51fa: \\(validBeacons.count)\u500b&quot;\n    }\n\n    for beacon in validBeacons {\n        let majorHex = String(format: &quot;%04X&quot;, beacon.major.uint16Value)\n        let minorHex = String(format: &quot;%04X&quot;, beacon.minor.uint16Value)\n        print(&quot;\u30d3\u30fc\u30b3\u30f3\u691c\u51fa - UUID: \\(beacon.uuid), Major: 0x\\(majorHex), Minor: 0x\\(minorHex), RSSI: \\(beacon.rssi), Distance: \\(String(format: &quot;%.2f&quot;, beacon.accuracy))m&quot;)\n        DispatchQueue.main.async {\n            self.onIBeacon?(beacon, now)\n        }\n    }\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\"><strong>EN API \u5f62\u5f0f\u3092\u53d7\u4fe1\u3059\u308b<\/strong><\/h2>\n\n\n\n<p>COCOA \u3067\u4f7f\u3063\u3066\u3044\u305f EN API \u5f62\u5f0f\u306e\u53d7\u4fe1\u6a5f\u3082 iOS \u7248\u3092\u4f5c\u3063\u3066\u3044\u304d\u307e\u3059\u3002\u3064\u307e\u308a\u306f\u300116 bit Service UUID \u3092\u6307\u5b9a\u3057\u3066\u53d7\u4fe1\u3059\u308b\u30d1\u30bf\u30fc\u30f3\u3067\u3059\u3002CBCentralManager \u3092\u4f7f\u3044\u307e\u3059\u3002<br>\u3053\u308c\u3001\u305a\u3063\u3068\u52d8\u9055\u3044\u3057\u3066\u3044\u305f\u306e\u3067\u3059\u304c\u3001iOS \u3067 16 bit Service UUID \u306f\u53d7\u4fe1\u3067\u304d\u307e\u3059\u306d\u3002\u73fe\u5728 EN API \u306e 0xFD6F \u306f\u585e\u304c\u308c\u305f\u307e\u307e\u306a\u306e\u3067\u3059\u304c\u3001\u5225\u306e 16 bit Service UUID \u3092\u9001\u308b\u3068 iOS \u3067\u53d7\u4fe1\u304c\u3067\u304d\u307e\u3059\u3002\u4ed6\u306e UUID \u3068\u3076\u3064\u304b\u3089\u306a\u3044\u3088\u3046\u306b\u5b9f\u9a13\u7684\u306b 0xFF00 \u3092\u4f7f\u3046\u3068\u53d7\u4fe1\u3067\u304d\u308b\u3053\u3068\u304c\u78ba\u8a8d\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/www.moonmile.net\/blog\/wp-content\/uploads\/2026\/02\/image-44.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"768\" src=\"https:\/\/www.moonmile.net\/blog\/wp-content\/uploads\/2026\/02\/image-44.png\" alt=\"\" class=\"wp-image-12059\" srcset=\"http:\/\/www.moonmile.net\/blog\/wp-content\/uploads\/2026\/02\/image-44.png 1024w, http:\/\/www.moonmile.net\/blog\/wp-content\/uploads\/2026\/02\/image-44-300x225.png 300w, http:\/\/www.moonmile.net\/blog\/wp-content\/uploads\/2026\/02\/image-44-768x576.png 768w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>\u3061\u306a\u307f\u306b iOS \u306f 16 bit Service UUID \u3067\u767a\u4fe1\u304c\u3067\u304d\u307e\u305b\u3093\u3002\u63a5\u89e6\u78ba\u8a8d\u30a2\u30d7\u30ea\u306e\u5834\u5408\u306f\u53d7\u767a\u4fe1\u304c\u5fc5\u8981\u306a\u306e\u3067\u3053\u306e\u30d1\u30bf\u30fc\u30f3\u306f\u4f7f\u3048\u306a\u3044\u306e\u3067\u3059\u304c\u3001\u4f55\u3089\u304b\u306e\u30c7\u30d0\u30a4\u30b9\u3067\u767a\u4fe1\uff08m5stack \u306a\u3069\uff09\u3057\u305f\u3082\u306e\u3092\u3001iOS \u3067\u53d7\u4fe1\u3059\u308b\u3053\u3068\u306f\u5341\u5206\u53ef\u80fd\u3067\u3059\u3002\u306a\u306e\u3067\u3001\u5165\u5834\u78ba\u8a8d\u3068\u304b\u306b\u3053\u306e\u65b9\u5f0f\u304c\u4f7f\u3048\u307e\u3059\u3002\u52ff\u8ad6\u3001Bluetooth SIG \u3067 16 bit Service UUID \u304c\u5fc5\u9808\u306b\u306a\u308a\u307e\u3059\u304c&#8230;\u307e\u3042\u3001\u5b9f\u9a13\u7684\u306b\u3068\u3044\u3046\u3053\u3068\u3067\u3002<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: swift; title: ; notranslate\" title=\"\">\nfinal class ENSimScan: NSObject, ObservableObject {\n    \n    \/\/\/ \u53d7\u4fe1\u6642\u306e\u30b3\u30fc\u30eb\u30d0\u30c3\u30af\uff08UI\u5074\u3067\u96c6\u8a08\u3059\u308b\u60f3\u5b9a\uff09\n    var onReadTraceData: ((TraceData) -&gt; Void)?\n\n    @Published var isScanning = false\n    @Published var scanningStatus = &quot;\u505c\u6b62\u4e2d&quot;\n\n    private var centralManager: CBCentralManager!\n\n    \/\/ ENSim (Exposure Notification Simulator) \u30b5\u30fc\u30d3\u30b9 UUID\n    private let serviceUUID = CBUUID(string: &quot;0000FD6F-0000-1000-8000-00805F9B34FB&quot;)\n    private let serviceDataUUID = CBUUID(string: &quot;0000FD6F-0000-1000-8000-00805F9B34FB&quot;)\n    private let serviceUUIDalt = CBUUID(string: &quot;0000FF00-0000-1000-8000-00805F9B34FB&quot;)\n    private let serviceDataUUIDalt = CBUUID(string: &quot;00000001-0000-1000-8000-00805F9B34FB&quot;)\n\n    override init() {\n        super.init()\n        setupCentralManager()\n    }\n    private func setupCentralManager() {\n        centralManager = CBCentralManager(delegate: self, queue: nil)\n    }\n\n    func startScan() {\n        guard centralManager.state == .poweredOn else {\n            print(&quot;ENSimScan: Bluetooth\u672a\u6e96\u5099\u306e\u305f\u3081\u958b\u59cb\u3067\u304d\u307e\u305b\u3093 state=\\(centralManager.state.rawValue)&quot;)\n            return\n        }\n        guard !isScanning else { return }\n\n        centralManager.scanForPeripherals(\n            \/\/ withServices: &#x5B;serviceUUID, serviceUUIDalt],\n            \/\/ FD6F \u3092\u5165\u308c\u308b\u3068\u30ac\u30fc\u30c9\u304c\u639b\u304b\u308b\u306e\u3067\u3001\u5916\u3059\n            withServices: &#x5B;serviceUUIDalt],\n            options: &#x5B;CBCentralManagerScanOptionAllowDuplicatesKey: true]\n        )\n\n        isScanning = true\n        scanningStatus = &quot;\u30b9\u30ad\u30e3\u30f3\u4e2d...&quot;\n        print(&quot;ENSimScan: \u30b9\u30ad\u30e3\u30f3\u958b\u59cb&quot;)\n    }\n\n    func stopScan() {\n        guard isScanning else { return }\n        centralManager.stopScan()\n        isScanning = false\n        scanningStatus = &quot;\u505c\u6b62\u4e2d&quot;\n        print(&quot;ENSimScan: \u30b9\u30ad\u30e3\u30f3\u505c\u6b62&quot;)\n    }\n\n    private func handleScanResult(peripheral: CBPeripheral, advertisementData: &#x5B;String: Any], rssi: NSNumber) {\n        \/\/ \u30b5\u30fc\u30d3\u30b9\u30c7\u30fc\u30bf\u304b\u3089 tempId \u3092\u53d6\u5f97\uff08FD6F\u512a\u5148\u3001FF00\u3084\u6d3e\u751fUUID\u3082\u8a31\u5bb9\uff09\n        guard let serviceData = advertisementData&#x5B;CBAdvertisementDataServiceDataKey] as? &#x5B;CBUUID: Data] else { return }\n\n        let data = serviceData&#x5B;serviceDataUUID]\n            ?? serviceData&#x5B;serviceUUID]          \/\/ \u4e00\u90e8\u30c7\u30d0\u30a4\u30b9\u306f\u30b5\u30fc\u30d3\u30b9UUID\u3067\u305d\u306e\u307e\u307e\u5165\u308b\u5834\u5408\u304c\u3042\u308b\n            ?? serviceData&#x5B;serviceUUIDalt]       \/\/ \u4ee3\u66ff\u30b5\u30fc\u30d3\u30b9UUID\n            ?? serviceData&#x5B;serviceDataUUIDalt]   \/\/ \u4ee3\u66ff\u30b5\u30fc\u30d3\u30b9\u30c7\u30fc\u30bfUUID\n\n        guard let payload = data, !payload.isEmpty else { return }\n\n        let tempId = payload.map { String(format: &quot;%02X&quot;, $0) }.joined()\n        let trace = TraceData(\n            timestamp: Date(),\n            tempId: tempId,\n            rssi: rssi.doubleValue,\n            txPower: (advertisementData&#x5B;CBAdvertisementDataTxPowerLevelKey] as? NSNumber)?.doubleValue\n        )\n\n        print(&quot;ENSim \u691c\u51fa: \\(peripheral.identifier.uuidString) tempId: \\(tempId) rssi: \\(rssi)&quot;)\n        onReadTraceData?(trace)\n    }\n}\n\n\/\/ MARK: - CBCentralManagerDelegate\nextension ENSimScan: CBCentralManagerDelegate {\n    func centralManagerDidUpdateState(_ central: CBCentralManager) {\n        switch central.state {\n        case .poweredOn:\n            print(&quot;ENSimScan: Bluetooth On&quot;)\n        case .unauthorized:\n            print(&quot;ENSimScan: Bluetooth unauthorized&quot;)\n        case .unsupported:\n            print(&quot;ENSimScan: Bluetooth unsupported&quot;)\n        case .poweredOff:\n            print(&quot;ENSimScan: Bluetooth Off&quot;)\n        default:\n            break\n        }\n    }\n\n    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: &#x5B;String: Any], rssi RSSI: NSNumber) {\n        handleScanResult(peripheral: peripheral, advertisementData: advertisementData, rssi: RSSI)\n    }\n}\n<\/pre><\/div>\n\n\n<p>\u3053\u3053\u3067\u306f FD6F \u3068 FF00 \u306e\u4e21\u65b9\u3092\u53d7\u4fe1\u3059\u308b\u3088\u3046\u306b\u3057\u305f\u3044\u3068\u3053\u308d\u3067\u3059\u304c\u3001scanForPeripherals \u3067 FD6F \u3092\u6307\u5b9a\u3059\u308b\u3068 FF00 \u306e\u307b\u3046\u3082\u30ac\u30fc\u30c9\u304c\u639b\u304b\u3063\u3066\u6392\u9664\u3055\u308c\u3066\u3057\u307e\u3044\u307e\u3059\uff08\u82e6\u7b11\uff09\u3002\u306a\u306e\u3067\u3001FF00 \u306e\u307b\u3046\u3060\u3051\u6307\u5b9a\u3057\u307e\u3059\u3002\u3053\u306e\u30ac\u30fc\u30c9\u306e\u4ed5\u65b9\u306f\u3069\u3046\u304b\u3068\u601d\u3046\u306e\u3067\u3059\u304c\u3001\u307e\u3042\u3001\u3044\u3044\u3067\u3057\u3087\u3046\u3002<\/p>\n\n\n\n<p>CBCentralManager \u306e\u307b\u3046\u3082 BeaconRegion \u3068\u540c\u69d8\u306b\u3001\u30d5\u30a3\u30eb\u30bf\u30fc\u3059\u308b UUID \u306e\u6307\u5b9a\u304c\u5fc5\u8981\u306b\u306a\u308a\u307e\u3059\u3002\u3064\u307e\u308a\u306f\u3001\u53d7\u4fe1\u3059\u308b\u3068\u304d\u306e\u30db\u30ef\u30a4\u30c8\u30ea\u30b9\u30c8\u304c\u5fc5\u8981\u306b\u306a\u308b\u308f\u3051\u3067\u3059\u3002\u3069\u306e\u7a0b\u5ea6\u306e BLE \u30c7\u30d0\u30a4\u30b9\u306e\u30ec\u30d9\u30eb\u3067\u30d5\u30a3\u30eb\u30bf\u30fc\u304c\u304b\u304b\u3063\u3066\u3044\u308b\u304b\u308f\u304b\u308a\u307e\u305b\u3093\u304c\u3001\u30a2\u30d7\u30ea\u3078\u306e\u30a4\u30d9\u30f3\u30c8\u306f Android \u306e\u3088\u3046\u306b\u3059\u3079\u3066\u306e\u30a4\u30d9\u30f3\u30c8\u304c\u98db\u3093\u3067\u304f\u308b\u308f\u3051\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002<\/p>\n\n\n\n<p>\u3061\u3087\u3063\u3068\u9577\u304f\u306a\u3063\u305f\u306e\u3067 GATT \u5f62\u5f0f\u3068 Manufacturer Data \u5f62\u5f0f\u306e\u53d7\u4fe1\u6a5f\u306e\u89e3\u8aac\u306f\u6b21\u56de\u306b\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>\u53c2\u8003\u5148<\/strong><\/h2>\n\n\n\n<p><a href=\"https:\/\/github.com\/FolkBearsGroup\/ble-tools\/tree\/master\/folkbears-monitor-ios\">https:\/\/github.com\/FolkBearsGroup\/ble-tools\/tree\/master\/folkbears-monitor-ios<\/a><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Android \u3067 iBeacon\/EN API \u53d7\u4fe1\u6a5f\u3092\u4f5c\u308b\u3053\u3068\u304c\u3067\u304d\u305f\u306e\u3067\u3001\u6b21\u306f iPhone(iOS) \u3067\u4f5c\u3063\u3066\u307f\u307e\u3059\u3002\u5148\u306b\u66f8\u3044\u305f\u901a\u308a\u3001Android \u3068 iOS \u3067\u306f Beacon \u306e\u53d7\u4fe1\u65b9\u6cd5\u304c\u7570\u306a\u308a\u307e\u3059\u3002\u3055\u3089\u306b &hellip; <a href=\"http:\/\/www.moonmile.net\/blog\/archives\/12058\">\u7d9a\u304d\u3092\u8aad\u3080 <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[3,110],"tags":[],"class_list":["post-12058","post","type-post","status-publish","format-standard","hentry","category-dev","category-folkbears"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"http:\/\/www.moonmile.net\/blog\/wp-json\/wp\/v2\/posts\/12058","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.moonmile.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.moonmile.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.moonmile.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.moonmile.net\/blog\/wp-json\/wp\/v2\/comments?post=12058"}],"version-history":[{"count":1,"href":"http:\/\/www.moonmile.net\/blog\/wp-json\/wp\/v2\/posts\/12058\/revisions"}],"predecessor-version":[{"id":12060,"href":"http:\/\/www.moonmile.net\/blog\/wp-json\/wp\/v2\/posts\/12058\/revisions\/12060"}],"wp:attachment":[{"href":"http:\/\/www.moonmile.net\/blog\/wp-json\/wp\/v2\/media?parent=12058"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.moonmile.net\/blog\/wp-json\/wp\/v2\/categories?post=12058"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.moonmile.net\/blog\/wp-json\/wp\/v2\/tags?post=12058"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}