commit 6d3cf24117a5b718596267b6a371c1622a549117
parent 4e58d38101f11328a4e7cdec05a7541d7a0ccaf7
Author: William Casarin <jb55@jb55.com>
Date: Sat, 5 Feb 2022 10:23:35 -0800
pay qr code scanner
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
4 files changed, 147 insertions(+), 0 deletions(-)
diff --git a/lightninglink.xcodeproj/project.pbxproj b/lightninglink.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
+ 4C0359FB27AEE86600FF92CE /* QRScan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0359FA27AEE86600FF92CE /* QRScan.swift */; };
4C641D192788FF2F002A36C9 /* lightninglinkApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C641D182788FF2F002A36C9 /* lightninglinkApp.swift */; };
4C641D1B2788FF2F002A36C9 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C641D1A2788FF2F002A36C9 /* ContentView.swift */; };
4C641D1D2788FF30002A36C9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4C641D1C2788FF30002A36C9 /* Assets.xcassets */; };
@@ -41,6 +42,8 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
+ 4C0359FA27AEE86600FF92CE /* QRScan.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScan.swift; sourceTree = "<group>"; };
+ 4C0359FE27AEEE8500FF92CE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
4C641D152788FF2F002A36C9 /* lightninglink.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = lightninglink.app; sourceTree = BUILT_PRODUCTS_DIR; };
4C641D182788FF2F002A36C9 /* lightninglinkApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = lightninglinkApp.swift; sourceTree = "<group>"; };
4C641D1A2788FF2F002A36C9 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@@ -117,12 +120,14 @@
4C641D172788FF2F002A36C9 /* lightninglink */ = {
isa = PBXGroup;
children = (
+ 4C0359FE27AEEE8500FF92CE /* Info.plist */,
4C873FD427A6EF3F008C972C /* LNSocket.swift */,
4C641D182788FF2F002A36C9 /* lightninglinkApp.swift */,
4C641D1A2788FF2F002A36C9 /* ContentView.swift */,
4C641D1C2788FF30002A36C9 /* Assets.xcassets */,
4C641D1E2788FF30002A36C9 /* Preview Content */,
4C873FD627A6F1F5008C972C /* RPC.swift */,
+ 4C0359FA27AEE86600FF92CE /* QRScan.swift */,
);
path = lightninglink;
sourceTree = "<group>";
@@ -310,6 +315,7 @@
4C641D492789083E002A36C9 /* lightninglink.c in Sources */,
4C641D192788FF2F002A36C9 /* lightninglinkApp.swift in Sources */,
4C873FD727A6F1F5008C972C /* RPC.swift in Sources */,
+ 4C0359FB27AEE86600FF92CE /* QRScan.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -488,6 +494,7 @@
DEVELOPMENT_TEAM = XK7H4JAB3D;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = lightninglink/Info.plist;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
@@ -524,6 +531,7 @@
DEVELOPMENT_TEAM = XK7H4JAB3D;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = lightninglink/Info.plist;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
diff --git a/lightninglink/ContentView.swift b/lightninglink/ContentView.swift
@@ -9,6 +9,7 @@ import SwiftUI
struct ContentView: View {
@State private var info: GetInfo
+ @State private var showingQRScanner = false
init(info: GetInfo) {
self.info = info
@@ -17,10 +18,18 @@ struct ContentView: View {
var body: some View {
let _self = self
VStack {
+ Button("Pay") {
+ showingQRScanner = true
+ }
Text(self.info.alias)
Text("\(self.info.num_active_channels) active channels")
Text("\(self.info.msatoshi_fees_collected / 1000) sats collected in fees")
}
+ .sheet(isPresented: $showingQRScanner) {
+ QRScanner() { code in
+ print(code)
+ }
+ }
}
}
diff --git a/lightninglink/Info.plist b/lightninglink/Info.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>NSCameraUsageDescription</key>
+ <string>Scanning connection string qr codes</string>
+</dict>
+</plist>
diff --git a/lightninglink/QRScan.swift b/lightninglink/QRScan.swift
@@ -0,0 +1,122 @@
+//
+// QRScan.swift
+// lightninglink
+//
+// Created by William Casarin on 2022-02-05.
+//
+
+import AVFoundation
+import UIKit
+import SwiftUI
+
+struct QRScanner: UIViewControllerRepresentable {
+ var found: (String) -> Void
+
+ init(found: @escaping (String) -> Void) {
+ self.found = found
+ }
+
+ func makeUIViewController(context: Context) -> some UIViewController {
+ return ScannerViewController().onScan(self.found)
+ }
+
+ func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
+ }
+}
+
+class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
+ var captureSession: AVCaptureSession!
+ var previewLayer: AVCaptureVideoPreviewLayer!
+ var completion: ((String) -> Void)? = nil
+
+ public func onScan(_ found: @escaping (String) -> Void) -> ScannerViewController {
+ self.completion = found
+ return self
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ view.backgroundColor = UIColor.black
+ captureSession = AVCaptureSession()
+
+ guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
+ let videoInput: AVCaptureDeviceInput
+
+ do {
+ videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
+ } catch {
+ return
+ }
+
+ if (captureSession.canAddInput(videoInput)) {
+ captureSession.addInput(videoInput)
+ } else {
+ failed()
+ return
+ }
+
+ let metadataOutput = AVCaptureMetadataOutput()
+
+ if (captureSession.canAddOutput(metadataOutput)) {
+ captureSession.addOutput(metadataOutput)
+
+ metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
+ metadataOutput.metadataObjectTypes = [.qr]
+ } else {
+ failed()
+ return
+ }
+
+ previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
+ previewLayer.frame = view.layer.bounds
+ previewLayer.videoGravity = .resizeAspectFill
+ view.layer.addSublayer(previewLayer)
+
+ captureSession.startRunning()
+ }
+
+ func failed() {
+ let ac = UIAlertController(title: "Scanning not supported", message: "Your device does not support scanning a code from an item. Please use a device with a camera.", preferredStyle: .alert)
+ ac.addAction(UIAlertAction(title: "OK", style: .default))
+ present(ac, animated: true)
+ captureSession = nil
+ }
+
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+
+ if (captureSession?.isRunning == false) {
+ captureSession.startRunning()
+ }
+ }
+
+ override func viewWillDisappear(_ animated: Bool) {
+ super.viewWillDisappear(animated)
+
+ if (captureSession?.isRunning == true) {
+ captureSession.stopRunning()
+ }
+ }
+
+ func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
+ captureSession.stopRunning()
+
+ if let metadataObject = metadataObjects.first {
+ guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
+ guard let stringValue = readableObject.stringValue else { return }
+ AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
+ completion?(stringValue)
+ }
+
+ dismiss(animated: true)
+ }
+
+ override var prefersStatusBarHidden: Bool {
+ return true
+ }
+
+ override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
+ return .portrait
+ }
+}