├── README.md
├── RHMQTTKit
├── RHMQTT.m
├── RHMQTT.h
├── RHMQTTDecoder.h
├── RHMQTTEncoder.h
├── RHMQTTEncoder.m
├── RHMQTTClient.h
├── RHMQTTDecoder.m
├── RHMQTTPacket.h
├── RHMQTTClient.m
└── RHMQTTPacket.m
├── RHMQTTKitDemo
├── Podfile
├── RHMQTTKitDemo.xcodeproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── project.pbxproj
├── RHMQTTKitDemo
│ ├── ViewController.h
│ ├── AppDelegate.h
│ ├── main.m
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Base.lproj
│ │ ├── Main.storyboard
│ │ └── LaunchScreen.storyboard
│ ├── Info.plist
│ ├── AppDelegate.m
│ └── ViewController.m
├── RHMQTTKitDemo.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── Podfile.lock
├── .gitignore
├── RHMQTTKit.podspec
└── LICENSE
/README.md:
--------------------------------------------------------------------------------
1 | # RHMQTTKit
2 | 基于RHSocketKit框架实现的MQTT协议,底层使用CocoaAsyncSocket。
3 |
--------------------------------------------------------------------------------
/RHMQTTKit/RHMQTT.m:
--------------------------------------------------------------------------------
1 | //
2 | // RHMQTT.m
3 | // RHSocketKitDemo
4 | //
5 | // Created by zhuruhong on 15/11/11.
6 | // Copyright © 2015年 zhuruhong. All rights reserved.
7 | //
8 |
9 | //#import "RHMQTT.h"
10 |
--------------------------------------------------------------------------------
/RHMQTTKitDemo/Podfile:
--------------------------------------------------------------------------------
1 | platform:ios,'9.0'
2 |
3 | target 'RHMQTTKitDemo' do
4 | pod 'RHSocketKit/Extend', :git=> 'https://github.com/zhu410289616/RHSocketKit.git', :tag => '2.3.6'
5 | pod 'RHMQTTKit', :path => '../RHMQTTKit.podspec'
6 | end
7 |
--------------------------------------------------------------------------------
/RHMQTTKitDemo/RHMQTTKitDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/RHMQTTKit/RHMQTT.h:
--------------------------------------------------------------------------------
1 | //
2 | // RHMQTT.h
3 | // RHSocketKitDemo
4 | //
5 | // Created by zhuruhong on 15/11/11.
6 | // Copyright © 2015年 zhuruhong. All rights reserved.
7 | //
8 |
9 | #import "RHMQTTPacket.h"
10 | #import "RHMQTTEncoder.h"
11 | #import "RHMQTTDecoder.h"
12 | #import "RHMQTTClient.h"
13 |
--------------------------------------------------------------------------------
/RHMQTTKitDemo/RHMQTTKitDemo/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // RHMQTTKitDemo
4 | //
5 | // Created by zhuruhong on 15/11/16.
6 | // Copyright © 2015年 zhuruhong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/RHMQTTKitDemo/RHMQTTKitDemo.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/RHMQTTKitDemo/RHMQTTKitDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/RHMQTTKit/RHMQTTDecoder.h:
--------------------------------------------------------------------------------
1 | //
2 | // RHMQTTDecoder.h
3 | // RHMQTTKitDemo
4 | //
5 | // Created by zhuruhong on 16/2/23.
6 | // Copyright © 2016年 zhuruhong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface RHMQTTDecoder : NSObject
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/RHMQTTKit/RHMQTTEncoder.h:
--------------------------------------------------------------------------------
1 | //
2 | // RHMQTTEncoder.h
3 | // RHMQTTKitDemo
4 | //
5 | // Created by zhuruhong on 16/2/23.
6 | // Copyright © 2016年 zhuruhong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface RHMQTTEncoder : NSObject
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/RHMQTTKitDemo/RHMQTTKitDemo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // RHMQTTKitDemo
4 | //
5 | // Created by zhuruhong on 15/11/16.
6 | // Copyright © 2015年 zhuruhong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/RHMQTTKitDemo/RHMQTTKitDemo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // RHMQTTKitDemo
4 | //
5 | // Created by zhuruhong on 15/11/16.
6 | // Copyright © 2015年 zhuruhong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.moved-aside
15 | DerivedData
16 | *.hmap
17 | *.ipa
18 | *.xcuserstate
19 |
20 | # CocoaPods
21 | #
22 | # We recommend against adding the Pods directory to your .gitignore. However
23 | # you should judge for yourself, the pros and cons are mentioned at:
24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
25 | #
26 | Pods/
27 |
--------------------------------------------------------------------------------
/RHMQTTKit.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 |
3 | spec.name = "RHMQTTKit"
4 | spec.version = "0.0.1"
5 | spec.summary = "MQTT 通信代码"
6 |
7 | spec.description = <<-DESC
8 | 基于RHSocketKit框架实现的MQTT协议,底层使用CocoaAsyncSocket。
9 | DESC
10 |
11 | spec.homepage = "https://github.com/zhu410289616/RHMQTTKit"
12 | spec.license = { :type => "MIT", :file => "LICENSE" }
13 |
14 | spec.author = { "zhu410289616" => "zhu410289616@163.com" }
15 | spec.platform = :ios, "9.0"
16 | spec.source = { :git => "https://github.com/zhu410289616/RHMQTTKit.git", :tag => "#{spec.version}" }
17 |
18 | spec.subspec "RHMQTTKit" do |cs|
19 | cs.source_files = "RHMQTTKit/**/*"
20 | cs.dependency "RHSocketKit"
21 | cs.dependency "CocoaAsyncSocket"
22 | end
23 |
24 | end
25 |
--------------------------------------------------------------------------------
/RHMQTTKit/RHMQTTEncoder.m:
--------------------------------------------------------------------------------
1 | //
2 | // RHMQTTEncoder.m
3 | // RHMQTTKitDemo
4 | //
5 | // Created by zhuruhong on 16/2/23.
6 | // Copyright © 2016年 zhuruhong. All rights reserved.
7 | //
8 |
9 | #import "RHMQTTEncoder.h"
10 | #import "RHMQTTPacket.h"
11 | #import "RHSocketUtils.h"
12 |
13 | @implementation RHMQTTEncoder
14 |
15 | - (void)encode:(id)upstreamPacket output:(id)output
16 | {
17 | RHMQTTPacket *packet = (RHMQTTPacket *)upstreamPacket;
18 | NSData *data = [packet data];
19 | NSMutableData *sendData = [NSMutableData dataWithData:data];
20 |
21 | NSTimeInterval timeout = [upstreamPacket timeout];
22 |
23 | RHSocketLog(@"[encode] timeout: %f, data: %@", timeout, [RHSocketUtils hexStringFromData:sendData]);
24 | [output didEncode:sendData timeout:timeout];
25 | }
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/RHMQTTKit/RHMQTTClient.h:
--------------------------------------------------------------------------------
1 | //
2 | // RHMQTTClient.h
3 | // Pods
4 | //
5 | // Created by ruhong zhu on 2021/8/21.
6 | //
7 |
8 | #import
9 | #import
10 | #import "RHMQTTPacket.h"
11 |
12 | @class RHMQTTClient;
13 |
14 | @protocol RHMQTTClientDelegate
15 |
16 | - (void)mqttConnected:(RHMQTTClient *)mqtt host:(NSString *)host port:(int)port;
17 | - (void)mqttDisconnect:(RHMQTTClient *)mqtt error:(NSError *)error;
18 | - (void)mqtt:(RHMQTTClient *)mqtt received:(RHMQTTPacket *)packet;
19 |
20 | - (void)mqtt:(RHMQTTClient *)mqtt publish:(RHMQTTPublish *)publish;
21 |
22 | @end
23 |
24 | @interface RHMQTTClient : NSObject
25 |
26 | @property (nonatomic, weak) id delegate;
27 |
28 | - (void)startWithHost:(NSString *)host port:(int)port;
29 | - (void)stop;
30 |
31 | - (void)asyncSendPacket:(RHMQTTPacket *)packet;
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 朱如洪
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 |
23 |
--------------------------------------------------------------------------------
/RHMQTTKitDemo/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - CocoaAsyncSocket (7.6.5)
3 | - RHMQTTKit (0.0.1):
4 | - RHMQTTKit/RHMQTTKit (= 0.0.1)
5 | - RHMQTTKit/RHMQTTKit (0.0.1):
6 | - CocoaAsyncSocket
7 | - RHSocketKit
8 | - RHSocketKit (2.3.0):
9 | - RHSocketKit/Core (= 2.3.0)
10 | - RHSocketKit/Core (2.3.0):
11 | - CocoaAsyncSocket
12 | - RHSocketKit/Extend (2.3.0):
13 | - RHSocketKit/Core
14 |
15 | DEPENDENCIES:
16 | - RHMQTTKit (from `../RHMQTTKit.podspec`)
17 | - RHSocketKit/Extend (from `https://github.com/zhu410289616/RHSocketKit.git`, tag `2.3.6`)
18 |
19 | SPEC REPOS:
20 | trunk:
21 | - CocoaAsyncSocket
22 |
23 | EXTERNAL SOURCES:
24 | RHMQTTKit:
25 | :path: "../RHMQTTKit.podspec"
26 | RHSocketKit:
27 | :git: https://github.com/zhu410289616/RHSocketKit.git
28 | :tag: 2.3.6
29 |
30 | CHECKOUT OPTIONS:
31 | RHSocketKit:
32 | :git: https://github.com/zhu410289616/RHSocketKit.git
33 | :tag: 2.3.6
34 |
35 | SPEC CHECKSUMS:
36 | CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
37 | RHMQTTKit: c563288b92a4dbeade92781987b1bcf5a33c0a6b
38 | RHSocketKit: f25606cece9e695cd3fc22a04d8308b1e2f9e572
39 |
40 | PODFILE CHECKSUM: 22677b35fcdf45f7e992999eb1eae61595c0a176
41 |
42 | COCOAPODS: 1.9.1
43 |
--------------------------------------------------------------------------------
/RHMQTTKitDemo/RHMQTTKitDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/RHMQTTKitDemo/RHMQTTKitDemo/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/RHMQTTKitDemo/RHMQTTKitDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/RHMQTTKitDemo/RHMQTTKitDemo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/RHMQTTKitDemo/RHMQTTKitDemo/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // RHMQTTKitDemo
4 | //
5 | // Created by zhuruhong on 15/11/16.
6 | // Copyright © 2015年 zhuruhong. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | return YES;
21 | }
22 |
23 | - (void)applicationWillResignActive:(UIApplication *)application {
24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
26 | }
27 |
28 | - (void)applicationDidEnterBackground:(UIApplication *)application {
29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
31 | }
32 |
33 | - (void)applicationWillEnterForeground:(UIApplication *)application {
34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
35 | }
36 |
37 | - (void)applicationDidBecomeActive:(UIApplication *)application {
38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
39 | }
40 |
41 | - (void)applicationWillTerminate:(UIApplication *)application {
42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
43 | }
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/RHMQTTKit/RHMQTTDecoder.m:
--------------------------------------------------------------------------------
1 | //
2 | // RHMQTTDecoder.m
3 | // RHMQTTKitDemo
4 | //
5 | // Created by zhuruhong on 16/2/23.
6 | // Copyright © 2016年 zhuruhong. All rights reserved.
7 | //
8 |
9 | #import "RHMQTTDecoder.h"
10 | #import "RHMQTTPacket.h"
11 | #import "RHSocketUtils.h"
12 |
13 | @implementation RHMQTTDecoder
14 |
15 | - (NSInteger)decode:(id)downstreamPacket output:(id)output
16 | {
17 | NSData *downstreamData = [downstreamPacket object];
18 | NSInteger headIndex = 0;
19 | NSInteger lengthMultiplier = 1;
20 | //先读区2个字节的协议长度 (前2个字节为数据包的固定长度)
21 | while (downstreamData && (downstreamData.length - headIndex) >= (1 + lengthMultiplier)) {
22 | NSData *lenData = [downstreamData subdataWithRange:NSMakeRange(headIndex, 1 + lengthMultiplier)];
23 |
24 | //剩余长度remainingLength(1-4个字节,可变)
25 | //可变头部内容字节长度 + Playload/负荷字节长度 = 剩余长度
26 | NSInteger remainingLength = 0;
27 | int8_t digit = 0;
28 | [lenData getBytes:&digit range:NSMakeRange(lengthMultiplier, 1)];
29 |
30 | if ((digit & 0x80) == 0x00) {
31 | //已经读区到剩余长度,可以解码
32 | remainingLength += (digit & 0x7f);
33 | } else {
34 | lengthMultiplier++;
35 | remainingLength += (digit & 0x7f);
36 | NSAssert(lengthMultiplier <= 4, @"remain length error ...");
37 | continue;
38 | }
39 |
40 | if (downstreamData.length - headIndex < remainingLength + 1 + lengthMultiplier) {
41 | break;
42 | }
43 | NSData *frameData = [downstreamData subdataWithRange:NSMakeRange(headIndex, remainingLength + 1 + lengthMultiplier)];
44 | RHSocketLog(@"[decode] frame data: %@", [RHSocketUtils hexStringFromData:frameData]);
45 |
46 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] initWithData:frameData];
47 | int8_t fixedHeaderData = [byteBuffer readInt8:0];
48 | RHMQTTFixedHeader *fixedHeader = [[RHMQTTFixedHeader alloc] initWithByte:fixedHeaderData];
49 |
50 | NSData *remainingLengthData = [byteBuffer readData:1+lengthMultiplier length:remainingLength];
51 |
52 | // RHMQTTVariableHeader *variableHeader = nil;
53 | // if (remainingLength - 1 - lengthMultiplier > 0) {
54 | // NSData *variableHeaderData = [byteBuffer readData:1+lengthMultiplier length:remainingLength];
55 | // variableHeader = [[RHMQTTVariableHeader alloc] initWithObject:variableHeaderData];
56 | // }
57 |
58 | RHMQTTPacket *packet = [[RHMQTTPacket alloc] initWithObject:frameData];
59 | packet.remainingLengthData = remainingLengthData;
60 | packet.remainingLength = remainingLength;
61 | packet.fixedHeader = fixedHeader;
62 | // packet.variableHeader = variableHeader;
63 | [output didDecode:packet];
64 |
65 | headIndex += remainingLength + 2;
66 | lengthMultiplier = 1;
67 | }
68 | return headIndex;
69 | }
70 |
71 | @end
72 |
--------------------------------------------------------------------------------
/RHMQTTKit/RHMQTTPacket.h:
--------------------------------------------------------------------------------
1 | //
2 | // RHMQTTPacket.h
3 | // Pods
4 | //
5 | // Created by ruhong zhu on 2021/8/21.
6 | //
7 |
8 | #import
9 | #import
10 |
11 | //http://www.blogjava.net/yongboy/archive/2014/02/07/409587.html
12 | //http://www.blogjava.net/yongboy/archive/2014/02/09/409630.html
13 | //http://www.blogjava.net/yongboy/archive/2014/02/10/409689.html
14 |
15 | /** message type */
16 | typedef NS_ENUM(UInt8, RHMQTTMessageType) {
17 | RHMQTTMessageTypeConnect = 1, //client request to connect to server
18 | RHMQTTMessageTypeConnAck = 2, //connect acknowledgment
19 | RHMQTTMessageTypePublish = 3, //publish message
20 | RHMQTTMessageTypePubAck = 4, //publish acknowledgment
21 | RHMQTTMessageTypePubRec = 5, //publish received (assured delivery part 1)
22 | RHMQTTMessageTypePubRel = 6, //publish release (assured delivery part 2)
23 | RHMQTTMessageTypePubComp = 7, //publish complete (assured delivery part 3)
24 | RHMQTTMessageTypeSubscribe = 8, //client subscribe request
25 | RHMQTTMessageTypeSubAck = 9, //subscribe acknowledgment
26 | RHMQTTMessageTypeUnsubscribe = 10, //client unsubscribe request
27 | RHMQTTMessageTypeUnsubAck = 11, //unsubscribe acknowledgment
28 | RHMQTTMessageTypePingReq = 12, //ping request
29 | RHMQTTMessageTypePingResp = 13, //ping response
30 | RHMQTTMessageTypeDisconnect = 14, //client is disconnecting
31 | RHMQTTMessageTypeReserved = 15 //reserved
32 | };
33 |
34 | /** QoS(Quality of Service,服务质量) */
35 | typedef NS_ENUM(UInt8, RHMQTTQosLevel) {
36 | RHMQTTQosLevelAtMostOnce = 0, //至多一次,发完即丢弃,<=1
37 | RHMQTTQosLevelAtLeastOnce = 1, //至少一次,需要确认回复,>=1
38 | RHMQTTQosLevelExactlyOnce = 2, //只有一次,需要确认回复,=1
39 | RHMQTTQosLevelReserved = 3 //待用,保留位置
40 | };
41 |
42 | // ------------------------------------------
43 |
44 | #pragma mark - RHMQTTFixedHeader
45 |
46 | /** Fixed header/固定头部 */
47 | @interface RHMQTTFixedHeader : NSObject
48 |
49 | @property (nonatomic, assign) RHMQTTMessageType type; //4bit
50 | @property (nonatomic, assign) BOOL dupFlag; //1bit
51 | @property (nonatomic, assign) RHMQTTQosLevel qos; //2bit
52 | @property (nonatomic, assign) BOOL retainFlag; //1bit
53 |
54 | - (instancetype)initWithByte:(UInt8)byte;
55 |
56 | @end
57 |
58 | // ------------------------------------------
59 |
60 | #pragma mark - RHMQTTVariableHeader
61 |
62 | /** Variable header/可变头部 */
63 | @interface RHMQTTVariableHeader : NSObject
64 |
65 | /** protocol name */
66 | @property (nonatomic, strong) NSString *name;
67 |
68 | /** protocol version number, 1byte */
69 | @property (nonatomic, assign) UInt8 version;
70 |
71 | /**
72 | * connect flags, 1byte
73 | *
74 | * user name flag: 1bit
75 | * password flag: 1bit
76 | * will retain: 1bit
77 | * will QoS: 2bit
78 | * will flag: 1bit
79 | * clean session: 1bit
80 | * reserved: 1bit
81 | */
82 | @property (nonatomic, assign) UInt8 connectFlags;
83 |
84 | /** keep alive timer, 2byte */
85 | @property (nonatomic, assign) UInt16 keepAlive;
86 |
87 | /** publish */
88 | @property (nonatomic, strong) NSString *topic;
89 |
90 | /** subscribe, publish */
91 | @property (nonatomic, assign) UInt16 messageId;
92 |
93 | @end
94 |
95 | // ------------------------------------------
96 |
97 | #pragma mark - RHMQTTPayload
98 |
99 | @interface RHMQTTTopic : NSObject
100 |
101 | /*
102 | * Will Topic
103 | *
104 | * Will Flag值为1,这里便是Will Topic的内容。
105 | * QoS级别通过Will QoS字段定义,RETAIN值通过Will RETAIN标识,都定义在可变头里面。
106 | */
107 | @property (nonatomic, strong) NSString *topic;
108 | @property (nonatomic, assign) RHMQTTQosLevel qos;
109 |
110 | @end
111 |
112 | // ------------------------------------------
113 |
114 | #pragma mark - RHMQTTPayload
115 |
116 | /** Payload/消息体 */
117 | @interface RHMQTTPayload : NSObject
118 |
119 | /*
120 | * Client Identifier(客户端ID) 必填项。
121 | *
122 | * 1-23个字符长度,客户端到服务器的全局唯一标志
123 | * 如果客户端ID超出23个字符长度,服务器需要返回码为2,标识符被拒绝响应的CONNACK消息。
124 | *
125 | * 处理QoS级别1和2的消息ID中,可以使用到。
126 | */
127 | @property (nonatomic, strong) NSString *clientId;
128 |
129 | /** see RHMQTTTopic */
130 | @property (nonatomic, strong) NSArray *topics;
131 |
132 | /*
133 | * Will Message (长度有可能为0)
134 | *
135 | * Will Flag若设为1,这里便是Will Message定义消息的内容,对应的主题为Will Topic。
136 | * 如果客户端意外的断开触发服务器PUBLISH此消息。
137 | * 在CONNECT消息中的Will Message是UTF-8编码的,当被服务器发布时则作为二进制的消息体。
138 | */
139 | @property (nonatomic, strong) NSData *message;
140 |
141 | /** 如果设置User Name标识,可以在此读取用户名称。一般可用于身份验证。协议建议用户名为不多于12个字符,不是必须。*/
142 | @property (nonatomic, strong) NSString *username;
143 |
144 | /** 如果设置Password标识,便可读取用户密码。建议密码为12个字符或者更少,但不是必须。*/
145 | @property (nonatomic, strong) NSString *password;
146 |
147 | @end
148 |
149 | // ------------------------------------------
150 |
151 | #pragma mark - RHMQTTPacket
152 |
153 | @interface RHMQTTPacket : RHSocketPacketRequest
154 |
155 | /** 剩余长度 remainingLength 的二进制数据(1-4个字节,可变) */
156 | @property (nonatomic, strong) NSData *remainingLengthData;
157 | /** 剩余长度 = 可变头部内容字节长度 + Playload/负荷字节长度 */
158 | @property (nonatomic, assign) NSInteger remainingLength;
159 |
160 | /** 1 个字节 */
161 | @property (nonatomic, strong) RHMQTTFixedHeader *fixedHeader;
162 | @property (nonatomic, strong) RHMQTTVariableHeader *variableHeader;
163 | @property (nonatomic, strong) RHMQTTPayload *payload;
164 |
165 | - (NSData *)dataWithFixedHeader;
166 | - (NSData *)dataWithVariableHeader;
167 | - (NSData *)dataWithPayload;
168 | - (NSData *)data;
169 |
170 | @end
171 |
172 | // ------------------------------------------
173 |
174 | #pragma mark - RHMQTTConnect
175 |
176 | @interface RHMQTTConnect : RHMQTTPacket
177 |
178 | + (RHMQTTConnect *)connectWithClientId:(NSString *)clientId username:(NSString *)username password:(NSString *)password keepAlive:(UInt16)keepAlive cleanSession:(BOOL)cleanSession;
179 |
180 | @end
181 |
182 | // ------------------------------------------
183 |
184 | #pragma mark - RHMQTTPublish
185 |
186 | @interface RHMQTTPublish : RHMQTTPacket
187 |
188 | @end
189 |
190 | #pragma mark - RHMQTTPublishAck
191 |
192 | @interface RHMQTTPublishAck : RHMQTTPacket
193 |
194 | - (instancetype)initWithMessageId:(int16_t)msgId;
195 |
196 | @end
197 |
198 | #pragma mark - RHMQTTPublishRec
199 |
200 | @interface RHMQTTPublishRec : RHMQTTPacket
201 |
202 | - (instancetype)initWithMessageId:(int16_t)msgId;
203 |
204 | @end
205 |
206 | #pragma mark - RHMQTTPublishRel
207 |
208 | @interface RHMQTTPublishRel : RHMQTTPacket
209 |
210 | - (instancetype)initWithMessageId:(int16_t)msgId;
211 |
212 | @end
213 |
214 | #pragma mark - RHMQTTPublishComp
215 |
216 | /**
217 | 作为QoS level = 2消息流第四个,也是最后一个消息,由收到PUBREL的一方向另一方做出的响应消息。
218 | 完整的消息一览,和PUBREL一致,除了消息类型。
219 | */
220 | @interface RHMQTTPublishComp : RHMQTTPacket
221 |
222 | - (instancetype)initWithMessageId:(int16_t)msgId;
223 |
224 | @end
225 |
226 | // ------------------------------------------
227 |
228 | #pragma mark - RHMQTTSubscribe
229 |
230 | @interface RHMQTTSubscribe : RHMQTTPacket
231 |
232 | + (RHMQTTSubscribe *)subscribeWithMessageId:(UInt16)msgId topic:(NSString *)topic qos:(RHMQTTQosLevel)qos;
233 |
234 | @end
235 |
236 | // ------------------------------------------
237 |
238 | #pragma mark - RHMQTTUnsubscribe
239 |
240 | @interface RHMQTTUnsubscribe : RHMQTTPacket
241 |
242 | + (RHMQTTUnsubscribe *)unsubscribeWithMessageId:(UInt16)msgId topic:(NSString *)topic;
243 |
244 | @end
245 |
246 | // ------------------------------------------
247 |
248 | #pragma mark - RHMQTTPingReq
249 |
250 | @interface RHMQTTPingReq : RHMQTTPacket
251 |
252 | @end
253 |
254 | // ------------------------------------------
255 |
256 | #pragma mark - RHMQTTDisconnect
257 |
258 | @interface RHMQTTDisconnect : RHMQTTPacket
259 |
260 | @end
261 |
--------------------------------------------------------------------------------
/RHMQTTKit/RHMQTTClient.m:
--------------------------------------------------------------------------------
1 | //
2 | // RHMQTTClient.m
3 | // Pods
4 | //
5 | // Created by ruhong zhu on 2021/8/21.
6 | //
7 |
8 | #import "RHMQTTClient.h"
9 | #import "RHMQTTDecoder.h"
10 | #import "RHMQTTEncoder.h"
11 |
12 | @interface RHMQTTClient ()
13 |
14 | @property (nonatomic, strong) RHChannelService *tcpChannel;
15 | @property (nonatomic, strong) RHSocketConnectParam *connectParam;
16 |
17 | @end
18 |
19 | @implementation RHMQTTClient
20 |
21 | - (instancetype)init
22 | {
23 | self = [super init];
24 | if (self) {
25 | _tcpChannel = [[RHChannelService alloc] init];
26 | }
27 | return self;
28 | }
29 |
30 | - (void)startWithHost:(NSString *)host port:(int)port
31 | {
32 | self.connectParam = [[RHSocketConnectParam alloc] init];
33 | self.connectParam.host = host;
34 | self.connectParam.port = port;
35 | self.connectParam.heartbeatInterval = 15;
36 | self.connectParam.heartbeatEnabled = YES;
37 | self.connectParam.autoReconnect = NO;
38 |
39 | RHChannelBeats *beats = [[RHChannelBeats alloc] init];
40 | beats.heartbeatBlock = ^id{
41 | return [[RHMQTTPingReq alloc] init];
42 | };
43 |
44 | [self.tcpChannel.channel addDelegate:self];
45 | [self.tcpChannel startWithConfig:^(RHChannelConfig *config) {
46 | config.encoder = [[RHMQTTEncoder alloc] init];
47 | config.decoder = [[RHMQTTDecoder alloc] init];
48 | config.channelBeats = beats;
49 | config.connectParam = self.connectParam;
50 | }];
51 | }
52 |
53 | - (void)stop
54 | {
55 | [self.tcpChannel stopService];
56 | }
57 |
58 | - (void)asyncSendPacket:(RHMQTTPacket *)packet
59 | {
60 | [self.tcpChannel asyncSendPacket:packet];
61 | }
62 |
63 | #pragma mark - RHSocketChannelDelegate
64 |
65 | - (void)channelOpened:(RHSocketChannel *)channel host:(NSString *)host port:(int)port
66 | {
67 | RHSocketLog(@"[RHMQTT] channelOpened: %@:%@", host, @(port));
68 |
69 | //需要在password_file.conf文件中设置帐号密码
70 | NSString *username = @"";//@"testuser";
71 | NSString *password = @"";//@"testuser";
72 | RHMQTTConnect *connect = [RHMQTTConnect connectWithClientId:@"RHMQTTKit" username:username password:password keepAlive:60 cleanSession:YES];
73 | [self asyncSendPacket:connect];
74 | }
75 |
76 | - (void)channelClosed:(RHSocketChannel *)channel error:(NSError *)error
77 | {
78 | RHSocketLog(@"[RHMQTT] channelClosed: %@", error);
79 | [self.tcpChannel.config.channelBeats stop];
80 | [self.delegate mqttDisconnect:self error:error];
81 | }
82 |
83 | - (void)channel:(RHSocketChannel *)channel received:(id)packet
84 | {
85 | if (![packet isKindOfClass:[RHMQTTPacket class]]) {
86 | RHSocketLog(@"[RHMQTT] packet(%@) is not kind of RHMQTTPacket", NSStringFromClass([packet class]));
87 | return;
88 | }
89 |
90 | RHMQTTPacket *mqttPacket = (RHMQTTPacket *)packet;
91 | RHSocketLog(@"[RHMQTT] mqttPacket object: %@", [mqttPacket object]);
92 | [self.delegate mqtt:self received:mqttPacket];
93 |
94 | NSData *buffer = mqttPacket.remainingLengthData;
95 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] initWithData:buffer];
96 | RHMQTTFixedHeader *fixedHeader = mqttPacket.fixedHeader;
97 | switch (fixedHeader.type) {
98 | case RHMQTTMessageTypeConnAck:
99 | {
100 | [self.delegate mqttConnected:self host:self.connectParam.host port:self.connectParam.port];
101 | RHSocketLog(@"RHMQTTMessageTypeConnAck: %d", fixedHeader.type);
102 | [self.tcpChannel.config.channelBeats start];
103 | }
104 | break;
105 | case RHMQTTMessageTypePublish: {
106 | RHSocketLog(@"RHMQTTMessageTypePublish: %d", fixedHeader.type);
107 |
108 | RHMQTTPublish *publish = [[RHMQTTPublish alloc] initWithObject:buffer];
109 | publish.fixedHeader = fixedHeader;
110 |
111 | /**
112 | 兄弟,针对QoS2,为了便于说明,我们先假设一个方向,Server -> Client:
113 | ----PUBLISH--->
114 | <----PUBREC----
115 | ----PUBREL---->
116 | <----PUBCOMP---
117 | */
118 | if (fixedHeader.qos == RHMQTTQosLevelAtMostOnce) {
119 | //at most once
120 | int16_t topicLength = [byteBuffer readInt16:0 endianSwap:YES];
121 | RHMQTTVariableHeader *variableHeader = [[RHMQTTVariableHeader alloc] init];
122 | variableHeader.topic = [byteBuffer readString:2 length:topicLength];
123 | int16_t payloadLength = buffer.length - (2 + topicLength);
124 | NSData *msgData = [byteBuffer readData:2 + topicLength length:payloadLength];
125 | RHMQTTPayload *payload = [[RHMQTTPayload alloc] init];
126 | payload.message = msgData;
127 |
128 | publish.variableHeader = variableHeader;
129 | publish.payload = payload;
130 | } else if (fixedHeader.qos == RHMQTTQosLevelAtLeastOnce) {
131 | int16_t topicLength = [byteBuffer readInt16:0 endianSwap:YES];
132 | RHMQTTVariableHeader *variableHeader = [[RHMQTTVariableHeader alloc] init];
133 | variableHeader.topic = [byteBuffer readString:2 length:topicLength];
134 | variableHeader.messageId = [byteBuffer readInt16:2+topicLength endianSwap:YES];
135 | int16_t payloadLength = buffer.length - (2 + topicLength + 2);
136 | NSData *msgData = [byteBuffer readData:2 + topicLength + 2 length:payloadLength];
137 | RHMQTTPayload *payload = [[RHMQTTPayload alloc] init];
138 | payload.message = msgData;
139 |
140 | publish.variableHeader = variableHeader;
141 | publish.payload = payload;
142 |
143 | /**
144 | 作为订阅者/服务器接收(QoS level = 1)PUBLISH消息之后对发送者的响应。
145 | */
146 | RHMQTTPublishAck *publishAck = [[RHMQTTPublishAck alloc] initWithMessageId:variableHeader.messageId];
147 | [self asyncSendPacket:publishAck];
148 | } else if (fixedHeader.qos == RHMQTTQosLevelExactlyOnce) {
149 | int16_t topicLength = [byteBuffer readInt16:0 endianSwap:YES];
150 | RHMQTTVariableHeader *variableHeader = [[RHMQTTVariableHeader alloc] init];
151 | variableHeader.topic = [byteBuffer readString:2 length:topicLength];
152 | variableHeader.messageId = [byteBuffer readInt16:2+topicLength endianSwap:YES];
153 | int16_t payloadLength = buffer.length - (2 + topicLength + 2);
154 | NSData *msgData = [byteBuffer readData:2 + topicLength + 2 length:payloadLength];
155 | RHMQTTPayload *payload = [[RHMQTTPayload alloc] init];
156 | payload.message = msgData;
157 |
158 | publish.variableHeader = variableHeader;
159 | publish.payload = payload;
160 |
161 | /**
162 | 作为订阅者/服务器对QoS level = 2的发布PUBLISH消息的发送方的响应,确认已经收到,为QoS level = 2消息流的第二个消息。 和PUBACK相比,除了消息类型不同外,其它都是一样。
163 | */
164 | RHMQTTPublishRec *publishRec = [[RHMQTTPublishRec alloc] initWithMessageId:variableHeader.messageId];
165 | [self asyncSendPacket:publishRec];
166 | }
167 |
168 | RHSocketLog(@"RHMQTTMessageTypePublish: msgid = %@, topic = %@, msg: %@", @(publish.variableHeader.messageId), publish.variableHeader.topic, [[NSString alloc] initWithData:publish.payload.message encoding:NSUTF8StringEncoding]);
169 | [self.delegate mqtt:self publish:publish];
170 | }
171 | break;
172 | case RHMQTTMessageTypePubAck: {
173 | RHSocketLog(@"RHMQTTMessageTypePubAck: %d", fixedHeader.type);
174 | RHMQTTVariableHeader *variableHeader = [[RHMQTTVariableHeader alloc] init];
175 | variableHeader.messageId = [byteBuffer readInt16:0 endianSwap:YES];
176 | RHMQTTPublish *publish = [[RHMQTTPublish alloc] init];
177 | publish.fixedHeader = fixedHeader;
178 | publish.variableHeader = variableHeader;
179 | }
180 | break;
181 | case RHMQTTMessageTypePubRec:
182 | {
183 | //qos = RHMQTTQosLevelExactlyOnce
184 | RHSocketLog(@"RHMQTTMessageTypePubRec: %d", fixedHeader.type);
185 | UInt16 msgId = [byteBuffer readInt16:0 endianSwap:YES];
186 | RHSocketLog(@"msgId: %d, ", msgId);
187 | }
188 | break;
189 | case RHMQTTMessageTypeSubAck:
190 | {
191 | UInt16 msgId = [byteBuffer readInt16:0 endianSwap:YES];
192 | NSData *grantedQos = [byteBuffer readData:2 length:byteBuffer.length - 2];
193 | RHSocketLog(@"msgId: %@, grantedQos: %@", @(msgId), grantedQos);
194 | }
195 | break;
196 | case RHMQTTMessageTypePingResp:
197 | {
198 | RHSocketLog(@"RHMQTTMessageTypePingResp: %d", fixedHeader.type);
199 | }
200 | break;
201 |
202 | default:
203 | break;
204 | }
205 | }
206 |
207 | @end
208 |
--------------------------------------------------------------------------------
/RHMQTTKitDemo/RHMQTTKitDemo/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // RHMQTTKitDemo
4 | //
5 | // Created by zhuruhong on 15/11/16.
6 | // Copyright © 2015年 zhuruhong. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 |
11 | #import
12 |
13 | @interface ViewController ()
14 | {
15 | UITextField *_hostTextField;
16 | UITextField *_portTextField;
17 |
18 | UIButton *_connectButton;
19 | UIButton *_disconnectButton;
20 | UIButton *_subscribeButton;
21 | UIButton *_unsubscribeButton;
22 | UIButton *_pingReqButton;
23 | UIButton *_publishButton;
24 |
25 | UITextView *_receivedTextView;
26 | }
27 |
28 | @property (nonatomic, strong) RHMQTTClient *mqttClient;
29 |
30 | @end
31 |
32 | @implementation ViewController
33 |
34 | - (void)loadView
35 | {
36 | [super loadView];
37 | self.mqttClient = [[RHMQTTClient alloc] init];
38 | self.mqttClient.delegate = self;
39 | }
40 |
41 | - (void)viewDidLoad {
42 | [super viewDidLoad];
43 | // Do any additional setup after loading the view, typically from a nib.
44 |
45 | _hostTextField = [[UITextField alloc] init];
46 | _hostTextField.frame = CGRectMake(20, 40, 200, 50);
47 | _hostTextField.borderStyle = UITextBorderStyleRoundedRect;
48 | _hostTextField.font = [UIFont systemFontOfSize:15];
49 | _hostTextField.text = @"mq.tongxinmao.com";
50 | [self.view addSubview:_hostTextField];
51 |
52 | _portTextField = [[UITextField alloc] init];
53 | _portTextField.frame = CGRectMake(20, CGRectGetMaxY(_hostTextField.frame) + 10, 200, 50);
54 | _portTextField.borderStyle = UITextBorderStyleRoundedRect;
55 | _portTextField.font = [UIFont systemFontOfSize:15];
56 | _portTextField.text = @"18830";
57 | [self.view addSubview:_portTextField];
58 |
59 | _connectButton = [UIButton buttonWithType:UIButtonTypeCustom];
60 | _connectButton.frame = CGRectMake(20, CGRectGetMaxY(_portTextField.frame) + 20, 130, 40);
61 | _connectButton.layer.borderColor = [UIColor blackColor].CGColor;
62 | _connectButton.layer.borderWidth = 0.5;
63 | _connectButton.layer.masksToBounds = YES;
64 | [_connectButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
65 | [_connectButton setTitle:@"connect" forState:UIControlStateNormal];
66 | [_connectButton addTarget:self action:@selector(doConnectButtonAction) forControlEvents:UIControlEventTouchUpInside];
67 | [self.view addSubview:_connectButton];
68 |
69 | _subscribeButton = [UIButton buttonWithType:UIButtonTypeCustom];
70 | _subscribeButton.frame = CGRectMake(20, CGRectGetMaxY(_connectButton.frame) + 20, 130, 40);
71 | _subscribeButton.layer.borderColor = [UIColor blackColor].CGColor;
72 | _subscribeButton.layer.borderWidth = 0.5;
73 | _subscribeButton.layer.masksToBounds = YES;
74 | [_subscribeButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
75 | [_subscribeButton setTitle:@"subscribe" forState:UIControlStateNormal];
76 | [_subscribeButton addTarget:self action:@selector(doSubscribeButtonAction) forControlEvents:UIControlEventTouchUpInside];
77 | [self.view addSubview:_subscribeButton];
78 |
79 | _pingReqButton = [UIButton buttonWithType:UIButtonTypeCustom];
80 | _pingReqButton.frame = CGRectMake(20, CGRectGetMaxY(_subscribeButton.frame) + 20, 130, 40);
81 | _pingReqButton.layer.borderColor = [UIColor blackColor].CGColor;
82 | _pingReqButton.layer.borderWidth = 0.5;
83 | _pingReqButton.layer.masksToBounds = YES;
84 | [_pingReqButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
85 | [_pingReqButton setTitle:@"ping" forState:UIControlStateNormal];
86 | [_pingReqButton addTarget:self action:@selector(doPingButtonAction) forControlEvents:UIControlEventTouchUpInside];
87 | [self.view addSubview:_pingReqButton];
88 |
89 | //
90 | _disconnectButton = [UIButton buttonWithType:UIButtonTypeCustom];
91 | _disconnectButton.frame = CGRectMake(160, CGRectGetMaxY(_portTextField.frame) + 20, 130, 40);
92 | _disconnectButton.layer.borderColor = [UIColor blackColor].CGColor;
93 | _disconnectButton.layer.borderWidth = 0.5;
94 | _disconnectButton.layer.masksToBounds = YES;
95 | [_disconnectButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
96 | [_disconnectButton setTitle:@"disconnect" forState:UIControlStateNormal];
97 | [_disconnectButton addTarget:self action:@selector(doDisconnectButtonAction) forControlEvents:UIControlEventTouchUpInside];
98 | [self.view addSubview:_disconnectButton];
99 |
100 | _unsubscribeButton = [UIButton buttonWithType:UIButtonTypeCustom];
101 | _unsubscribeButton.frame = CGRectMake(160, CGRectGetMaxY(_disconnectButton.frame) + 20, 130, 40);
102 | _unsubscribeButton.layer.borderColor = [UIColor blackColor].CGColor;
103 | _unsubscribeButton.layer.borderWidth = 0.5;
104 | _unsubscribeButton.layer.masksToBounds = YES;
105 | [_unsubscribeButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
106 | [_unsubscribeButton setTitle:@"unsubscribe" forState:UIControlStateNormal];
107 | [_unsubscribeButton addTarget:self action:@selector(doUnsubscribeButtonAction) forControlEvents:UIControlEventTouchUpInside];
108 | [self.view addSubview:_unsubscribeButton];
109 |
110 | _publishButton = [UIButton buttonWithType:UIButtonTypeCustom];
111 | _publishButton.frame = CGRectMake(160, CGRectGetMaxY(_unsubscribeButton.frame) + 20, 130, 40);
112 | _publishButton.layer.borderColor = [UIColor blackColor].CGColor;
113 | _publishButton.layer.borderWidth = 0.5;
114 | _publishButton.layer.masksToBounds = YES;
115 | [_publishButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
116 | [_publishButton setTitle:@"publish" forState:UIControlStateNormal];
117 | [_publishButton addTarget:self action:@selector(doPublishButtonAction) forControlEvents:UIControlEventTouchUpInside];
118 | [self.view addSubview:_publishButton];
119 |
120 | CGFloat x = CGRectGetMinX(_connectButton.frame);
121 | CGFloat y = CGRectGetMaxY(_pingReqButton.frame) + 20;
122 | CGFloat width = CGRectGetWidth(self.view.frame) - x - x;
123 | CGFloat height = CGRectGetHeight(self.view.frame) - y - 60;
124 | _receivedTextView = [[UITextView alloc] init];
125 | _receivedTextView.frame = CGRectMake(x, y, width, height);
126 | _receivedTextView.layer.borderColor = [UIColor blackColor].CGColor;
127 | _receivedTextView.layer.borderWidth = 0.5f;
128 | _receivedTextView.layer.masksToBounds = YES;
129 | _receivedTextView.font = [UIFont systemFontOfSize:20];
130 | _receivedTextView.text = @"MQTT Log:\n";
131 | [self.view addSubview:_receivedTextView];
132 | }
133 |
134 | - (void)doConnectButtonAction
135 | {
136 | NSString *host = _hostTextField.text.length > 5 ? _hostTextField.text : @"127.0.0.1";
137 | int port = _portTextField.text.length > 1 ? [_portTextField.text intValue] : 1883;
138 | [self.mqttClient startWithHost:host port:port];
139 | }
140 |
141 | - (void)doSubscribeButtonAction
142 | {
143 | NSString *topic = @"/MQTT/Messanger/#";
144 | [self showCommand:@"Subscribe" log:topic];
145 |
146 | //finance/stock/# finance/sotkc/ibm/+
147 | RHMQTTSubscribe *req = [RHMQTTSubscribe subscribeWithMessageId:3 topic:topic qos:1];
148 | [self.mqttClient asyncSendPacket:req];
149 | }
150 |
151 | - (void)doPingButtonAction
152 | {
153 | [self showCommand:@"ping" log:@""];
154 |
155 | RHMQTTPingReq *req = [[RHMQTTPingReq alloc] init];
156 | [self.mqttClient asyncSendPacket:req];
157 | }
158 |
159 | - (void)doDisconnectButtonAction
160 | {
161 | [self showCommand:@"Disconnect" log:@""];
162 |
163 | RHMQTTDisconnect *req = [[RHMQTTDisconnect alloc] init];
164 | [self.mqttClient asyncSendPacket:req];
165 | }
166 |
167 | - (void)doUnsubscribeButtonAction
168 | {
169 | NSString *topic = @"/MQTT/Messanger/#";
170 | [self showCommand:@"Unsubscribe" log:topic];
171 |
172 | RHMQTTUnsubscribe *req = [RHMQTTUnsubscribe unsubscribeWithMessageId:22 topic:topic];
173 | [self.mqttClient asyncSendPacket:req];
174 | }
175 |
176 | - (void)doPublishButtonAction
177 | {
178 | NSString *topic = @"/MQTT/Messanger/rh";
179 | [self showCommand:@"Publish" log:topic];
180 |
181 | RHMQTTPublish *req = [[RHMQTTPublish alloc] init];
182 | req.fixedHeader.qos = RHMQTTQosLevelAtLeastOnce;//RHMQTTQosLevelExactlyOnce
183 | req.variableHeader.topic = topic;
184 | req.variableHeader.messageId = 99;
185 | req.payload.message = [@"test publish" dataUsingEncoding:NSUTF8StringEncoding];
186 | [self.mqttClient asyncSendPacket:req];
187 | }
188 |
189 | - (void)showCommand:(NSString *)command log:(NSString *)log
190 | {
191 | NSMutableString *logStr = [NSMutableString string];
192 | [logStr appendFormat:@"%@\n", _receivedTextView.text];
193 | [logStr appendFormat:@"%@: %@\n", command, log];
194 | _receivedTextView.text = logStr;
195 | }
196 |
197 | #pragma mark - RHMQTTClientDelegate
198 |
199 | - (void)mqttConnected:(RHMQTTClient *)mqtt host:(NSString *)host port:(int)port
200 | {
201 | _connectButton.hidden = YES;
202 |
203 | NSString *topic = [NSString stringWithFormat:@"%@:%@", host, @(port)];
204 | [self showCommand:@"Connected" log:topic];
205 | }
206 |
207 | - (void)mqttDisconnect:(RHMQTTClient *)mqtt error:(NSError *)error
208 | {
209 | _connectButton.hidden = NO;
210 |
211 | NSString *topic = [NSString stringWithFormat:@"%@", error];
212 | [self showCommand:@"Disconnect" log:topic];
213 | }
214 |
215 | - (void)mqtt:(RHMQTTClient *)mqtt received:(RHMQTTPacket *)packet
216 | {
217 | RHMQTTPacket *mqttPacket = (RHMQTTPacket *)packet;
218 | RHSocketLog(@"[RHMQTT] mqttPacket object: %@", [mqttPacket object]);
219 |
220 | NSData *buffer = mqttPacket.object;
221 | NSData *remainingData = mqttPacket.remainingLengthData;
222 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] initWithData:remainingData];
223 | RHMQTTFixedHeader *fixedHeader = mqttPacket.fixedHeader;
224 | switch (fixedHeader.type) {
225 | case RHMQTTMessageTypeConnAck:
226 | {
227 | RHSocketLog(@"RHMQTTMessageTypeConnAck: %d", fixedHeader.type);
228 | NSString *topic = [RHSocketUtils hexStringFromData:buffer];
229 | [self showCommand:@"ReceivedData ConnAck" log:topic];
230 | }
231 | break;
232 | case RHMQTTMessageTypePublish: {
233 | RHSocketLog(@"RHMQTTMessageTypePublish: %d", fixedHeader.type);
234 | NSString *topic = [RHSocketUtils hexStringFromData:buffer];
235 | [self showCommand:@"ReceivedData Publish" log:topic];
236 | }
237 | break;
238 | case RHMQTTMessageTypePubAck: {
239 | //qos = RHMQTTQosLevelAtLeastOnce
240 | RHSocketLog(@"RHMQTTMessageTypePubAck: %d", fixedHeader.type);
241 | NSString *topic = [RHSocketUtils hexStringFromData:buffer];
242 | [self showCommand:@"ReceivedData PubAck" log:topic];
243 |
244 | }
245 | break;
246 | case RHMQTTMessageTypePubRec: {
247 | //qos = RHMQTTQosLevelExactlyOnce
248 | RHSocketLog(@"RHMQTTMessageTypePubRec: %d", fixedHeader.type);
249 | NSString *topic = [RHSocketUtils hexStringFromData:buffer];
250 | [self showCommand:@"ReceivedData PubRec" log:topic];
251 |
252 | }
253 | break;
254 | case RHMQTTMessageTypePubRel: {
255 | RHSocketLog(@"RHMQTTMessageTypePubRel: %d", fixedHeader.type);
256 | NSString *topic = [RHSocketUtils hexStringFromData:buffer];
257 | [self showCommand:@"ReceivedData PubRel" log:topic];
258 | }
259 | break;
260 | case RHMQTTMessageTypePubComp: {
261 | RHSocketLog(@"RHMQTTMessageTypePubComp: %d", fixedHeader.type);
262 | NSString *topic = [RHSocketUtils hexStringFromData:buffer];
263 | [self showCommand:@"ReceivedData PubComp" log:topic];
264 | }
265 | break;
266 | case RHMQTTMessageTypeSubAck:
267 | {
268 | RHSocketLog(@"RHMQTTMessageTypeSubAck: %d", fixedHeader.type);
269 | NSString *topic = [RHSocketUtils hexStringFromData:buffer];
270 | [self showCommand:@"ReceivedData SubAck" log:topic];
271 |
272 | }
273 | break;
274 | case RHMQTTMessageTypePingResp:
275 | {
276 | RHSocketLog(@"RHMQTTMessageTypePingResp: %d", fixedHeader.type);
277 | NSString *topic = [RHSocketUtils hexStringFromData:buffer];
278 | [self showCommand:@"ReceivedData PingResp" log:topic];
279 | }
280 | break;
281 |
282 | default:
283 | break;
284 | }
285 | }
286 |
287 | - (void)mqtt:(RHMQTTClient *)mqtt publish:(RHMQTTPublish *)publish
288 | {
289 | NSString *topic = [[NSString alloc] initWithData:publish.payload.message encoding:NSUTF8StringEncoding];
290 | [self showCommand:@"Received publish message" log:topic];
291 | }
292 |
293 | @end
294 |
--------------------------------------------------------------------------------
/RHMQTTKit/RHMQTTPacket.m:
--------------------------------------------------------------------------------
1 | //
2 | // RHMQTTPacket.m
3 | // Pods
4 | //
5 | // Created by ruhong zhu on 2021/8/21.
6 | //
7 |
8 | #import "RHMQTTPacket.h"
9 |
10 | // ------------------------------------------
11 |
12 | #pragma mark - RHMQTTFixedHeader
13 |
14 | @implementation RHMQTTFixedHeader
15 |
16 | - (instancetype)initWithByte:(UInt8)byte
17 | {
18 | if (self = [super init]) {
19 | UInt8 header = byte;
20 | _retainFlag = header & 0x01;
21 | _qos = (header & 0x06) >> 1;
22 | _dupFlag = (header & 0x08) >> 3;
23 | _type = (header & 0xf0) >> 4;
24 | }
25 | return self;
26 | }
27 |
28 | @end
29 |
30 | // ------------------------------------------
31 |
32 | #pragma mark - RHMQTTVariableHeader
33 |
34 | @implementation RHMQTTVariableHeader
35 |
36 | @synthesize object = _object;
37 |
38 | - (instancetype)init
39 | {
40 | if (self = [super init]) {
41 | _name = @"MQIsdp";
42 | _version = 3;
43 | _connectFlags = 0x02;
44 | _keepAlive = 60;
45 | }
46 | return self;
47 | }
48 |
49 | - (instancetype)initWithObject:(id)aObject
50 | {
51 | if (self = [super init]) {
52 | _object = aObject;
53 | }
54 | return self;
55 | }
56 |
57 | @end
58 |
59 | // ------------------------------------------
60 |
61 | #pragma mark - RHMQTTTopic
62 |
63 | @implementation RHMQTTTopic
64 |
65 | - (instancetype)init
66 | {
67 | if (self = [super init]) {
68 | //
69 | }
70 | return self;
71 | }
72 |
73 | @end
74 |
75 | // ------------------------------------------
76 |
77 | #pragma mark - RHMQTTPayload
78 |
79 | @implementation RHMQTTPayload
80 |
81 | - (instancetype)init
82 | {
83 | if (self = [super init]) {
84 | _clientId = @"zrh";
85 | }
86 | return self;
87 | }
88 |
89 | @end
90 |
91 | // ------------------------------------------
92 |
93 | #pragma mark - RHMQTTPacket
94 |
95 | @implementation RHMQTTPacket
96 |
97 | - (instancetype)init
98 | {
99 | if (self = [super init]) {
100 | _fixedHeader = [[RHMQTTFixedHeader alloc] init];
101 | _variableHeader = [[RHMQTTVariableHeader alloc] init];
102 | _payload = [[RHMQTTPayload alloc] init];
103 | }
104 | return self;
105 | }
106 |
107 | - (NSData *)dataWithFixedHeader
108 | {
109 | NSMutableData *buffer = [[NSMutableData alloc] init];
110 | UInt8 header = self.fixedHeader.type << 4;
111 | header |= self.fixedHeader.dupFlag ? 0x08 : 0x00;
112 | header |= self.fixedHeader.qos << 1;
113 | header |= self.fixedHeader.retainFlag ? 0x01 : 0x00;
114 | [buffer appendBytes:&header length:1];
115 | return buffer;
116 | }
117 |
118 | - (NSData *)dataWithVariableHeader
119 | {
120 | return nil;
121 | }
122 |
123 | - (NSData *)dataWithPayload
124 | {
125 | return nil;
126 | }
127 |
128 | - (NSData *)data
129 | {
130 | NSMutableData *buffer = [[NSMutableData alloc] init];
131 | [buffer appendData:[self dataWithFixedHeader]];
132 | return buffer;
133 | }
134 |
135 | @end
136 |
137 | // ------------------------------------------
138 |
139 | #pragma mark - RHMQTTConnect
140 |
141 | @implementation RHMQTTConnect
142 |
143 | + (RHMQTTConnect *)connectWithClientId:(NSString *)clientId username:(NSString *)username password:(NSString *)password keepAlive:(UInt16)keepAlive cleanSession:(BOOL)cleanSession
144 | {
145 | RHMQTTConnect *connect = [[RHMQTTConnect alloc] init];
146 | connect.variableHeader.keepAlive = keepAlive;
147 | connect.variableHeader.connectFlags = cleanSession ? 0x02 : 0x00;
148 | connect.payload.clientId = clientId;
149 | connect.payload.username = username;
150 | connect.payload.password = password;
151 | if (username.length > 0) {
152 | connect.variableHeader.connectFlags = connect.variableHeader.connectFlags | 0x80;
153 | }
154 | if (password.length > 0) {
155 | connect.variableHeader.connectFlags = connect.variableHeader.connectFlags | 0x40;
156 | }
157 | return connect;
158 | }
159 |
160 | - (instancetype)init
161 | {
162 | if (self = [super init]) {
163 | self.fixedHeader.type = RHMQTTMessageTypeConnect;
164 | }
165 | return self;
166 | }
167 |
168 | - (NSData *)dataWithVariableHeader
169 | {
170 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
171 | [byteBuffer writeInt16:self.variableHeader.name.length endianSwap:YES];
172 | [byteBuffer writeString:self.variableHeader.name];
173 | [byteBuffer writeInt8:self.variableHeader.version];
174 | [byteBuffer writeInt8:self.variableHeader.connectFlags];
175 | [byteBuffer writeInt16:self.variableHeader.keepAlive endianSwap:YES];
176 | return [byteBuffer data];
177 | }
178 |
179 | - (NSData *)dataWithPayload
180 | {
181 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
182 |
183 | [byteBuffer writeInt16:self.payload.clientId.length endianSwap:YES];
184 | [byteBuffer writeString:self.payload.clientId];
185 |
186 | if (self.payload.username.length > 0) {
187 | [byteBuffer writeInt16:self.payload.username.length endianSwap:YES];
188 | [byteBuffer writeString:self.payload.username];
189 | }
190 |
191 | if (self.payload.password.length > 0) {
192 | [byteBuffer writeInt16:self.payload.password.length endianSwap:YES];
193 | [byteBuffer writeString:self.payload.password];
194 | }
195 |
196 | return [byteBuffer data];
197 | }
198 |
199 | - (NSData *)data
200 | {
201 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
202 | [byteBuffer writeData:[self dataWithFixedHeader]];
203 |
204 | NSData *variableHeaderData = [self dataWithVariableHeader];
205 | NSData *payloadData = [self dataWithPayload];
206 |
207 | //remaining length
208 | NSUInteger length = variableHeaderData.length + payloadData.length;
209 | [byteBuffer writeData:[RHSocketUtils dataWithRawVarint32:length]];
210 | [byteBuffer writeData:variableHeaderData];
211 | [byteBuffer writeData:payloadData];
212 |
213 | return [byteBuffer data];
214 | }
215 |
216 | @end
217 |
218 | // ------------------------------------------
219 |
220 | #pragma mark - RHMQTTPublish
221 |
222 | @implementation RHMQTTPublish
223 |
224 | - (instancetype)init
225 | {
226 | if (self = [super init]) {
227 | self.fixedHeader.type = RHMQTTMessageTypePublish;
228 | self.fixedHeader.qos = RHMQTTQosLevelAtLeastOnce;
229 | }
230 | return self;
231 | }
232 |
233 | - (NSData *)dataWithVariableHeader
234 | {
235 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
236 | [byteBuffer writeInt16:self.variableHeader.topic.length endianSwap:YES];
237 | [byteBuffer writeString:self.variableHeader.topic];
238 | [byteBuffer writeInt16:self.variableHeader.messageId endianSwap:YES];
239 | return [byteBuffer data];
240 | }
241 |
242 | - (NSData *)dataWithPayload
243 | {
244 | NSMutableData *buffer = [[NSMutableData alloc] init];
245 |
246 | if (self.payload.message.length > 0) {
247 | [buffer appendData:self.payload.message];
248 | }//if
249 |
250 | return buffer;
251 | }
252 |
253 | - (NSData *)data
254 | {
255 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
256 | [byteBuffer writeData:[self dataWithFixedHeader]];
257 |
258 | NSData *variableHeaderData = [self dataWithVariableHeader];
259 | NSData *payloadData = [self dataWithPayload];
260 |
261 | //remaining length
262 | NSUInteger length = variableHeaderData.length + payloadData.length;
263 | [byteBuffer writeData:[RHSocketUtils dataWithRawVarint32:length]];
264 | [byteBuffer writeData:variableHeaderData];
265 | [byteBuffer writeData:payloadData];
266 |
267 | return [byteBuffer data];
268 | }
269 |
270 | @end
271 |
272 | @implementation RHMQTTPublishAck
273 |
274 | - (instancetype)initWithMessageId:(int16_t)msgId
275 | {
276 | if (self = [super init]) {
277 | self.fixedHeader.type = RHMQTTMessageTypePubAck;
278 | self.variableHeader.messageId = msgId;
279 | }
280 | return self;
281 | }
282 |
283 | - (NSData *)data
284 | {
285 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
286 | [byteBuffer writeData:[self dataWithFixedHeader]];
287 | [byteBuffer writeInt8:2];
288 | [byteBuffer writeInt16:self.variableHeader.messageId endianSwap:YES];
289 | return [byteBuffer data];
290 | }
291 |
292 | @end
293 |
294 | @implementation RHMQTTPublishRec
295 |
296 | - (instancetype)initWithMessageId:(int16_t)msgId
297 | {
298 | if (self = [super init]) {
299 | self.fixedHeader.type = RHMQTTMessageTypePubRec;
300 | self.variableHeader.messageId = msgId;
301 | }
302 | return self;
303 | }
304 |
305 | - (NSData *)data
306 | {
307 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
308 | [byteBuffer writeData:[self dataWithFixedHeader]];
309 | [byteBuffer writeInt8:2];
310 | [byteBuffer writeInt16:self.variableHeader.messageId endianSwap:YES];
311 | return [byteBuffer data];
312 | }
313 |
314 | @end
315 |
316 | @implementation RHMQTTPublishRel
317 |
318 | - (instancetype)initWithMessageId:(int16_t)msgId
319 | {
320 | if (self = [super init]) {
321 | self.fixedHeader.type = RHMQTTMessageTypePubRel;
322 | self.variableHeader.messageId = msgId;
323 | }
324 | return self;
325 | }
326 |
327 | - (NSData *)data
328 | {
329 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
330 | [byteBuffer writeData:[self dataWithFixedHeader]];
331 | [byteBuffer writeInt8:2];
332 | [byteBuffer writeInt16:self.variableHeader.messageId endianSwap:YES];
333 | return [byteBuffer data];
334 | }
335 |
336 | @end
337 |
338 | @implementation RHMQTTPublishComp
339 |
340 | - (instancetype)initWithMessageId:(int16_t)msgId
341 | {
342 | if (self = [super init]) {
343 | self.fixedHeader.type = RHMQTTMessageTypePubComp;
344 | self.variableHeader.messageId = msgId;
345 | }
346 | return self;
347 | }
348 |
349 | - (NSData *)data
350 | {
351 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
352 | [byteBuffer writeData:[self dataWithFixedHeader]];
353 | [byteBuffer writeInt8:2];
354 | [byteBuffer writeInt16:self.variableHeader.messageId endianSwap:YES];
355 | return [byteBuffer data];
356 | }
357 |
358 | @end
359 |
360 | // ------------------------------------------
361 |
362 | #pragma mark - RHMQTTSubscribe
363 |
364 | @implementation RHMQTTSubscribe
365 |
366 | + (RHMQTTSubscribe *)subscribeWithMessageId:(UInt16)msgId topic:(NSString *)topic qos:(RHMQTTQosLevel)qos
367 | {
368 | RHMQTTTopic *payloadTopic = [[RHMQTTTopic alloc] init];
369 | payloadTopic.topic = topic;
370 | payloadTopic.qos = qos;
371 |
372 | RHMQTTSubscribe *subscribe = [[RHMQTTSubscribe alloc] init];
373 | subscribe.fixedHeader.qos = RHMQTTQosLevelAtLeastOnce;
374 | subscribe.variableHeader.messageId = msgId;
375 | subscribe.payload.topics = @[payloadTopic];
376 | return subscribe;
377 | }
378 |
379 | - (instancetype)init
380 | {
381 | if (self = [super init]) {
382 | self.fixedHeader.type = RHMQTTMessageTypeSubscribe;
383 | }
384 | return self;
385 | }
386 |
387 | - (NSData *)dataWithVariableHeader
388 | {
389 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
390 | [byteBuffer writeInt16:self.variableHeader.messageId endianSwap:YES];
391 | return [byteBuffer data];
392 | }
393 |
394 | - (NSData *)dataWithPayload
395 | {
396 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
397 | [self.payload.topics enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
398 | RHMQTTTopic *temp = obj;
399 | [byteBuffer writeInt16:temp.topic.length endianSwap:YES];
400 | [byteBuffer writeString:temp.topic];
401 | [byteBuffer writeInt8:temp.qos];
402 | }];
403 | return [byteBuffer data];
404 | }
405 |
406 | - (NSData *)data
407 | {
408 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
409 | [byteBuffer writeData:[self dataWithFixedHeader]];
410 |
411 | NSData *variableHeaderData = [self dataWithVariableHeader];
412 | NSData *payloadData = [self dataWithPayload];
413 |
414 | //remaining length
415 | NSUInteger length = variableHeaderData.length + payloadData.length;
416 | [byteBuffer writeData:[RHSocketUtils dataWithRawVarint32:length]];
417 | [byteBuffer writeData:variableHeaderData];
418 | [byteBuffer writeData:payloadData];
419 |
420 | return [byteBuffer data];
421 | }
422 |
423 | @end
424 |
425 | // ------------------------------------------
426 |
427 | #pragma mark - RHMQTTUnsubscribe
428 |
429 | @implementation RHMQTTUnsubscribe
430 |
431 | + (RHMQTTUnsubscribe *)unsubscribeWithMessageId:(UInt16)msgId topic:(NSString *)topic
432 | {
433 | RHMQTTTopic *payloadTopic = [[RHMQTTTopic alloc] init];
434 | payloadTopic.topic = topic;
435 |
436 | RHMQTTUnsubscribe *unsubscribe = [[RHMQTTUnsubscribe alloc] init];
437 | unsubscribe.fixedHeader.qos = RHMQTTQosLevelAtLeastOnce;
438 | unsubscribe.variableHeader.messageId = msgId;
439 | unsubscribe.payload.topics = @[payloadTopic];
440 | return unsubscribe;
441 | }
442 |
443 | - (instancetype)init
444 | {
445 | if (self = [super init]) {
446 | self.fixedHeader.type = RHMQTTMessageTypeUnsubscribe;
447 | }
448 | return self;
449 | }
450 |
451 | - (NSData *)dataWithVariableHeader
452 | {
453 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
454 | [byteBuffer writeInt16:self.variableHeader.messageId endianSwap:YES];
455 | return [byteBuffer data];
456 | }
457 |
458 | - (NSData *)dataWithPayload
459 | {
460 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
461 | [self.payload.topics enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
462 | RHMQTTTopic *temp = obj;
463 | [byteBuffer writeInt16:temp.topic.length endianSwap:YES];
464 | [byteBuffer writeString:temp.topic];
465 | }];
466 | return [byteBuffer data];
467 | }
468 |
469 | - (NSData *)data
470 | {
471 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
472 | [byteBuffer writeData:[self dataWithFixedHeader]];
473 |
474 | NSData *variableHeaderData = [self dataWithVariableHeader];
475 | NSData *payloadData = [self dataWithPayload];
476 |
477 | //remaining length
478 | NSUInteger length = variableHeaderData.length + payloadData.length;
479 | [byteBuffer writeData:[RHSocketUtils dataWithRawVarint32:length]];
480 | [byteBuffer writeData:variableHeaderData];
481 | [byteBuffer writeData:payloadData];
482 |
483 | return [byteBuffer data];
484 | }
485 |
486 | @end
487 |
488 | // ------------------------------------------
489 |
490 | #pragma mark - RHMQTTPingReq
491 |
492 | @implementation RHMQTTPingReq
493 |
494 | - (instancetype)init
495 | {
496 | if (self = [super init]) {
497 | self.fixedHeader.type = RHMQTTMessageTypePingReq;
498 | }
499 | return self;
500 | }
501 |
502 | - (NSData *)data
503 | {
504 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
505 | [byteBuffer writeData:[self dataWithFixedHeader]];
506 | [byteBuffer writeInt8:0];
507 | return [byteBuffer data];
508 | }
509 |
510 | @end
511 |
512 | // ------------------------------------------
513 |
514 | #pragma mark - RHMQTTDisconnect
515 |
516 | @implementation RHMQTTDisconnect
517 |
518 | - (instancetype)init
519 | {
520 | if (self = [super init]) {
521 | self.fixedHeader.type = RHMQTTMessageTypeDisconnect;
522 | }
523 | return self;
524 | }
525 |
526 | - (NSData *)data
527 | {
528 | RHSocketByteBuf *byteBuffer = [[RHSocketByteBuf alloc] init];
529 | [byteBuffer writeData:[self dataWithFixedHeader]];
530 | [byteBuffer writeInt8:0];
531 | return [byteBuffer data];
532 | }
533 |
534 | @end
535 |
--------------------------------------------------------------------------------
/RHMQTTKitDemo/RHMQTTKitDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 32EA50CFF73D221668F11622 /* libPods-RHMQTTKitDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FE9A318ED4E03C7D18029D82 /* libPods-RHMQTTKitDemo.a */; };
11 | 83C446501BF9FAE200065AAC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C4464F1BF9FAE200065AAC /* main.m */; };
12 | 83C446531BF9FAE200065AAC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C446521BF9FAE200065AAC /* AppDelegate.m */; };
13 | 83C446561BF9FAE200065AAC /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C446551BF9FAE200065AAC /* ViewController.m */; };
14 | 83C446591BF9FAE200065AAC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 83C446571BF9FAE200065AAC /* Main.storyboard */; };
15 | 83C4465B1BF9FAE200065AAC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 83C4465A1BF9FAE200065AAC /* Assets.xcassets */; };
16 | 83C4465E1BF9FAE200065AAC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 83C4465C1BF9FAE200065AAC /* LaunchScreen.storyboard */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXFileReference section */
20 | 42EA541C4FD571A317E957D3 /* Pods-RHMQTTKitDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RHMQTTKitDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RHMQTTKitDemo/Pods-RHMQTTKitDemo.debug.xcconfig"; sourceTree = ""; };
21 | 69311776CAE2A73C96A62450 /* Pods-RHMQTTKitDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RHMQTTKitDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-RHMQTTKitDemo/Pods-RHMQTTKitDemo.release.xcconfig"; sourceTree = ""; };
22 | 83C4464B1BF9FAE200065AAC /* RHMQTTKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RHMQTTKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
23 | 83C4464F1BF9FAE200065AAC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
24 | 83C446511BF9FAE200065AAC /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
25 | 83C446521BF9FAE200065AAC /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
26 | 83C446541BF9FAE200065AAC /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
27 | 83C446551BF9FAE200065AAC /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
28 | 83C446581BF9FAE200065AAC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
29 | 83C4465A1BF9FAE200065AAC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
30 | 83C4465D1BF9FAE200065AAC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
31 | 83C4465F1BF9FAE200065AAC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
32 | FE9A318ED4E03C7D18029D82 /* libPods-RHMQTTKitDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RHMQTTKitDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; };
33 | /* End PBXFileReference section */
34 |
35 | /* Begin PBXFrameworksBuildPhase section */
36 | 83C446481BF9FAE200065AAC /* Frameworks */ = {
37 | isa = PBXFrameworksBuildPhase;
38 | buildActionMask = 2147483647;
39 | files = (
40 | 32EA50CFF73D221668F11622 /* libPods-RHMQTTKitDemo.a in Frameworks */,
41 | );
42 | runOnlyForDeploymentPostprocessing = 0;
43 | };
44 | /* End PBXFrameworksBuildPhase section */
45 |
46 | /* Begin PBXGroup section */
47 | 3D1D7B80482838CD42724940 /* Frameworks */ = {
48 | isa = PBXGroup;
49 | children = (
50 | FE9A318ED4E03C7D18029D82 /* libPods-RHMQTTKitDemo.a */,
51 | );
52 | name = Frameworks;
53 | sourceTree = "";
54 | };
55 | 83C446421BF9FAE200065AAC = {
56 | isa = PBXGroup;
57 | children = (
58 | 83C4464D1BF9FAE200065AAC /* RHMQTTKitDemo */,
59 | 83C4464C1BF9FAE200065AAC /* Products */,
60 | B942A8773E406570646AF5C7 /* Pods */,
61 | 3D1D7B80482838CD42724940 /* Frameworks */,
62 | );
63 | sourceTree = "";
64 | };
65 | 83C4464C1BF9FAE200065AAC /* Products */ = {
66 | isa = PBXGroup;
67 | children = (
68 | 83C4464B1BF9FAE200065AAC /* RHMQTTKitDemo.app */,
69 | );
70 | name = Products;
71 | sourceTree = "";
72 | };
73 | 83C4464D1BF9FAE200065AAC /* RHMQTTKitDemo */ = {
74 | isa = PBXGroup;
75 | children = (
76 | 83C446511BF9FAE200065AAC /* AppDelegate.h */,
77 | 83C446521BF9FAE200065AAC /* AppDelegate.m */,
78 | 83C446541BF9FAE200065AAC /* ViewController.h */,
79 | 83C446551BF9FAE200065AAC /* ViewController.m */,
80 | 83C446571BF9FAE200065AAC /* Main.storyboard */,
81 | 83C4465A1BF9FAE200065AAC /* Assets.xcassets */,
82 | 83C4465C1BF9FAE200065AAC /* LaunchScreen.storyboard */,
83 | 83C4465F1BF9FAE200065AAC /* Info.plist */,
84 | 83C4464E1BF9FAE200065AAC /* Supporting Files */,
85 | );
86 | path = RHMQTTKitDemo;
87 | sourceTree = "";
88 | };
89 | 83C4464E1BF9FAE200065AAC /* Supporting Files */ = {
90 | isa = PBXGroup;
91 | children = (
92 | 83C4464F1BF9FAE200065AAC /* main.m */,
93 | );
94 | name = "Supporting Files";
95 | sourceTree = "";
96 | };
97 | B942A8773E406570646AF5C7 /* Pods */ = {
98 | isa = PBXGroup;
99 | children = (
100 | 42EA541C4FD571A317E957D3 /* Pods-RHMQTTKitDemo.debug.xcconfig */,
101 | 69311776CAE2A73C96A62450 /* Pods-RHMQTTKitDemo.release.xcconfig */,
102 | );
103 | name = Pods;
104 | sourceTree = "";
105 | };
106 | /* End PBXGroup section */
107 |
108 | /* Begin PBXNativeTarget section */
109 | 83C4464A1BF9FAE200065AAC /* RHMQTTKitDemo */ = {
110 | isa = PBXNativeTarget;
111 | buildConfigurationList = 83C446621BF9FAE200065AAC /* Build configuration list for PBXNativeTarget "RHMQTTKitDemo" */;
112 | buildPhases = (
113 | 54DA4D34A04F5F00C988D100 /* [CP] Check Pods Manifest.lock */,
114 | 83C446471BF9FAE200065AAC /* Sources */,
115 | 83C446481BF9FAE200065AAC /* Frameworks */,
116 | 83C446491BF9FAE200065AAC /* Resources */,
117 | );
118 | buildRules = (
119 | );
120 | dependencies = (
121 | );
122 | name = RHMQTTKitDemo;
123 | productName = RHMQTTKitDemo;
124 | productReference = 83C4464B1BF9FAE200065AAC /* RHMQTTKitDemo.app */;
125 | productType = "com.apple.product-type.application";
126 | };
127 | /* End PBXNativeTarget section */
128 |
129 | /* Begin PBXProject section */
130 | 83C446431BF9FAE200065AAC /* Project object */ = {
131 | isa = PBXProject;
132 | attributes = {
133 | LastUpgradeCheck = 0720;
134 | ORGANIZATIONNAME = zhuruhong;
135 | TargetAttributes = {
136 | 83C4464A1BF9FAE200065AAC = {
137 | CreatedOnToolsVersion = 7.2;
138 | DevelopmentTeam = 764XM498XX;
139 | };
140 | };
141 | };
142 | buildConfigurationList = 83C446461BF9FAE200065AAC /* Build configuration list for PBXProject "RHMQTTKitDemo" */;
143 | compatibilityVersion = "Xcode 3.2";
144 | developmentRegion = English;
145 | hasScannedForEncodings = 0;
146 | knownRegions = (
147 | English,
148 | en,
149 | Base,
150 | );
151 | mainGroup = 83C446421BF9FAE200065AAC;
152 | productRefGroup = 83C4464C1BF9FAE200065AAC /* Products */;
153 | projectDirPath = "";
154 | projectRoot = "";
155 | targets = (
156 | 83C4464A1BF9FAE200065AAC /* RHMQTTKitDemo */,
157 | );
158 | };
159 | /* End PBXProject section */
160 |
161 | /* Begin PBXResourcesBuildPhase section */
162 | 83C446491BF9FAE200065AAC /* Resources */ = {
163 | isa = PBXResourcesBuildPhase;
164 | buildActionMask = 2147483647;
165 | files = (
166 | 83C4465E1BF9FAE200065AAC /* LaunchScreen.storyboard in Resources */,
167 | 83C4465B1BF9FAE200065AAC /* Assets.xcassets in Resources */,
168 | 83C446591BF9FAE200065AAC /* Main.storyboard in Resources */,
169 | );
170 | runOnlyForDeploymentPostprocessing = 0;
171 | };
172 | /* End PBXResourcesBuildPhase section */
173 |
174 | /* Begin PBXShellScriptBuildPhase section */
175 | 54DA4D34A04F5F00C988D100 /* [CP] Check Pods Manifest.lock */ = {
176 | isa = PBXShellScriptBuildPhase;
177 | buildActionMask = 2147483647;
178 | files = (
179 | );
180 | inputPaths = (
181 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
182 | "${PODS_ROOT}/Manifest.lock",
183 | );
184 | name = "[CP] Check Pods Manifest.lock";
185 | outputPaths = (
186 | "$(DERIVED_FILE_DIR)/Pods-RHMQTTKitDemo-checkManifestLockResult.txt",
187 | );
188 | runOnlyForDeploymentPostprocessing = 0;
189 | shellPath = /bin/sh;
190 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
191 | showEnvVarsInLog = 0;
192 | };
193 | /* End PBXShellScriptBuildPhase section */
194 |
195 | /* Begin PBXSourcesBuildPhase section */
196 | 83C446471BF9FAE200065AAC /* Sources */ = {
197 | isa = PBXSourcesBuildPhase;
198 | buildActionMask = 2147483647;
199 | files = (
200 | 83C446561BF9FAE200065AAC /* ViewController.m in Sources */,
201 | 83C446531BF9FAE200065AAC /* AppDelegate.m in Sources */,
202 | 83C446501BF9FAE200065AAC /* main.m in Sources */,
203 | );
204 | runOnlyForDeploymentPostprocessing = 0;
205 | };
206 | /* End PBXSourcesBuildPhase section */
207 |
208 | /* Begin PBXVariantGroup section */
209 | 83C446571BF9FAE200065AAC /* Main.storyboard */ = {
210 | isa = PBXVariantGroup;
211 | children = (
212 | 83C446581BF9FAE200065AAC /* Base */,
213 | );
214 | name = Main.storyboard;
215 | sourceTree = "";
216 | };
217 | 83C4465C1BF9FAE200065AAC /* LaunchScreen.storyboard */ = {
218 | isa = PBXVariantGroup;
219 | children = (
220 | 83C4465D1BF9FAE200065AAC /* Base */,
221 | );
222 | name = LaunchScreen.storyboard;
223 | sourceTree = "";
224 | };
225 | /* End PBXVariantGroup section */
226 |
227 | /* Begin XCBuildConfiguration section */
228 | 83C446601BF9FAE200065AAC /* Debug */ = {
229 | isa = XCBuildConfiguration;
230 | buildSettings = {
231 | ALWAYS_SEARCH_USER_PATHS = NO;
232 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
233 | CLANG_CXX_LIBRARY = "libc++";
234 | CLANG_ENABLE_MODULES = YES;
235 | CLANG_ENABLE_OBJC_ARC = YES;
236 | CLANG_WARN_BOOL_CONVERSION = YES;
237 | CLANG_WARN_CONSTANT_CONVERSION = YES;
238 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
239 | CLANG_WARN_EMPTY_BODY = YES;
240 | CLANG_WARN_ENUM_CONVERSION = YES;
241 | CLANG_WARN_INT_CONVERSION = YES;
242 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
243 | CLANG_WARN_UNREACHABLE_CODE = YES;
244 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
245 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
246 | COPY_PHASE_STRIP = NO;
247 | DEBUG_INFORMATION_FORMAT = dwarf;
248 | ENABLE_STRICT_OBJC_MSGSEND = YES;
249 | ENABLE_TESTABILITY = YES;
250 | GCC_C_LANGUAGE_STANDARD = gnu99;
251 | GCC_DYNAMIC_NO_PIC = NO;
252 | GCC_NO_COMMON_BLOCKS = YES;
253 | GCC_OPTIMIZATION_LEVEL = 0;
254 | GCC_PREPROCESSOR_DEFINITIONS = (
255 | "DEBUG=1",
256 | "$(inherited)",
257 | );
258 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
259 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
260 | GCC_WARN_UNDECLARED_SELECTOR = YES;
261 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
262 | GCC_WARN_UNUSED_FUNCTION = YES;
263 | GCC_WARN_UNUSED_VARIABLE = YES;
264 | IPHONEOS_DEPLOYMENT_TARGET = 9.2;
265 | MTL_ENABLE_DEBUG_INFO = YES;
266 | ONLY_ACTIVE_ARCH = YES;
267 | SDKROOT = iphoneos;
268 | TARGETED_DEVICE_FAMILY = "1,2";
269 | };
270 | name = Debug;
271 | };
272 | 83C446611BF9FAE200065AAC /* Release */ = {
273 | isa = XCBuildConfiguration;
274 | buildSettings = {
275 | ALWAYS_SEARCH_USER_PATHS = NO;
276 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
277 | CLANG_CXX_LIBRARY = "libc++";
278 | CLANG_ENABLE_MODULES = YES;
279 | CLANG_ENABLE_OBJC_ARC = YES;
280 | CLANG_WARN_BOOL_CONVERSION = YES;
281 | CLANG_WARN_CONSTANT_CONVERSION = YES;
282 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
283 | CLANG_WARN_EMPTY_BODY = YES;
284 | CLANG_WARN_ENUM_CONVERSION = YES;
285 | CLANG_WARN_INT_CONVERSION = YES;
286 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
287 | CLANG_WARN_UNREACHABLE_CODE = YES;
288 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
289 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
290 | COPY_PHASE_STRIP = NO;
291 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
292 | ENABLE_NS_ASSERTIONS = NO;
293 | ENABLE_STRICT_OBJC_MSGSEND = YES;
294 | GCC_C_LANGUAGE_STANDARD = gnu99;
295 | GCC_NO_COMMON_BLOCKS = YES;
296 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
297 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
298 | GCC_WARN_UNDECLARED_SELECTOR = YES;
299 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
300 | GCC_WARN_UNUSED_FUNCTION = YES;
301 | GCC_WARN_UNUSED_VARIABLE = YES;
302 | IPHONEOS_DEPLOYMENT_TARGET = 9.2;
303 | MTL_ENABLE_DEBUG_INFO = NO;
304 | SDKROOT = iphoneos;
305 | TARGETED_DEVICE_FAMILY = "1,2";
306 | VALIDATE_PRODUCT = YES;
307 | };
308 | name = Release;
309 | };
310 | 83C446631BF9FAE200065AAC /* Debug */ = {
311 | isa = XCBuildConfiguration;
312 | baseConfigurationReference = 42EA541C4FD571A317E957D3 /* Pods-RHMQTTKitDemo.debug.xcconfig */;
313 | buildSettings = {
314 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
315 | INFOPLIST_FILE = RHMQTTKitDemo/Info.plist;
316 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
317 | PRODUCT_BUNDLE_IDENTIFIER = com.RHMQTTKitDemo;
318 | PRODUCT_NAME = "$(TARGET_NAME)";
319 | };
320 | name = Debug;
321 | };
322 | 83C446641BF9FAE200065AAC /* Release */ = {
323 | isa = XCBuildConfiguration;
324 | baseConfigurationReference = 69311776CAE2A73C96A62450 /* Pods-RHMQTTKitDemo.release.xcconfig */;
325 | buildSettings = {
326 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
327 | INFOPLIST_FILE = RHMQTTKitDemo/Info.plist;
328 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
329 | PRODUCT_BUNDLE_IDENTIFIER = com.RHMQTTKitDemo;
330 | PRODUCT_NAME = "$(TARGET_NAME)";
331 | };
332 | name = Release;
333 | };
334 | /* End XCBuildConfiguration section */
335 |
336 | /* Begin XCConfigurationList section */
337 | 83C446461BF9FAE200065AAC /* Build configuration list for PBXProject "RHMQTTKitDemo" */ = {
338 | isa = XCConfigurationList;
339 | buildConfigurations = (
340 | 83C446601BF9FAE200065AAC /* Debug */,
341 | 83C446611BF9FAE200065AAC /* Release */,
342 | );
343 | defaultConfigurationIsVisible = 0;
344 | defaultConfigurationName = Release;
345 | };
346 | 83C446621BF9FAE200065AAC /* Build configuration list for PBXNativeTarget "RHMQTTKitDemo" */ = {
347 | isa = XCConfigurationList;
348 | buildConfigurations = (
349 | 83C446631BF9FAE200065AAC /* Debug */,
350 | 83C446641BF9FAE200065AAC /* Release */,
351 | );
352 | defaultConfigurationIsVisible = 0;
353 | defaultConfigurationName = Release;
354 | };
355 | /* End XCConfigurationList section */
356 | };
357 | rootObject = 83C446431BF9FAE200065AAC /* Project object */;
358 | }
359 |
--------------------------------------------------------------------------------