Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions EmpowerPlant.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
843BD60F2AD08CE900B0098F /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 843BD60E2AD08CE900B0098F /* Utils.swift */; };
843BD6272AD7798C00B0098F /* jwt-deep-field.png in Resources */ = {isa = PBXBuildFile; fileRef = 843BD6262AD7798C00B0098F /* jwt-deep-field.png */; };
846BEA1C2ABE611A0032F77F /* mobydick.txt in Resources */ = {isa = PBXBuildFile; fileRef = 846BEA1B2ABE611A0032F77F /* mobydick.txt */; };
846D3F642E297C3E00D4E7E3 /* CoreDataController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846D3F632E297C3E00D4E7E3 /* CoreDataController.swift */; };
848716A32AD8C3CD00756467 /* BigInt in Frameworks */ = {isa = PBXBuildFile; productRef = 848716A22AD8C3CD00756467 /* BigInt */; };
848716B72ADFAA2000756467 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 848716B62ADFAA2000756467 /* Sentry */; };
8B21663C29D3F8C80009C890 /* RandomErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B21663B29D3F8C80009C890 /* RandomErrors.swift */; };
8BA3AB382A20212C00BE1EA8 /* CartViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA3AB372A20212C00BE1EA8 /* CartViewControllerTests.swift */; };
D15EDF12282BF7FB00FC13D6 /* Product+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = D15EDF11282BF7FB00FC13D6 /* Product+CoreDataProperties.swift */; };
D15EDF14282BF80400FC13D6 /* Product+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = D15EDF13282BF80400FC13D6 /* Product+CoreDataClass.swift */; };
D15EDF14282BF80400FC13D6 /* Product.swift in Sources */ = {isa = PBXBuildFile; fileRef = D15EDF13282BF80400FC13D6 /* Product.swift */; };
D15FCDA927E00F0D00258BF3 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = D15FCDA727E00F0D00258BF3 /* Model.xcdatamodeld */; };
D17C73B327D8291D006650AF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D17C73B227D8291D006650AF /* AppDelegate.swift */; };
D17C73B527D8291D006650AF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D17C73B427D8291D006650AF /* SceneDelegate.swift */; };
Expand Down Expand Up @@ -44,6 +44,7 @@
843BD6262AD7798C00B0098F /* jwt-deep-field.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "jwt-deep-field.png"; sourceTree = "<group>"; };
846BEA1A2ABE46880032F77F /* upload-symbols.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "upload-symbols.sh"; sourceTree = "<group>"; };
846BEA1B2ABE611A0032F77F /* mobydick.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = mobydick.txt; sourceTree = "<group>"; };
846D3F632E297C3E00D4E7E3 /* CoreDataController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataController.swift; sourceTree = "<group>"; };
8474F0482ACCE2D800F21E06 /* deploy_project.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = deploy_project.sh; sourceTree = "<group>"; };
8474F04D2ACE54F300F21E06 /* .github */ = {isa = PBXFileReference; lastKnownFileType = folder; path = .github; sourceTree = "<group>"; };
848A45262BBFC79E006AAAEC /* .codecov.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .codecov.yml; sourceTree = "<group>"; };
Expand All @@ -55,8 +56,7 @@
84A2641E2AA926A300A62A88 /* .env */ = {isa = PBXFileReference; lastKnownFileType = text; path = .env; sourceTree = "<group>"; };
8B21663B29D3F8C80009C890 /* RandomErrors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomErrors.swift; sourceTree = "<group>"; };
8BA3AB372A20212C00BE1EA8 /* CartViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CartViewControllerTests.swift; sourceTree = "<group>"; };
D15EDF11282BF7FB00FC13D6 /* Product+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Product+CoreDataProperties.swift"; sourceTree = "<group>"; };
D15EDF13282BF80400FC13D6 /* Product+CoreDataClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Product+CoreDataClass.swift"; sourceTree = "<group>"; };
D15EDF13282BF80400FC13D6 /* Product.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Product.swift; sourceTree = "<group>"; };
D15FCDA827E00F0D00258BF3 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = "<group>"; };
D17C73B227D8291D006650AF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
D17C73B427D8291D006650AF /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -124,12 +124,12 @@
D17C73B227D8291D006650AF /* AppDelegate.swift */,
D17C73B427D8291D006650AF /* SceneDelegate.swift */,
D17C73B827D8291D006650AF /* Main.storyboard */,
D15EDF13282BF80400FC13D6 /* Product+CoreDataClass.swift */,
D15EDF11282BF7FB00FC13D6 /* Product+CoreDataProperties.swift */,
D15EDF13282BF80400FC13D6 /* Product.swift */,
D17C73D127D83321006650AF /* ListAppViewController.swift */,
843BD60E2AD08CE900B0098F /* Utils.swift */,
D15FCDA727E00F0D00258BF3 /* Model.xcdatamodeld */,
D17C73CB27D82EB8006650AF /* EmpowerPlantViewController.swift */,
846D3F632E297C3E00D4E7E3 /* CoreDataController.swift */,
846BEA1B2ABE611A0032F77F /* mobydick.txt */,
D19EBE6E2805ED52007022DC /* ShoppingCart.swift */,
D17C73CE27D82ED1006650AF /* CartViewController.swift */,
Expand Down Expand Up @@ -285,15 +285,15 @@
files = (
D17C73CC27D82EB8006650AF /* EmpowerPlantViewController.swift in Sources */,
D17C73D227D83321006650AF /* ListAppViewController.swift in Sources */,
D15EDF14282BF80400FC13D6 /* Product+CoreDataClass.swift in Sources */,
846D3F642E297C3E00D4E7E3 /* CoreDataController.swift in Sources */,
D15EDF14282BF80400FC13D6 /* Product.swift in Sources */,
D15FCDA927E00F0D00258BF3 /* Model.xcdatamodeld in Sources */,
D17C73CF27D82ED1006650AF /* CartViewController.swift in Sources */,
8B21663C29D3F8C80009C890 /* RandomErrors.swift in Sources */,
D19EBE6F2805ED52007022DC /* ShoppingCart.swift in Sources */,
843BD60F2AD08CE900B0098F /* Utils.swift in Sources */,
D17C73B327D8291D006650AF /* AppDelegate.swift in Sources */,
D17C73B527D8291D006650AF /* SceneDelegate.swift in Sources */,
D15EDF12282BF7FB00FC13D6 /* Product+CoreDataProperties.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@
value = "$(USER)"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "--io.sentry.dsn="
value = ""
isEnabled = "NO">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
Expand Down
52 changes: 0 additions & 52 deletions EmpowerPlant/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,9 @@

import UIKit
import Sentry
import CoreData

@main
class AppDelegate: UIResponder, UIApplicationDelegate {



func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

// the Sentry default is to enable swizzling. we'll use that as our default as well. we check for the launch arg to disable swizzling; if it's provided, then we'll disable swizzling. if it's absent, then swizzling will be enabled.
Expand Down Expand Up @@ -65,53 +61,5 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}

func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}

lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "Model")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()

// MARK: - Core Data Saving support

func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// TODO: error
}
}
}


}

145 changes: 145 additions & 0 deletions EmpowerPlant/CoreDataController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
//
// CoreDataController.swift
// EmpowerPlant
//
// Created by Andrew McKnight on 7/17/25.
//

import Foundation
import CoreData

struct ProductMap: Decodable {
let id: Int
let title: String
let description: String
let descriptionfull: String
let img: String
let imgcropped: String
let price: Int
// reviews: [{id: 4, productid: 4, rating: 4, customerid: null, description: null, created: String},...]
}

class CoreDataController {
static let shared = CoreDataController()

lazy var context = persistentContainer.viewContext

lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "Model")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()

// MARK: CRUD Operations

func createProduct(productId: String, title: String, productDescription: String, productDescriptionFull: String, img: String, imgCropped: String, price: String) {
let newProduct = Product(context: context)

newProduct.productId = productId
newProduct.title = title
newProduct.productDescription = productDescription
newProduct.productDescriptionFull = productDescriptionFull
newProduct.img = img
newProduct.imgCropped = imgCropped
newProduct.price = price
}

func createProduct(product: ProductMap) {
createProduct(productId: String(product.id), title: product.title, productDescription: product.description, productDescriptionFull: product.descriptionfull, img: product.img, imgCropped: product.imgcropped, price: String(product.price))
}

func getAllProducts() throws -> [Product] {
let request: NSFetchRequest<Product> = Product.fetchRequest()
return try context.fetch(request)
}

func deleteProduct(product: Product) {
context.delete(product)
do {
try context.save()
}
catch {
// TODO: error
}
}

func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// TODO: error
}
}
}

func generateDBItems(total: Int) {
let itemsPerBatch = 1_000
let batches = total / itemsPerBatch
DispatchQueue.global(qos: .utility).async {
for i in 0..<batches {
DispatchQueue.main.async {
for j in 0..<itemsPerBatch {
let newProduct = Product(context: self.context)
let productNum = i * itemsPerBatch + j

newProduct.productId = "Product \(productNum)" // 'id' was a reserved word in swift
newProduct.title = "Product \(productNum)"
newProduct.productDescription = "Description for product \(i)" // 'description' was a reserved word in swift
newProduct.productDescriptionFull = "Full description for product \(productNum)"
newProduct.img = "img"
newProduct.imgCropped = "img.cropped"
newProduct.price = "\(productNum)"
}

do {
try self.context.save()
NotificationCenter.default.post(name: modifiedDBNotificationName, object: nil)
} catch {
// TODO: error
}
}
// add a small delay so it doesn't lock up the UI
usleep(100_000) // 100 milliseconds
}
}
}
}

public let modifiedDBNotificationName = Notification.Name("io.sentry.empowerplants.newly-generated-db-items-available")

public func wipeDB() {
guard let url = CoreDataController.shared.persistentContainer.persistentStoreCoordinator.persistentStores.first?.url else {
// TODO: error
return
}

do {
try FileManager.default.removeItem(at: url)
} catch {
// TODO: error
return
}
}
Loading
Loading