krz/libre-secrets

A free and private password manager for iOS.

clone: git clone https://gitbay.org/krz/libre-secrets.git

d26ff0c96da53faa850b02176f8fc3dce3fdfd0c

verified · cmc

author: Christian Cleberg <hello@cleberg.net> · 2024-01-14T02:53:07Z

fix: refactor project to LibreSecrets
 LibreSecrets/ContentView.swift | 164 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 159 insertions(+), 5 deletions(-)

diff --git a/LibreSecrets/ContentView.swift b/LibreSecrets/ContentView.swift
index c4de5a2..25c34c4 100644
--- a/LibreSecrets/ContentView.swift
+++ b/LibreSecrets/ContentView.swift
@@ -2,18 +2,172 @@
 //  ContentView.swift
 //  LibreSecrets
 //
-//  Created by Christian Cleberg on 2024-01-13.
+//  Created by Christian Cleberg on 2024-01-10.
 //
 
 import SwiftUI
+import UniformTypeIdentifiers
 
 struct ContentView: View {
+    // Create initial variables
+    @State private var speed = 50.0
+    @State private var isEditing = false
+    @State private var enableNumbers = false
+    @State private var enableSpecial = false
+    @State private var enableCapitalization = false
+    @State private var isCopied: Bool = false
+    
+    // Create Picker options to choose password type
+    enum PasswordType: String, CaseIterable, Identifiable {
+        case random, xkcd
+        var id: Self { self }
+    }
+    @State private var passwordType: PasswordType = .random
+    
+    // Generates a random string of alphanumeric, numeric (optional), and special (optional) characters per user-determined length
+    func randomString(length: Int) -> String {
+        var characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        if enableNumbers {
+            characters.append("0123456789")
+        }
+        if enableSpecial {
+            characters.append("!%&()*+,-./:;<=>?@[^_`{|}~")
+        }
+        return String((0..<length).map{ _ in characters.randomElement()! })
+    }
+    
+    // Generates a series of words separated by "-", including a digit (optional), and capitalization (optional) per user-determined length
+    func randomWord(length: Int) -> String {
+        var randomLine = ""
+        for i in 1...length {
+            if let wordsFilePath = Bundle.main.path(forResource: "words", ofType: nil) {
+                do {
+                    let wordsString = try String(contentsOfFile: wordsFilePath)
+                    let wordLines = wordsString.components(separatedBy: .newlines)
+                    if enableCapitalization {
+                        randomLine += wordLines[numericCast(arc4random_uniform(numericCast(wordLines.count)))].capitalized
+                    } else {
+                        randomLine += wordLines[numericCast(arc4random_uniform(numericCast(wordLines.count)))]
+                    }
+                    if i != length {
+                        randomLine += "-"
+                    }
+                    if i == length && enableNumbers {
+                        randomLine += String(Int.random(in: 0..<9))
+                    }
+                } catch { // contentsOfFile throws an error
+                    print("Error: \(error)")
+                }
+            }
+        }
+        return randomLine
+    }
+
+    // Generate the view
     var body: some View {
         VStack {
-            Image(systemName: "globe")
-                .imageScale(.large)
-                .foregroundStyle(.tint)
-            Text("Hello, world!")
+            
+            VStack {
+                Text("Password Generator")
+                    .font(.largeTitle)
+                Text("Save your password somewhere safe!")
+                    .font(.caption)
+            }
+            
+            Form {
+                Section(header: Text("Password Type")) {
+                    Picker("Type", selection: $passwordType) {
+                        Text("Random").tag(PasswordType.random)
+                        Text("XKCD").tag(PasswordType.xkcd)
+                    }
+                }
+                
+                if passwordType == .random {
+                    Section(header: Text("Random Password")) {
+                        Slider(
+                            value: $speed,
+                            in: 8...36,
+                            step: 1
+                        ) {
+                            Text("Characters")
+                        } minimumValueLabel: {
+                            Text("8")
+                        } maximumValueLabel: {
+                            Text("36")
+                        } onEditingChanged: { editing in
+                            isEditing = editing
+                        }
+                        .onAppear {
+                            self.speed = 12
+                        }
+                        Toggle("Numbers", isOn: $enableNumbers)
+                        Toggle("Special Characters", isOn: $enableSpecial)
+                    }
+                    
+                    let password = randomString(length: Int(speed))
+                    
+                    Text("\(password)")
+                        .onTapGesture {
+                            let clipboard = UIPasteboard.general
+                            clipboard.setValue(password, forPasteboardType: UTType.plainText.identifier)
+                            withAnimation {
+                                isCopied = true
+                            }
+                            DispatchQueue.main.asyncAfter(wallDeadline: .now() + 3) {
+                                withAnimation {
+                                    isCopied = false
+                                }
+                            }
+                        }
+                } else {
+                    Section(header: Text("XKCD Password")) {
+                        Slider(
+                            value: $speed,
+                            in: 1...10,
+                            step: 1
+                        ) {
+                            Text("Words")
+                        } minimumValueLabel: {
+                            Text("1")
+                        } maximumValueLabel: {
+                            Text("10")
+                        } onEditingChanged: { editing in
+                            isEditing = editing
+                        }
+                        .onAppear {
+                            self.speed = 4
+                        }
+                        Toggle("Numbers", isOn: $enableNumbers)
+                        Toggle("Capitalize Words", isOn: $enableCapitalization)
+                    }
+                    
+                    let password = randomWord(length: Int(speed))
+                    
+                    Text("\(password)")
+                        .onTapGesture {
+                            let clipboard = UIPasteboard.general
+                            clipboard.setValue(password, forPasteboardType: UTType.plainText.identifier)
+                            withAnimation {
+                                isCopied = true
+                            }
+                            DispatchQueue.main.asyncAfter(wallDeadline: .now() + 3) {
+                                withAnimation {
+                                    isCopied = false
+                                }
+                            }
+                        }
+                }
+            }
+            
+            if isCopied {
+                Text("Copied successfully!")
+                    .foregroundColor(.white)
+                    .bold()
+                    .font(.footnote)
+                    .frame(width: 140, height: 30)
+                    .background(Color.indigo.cornerRadius(7))
+            }
+            
         }
         .padding()
     }