Skip to content

Commit ee31e98

Browse files
authored
feat(iOS): Complete websocket client. (didi#1022)
* feat(iOS): Complete QR code recognization logic * chore(iOS): Add directory structure. 1. Add DTO model. * feat(iOS): Complete websocket client.
1 parent 8308a80 commit ee31e98

30 files changed

+1179
-30
lines changed

DoraemonKit.podspec

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ Pod::Spec.new do |s|
2020
iOS各式各样的工具集合
2121
DESC
2222

23-
s.homepage = 'https://github.com/${USER_NAME}/${POD_NAME}'
23+
s.homepage = 'https://www.dokit.cn'
2424
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
25-
s.license = { :type => 'Apache License, Version 2.0', :file => 'LICENSE' }
25+
s.license = { :type => 'Apache-2.0', :file => 'LICENSE' }
2626
s.author = { 'OrangeLab' => '[email protected]' }
2727
s.source = { :git => 'https://github.com/didi/DoraemonKit.git', :tag => s.version.to_s }
2828
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
@@ -34,21 +34,32 @@ iOS各式各样的工具集合
3434
'DEFINES_MODULE' => 'YES'
3535
}
3636

37-
s.subspec 'Common' do |ss|
38-
ss.source_files = 'iOS/DoKit/Classes/Common/*.h'
37+
s.subspec 'Foundation' do |ss|
38+
ss.source_files = 'iOS/DoKit/Classes/Foundation/*.{h,c}'
39+
ss.compiler_flags = '-Wall', '-Wextra', '-Wpedantic', '-Werror', '-fvisibility=hidden'
3940
end
40-
41-
s.subspec 'Hook' do |ss|
42-
ss.dependency 'DoraemonKit/Common'
43-
ss.source_files = 'iOS/DoKit/Classes/Hook/*.{h,c}'
44-
ss.compiler_flags = '-Wall', '-Wextra', '-Wpedantic', '-Werror', '-Wno-zero-length-array', '-fvisibility=hidden'
41+
42+
s.subspec 'CoreNG' do |ss|
43+
ss.dependency 'DoraemonKit/Foundation'
44+
ss.source_files = 'iOS/DoKit/Classes/Core/**/*.{h,m}'
45+
# language-extension-token warning be used to implement Objective-C typeof().
46+
# ?: grammar
47+
ss.compiler_flags = '-Wall', '-Wextra', '-Wpedantic', '-Werror', '-Wno-language-extension-token', '-Wno-gnu-conditional-omitted-operand'
48+
ss.resource_bundle = {
49+
'DoKitResource' => [
50+
'iOS/DoKit/Assets/Assets.xcassets',
51+
'iOS/DoKit/Assets/*.xib'
52+
]
53+
}
54+
ss.dependency 'SocketRocket', '~> 0.6'
55+
ss.dependency 'Mantle', '~> 2.2'
4556
end
4657

4758
s.subspec 'EventSynthesize' do |ss|
4859
ss.source_files = 'iOS/DoKit/Classes/EventSynthesize/*.{h,m}'
4960
ss.compiler_flags = '-Wall', '-Wextra', '-Wpedantic', '-Werror', '-fvisibility=hidden', '-Wno-gnu-conditional-omitted-operand', '-Wno-pointer-arith'
5061
ss.framework = 'IOKit'
51-
ss.dependency 'DoraemonKit/Common'
62+
ss.dependency 'DoraemonKit/Foundation'
5263
end
5364

5465
s.subspec 'Core' do |ss|

iOS/Demo.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@
356356
CURRENT_PROJECT_VERSION = 1;
357357
GENERATE_INFOPLIST_FILE = YES;
358358
INFOPLIST_FILE = Demo/Info.plist;
359+
INFOPLIST_KEY_NSCameraUsageDescription = "扫码需要开启您的摄像头权限,用于识别";
359360
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
360361
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
361362
INFOPLIST_KEY_UIMainStoryboardFile = Main;
@@ -385,6 +386,7 @@
385386
CURRENT_PROJECT_VERSION = 1;
386387
GENERATE_INFOPLIST_FILE = YES;
387388
INFOPLIST_FILE = Demo/Info.plist;
389+
INFOPLIST_KEY_NSCameraUsageDescription = "扫码需要开启您的摄像头权限,用于识别";
388390
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
389391
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
390392
INFOPLIST_KEY_UIMainStoryboardFile = Main;

iOS/Demo/AppDelegate.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import UIKit
18+
import DoraemonKit
1819

