iOS-蓝牙开发

iOS个版本蓝牙比较

随着蓝牙低功耗技术BLE(Bluetooth Low Energy)的发展,蓝牙技术正在一步步成熟,如今的大部分移动设备都配备有蓝牙4.0,相比之前的蓝牙技术耗电量大大降低。从iOS的发展史也不难看出苹果目前对蓝牙技术也是越来越关注,例如苹果于2013年9月发布的iOS7就配备了iBeacon技术,这项技术完全基于蓝牙传输。但是众所周知苹果的设备对于权限要求也是比较高的,因此在iOS中并不能像Android一样随意使用蓝牙进行文件传输(除非你已经越狱)。在iOS中进行蓝牙传输应用开发常用的框架有如下几种:

- GameKit.framework:iOS7之前的蓝牙通讯框架,从iOS7开始过期,但是目前多数应用还是基于此框架。
- MultipeerConnectivity.framework:iOS7开始引入的新的蓝牙通讯开发框架,用于取代GameKit。
- CoreBluetooth.framework:功能强大的蓝牙开发框架,要求设备必须支持蓝牙4.0。

前两个框架使用起来比较简单,但是缺点也比较明显:仅仅支持iOS设备,传输内容仅限于沙盒或者照片库中用户选择的文件,并且第一个框架只能在同一个应用之间进行传输(一个iOS设备安装应用A,另一个iOS设备上安装应用B是无法传输的)。当然CoreBluetooth就摆脱了这些束缚,它不再局限于iOS设备之间进行传输,你可以通过iOS设备向Android、Windows Phone以及其他安装有蓝牙4.0芯片的智能设备传输,因此也是目前智能家居、无线支付等热门智能设备所推崇的技术。

对比

框架 版本 运用
GameKit.framework 仅支持iOS设备(iOS7之后淘汰) 同一游戏应用相关
MultipeerConnectivity.framework 仅支持iOS设备(替代GameKit) 不同应用之间
CoreBluetooth.framework 支持第三方外设(蓝牙4.0以上) 不同应用之间

GameKit

目前因为GameKit已经淘汰过时,那么就不在详细写这个框架,网上也有相关的讲解,有兴趣就可以去了解了解,看看别人写的代码。

MultipeerConnectivity

MultipeerConnectivity并不仅仅支持蓝牙连接,准确的说它是一种支持Wi-Fi网络、P2P Wi-Fi已经蓝牙个人局域网的通信框架,它屏蔽了具体的连接技术,让开发人员有统一的接口编程方法。通过MultipeerConnectivity连接的节点之间可以安全的传递信息、流或者其他文件资源而不必通过网络服务。此外使用MultipeerConnectivity进行近场通信也不再局限于同一个应用之间传输,而是可以在不同的应用之间进行数据传输(当然如果有必要的话你仍然可以选择在一个应用程序之间传输)。
要了解MultipeerConnectivity的使用必须要清楚一个概念:广播(Advertisting)和发现(Disconvering),这很类似于一种Client-Server模式。假设有两台设备A、B,B作为广播去发送自身服务,A作为发现的客户端。一旦A发现了B就试图建立连接,经过B同意二者建立连接就可以相互发送数据。在使用GameKit框架时,A和B既作为广播又作为发现,当然这种情况在MultipeerConnectivity中也很常见。

  • A.广播

无论是作为服务器端去广播还是作为客户端去发现广播服务,那么两个(或更多)不同的设备之间必须要有区分,通常情况下使用MCPeerID对象来区分一台设备,在这个设备中可以指定显示给对方查看的名称(display name)。另外不管是哪一方,还必须建立一个会话MCSession用于发送和接受数据。通常情况下会在会话的-(void)session:(MCSession )session peer:(MCPeerID )peerID didChangeState:(MCSessionState)state代理方法中跟踪会话状态(已连接、正在连接、未连接);在会话的-(void)session:(MCSession )session didReceiveData:(NSData )data fromPeer:(MCPeerID )peerID代理方法中接收数据;同时还会调用会话的-(void)sendData: toPeers:withMode: error:*方法去发送数据。广播作为一个服务器去发布自身服务,供周边设备发现连接。在MultipeerConnectivity中使用MCAdvertiserAssistant来表示一个广播,通常创建广播时指定一个会话MCSession对象将广播服务和会话关联起来。一旦调用广播的start方法周边的设备就可以发现该广播并可以连接到此服务。在MCSession的代理方法中可以随时更新连接状态,一旦建立了连接之后就可以通过MCSession的connectedPeers获得已经连接的设备。

  • B.发现

前面已经说过作为发现的客户端同样需要一个MCPeerID来标志一个客户端,同时会拥有一个MCSession来监听连接状态并发送、接受数据。除此之外,要发现广播服务,客户端就必须要随时查找服务来连接,在MultipeerConnectivity中提供了一个控制器MCBrowserViewController来展示可连接和已连接的设备(这类似于GameKit中的GKPeerPickerController),当然如果想要自己定制一个界面来展示设备连接的情况你可以选择自己开发一套UI界面。一旦通过MCBroserViewController选择一个节点去连接,那么作为广播的节点就会收到通知,询问用户是否允许连接。由于初始化MCBrowserViewController的过程已经指定了会话MCSession,所以连接过程中会随时更新会话状态,一旦建立了连接,就可以通过会话的connected属性获得已连接设备并且可以使用会话发送、接受数据

