-
-
Notifications
You must be signed in to change notification settings - Fork 464
Adding a wrapper for iOS #337
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
92 commits
Select commit
Hold shift + click to select a range
6dd5c57
Add iOS Wrapper
parallaxe 9c7659d
Add a demo project
parallaxe cc699a8
Add SPDX-License-Identifier: Apache-2. to sources
parallaxe b79fb7a
Update copyright comments
parallaxe 7074871
ci: exclude test/samples from "Source code" release assets
axxel 71adf1d
example: install the ZXingReader and ZXingWriter
axxel 109d81e
CharacterSet: new CharacterSetFromString and deprecated CharacterSetECI
axxel e4604f6
CharacterSet: introduce name normalization like in BarcodeFormatFromS…
axxel eea9ea0
ECI: remove the need to publish ECI.h (via Content.h -> Result.h)
axxel 305ef8a
cmake: only install minimal set of header files
axxel 798673c
Add .gitignore
ChristianNorbertBraun 4de75d7
Add license comments
ChristianNorbertBraun 20cd066
Rename to ZXingCpp
ChristianNorbertBraun fe75d0f
Disable printing of written code in debug mode
ChristianNorbertBraun 24044e3
Add WriteViewController to demo project
ChristianNorbertBraun 82dceba
Use .binary() on result for reader
ChristianNorbertBraun dd9831a
Update .gitignore
ChristianNorbertBraun fd79e7a
Make demo app only work in portrait
ChristianNorbertBraun 0840db6
Set demo development team to None
ChristianNorbertBraun b43cc2b
Remove whitelines
ChristianNorbertBraun 23c2051
Add maxSymbolNumber and downscale option to hints
ChristianNorbertBraun 0c3e097
WIP: Return multiple results
ChristianNorbertBraun 32e813b
CharacterSet: replace deprecated CharsetFromName in WASM wrapper
axxel 6b7c776
DMDecoder: set missing Result::applicationIndicator
axxel 8e64c30
Result: add GS1, ISO15434 and UnknownECI ContentTypes
axxel 9aac5f6
Result: deprecate rawBytes and numBits
axxel 72e8ae1
Result: rename `binary` to `bytes`
axxel 27ef8f6
python: add the `bytes` property to the Result struct
axxel 97adf17
Result: add MergeStructuredAppendResults()
axxel e6b8558
DecoderResult: remove `text` and `symbologyIdentifier` member variables
axxel 9633b25
TextDecoder: non-printable ASCII control chars -> probably not Shift_JIS
axxel 84ec5b9
ECI: change handling of pseudo-ECIs to fix MergeStructuredAppendResults
axxel f8e4117
Result: clear position and structuredAppendIndex in merged result
axxel 69576e3
Result: add MergeStructuredAppendSequences for automatically merging
axxel d0a8b9f
example: port ZXingQtReader to Qt6
axxel 5bd41cd
cmake: revert breaking change of `INTERFACE "$<INSTALL_INTERFACE:incl…
axxel 9df7ef5
API: improve a few comments for public symbols
axxel 29388db
Result: remove applicationIndicator property (for now)
axxel f4c18c1
Result: increase API resilience against future ABI breakages (de-inli…
axxel 0a3f169
GridSampler: support multi-symbol debugging
axxel 9760a0b
QRCode: detect mirrored symbols directly from FormatInfo bits
axxel 2de9605
MicoQRCode: choose orientation with the lowest hamming distance Forma…
axxel 78f2b4e
Result: compare bytes instead of text in opoerator== (performance)
axxel ad70b40
Result: rename utf8Protocol -> utf8ECI
axxel c4748b1
GTIN: parse the `bytes()` instead of `text()` in `EanAddOn()`
axxel fdca0b6
example: don't double quote the `bytes` result in ZXingReader output
axxel 805c793
example: use new Result::utf8()
axxel 4618689
Result: add new `utf8` property (forgot in last commit)
axxel feba841
Writers: add utf8 overload for `encode()` functions
axxel 6284572
Content: cleanup text/utf8/utf16 situation and remove code duplication
axxel 48ec194
Result: #define ZX_USE_UTF8 to transition from std::wstring to std::s…
axxel 1a27185
License: add overlooked SPDX-License-Identifier in 2 files
axxel c44da70
Use .bytes() instead of .binary() on Result
ChristianNorbertBraun 7a23355
Use NSInteger for maxNumberOfSymbols
ChristianNorbertBraun 82ec9c9
Renaming rawBytes to bytes on Result
ChristianNorbertBraun 403337e
Move MicroQRCode next to QRCode in ZXIFormat
ChristianNorbertBraun d843be1
Remove errors from reader
ChristianNorbertBraun da3a9c6
Update ZXIErrors
ChristianNorbertBraun 222841c
Update demo project for new read api
ChristianNorbertBraun f6090cc
Remove whiteline
ChristianNorbertBraun ec22310
Add position to ZXIResult
ChristianNorbertBraun febea8d
Update umbrella header
ChristianNorbertBraun ba8def7
android: add `bytes` and `contentType` members to `Result`
axxel a4f359f
python: add `content_type` property in Result
axxel 2b03796
Code128: properly report contentType GS1
axxel 4122e14
Result: report (empty) NotFound results as ContentType::Text
axxel 509a4ab
test: add a few checks for contentType to black box tests
axxel 520bc12
Rename local variable to bytes
ChristianNorbertBraun 25393b5
Add correct license header
ChristianNorbertBraun 1fcd508
Merge branch 'master' into ios_wrapper
axxel d5183d1
Add StructuredAppend.h to common files again
ChristianNorbertBraun 6505e3c
Merge remote-tracking branch 'origin/master' into ios_wrapper
ChristianNorbertBraun 8fbe696
Update README.md
ChristianNorbertBraun 7f6256f
Set team to None
ChristianNorbertBraun 81d6746
Add api to work directly with CVPixelBuffer
ChristianNorbertBraun 57a223a
Update .gitignore
ChristianNorbertBraun 6ef880d
Merge remote-tracking branch 'origin/master' into ios_wrapper
ChristianNorbertBraun 399b3a0
Create build-ios.yml
ChristianNorbertBraun 2c09991
Revert "Create build-ios.yml"
ChristianNorbertBraun be84df6
Add ci for iOS wrapper
ChristianNorbertBraun 104b4e5
Merge remote-tracking branch 'origin/master' into ios_wrapper
ChristianNorbertBraun 42fa8ea
Remove fmtlib-src
ChristianNorbertBraun 19556e3
Use readImageView to remove code duplication
ChristianNorbertBraun 48c9315
Remove ZXing from target include directory path
ChristianNorbertBraun 9c9d257
Add #define ZX_USE_UTF8 to get utf8 std::string
ChristianNorbertBraun 4250ba4
Rename 1D and 2D codes to linear and matrix
ChristianNorbertBraun 9cbe49f
Remove error check for result
ChristianNorbertBraun 8621191
Remove charset from writer api
ChristianNorbertBraun 651e54d
Remove character encoding
ChristianNorbertBraun c1c7898
Use initWithBytes for text
ChristianNorbertBraun 5844605
Merge branch 'master' of https://github.com/nu-book/zxing-cpp into io…
ChristianNorbertBraun f1fe8b2
Remove #define ZX_USE_UTF8
ChristianNorbertBraun File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.swiftpm | ||
_builds | ||
.build | ||
xcuserdata | ||
ZXingCpp.xcframework |
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// swift-tools-version:5.3 | ||
import PackageDescription | ||
|
||
let package = Package( | ||
name: "ZXingCppWrapper", | ||
platforms: [ | ||
.iOS(.v13) | ||
], | ||
products: [ | ||
.library( | ||
name: "ZXingCppWrapper", | ||
type: .static, | ||
targets: ["ZXingCppWrapper"]) | ||
], | ||
targets: [ | ||
.binaryTarget( | ||
name: "ZXingCpp", | ||
path: "ZXingCpp.xcframework" | ||
), | ||
.target( | ||
name: "ZXingCppWrapper", | ||
dependencies: ["ZXingCpp"], | ||
path: "Sources/Wrapper", | ||
publicHeadersPath: ".", | ||
cxxSettings: [ | ||
.unsafeFlags(["-stdlib=libc++"]), | ||
.unsafeFlags(["-std=gnu++17"]) | ||
] | ||
) | ||
] | ||
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# ZXingCpp iOS Framework | ||
|
||
To use the iOS (wrapper) framework in other apps, it is easiest | ||
to build the library project and include the resulting xcframework | ||
file in your app. | ||
|
||
## How to build and use | ||
|
||
To build the xcframework: | ||
|
||
$ ./build-release.sh | ||
|
||
Then you can add the iOS Wrapper as a local Swift Package by adding it as a dependency to your app. | ||
Don't forget to add the wrapper to the `Frameworks, Libraries, and Embedded Content` section within the `General` tab. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// Copyright 2022 KURZ Digital Solutions GmbH | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#import <CoreGraphics/CoreGraphics.h> | ||
#import <CoreImage/CoreImage.h> | ||
#import "ZXIResult.h" | ||
#import "ZXIDecodeHints.h" | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface ZXIBarcodeReader : NSObject | ||
@property(nonatomic, strong) ZXIDecodeHints *hints; | ||
|
||
- (instancetype)initWithHints:(ZXIDecodeHints*)options; | ||
- (NSArray<ZXIResult *> *)readCIImage:(nonnull CIImage *)image; | ||
- (NSArray<ZXIResult *> *)readCGImage:(nonnull CGImageRef)image; | ||
- (NSArray<ZXIResult *> *)readCVPixelBuffer:(nonnull CVPixelBufferRef)pixelBuffer; | ||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
127 changes: 127 additions & 0 deletions
127
wrappers/ios/Sources/Wrapper/Reader/ZXIBarcodeReader.mm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// Copyright 2022 KURZ Digital Solutions GmbH | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#import "ZXIBarcodeReader.h" | ||
#import "ZXing/ReadBarcode.h" | ||
#import "ZXing/ImageView.h" | ||
#import "ZXing/Result.h" | ||
#import "ZXIFormatHelper.h" | ||
#import "ZXIPosition+Helper.h" | ||
|
||
using namespace ZXing; | ||
|
||
@interface ZXIBarcodeReader() | ||
@property (nonatomic, strong) CIContext* ciContext; | ||
@end | ||
|
||
@implementation ZXIBarcodeReader | ||
|
||
- (instancetype)init { | ||
return [self initWithHints: [[ZXIDecodeHints alloc]initWithTryHarder:NO tryRotate:NO tryDownscale:NO maxNumberOfSymbols:1 formats:@[]]]; | ||
} | ||
|
||
- (instancetype)initWithHints:(ZXIDecodeHints*)hints{ | ||
self = [super init]; | ||
self.ciContext = [CIContext new]; | ||
self.hints = hints; | ||
return self; | ||
} | ||
|
||
- (NSArray<ZXIResult *> *)readCVPixelBuffer:(nonnull CVPixelBufferRef)pixelBuffer { | ||
OSType pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer); | ||
|
||
// We tried to work with all luminance based formats listed in kCVPixelFormatType | ||
// but only the following ones seem to be supported on iOS. | ||
switch (pixelFormat) { | ||
case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: | ||
case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange: | ||
NSInteger cols = CVPixelBufferGetWidth(pixelBuffer); | ||
NSInteger rows = CVPixelBufferGetHeight(pixelBuffer); | ||
NSInteger bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0); | ||
CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); | ||
const uint8_t * bytes = static_cast<const uint8_t *>(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)); | ||
ImageView imageView = ImageView( | ||
static_cast<const uint8_t *>(bytes), | ||
static_cast<int>(cols), | ||
static_cast<int>(rows), | ||
ImageFormat::Lum, | ||
static_cast<int>(bytesPerRow), | ||
0); | ||
NSArray* results = [self readImageView:imageView]; | ||
CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); | ||
return results; | ||
} | ||
|
||
// If given pixel format is not a supported type with a luminance channel we just use the | ||
// default method | ||
return [self readCIImage:[[CIImage alloc] initWithCVImageBuffer:pixelBuffer]]; | ||
} | ||
|
||
- (NSArray<ZXIResult *> *)readCIImage:(nonnull CIImage *)image { | ||
CGImageRef cgImage = [self.ciContext createCGImage:image fromRect:image.extent]; | ||
auto results = [self readCGImage:cgImage]; | ||
CGImageRelease(cgImage); | ||
return results; | ||
} | ||
|
||
- (NSArray<ZXIResult *> *)readCGImage: (nonnull CGImageRef)image { | ||
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); | ||
CGFloat cols = CGImageGetWidth(image); | ||
CGFloat rows = CGImageGetHeight(image); | ||
NSMutableData *data = [NSMutableData dataWithLength: cols * rows]; | ||
|
||
|
||
CGContextRef contextRef = CGBitmapContextCreate( | ||
data.mutableBytes,// Pointer to backing data | ||
cols, // Width of bitmap | ||
rows, // Height of bitmap | ||
8, // Bits per component | ||
cols, // Bytes per row | ||
colorSpace, // Colorspace | ||
kCGBitmapByteOrderDefault); // Bitmap info flags | ||
CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image); | ||
CGContextRelease(contextRef); | ||
|
||
ImageView imageView = ImageView( | ||
static_cast<const uint8_t *>(data.bytes), | ||
static_cast<int>(cols), | ||
static_cast<int>(rows), | ||
ImageFormat::Lum); | ||
return [self readImageView:imageView]; | ||
} | ||
|
||
+ (DecodeHints)DecodeHintsFromZXIOptions:(ZXIDecodeHints*)hints { | ||
BarcodeFormats formats; | ||
for(NSNumber* flag in hints.formats) { | ||
formats.setFlag(BarcodeFormatFromZXIFormat((ZXIFormat)flag.integerValue)); | ||
} | ||
DecodeHints resultingHints = DecodeHints() | ||
.setTryRotate(hints.tryRotate) | ||
.setTryHarder(hints.tryHarder) | ||
.setTryDownscale(hints.tryDownscale) | ||
.setFormats(formats) | ||
.setMaxNumberOfSymbols(hints.maxNumberOfSymbols); | ||
return resultingHints; | ||
} | ||
|
||
- (NSArray<ZXIResult*> *)readImageView: (ImageView)imageView { | ||
Results results = ReadBarcodes(imageView, [ZXIBarcodeReader DecodeHintsFromZXIOptions:self.hints]); | ||
|
||
NSMutableArray* zxiResults = [NSMutableArray array]; | ||
for (auto result: results) { | ||
auto resultText = result.text(); | ||
NSString *text = [[NSString alloc]initWithBytes:resultText.data() length:resultText.size() encoding:NSUTF8StringEncoding]; | ||
|
||
NSData *bytes = [[NSData alloc] initWithBytes:result.bytes().data() length:result.bytes().size()]; | ||
[zxiResults addObject: | ||
[[ZXIResult alloc] init:text | ||
format:ZXIFormatFromBarcodeFormat(result.format()) | ||
bytes:bytes | ||
position:[[ZXIPosition alloc]initWithPosition: result.position()] | ||
]]; | ||
} | ||
return zxiResults; | ||
} | ||
|
||
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// Copyright 2022 KURZ Digital Solutions GmbH | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface ZXIDecodeHints : NSObject | ||
@property(nonatomic) BOOL tryHarder; | ||
@property(nonatomic) BOOL tryRotate; | ||
@property(nonatomic) BOOL tryDownscale; | ||
@property(nonatomic) NSInteger maxNumberOfSymbols; | ||
/// An array of ZXIFormat | ||
@property(nonatomic, strong) NSArray<NSNumber*> *formats; | ||
|
||
- (instancetype)initWithTryHarder:(BOOL)tryHarder | ||
tryRotate:(BOOL)tryRotate | ||
tryDownscale:(BOOL)tryDownscale | ||
maxNumberOfSymbols:(NSInteger)maxNumberOfSymbol | ||
formats:(NSArray<NSNumber*>*)formats; | ||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright 2022 KURZ Digital Solutions GmbH | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#import "ZXIDecodeHints.h" | ||
|
||
@implementation ZXIDecodeHints | ||
|
||
- (instancetype)initWithTryHarder:(BOOL)tryHarder | ||
tryRotate:(BOOL)tryRotate | ||
tryDownscale:(BOOL)tryDownscale | ||
maxNumberOfSymbols:(NSInteger)maxNumberOfSymbols | ||
formats:(NSArray<NSNumber*>*)formats { | ||
self = [super init]; | ||
self.tryHarder = tryHarder; | ||
self.tryRotate = tryRotate; | ||
self.tryDownscale = tryDownscale; | ||
self.maxNumberOfSymbols = maxNumberOfSymbols; | ||
self.formats = formats; | ||
return self; | ||
} | ||
|
||
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Copyright 2022 KURZ Digital Solutions GmbH | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
|
||
#import <Foundation/Foundation.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface ZXIPoint : NSObject | ||
@property(nonatomic) NSInteger x; | ||
@property(nonatomic) NSInteger y; | ||
|
||
- (instancetype)initWithX:(NSInteger)x y:(NSInteger)y; | ||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Copyright 2022 KURZ Digital Solutions GmbH | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
|
||
#import "ZXIPoint.h" | ||
|
||
@implementation ZXIPoint | ||
|
||
- (instancetype)initWithX:(NSInteger)x y:(NSInteger)y { | ||
self = [super init]; | ||
self.x = x; | ||
self.y = y; | ||
return self; | ||
} | ||
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Copyright 2022 KURZ Digital Solutions GmbH | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#import <Foundation/Foundation.h> | ||
#import "ZXIPosition.h" | ||
#import "ZXing/Result.h" | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface ZXIPosition(Helper) | ||
- (instancetype)initWithPosition:(ZXing::Position)position; | ||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Copyright 2022 KURZ Digital Solutions GmbH | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
|
||
#import "ZXIPosition+Helper.h" | ||
axxel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#import "ZXIPoint.h" | ||
|
||
@implementation ZXIPosition(Helper) | ||
-(instancetype)initWithPosition:(ZXing::Position)position { | ||
return [self initWithTopLeft:[[ZXIPoint alloc] initWithX:position.topLeft().x y:position.topLeft().y] | ||
topRight:[[ZXIPoint alloc] initWithX:position.topRight().x y:position.topRight().y] | ||
bottomRight:[[ZXIPoint alloc] initWithX:position.bottomRight().x y:position.bottomRight().y] | ||
bottomLeft:[[ZXIPoint alloc] initWithX:position.bottomLeft().x y:position.bottomLeft().y]]; | ||
} | ||
|
||
@end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.