1920
@main
2021
private class AppDelegate: UIResponder, UIApplicationDelegate {
@@ -23,6 +24,8 @@ private class AppDelegate: UIResponder, UIApplicationDelegate {
2324

2425
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
2526
// Override point for customization after application launch.
27+
DoKit.install(withProductId: nil)
28+
2629
return true
2730
}
2831

iOS/Demo/Base.lproj/Main.storyboard

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
</viewController>
2323
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
2424
</objects>
25-
<point key="canvasLocation" x="139" y="80"/>
25+
<point key="canvasLocation" x="137.68115942028987" y="79.6875"/>
2626
</scene>
2727
</scenes>
2828
<resources>

iOS/Demo/ViewController.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class ViewController: UIViewController {
2222
override func viewDidLoad() {
2323
super.viewDidLoad()
2424
// Do any additional setup after loading the view.
25+
DoKit.install(withProductId: nil)
2526
}
2627

2728
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
}
6+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"images" : [
3+
{
4+
"idiom" : "universal",
5+
"scale" : "1x"
6+
},
7+
{
8+
"idiom" : "universal",
9+
"scale" : "2x"
10+
},
11+
{
12+
"filename" : "[email protected]",
13+
"idiom" : "universal",
14+
"scale" : "3x"
15+
}
16+
],
17+
"info" : {
18+
"author" : "xcode",
19+
"version" : 1
20+
}
21+
}
Loading
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
3+
<device id="retina6_1" orientation="portrait" appearance="light"/>
4+
<dependencies>
5+
<deployment identifier="iOS"/>
6+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
7+
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
8+
<capability name="System colors in document resources" minToolsVersion="11.0"/>
9+
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
10+
</dependencies>
11+
<objects>
12+
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="DKTrayViewController">
13+
<connections>
14+
<outlet property="view" destination="iN0-l3-epB" id="V21-eE-Ekr"/>
15+
</connections>
16+
</placeholder>
17+
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
18+
<view contentMode="scaleToFill" id="iN0-l3-epB">
19+
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
20+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
21+
<subviews>
22+
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="EDq-pO-2FR">
23+
<rect key="frame" x="0.0" y="0.0" width="58" height="58"/>
24+
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
25+
<state key="normal" title="Button"/>
26+
<buttonConfiguration key="configuration" style="plain" image="dokit_logo" title=""/>
27+
<connections>
28+
<action selector="buttonHandler" destination="-1" eventType="touchUpInside" id="cFG-UF-Q7R"/>
29+
</connections>
30+
</button>
31+
</subviews>
32+
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
33+
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
34+
<point key="canvasLocation" x="139" y="63"/>
35+
</view>
36+
</objects>
37+
<resources>
38+
<image name="dokit_logo" width="58" height="58"/>
39+
<systemColor name="systemBackgroundColor">
40+
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
41+
</systemColor>
42+
</resources>
43+
</document>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Copyright 2017 Beijing DiDi Infinity Technology and Development Co., Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <Foundation/Foundation.h>
18+
19+
@class AVCaptureVideoPreviewLayer;
20+
21+
NS_ASSUME_NONNULL_BEGIN
22+
23+
@interface DKQRCodeScanLogic : NSObject
24+
25+
@property(nonatomic, nullable, copy) void (^completionBlock)(NSString *_Nullable decodedString);
26+
27+
- (void)startScanQRCodeWithCompletionBlock:(void (^)(AVCaptureVideoPreviewLayer *_Nullable captureVideoPreviewLayer))completionBlock;
28+
29+
@end
30+
31+
NS_ASSUME_NONNULL_END
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/**
2+
* Copyright 2017 Beijing DiDi Infinity Technology and Development Co., Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <AVFoundation/AVFoundation.h>
18+
#import "DKQRCodeScanLogic.h"
19+
20+
NS_ASSUME_NONNULL_BEGIN
21+
22+
static void requestVideoGrant(void (^completionBlock)(BOOL isGranted));
23+
24+
static inline void safeMainThread(dispatch_block_t block);
25+
26+
@interface DKQRCodeScanLogic () <AVCaptureMetadataOutputObjectsDelegate>
27+
28+
@property(nonatomic, nullable, strong) AVCaptureSession *captureSession;
29+
30+
@end
31+
32+
NS_ASSUME_NONNULL_END
33+
34+
void requestVideoGrant(void (^completionBlock)(BOOL isGranted)) {
35+
AVAuthorizationStatus authorizationStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
36+
if (authorizationStatus == AVAuthorizationStatusNotDetermined) {
37+
// Request system authority.
38+
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
39+
safeMainThread(^{
40+
completionBlock(granted);
41+
});
42+
}];
43+
} else {
44+
completionBlock(authorizationStatus == AVAuthorizationStatusAuthorized);
45+
}
46+
}
47+
48+
void safeMainThread(dispatch_block_t block) {
49+
if (!NSThread.isMainThread) {
50+
dispatch_async(dispatch_get_main_queue(), block);
51+
} else {
52+
block();
53+
}
54+
}
55+
56+
@implementation DKQRCodeScanLogic
57+
58+
- (void)startScanQRCodeWithCompletionBlock:(void (^)(AVCaptureVideoPreviewLayer *_Nullable captureVideoPreviewLayer))completionBlock {
59+
__weak typeof(self) weakSelf = self;
60+
requestVideoGrant(^(BOOL isGranted) {
61+
if (!weakSelf) {
62+
completionBlock(nil);
63+
64+
return;
65+
}
66+
typeof(weakSelf) self = weakSelf;
67+
if (!isGranted) {
68+
completionBlock(nil);
69+
} else {
70+
AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
71+
#ifndef NS_BLOCK_ASSERTIONS
72+
NSCAssert(captureDevice, @"+[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] return nil.");
73+
#endif
74+
75+
NSError *error = nil;
76+
AVCaptureDeviceInput *captureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:captureDevice error:&error];
77+
#ifndef NS_BLOCK_ASSERTIONS
78+
NSCAssert(!error && captureDeviceInput, @"AVCaptureDeviceInput creation is failed.");
79+
#endif
80+
81+
AVCaptureMetadataOutput *captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];
82+
[captureMetadataOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
83+
84+
self.captureSession = [[AVCaptureSession alloc] init];
85+
[self.captureSession beginConfiguration];
86+
#ifndef NS_BLOCK_ASSERTIONS
87+
NSCAssert([self.captureSession canAddInput:captureDeviceInput], @"-[captureSession canAddInput:captureDeviceInput] is failed.");
88+
#endif
89+
[self.captureSession addInput:captureDeviceInput];
90+
#ifndef NS_BLOCK_ASSERTIONS
91+
NSCAssert([self.captureSession canAddOutput:captureMetadataOutput], @"-[AVCaptureSession canAddOutput:captureMetadataOutput] return NO.");
92+
#endif
93+
[self.captureSession addOutput:captureMetadataOutput];
94+
if ([captureMetadataOutput.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeQRCode]) {
95+
captureMetadataOutput.metadataObjectTypes = @[AVMetadataObjectTypeQRCode];
96+
}
97+
[self.captureSession commitConfiguration];
98+
99+
AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
100+
completionBlock(captureVideoPreviewLayer);
101+
102+
[self.captureSession startRunning];
103+
}
104+
});
105+
}
106+
107+
- (void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
108+
if (metadataObjects.count > 0) {
109+
AVMetadataObject *metadataObject = metadataObjects.firstObject;
110+
if ([metadataObject.type isEqualToString:AVMetadataObjectTypeQRCode] && [metadataObject isKindOfClass:AVMetadataMachineReadableCodeObject.class]) {
111+
[self.captureSession stopRunning];
112+
AVMetadataMachineReadableCodeObject *metadataMachineReadableCodeObject = (AVMetadataMachineReadableCodeObject *) metadataObject;
113+
self.completionBlock ? self.completionBlock(metadataMachineReadableCodeObject.stringValue) : (void) nil;
114+
} else {
115+
goto exit;
116+
}
117+
} else {
118+
goto exit;
119+
}
120+
121+
return;
122+
123+
exit:
124+
self.completionBlock ? self.completionBlock(nil) : (void) nil;
125+
}
126+
127+
@end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Copyright 2017 Beijing DiDi Infinity Technology and Development Co., Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <UIKit/UIKit.h>
18+
19+
NS_ASSUME_NONNULL_BEGIN
20+
21+
typedef NS_ENUM(NSUInteger, DKQRCodeScanResult) {
22+
DKQRCodeScanResultSuccess = 0,
23+
DKQRCodeScanResultAuthorityError
24+
};
25+
26+
@interface DKQRCodeScanView : UIView
27+
28+
- (void)startScanQRCodeWithCompletionBlock:(void (^)(DKQRCodeScanResult qrCodeScanResult, NSString *_Nullable decodedString))completionBlock;
29+
30+
@end
31+
32+
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)