下面用两个不同的应用程序来演示使用MultipeerConnectivity的使用过程,其中:

  1. 一个应用运行在模拟器中作为广播节点,
  2. 另一个运行在iPhone真机上作为发现节点,并且实现两个节点的图片互传。

效果展示(同一个分代码,同一个UI界面):


运行结果.png

SB中的界面搭建:


XIB界面搭建.png

代码:
<pre ><code>

// ViewController.swift
// 蓝牙数据传输(MultipeerConnectivity)
// Created by 礼光 on 2016/11/2.

// Copyright © 2016年 LLG. All rights reserved.

import UIKit

import MultipeerConnectivity

//需要用到的内容是:

//1.创建设备ID.发现的名字

//2.会话对象

//3.广播助理

//4.代理方法(状态管理,发送接收消息)

//服务类型随便取
let serviceType = "LLG"

class ViewController: UIViewController {

    //控件属性
    @IBOutlet weak var stateLabel: UILabel!
    @IBOutlet weak var isFound: UISwitch!
    @IBOutlet weak var photo: UIImageView!
    @IBOutlet weak var isConnect: UIButton!

    //自定义属性
    lazy var session : MCSession = {
        let peerID = MCPeerID(displayName: UIDevice.current.name)//使用当前设备的名称
        let session = MCSession(peer: peerID)
        session.delegate = self
        return session
    }()

    var advertiserAssistant : MCAdvertiserAssistant!

    lazy var matchPeerIDs : NSMutableArray = {
        let matchPeerIDs = NSMutableArray.init(array: [])
        return matchPeerIDs;
    }()


    override func viewDidLoad() {
        super.viewDidLoad()
        self.isFound.isOn = false//初始化不能被发现
        self.stateLabel.text = "匹配状态: 未连接"
        self.isConnect.isEnabled = false
        self.isConnect.backgroundColor = UIColor.gray
        self.isConnect.setTitle("未连接成功", for: .normal)
    }


    @IBAction func switchToBeFound(_ sender: UISwitch) {
    //创建一个广播助手
    self.advertiserAssistant = MCAdvertiserAssistant(serviceType: serviceType, discoveryInfo: nil, session: self.session)
  
    switch sender.isOn {
        case true:
            //"可被发现"
            print("可被发现")
            self.advertiserAssistant.start()
        default:
            //"不可被发现"
            print("不可被发现")
            self.advertiserAssistant.stop()
        }
    }


    @IBAction func searchDevice(_ sender: UIButton) {
        //创建一个蓝牙发现弹出页面,用于选择连接的设备
        let browserVc = MCBrowserViewController(serviceType: serviceType, session: self.session)
        browserVc.delegate = self;
        self.present(browserVc, animated: true, completion: nil)
    }



    @IBAction func sendData(_ sender: UIButton) {
        let data = UIImagePNGRepresentation(UIImage(named: "01")!)
        //发送数据给已经连接的所有蓝牙设备,以不可靠的方式
        try?self.session.send(data!, toPeers: self.session.connectedPeers, with: .unreliable)
    }
}

//MARK: - MCBrowserViewController代理

extension ViewController : MCBrowserViewControllerDelegate {
//点击完成

    func browserViewControllerDidFinish(_ browserViewController: MCBrowserViewController) {
        self.dismiss(animated: true, completion: nil)
    }
    //点击取消
    func browserViewControllerWasCancelled(_ browserViewController: MCBrowserViewController) {
        self.dismiss(animated: true, completion: nil)
    }

}

//MARK: - MCSession代理

extension ViewController : MCSessionDelegate {

//监听数据传输

    func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {
        let image = UIImage(data: data)
        DispatchQueue.main.async {
        self.photo.image = image
        }
    }
    //监听连接状态
    func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) {
        switch state {
        case .connected:
            //回到主线程刷新界面
            DispatchQueue.main.async(execute: {
                self.stateLabel.text = "匹配状态: 已连接设备\(peerID.displayName)"
                self.isConnect.isEnabled = true
                self.isConnect.backgroundColor = UIColor.red
                self.isConnect.setTitle("传输数据", for: .normal)
            })
        case .connecting:
            //回到主线程刷新界面
            DispatchQueue.main.async(execute: {
                self.stateLabel.text = "匹配状态: 连接中..."
            })
        default:
            DispatchQueue.main.async(execute: {
                self.stateLabel.text = "匹配状态: 连接失败"
            })
        }
    
    }

//用不到,代理空实现
    func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) {
    }
    func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) {
    }
    func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL, withError error: Error?) {
    }

}</code></pre>

<pre><code>
ahdiuiqhwudbqwiuiuqwbsjdhvfajfhaskdhfasidhfisadhfjashdfkshdjkfhsjkdfhaksjdhfkaljshdfklasjhdfiueoiruweoriuweoirkjsdalksjdlkasjdio wqeajdhjasdhiehr iuhdksajd qidoiq dasd
qwdhashdoi dq dakjsdn qw
</code></pre>

这个使用markdown来编辑的的代码效果实在是太恶心了,后续再补充

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348

推荐阅读更多精彩内容