Feat/ios detect edges#3
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request adds iOS edge detection functionality by integrating the WeScan library as vendor code. The implementation creates a React Native component that wraps WeScan's scanner view controller to enable document edge detection in iOS apps.
Changes:
- Adds WeScan library (vendored) with extensive scanner, editor, and review functionality
- Implements Swift wrapper (
LiveDetectEdgesScannerWrapper) to bridge WeScan with React Native - Adds localization support for 18 languages
- Updates example app with camera usage permissions and demonstrates the scanner integration
Reviewed changes
Copilot reviewed 7 out of 76 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| ios/LiveDetectEdgesScannerWrapper.swift | Swift wrapper bridging WeScan scanner to React Native |
| ios/LiveDetectEdgesView.mm | Objective-C++ view implementation for Fabric/New Architecture |
| ios/Vendor/WeScan/**/* | Complete WeScan library vendored code (scanning, editing, review, detection) |
| LiveDetectEdges.podspec | Updated to include WeScan vendor files in build |
| example/src/App.tsx | Example app updated with SafeAreaView and adjusted stroke width |
| example/ios/LiveDetectEdgesExample/Info.plist | Added camera usage description and new architecture flag |
Files not reviewed (1)
- example/ios/LiveDetectEdgesExample.xcworkspace/contents.xcworkspacedata: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// The maximum number of rectangles to compare newly added rectangles with. Determines the maximum size of `rectangles`. Increasing this value will impact performance. | ||
| let maxNumberOfRectangles = 8 | ||
|
|
||
| /// The minimum number of rectangles needed to start making comparaisons and determining which rectangle to display. This value should always be inferior than `maxNumberOfRectangles`. |
There was a problem hiding this comment.
The comment has a typo: "comparaisons" should be "comparisons".
| /// The value in pixels used to determine if two rectangle match or not. A higher value will prevent displayed rectangles to be refreshed. On the opposite, a smaller value will make new rectangles be displayed constantly. | ||
| let matchingThreshold: CGFloat = 40.0 | ||
|
|
||
| /// The minumum number of matching rectangles (within the `rectangle` queue), to be confident enough to display a rectangle. |
There was a problem hiding this comment.
The comment has a typo: "minumum" should be "minimum".
| private func setupScanner() { | ||
| let handler = ScannerDelegateHandler() | ||
| self.delegateHandler = handler | ||
|
|
||
| // Use ScannerViewController directly for custom UI (no buttons) | ||
| let scanner = ScannerViewController() | ||
|
|
||
| // We need to set the delegate if ScannerViewController exposes one. | ||
| // Looking at ScannerViewController source, it doesn't have a public delegate property easily accessible | ||
| // that matches ImageScannerControllerDelegate. | ||
| // It uses CaptureSessionManager internally and acts as a delegate to it. | ||
| // It pushes EditScanViewController on capture. | ||
|
|
||
| // However, we want to intercept the results. | ||
| // ScannerViewController doesn't seem to expose a delegate property for external use easily in its original form. | ||
| // But since we vendored it, we can check if we can access the results. | ||
|
|
||
| self.scannerController = scanner | ||
|
|
||
| if let scannerView = scanner.view { | ||
| scannerView.frame = self.bounds | ||
| scannerView.autoresizingMask = [.flexibleWidth, .flexibleHeight] | ||
| self.addSubview(scannerView) | ||
|
|
||
| // Trigger view lifecycle | ||
| // In a real app we might want to parent the VC, but for now just adding view. | ||
| } | ||
| } | ||
|
|
||
| @objc public override func didMoveToWindow() { | ||
| super.didMoveToWindow() | ||
| // If we needed to parent the VC, we would traverse responder chain here. | ||
| } | ||
| } | ||
|
|
||
| // Private delegate handler to avoid exposing WeScan types to Objective-C | ||
| private class ScannerDelegateHandler: NSObject, ImageScannerControllerDelegate { | ||
|
|
||
| func imageScannerController(_ scanner: ImageScannerController, didFailWithError error: Error) { | ||
| print("WeScan failed with error: \(error)") | ||
| } | ||
|
|
||
| func imageScannerController(_ scanner: ImageScannerController, didFinishScanningWithResults results: ImageScannerResults) { | ||
| // Handle successful scan | ||
| // results.originalScan and results.croppedScan are non-optional in this version of WeScan | ||
| let originalImage = results.originalScan.image | ||
| print("WeScan captured original image: \(originalImage)") | ||
|
|
||
| let croppedImage = results.croppedScan.image | ||
| print("WeScan captured cropped image: \(croppedImage)") | ||
|
|
||
| // Depending on requirements, we might want to dismiss or reset. | ||
| // Since this is an embedded view, 'dismiss' might not make sense if we want to keep scanning? | ||
| // But ImageScannerController is a flow (Scan -> Edit -> Review). | ||
| // If we want continuous scanning, WeScan might not be the best fit without customization or using internal components. | ||
| // But user asked to use WeScan, so we follow the standard flow. | ||
| } | ||
|
|
||
| func imageScannerControllerDidCancel(_ scanner: ImageScannerController) { | ||
| print("WeScan cancelled") | ||
| } | ||
| } |
There was a problem hiding this comment.
The LiveDetectEdgesScannerWrapper contains extensive TODO comments and unfinished logic. The delegate handler is created but never actually used to connect with the scanner. The ScannerViewController is instantiated without proper view controller containment, which could lead to lifecycle issues. Consider properly embedding the view controller in the view hierarchy or removing the incomplete delegate handler code.
No description provided.