diff --git a/Todoey.xcodeproj/project.pbxproj b/Todoey.xcodeproj/project.pbxproj index 423123ef..11c2cb44 100644 --- a/Todoey.xcodeproj/project.pbxproj +++ b/Todoey.xcodeproj/project.pbxproj @@ -8,20 +8,24 @@ /* Begin PBXBuildFile section */ EB2BE4FA239524DB00FB933B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB2BE4F9239524DB00FB933B /* AppDelegate.swift */; }; - EB2BE4FE239524DB00FB933B /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB2BE4FD239524DB00FB933B /* ViewController.swift */; }; + EB2BE4FE239524DB00FB933B /* TodoListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB2BE4FD239524DB00FB933B /* TodoListViewController.swift */; }; EB2BE501239524DB00FB933B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EB2BE4FF239524DB00FB933B /* Main.storyboard */; }; EB2BE503239524DC00FB933B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EB2BE502239524DC00FB933B /* Assets.xcassets */; }; EB2BE506239524DC00FB933B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EB2BE504239524DC00FB933B /* LaunchScreen.storyboard */; }; + F65EB8DD2B2B709A008A33A0 /* CategoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F65EB8DC2B2B709A008A33A0 /* CategoryViewController.swift */; }; + F696893A2B28BB870063C5EB /* DataModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = F69689382B28BB870063C5EB /* DataModel.xcdatamodeld */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ EB2BE4F6239524DB00FB933B /* Todoey.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Todoey.app; sourceTree = BUILT_PRODUCTS_DIR; }; EB2BE4F9239524DB00FB933B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - EB2BE4FD239524DB00FB933B /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + EB2BE4FD239524DB00FB933B /* TodoListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodoListViewController.swift; sourceTree = ""; }; EB2BE500239524DB00FB933B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; EB2BE502239524DC00FB933B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; EB2BE505239524DC00FB933B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; EB2BE507239524DC00FB933B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F65EB8DC2B2B709A008A33A0 /* CategoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryViewController.swift; sourceTree = ""; }; + F69689392B28BB870063C5EB /* DataModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = DataModel.xcdatamodel; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -55,15 +59,49 @@ isa = PBXGroup; children = ( EB2BE4F9239524DB00FB933B /* AppDelegate.swift */, - EB2BE4FD239524DB00FB933B /* ViewController.swift */, - EB2BE4FF239524DB00FB933B /* Main.storyboard */, - EB2BE502239524DC00FB933B /* Assets.xcassets */, - EB2BE504239524DC00FB933B /* LaunchScreen.storyboard */, + F6AD32A72B1F9B5600396DBF /* Models */, + F6AD32AA2B1F9BC100396DBF /* Views */, + F6AD32A62B1F9B4900396DBF /* Controller */, + F6AD32AB2B1F9C4600396DBF /* Supporting Files */, EB2BE507239524DC00FB933B /* Info.plist */, ); path = Todoey; sourceTree = ""; }; + F6AD32A62B1F9B4900396DBF /* Controller */ = { + isa = PBXGroup; + children = ( + F65EB8DC2B2B709A008A33A0 /* CategoryViewController.swift */, + EB2BE4FD239524DB00FB933B /* TodoListViewController.swift */, + ); + path = Controller; + sourceTree = ""; + }; + F6AD32A72B1F9B5600396DBF /* Models */ = { + isa = PBXGroup; + children = ( + F69689382B28BB870063C5EB /* DataModel.xcdatamodeld */, + ); + path = Models; + sourceTree = ""; + }; + F6AD32AA2B1F9BC100396DBF /* Views */ = { + isa = PBXGroup; + children = ( + EB2BE4FF239524DB00FB933B /* Main.storyboard */, + ); + path = Views; + sourceTree = ""; + }; + F6AD32AB2B1F9C4600396DBF /* Supporting Files */ = { + isa = PBXGroup; + children = ( + EB2BE504239524DC00FB933B /* LaunchScreen.storyboard */, + EB2BE502239524DC00FB933B /* Assets.xcassets */, + ); + path = "Supporting Files"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -91,7 +129,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1120; - LastUpgradeCheck = 1120; + LastUpgradeCheck = 1420; ORGANIZATIONNAME = "App Brewery"; TargetAttributes = { EB2BE4F5239524DB00FB933B = { @@ -135,7 +173,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - EB2BE4FE239524DB00FB933B /* ViewController.swift in Sources */, + F65EB8DD2B2B709A008A33A0 /* CategoryViewController.swift in Sources */, + EB2BE4FE239524DB00FB933B /* TodoListViewController.swift in Sources */, + F696893A2B28BB870063C5EB /* DataModel.xcdatamodeld in Sources */, EB2BE4FA239524DB00FB933B /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -188,6 +228,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -248,6 +289,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -287,7 +329,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "com.londonappbrewery.todoey-ios13.Todoey"; + PRODUCT_BUNDLE_IDENTIFIER = "com.javierhammad.todoey-ios13.Todoey"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -305,7 +347,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = "com.londonappbrewery.todoey-ios13.Todoey"; + PRODUCT_BUNDLE_IDENTIFIER = "com.javierhammad.todoey-ios13.Todoey"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -334,6 +376,19 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCVersionGroup section */ + F69689382B28BB870063C5EB /* DataModel.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + F69689392B28BB870063C5EB /* DataModel.xcdatamodel */, + ); + currentVersion = F69689392B28BB870063C5EB /* DataModel.xcdatamodel */; + path = DataModel.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; +/* End XCVersionGroup section */ }; rootObject = EB2BE4EE239524DB00FB933B /* Project object */; } diff --git a/Todoey.xcodeproj/project.xcworkspace/xcuserdata/javierhammad.xcuserdatad/UserInterfaceState.xcuserstate b/Todoey.xcodeproj/project.xcworkspace/xcuserdata/javierhammad.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 00000000..3eb41f13 Binary files /dev/null and b/Todoey.xcodeproj/project.xcworkspace/xcuserdata/javierhammad.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Todoey.xcodeproj/xcuserdata/javierhammad.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Todoey.xcodeproj/xcuserdata/javierhammad.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 00000000..1b4ceede --- /dev/null +++ b/Todoey.xcodeproj/xcuserdata/javierhammad.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/Todoey.xcodeproj/xcuserdata/javierhammad.xcuserdatad/xcschemes/xcschememanagement.plist b/Todoey.xcodeproj/xcuserdata/javierhammad.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..a6e80f53 --- /dev/null +++ b/Todoey.xcodeproj/xcuserdata/javierhammad.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + Todoey.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Todoey/AppDelegate.swift b/Todoey/AppDelegate.swift index 9fbce18a..5e8c1dc5 100644 --- a/Todoey/AppDelegate.swift +++ b/Todoey/AppDelegate.swift @@ -7,6 +7,7 @@ // import UIKit +import CoreData @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { @@ -14,31 +15,60 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { return true } - func applicationWillResignActive(_ application: UIApplication) { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. - } + - func applicationDidEnterBackground(_ application: UIApplication) { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + self.saveContext() } + + // MARK: - Core Data stack - func applicationWillEnterForeground(_ application: UIApplication) { - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. - } + 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: "DataModel") + 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 + }() - func applicationDidBecomeActive(_ application: UIApplication) { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. - } + // MARK: - Core Data Saving support - func applicationWillTerminate(_ application: UIApplication) { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + func saveContext () { + let context = persistentContainer.viewContext + if context.hasChanges { + do { + try context.save() + } catch { + // 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. + let nserror = error as NSError + fatalError("Unresolved error \(nserror), \(nserror.userInfo)") + } + } } diff --git a/Todoey/Base.lproj/Main.storyboard b/Todoey/Base.lproj/Main.storyboard deleted file mode 100644 index 25a76385..00000000 --- a/Todoey/Base.lproj/Main.storyboard +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Todoey/Controller/CategoryViewController.swift b/Todoey/Controller/CategoryViewController.swift new file mode 100644 index 00000000..78730cfc --- /dev/null +++ b/Todoey/Controller/CategoryViewController.swift @@ -0,0 +1,104 @@ +import UIKit +import CoreData + +class CategoryViewController: UITableViewController { + var categories = [Category]() + let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext + + override func viewDidLoad() { + super.viewDidLoad() + loadCategories() + + } + + // MARK: - TableView DataSource Methods + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return categories.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "CategoryCell", for: indexPath) + let item = categories[indexPath.row] + cell.textLabel?.text = item.name + return cell + } + + // MARK: - TableView Delegate Methods + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + performSegue(withIdentifier: "goToItems", sender: self) + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + let destinationVC = segue.destination as! TodoListViewController + + if let indexPath = tableView.indexPathForSelectedRow { + destinationVC.selectedCategory = categories[indexPath.row] + } + } + + // MARK: - Data Manipulation Methods + + func saveCategories() { + + do { + try context.save() + + } catch { + print("Error saving category, \(error)") + } + self.tableView.reloadData() + } + // this function is to load items based on a given fetch request. The flexibility is provided by allowing you to either provide a custom fetch request (with:) or use the default one (Item.fetchRequest()). + func loadCategories() { + + let request : NSFetchRequest = Category.fetchRequest() + + do { + // Attempt to fetch items from Core Data using the fetch request. + // The fetched items will be stored in the itemArray. + categories = try context.fetch(request) + } catch { + // If an error occurs during the fetch operation, print an error message. + print("Error loading categories, \(error)") + } + tableView.reloadData() + } + + // MARK: - Add New Categories + + @IBAction func addButtonPressed(_ sender: UIBarButtonItem) { + // Declare a variable to capture the text field + var textField = UITextField() + + let alert = UIAlertController(title: "Add New Todoey Category", message: "", preferredStyle: .alert) + + let action = UIAlertAction(title: "Add Category", style: .default) { (action) in + + let newCategory = Category(context: self.context) + + // create the title for the new file + newCategory.name = textField.text! + + // Use the captured text field to access the entered text and add it to the array + self.categories.append(newCategory) + + // call to the function saveItems + self.saveCategories() + } + alert.addTextField { (alertTextField) in + + // Set a placeholder text for the text field + alertTextField.placeholder = "Create new category" + + // Capture the reference to the text field for later use + textField = alertTextField + } + + alert.addAction(action) + present(alert, animated: true, completion: nil) + } +} + + + diff --git a/Todoey/Controller/TodoListViewController.swift b/Todoey/Controller/TodoListViewController.swift new file mode 100644 index 00000000..a3f2d862 --- /dev/null +++ b/Todoey/Controller/TodoListViewController.swift @@ -0,0 +1,213 @@ +import UIKit +import CoreData + +class TodoListViewController: UITableViewController { + + var itemArray = [Item]() + + var selectedCategory : Category? { + didSet { + loadItems() + } + } + + // // gets the shared instance of the UIApplication, which represents the current running app. + // -- let sharedApplication = UIApplication.shared + // + // // gets the AppDelegate instance using the shared application instance. + // -- let appDelegate = sharedApplication.delegate + // + // // casts the delegate as an instance of the AppDelegate class. + // // The "as!" operator is used for a forced downcast. It assumes that the delegate is an instance of AppDelegate, and if it's not, it will crash at runtime. + // -- let appDelegateAsAppDelegate = appDelegate as! AppDelegate + // + // // accesses the persistentContainer property of the AppDelegate. + // // The persistent container is a part of Core Data, which is a framework used for data storage and management in iOS applications. + // -- let persistentContainer = appDelegateAsAppDelegate.persistentContainer + // + // // accesses the viewContext property of the persistent container. + // // The view context is a managed object context that is used for interacting with the Core Data objects. + // -- let context = persistentContainer.viewContext + // + // // Now, 'context' can be used to perform Core Data operations like fetching, saving, and deleting objects. + + + let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext + + + // Get the URL of the document directory for the current app's sandboxed environment + // FileManager.default: The default file manager for the app. + // urls(for:in:): A method to get URLs for specified directories in a specified domain. + // .documentDirectory: Indicates the document directory, a location where you can store user-generated content. + // .userDomainMask: Specifies the user's home directory as the domain for the search. + // .first: Retrieves the first URL from the array of URLs returned by urls(for:in:). + let dataFilePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("Items.plist") + + override func viewDidLoad() { + super.viewDidLoad() + + } + + // MARK: - Tableview Datasource Methods + + // This method is called to determine the number of rows in the specified section. + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + + // The number of rows is equal to the count of items in the itemArray. + return itemArray.count + } + + // This method is called to provide a cell for a specific row. + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + + // Dequeue a reusable cell with the identifier "ToDoItemCell" for the specified indexPath. + let cell = tableView.dequeueReusableCell(withIdentifier: "ToDoItemCell", for: indexPath) + + // define current item + let item = itemArray[indexPath.row] + + // Set the text label of the cell to the corresponding item in the itemArray defined above + cell.textLabel?.text = item.title + + // terniary operator to change the done + cell.accessoryType = item.done ? .checkmark : .none + + // Return the configured cell. + return cell + } + + // MARK: - TableView Delegate Methods + + // This method is called when a cell is selected + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + + // Toggle the 'done' property of the item at the specified index path row + itemArray[indexPath.row].done = !itemArray[indexPath.row].done + + ////////////CODE TO DELETE INSTEAD OF CHECK + // // This line of code deletes the item at the specified indexPath.row from the Core Data context. + // context.delete(itemArray[indexPath.row]) + // + // // This line of code removes the same item from the local itemArray. + // itemArray.remove(at: indexPath.row) + + + // call to the function saveItems to pass from the context to the real database + saveItems() + + // Animation when the row is clicked + tableView.deselectRow(at: indexPath, animated: true) + + + } + + // MARK: - Add New Item + + @IBAction func addButtonPressed(_ sender: UIBarButtonItem) { + // Declare a variable to capture the text field + var textField = UITextField() + + let alert = UIAlertController(title: "Add New Todoey Item", message: "", preferredStyle: .alert) + + let action = UIAlertAction(title: "Add item", style: .default) { (action) in + + + let newItem = Item(context: self.context) + + // create the title for the new file + newItem.title = textField.text! + // set done property of the new item to false because done is mandatory according to our entity + newItem.done = false + // property of the category parent of the item + newItem.categoryName = self.selectedCategory + + // Use the captured text field to access the entered text and add it to the array + self.itemArray.append(newItem) + + // call to the function saveItems + self.saveItems() + + + } + + // Capture the textfield when it's added + alert.addTextField { (alertTextField) in + + // Set a placeholder text for the text field + alertTextField.placeholder = "Create new item" + + // Capture the reference to the text field for later use + textField = alertTextField + } + + alert.addAction(action) + present(alert, animated: true, completion: nil) + } + + + // MARK: - Model Manipulation Methods + + func saveItems() { + + do { + try context.save() + + } catch { + print("Error saving context, \(error)") + } + self.tableView.reloadData() + } + // this function is to load items based on a given fetch request. The flexibility is provided by allowing you to either provide a custom fetch request (with:) or use the default one (Item.fetchRequest()). + func loadItems(with request: NSFetchRequest = Item.fetchRequest()) { + + let predicate = NSPredicate(format: "categoryName.name MATCHES %@", selectedCategory!.name!) + + request.predicate = predicate // correct predicate + + do { + // Attempt to fetch items from Core Data using the fetch request. + // The fetched items will be stored in the itemArray. + itemArray = try context.fetch(request) + } catch { + // If an error occurs during the fetch operation, print an error message. + print("Error fetching data from context, \(error)") + } + tableView.reloadData() + } +} + +// MARK: - Extension for the SearchBar Method +extension TodoListViewController: UISearchBarDelegate { + func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { + if searchBar.text?.isEmpty ?? true { + // If the search bar text is empty, load all items and resign first responder + loadItems() + + // send the request to backfround thread to avoud app crashes + DispatchQueue.main.async { + // Hide the keyboard when the search bar is empty + searchBar.resignFirstResponder() + } + } else { + // If there is text in the search bar, filter and load items based on the search text + let request: NSFetchRequest = Item.fetchRequest() + + if let text = searchBar.text { + // Set the predicate of the fetch request to filter results based on a condition. + // In this case, the condition is defined using NSPredicate. + request.predicate = NSPredicate(format: "title CONTAINS[cd] %@", text) + + // Set the sort descriptors for the fetch request to define the order of fetched results. + request.sortDescriptors = [NSSortDescriptor(key: "title", ascending: true)] + + // calling loadItem with the new request value + loadItems(with: request) + } + } + } + + +} + + + diff --git a/Todoey/Models/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents b/Todoey/Models/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents new file mode 100644 index 00000000..bdc96d7e --- /dev/null +++ b/Todoey/Models/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Todoey/Assets.xcassets/AppIcon.appiconset/Contents.json b/Todoey/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from Todoey/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Todoey/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/Todoey/Assets.xcassets/Contents.json b/Todoey/Supporting Files/Assets.xcassets/Contents.json similarity index 100% rename from Todoey/Assets.xcassets/Contents.json rename to Todoey/Supporting Files/Assets.xcassets/Contents.json diff --git a/Todoey/Base.lproj/LaunchScreen.storyboard b/Todoey/Supporting Files/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from Todoey/Base.lproj/LaunchScreen.storyboard rename to Todoey/Supporting Files/Base.lproj/LaunchScreen.storyboard diff --git a/Todoey/ViewController.swift b/Todoey/ViewController.swift deleted file mode 100644 index 4a475a34..00000000 --- a/Todoey/ViewController.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// ViewController.swift -// Todoey -// -// Created by Philipp Muellauer on 02/12/2019. -// Copyright © 2019 App Brewery. All rights reserved. -// - -import UIKit - -class ViewController: UIViewController { - - override func viewDidLoad() { - super.viewDidLoad() - // Do any additional setup after loading the view. - } - - -} - diff --git a/Todoey/Views/Base.lproj/Main.storyboard b/Todoey/Views/Base.lproj/Main.storyboard new file mode 100644 index 00000000..4d3ba0b6 --- /dev/null +++ b/Todoey/Views/Base.lproj/Main.storyboard @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +