diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..072f5db Binary files /dev/null and b/.DS_Store differ diff --git a/POST and PUT Comprehension Questions.rtf b/POST and PUT Comprehension Questions.rtf new file mode 100644 index 0000000..01e9011 --- /dev/null +++ b/POST and PUT Comprehension Questions.rtf @@ -0,0 +1,65 @@ +{\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf600 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +{\*\expandedcolortbl;;} +\margl1440\margr1440\vieww10800\viewh8400\viewkind0 +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 + +\f0\b\fs28 \cf0 POST and PUT Comprehension Questions +\fs24 \ +[ Week 4 ]\ +\ +What is a RESTful interface? When do you use it? What are its characteristics?\ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qr\partightenfactor0 + +\b0 \cf0 >> A RESTful interface is an architectural style that provides standards or guidelines between computer systems on the web. It allows for communication between the API and the device. REST uses HTTP requests to do different things with data on databases stored on a remote web service. The four resource methods of REST line up with CRUD methods - which are POST, GET, PUT, and DELETE. You use a RESTful interface anytime you want to retrieve, modify, or store resources on a remote web service. One characteristic of a RESTful interface, or a system that is compliant with RESTs guidelines, is that it keeps the user interface separate from the data storage. Another characteristic is that components can be managed or updated without affecting the system as a whole - this is called \'93statelessness.\'94 +\b \ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 +\cf0 \ +What is the difference between POST and PUT?\ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qr\partightenfactor0 + +\b0 \cf0 >> POST and PUT are both ways of giving data to an API. However, PUT is idempotent and POST is not. It doesn\'92t matter how many times you send the data, the API will place the info exactly where you told it to. So if you have already created the data, if you send the data again, it will update the existing resources. If you send the data multiple times using POST, the API will create a unique identifier and add it onto your base URL each time, putting the information at a newly created, unique URL for each time you POST the data. +\b \ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 +\cf0 \ +How does the record ID work in Firebase? Why does it take two steps to save a record (first POST then PUT) app? Why does the FirebaseItem protocol even exist?\ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qr\partightenfactor0 + +\b0 \cf0 >> When you post to the main database, it will return a new record identifier, which allows you to have a URL that is specific to your method. It takes multiple steps to save a record because the first time we POST in order to get a record ID. This is the first time we receive a record ID. Then we add the record ID to our device and save it again (PUT), which then firebase will have the record ID with the data stored. The FirebaseItem protocol is needed so that we have a record ID field. +\b \ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 +\cf0 \ +Explain what a generic type is and what advantages it offers.\ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qr\partightenfactor0 + +\b0 \cf0 >> A generic type is a feature in Swift that allows you to use any type - you don\'92t have to write code that is specific to one type, as long as the behavior is followed of what you conform your type variable to. We call the generic type with . +\b \ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 +\cf0 \ +What does a URLRequest do? When do you need one and when can you just use a URL?\ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qr\partightenfactor0 + +\b0 \cf0 >> URLRequest objects are the thing that we send to an API. Inside the request is our URL, telling the API where we want to access data, and the HTTP Method that we want to use at that URL. You can use just the URL without a URLRequest when you are doing one of the \'93reading\'94 actions - GET. If you are passing a method and a body, like a PUT/POST/DELETE, then you need to create a URL Request instance. +\b \ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 +\cf0 \ +What is the role of a URL session? How does it work?\ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qr\partightenfactor0 + +\b0 \cf0 >> The role of a URLSession is to create the data task that will actually perform the request. It\'92s a very large class that does many things relating to interacting with network resources. After you encode, you create a data task that will perform the request by creating a dataTask constant and setting it to URLSession.shared.dataTask(with: request) and then a completion closure. +\b \ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 +\cf0 \ +What are completion handlers? Why use completion handlers? What advantages do they provide?\ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qr\partightenfactor0 + +\b0 \cf0 >> Completion handlers take the place of delegation or notification because they allow you to send things that need to happen after a task is done. When something is running asynchronously, we can give it something to do as soon as it has finished its first task. A completion handler is the same concept as using protocols and delegates, but it\'92s much better because nothing is stored in multiple places - it stores no information other than the stuff that\'92s going to run once something is complete. +\b \ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 +\cf0 \ +Explain at least three strategies that promote code reuse that we used in today\'92s project.\ + +\b0 >> The firebase.swift file was completely reusable because nothing in our code was specific to the project. Using a model that stores Core Database Management Methods, like the add, count, delete, and update functions are reusable and nice to keep in our Model for this reason. And keeping things in separate files and organized also promotes code reuse by sectioning off tasks. +\b \ +} \ No newline at end of file diff --git a/Table Project - Student & Cohort copy/.DS_Store b/Table Project - Student & Cohort copy/.DS_Store new file mode 100644 index 0000000..5938322 Binary files /dev/null and b/Table Project - Student & Cohort copy/.DS_Store differ diff --git a/Table Project - Student & Cohort copy/Table Project - copy.xcodeproj/project.pbxproj b/Table Project - Student & Cohort copy/Table Project - copy.xcodeproj/project.pbxproj new file mode 100644 index 0000000..e2a8b77 --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project - copy.xcodeproj/project.pbxproj @@ -0,0 +1,367 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 841F119321B9CED7002BFD09 /* FirebaseItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841F119221B9CED6002BFD09 /* FirebaseItem.swift */; }; + 841F119521B9CF9D002BFD09 /* Firebase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841F119421B9CF9D002BFD09 /* Firebase.swift */; }; + 8464AC7821ADBB8500E85E34 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8464AC7721ADBB8500E85E34 /* AppDelegate.swift */; }; + 8464AC7D21ADBB8500E85E34 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8464AC7B21ADBB8500E85E34 /* Main.storyboard */; }; + 8464AC7F21ADBB8700E85E34 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8464AC7E21ADBB8700E85E34 /* Assets.xcassets */; }; + 8464AC8221ADBB8700E85E34 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8464AC8021ADBB8700E85E34 /* LaunchScreen.storyboard */; }; + 8464AC8A21ADC1BC00E85E34 /* AddPersonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8464AC8921ADC1BC00E85E34 /* AddPersonCell.swift */; }; + 8464AC8C21ADC1D800E85E34 /* PersonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8464AC8B21ADC1D800E85E34 /* PersonCell.swift */; }; + 8464AC8E21ADC20E00E85E34 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8464AC8D21ADC20E00E85E34 /* DetailViewController.swift */; }; + 8464AC9021ADC23300E85E34 /* PersonTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8464AC8F21ADC23300E85E34 /* PersonTableViewController.swift */; }; + 8464AC9221ADC26200E85E34 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8464AC9121ADC26200E85E34 /* Model.swift */; }; + 8464AC9421ADC27300E85E34 /* Person.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8464AC9321ADC27300E85E34 /* Person.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 841F119221B9CED6002BFD09 /* FirebaseItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebaseItem.swift; sourceTree = ""; }; + 841F119421B9CF9D002BFD09 /* Firebase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Firebase.swift; sourceTree = ""; }; + 8464AC7421ADBB8500E85E34 /* Table Project.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Table Project.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 8464AC7721ADBB8500E85E34 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 8464AC7C21ADBB8500E85E34 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 8464AC7E21ADBB8700E85E34 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 8464AC8121ADBB8700E85E34 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 8464AC8321ADBB8700E85E34 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8464AC8921ADC1BC00E85E34 /* AddPersonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddPersonCell.swift; sourceTree = ""; }; + 8464AC8B21ADC1D800E85E34 /* PersonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersonCell.swift; sourceTree = ""; }; + 8464AC8D21ADC20E00E85E34 /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; + 8464AC8F21ADC23300E85E34 /* PersonTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersonTableViewController.swift; sourceTree = ""; }; + 8464AC9121ADC26200E85E34 /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = ""; }; + 8464AC9321ADC27300E85E34 /* Person.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Person.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8464AC7121ADBB8500E85E34 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 8464AC6B21ADBB8500E85E34 = { + isa = PBXGroup; + children = ( + 8464AC7621ADBB8500E85E34 /* Table Project */, + 8464AC7521ADBB8500E85E34 /* Products */, + ); + sourceTree = ""; + }; + 8464AC7521ADBB8500E85E34 /* Products */ = { + isa = PBXGroup; + children = ( + 8464AC7421ADBB8500E85E34 /* Table Project.app */, + ); + name = Products; + sourceTree = ""; + }; + 8464AC7621ADBB8500E85E34 /* Table Project */ = { + isa = PBXGroup; + children = ( + 8464AC7721ADBB8500E85E34 /* AppDelegate.swift */, + 8464AC7B21ADBB8500E85E34 /* Main.storyboard */, + 841F119221B9CED6002BFD09 /* FirebaseItem.swift */, + 841F119421B9CF9D002BFD09 /* Firebase.swift */, + 8464AC9321ADC27300E85E34 /* Person.swift */, + 8464AC8B21ADC1D800E85E34 /* PersonCell.swift */, + 8464AC8921ADC1BC00E85E34 /* AddPersonCell.swift */, + 8464AC9121ADC26200E85E34 /* Model.swift */, + 8464AC8D21ADC20E00E85E34 /* DetailViewController.swift */, + 8464AC8F21ADC23300E85E34 /* PersonTableViewController.swift */, + 8464AC7E21ADBB8700E85E34 /* Assets.xcassets */, + 8464AC8021ADBB8700E85E34 /* LaunchScreen.storyboard */, + 8464AC8321ADBB8700E85E34 /* Info.plist */, + ); + path = "Table Project"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8464AC7321ADBB8500E85E34 /* Table Project */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8464AC8621ADBB8700E85E34 /* Build configuration list for PBXNativeTarget "Table Project" */; + buildPhases = ( + 8464AC7021ADBB8500E85E34 /* Sources */, + 8464AC7121ADBB8500E85E34 /* Frameworks */, + 8464AC7221ADBB8500E85E34 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Table Project"; + productName = "Table Project"; + productReference = 8464AC7421ADBB8500E85E34 /* Table Project.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8464AC6C21ADBB8500E85E34 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1010; + LastUpgradeCheck = 1010; + ORGANIZATIONNAME = "Audrey Welch"; + TargetAttributes = { + 8464AC7321ADBB8500E85E34 = { + CreatedOnToolsVersion = 10.1; + }; + }; + }; + buildConfigurationList = 8464AC6F21ADBB8500E85E34 /* Build configuration list for PBXProject "Table Project - copy" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 8464AC6B21ADBB8500E85E34; + productRefGroup = 8464AC7521ADBB8500E85E34 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8464AC7321ADBB8500E85E34 /* Table Project */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8464AC7221ADBB8500E85E34 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8464AC8221ADBB8700E85E34 /* LaunchScreen.storyboard in Resources */, + 8464AC7F21ADBB8700E85E34 /* Assets.xcassets in Resources */, + 8464AC7D21ADBB8500E85E34 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8464AC7021ADBB8500E85E34 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8464AC9021ADC23300E85E34 /* PersonTableViewController.swift in Sources */, + 8464AC8C21ADC1D800E85E34 /* PersonCell.swift in Sources */, + 8464AC8A21ADC1BC00E85E34 /* AddPersonCell.swift in Sources */, + 8464AC9221ADC26200E85E34 /* Model.swift in Sources */, + 8464AC9421ADC27300E85E34 /* Person.swift in Sources */, + 841F119321B9CED7002BFD09 /* FirebaseItem.swift in Sources */, + 841F119521B9CF9D002BFD09 /* Firebase.swift in Sources */, + 8464AC7821ADBB8500E85E34 /* AppDelegate.swift in Sources */, + 8464AC8E21ADC20E00E85E34 /* DetailViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 8464AC7B21ADBB8500E85E34 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8464AC7C21ADBB8500E85E34 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 8464AC8021ADBB8700E85E34 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8464AC8121ADBB8700E85E34 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 8464AC8421ADBB8700E85E34 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 8464AC8521ADBB8700E85E34 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 8464AC8721ADBB8700E85E34 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Table Project/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.audreywelch.Table-Project"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 8464AC8821ADBB8700E85E34 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Table Project/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.audreywelch.Table-Project"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8464AC6F21ADBB8500E85E34 /* Build configuration list for PBXProject "Table Project - copy" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8464AC8421ADBB8700E85E34 /* Debug */, + 8464AC8521ADBB8700E85E34 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8464AC8621ADBB8700E85E34 /* Build configuration list for PBXNativeTarget "Table Project" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8464AC8721ADBB8700E85E34 /* Debug */, + 8464AC8821ADBB8700E85E34 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 8464AC6C21ADBB8500E85E34 /* Project object */; +} diff --git a/Table Project - Student & Cohort copy/Table Project - copy.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Table Project - Student & Cohort copy/Table Project - copy.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..98bf322 --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project - copy.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Table Project - Student & Cohort copy/Table Project - copy.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Table Project - Student & Cohort copy/Table Project - copy.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project - copy.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Table Project - Student & Cohort copy/Table Project - copy.xcodeproj/project.xcworkspace/xcuserdata/audreywelch.xcuserdatad/UserInterfaceState.xcuserstate b/Table Project - Student & Cohort copy/Table Project - copy.xcodeproj/project.xcworkspace/xcuserdata/audreywelch.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..625397b Binary files /dev/null and b/Table Project - Student & Cohort copy/Table Project - copy.xcodeproj/project.xcworkspace/xcuserdata/audreywelch.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Table Project - Student & Cohort copy/Table Project - copy.xcodeproj/xcuserdata/audreywelch.xcuserdatad/xcschemes/xcschememanagement.plist b/Table Project - Student & Cohort copy/Table Project - copy.xcodeproj/xcuserdata/audreywelch.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..71cc2ed --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project - copy.xcodeproj/xcuserdata/audreywelch.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + Table Project.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Table Project - Student & Cohort copy/Table Project/AddPersonCell.swift b/Table Project - Student & Cohort copy/Table Project/AddPersonCell.swift new file mode 100644 index 0000000..ee4763f --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project/AddPersonCell.swift @@ -0,0 +1,14 @@ +import UIKit + +class AddPersonCell: UITableViewCell { + static let reuseIdentifier = "addpersoncell" + + // Add text fields where people will enter info in + + @IBOutlet weak var nameField: UITextField! + @IBOutlet weak var cohortField: UITextField! + + + + +} diff --git a/Table Project - Student & Cohort copy/Table Project/AppDelegate.swift b/Table Project - Student & Cohort copy/Table Project/AppDelegate.swift new file mode 100644 index 0000000..2b6e83c --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project/AppDelegate.swift @@ -0,0 +1,16 @@ + + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + return true + } + + +} + diff --git a/Table Project - Student & Cohort copy/Table Project/Assets.xcassets/AppIcon.appiconset/Contents.json b/Table Project - Student & Cohort copy/Table Project/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d8db8d6 --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Table Project - Student & Cohort copy/Table Project/Assets.xcassets/Contents.json b/Table Project - Student & Cohort copy/Table Project/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Table Project - Student & Cohort copy/Table Project/Base.lproj/LaunchScreen.storyboard b/Table Project - Student & Cohort copy/Table Project/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..bfa3612 --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Table Project - Student & Cohort copy/Table Project/Base.lproj/Main.storyboard b/Table Project - Student & Cohort copy/Table Project/Base.lproj/Main.storyboard new file mode 100644 index 0000000..a666d58 --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project/Base.lproj/Main.storyboard @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Table Project - Student & Cohort copy/Table Project/DetailViewController.swift b/Table Project - Student & Cohort copy/Table Project/DetailViewController.swift new file mode 100644 index 0000000..6f8431d --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project/DetailViewController.swift @@ -0,0 +1,51 @@ +import UIKit + +class DetailViewController: UIViewController { + + @IBOutlet weak var cohortField: UITextField! + + @IBOutlet weak var nameField: UITextField! + + // This property allows the detail view controller to have a reference + // + var person: Person? + + @IBAction func save(_ sender: Any) { + // update our person in our model - but first add a property above (var person) because we don't have anything to work with + + // unwrap in order to check to see there is a person + guard let person = person else { return } + + // if user has deleted the information in the field, we don't want to save it - so we will make two tests + // check to see if there is text there + // check to see whether or not the text is empty + guard let name = nameField.text, !name.isEmpty else { return } + + // Perform our edits + person.name = name + person.cohort = cohortField.text ?? "" // nil coalescing, if the result is nil, give it a fallback value (an empty string) + + // Remote: + Model.shared.updatePerson(person: person) { + return + } + + + // Leave + navigationController?.popViewController(animated: true) // Optional/Conditional Chaining: if there is a value in the navigation controller, perform what comes after the question mark. otherwise don't do anything. + + } + + // Called just before view controller appears on-screen + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + // Ensure we have a person to present + guard let person = person else { return } + + // Populate the two text fields + nameField.text = person.name + cohortField.text = person.cohort + } +} + diff --git a/Table Project - Student & Cohort copy/Table Project/Firebase.swift b/Table Project - Student & Cohort copy/Table Project/Firebase.swift new file mode 100644 index 0000000..2936a09 --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project/Firebase.swift @@ -0,0 +1,178 @@ + +import Foundation + +// https://names-and-cohorts.firebaseio.com/ + +/* + + READ <- start the application + + POST <- create, creating a new record, Firebase will return a new record identifier + PUT <- update a specific record + DELETE <- delete a specific record + + */ + +class Firebase { + + // Base URL + static var baseURL: URL! { return URL(string:"https://names-and-cohorts.firebaseio.com/") } + + // Function that constructs a URL specific to my method, passes the method as a string. + static func requestURL(_ method: String, for recordIdentifier: String = "unknownid") -> URL { + switch method { + case "POST": + // Post to the main database. It will return a new record identifier + return baseURL.appendingPathExtension("json") + case "DELETE", "PUT", "GET": + // You need the record identifier in your URL, except if accessing all the records at once + return baseURL + // return the baseURL + the recordIdentifier + .json + .appendingPathComponent(recordIdentifier) + .appendingPathExtension("json") + default: + fatalError("Unknown request method: \(method)") + } + } + + // Handle a single request: meant for DELETE, PUT, POST. + static func processRequest( + method: String, // Get PUT POST or DELETE + for item: Item, // Conforms to Codable & FirebaseItem (so that we have a record id field) + with completion: @escaping (_ success: Bool) -> Void = { _ in } + ) { + + // Fetch appropriate request URL customized in method by creating a URL request + var request = URLRequest(url: requestURL(method, for: item.recordIdentifier)) + //set the http method + request.httpMethod = method + + // Encode this record + do { + // fill out the request body - needed when pushing info to a service + request.httpBody = try JSONEncoder().encode(item) + } catch { + NSLog("Unable to encode \(item): \(error)") + completion(false) + return + } + + + // Create a data task to perform request, then add a completion handler + let dataTask = URLSession.shared.dataTask(with: request) { data, _, error in + + // Fail on error + if let error = error { + NSLog("Server \(method) error: \(error)") + completion(false) // run a completion + return + } + + // If we have reached this point, we haven't failed and there is data + + // Handle PUT, GET, DELETE, then leave the function + guard method == "POST" else { + completion(true) // Successful with our completion and leave + return + } + + // If it is POST, then we continue + + // Process POST requests by posting it, getting the record id, adding record id to device, save it again + + // Fetch identifier from POST + // Look at data and make sure it is a dictionary with a name and record identifier + guard let data = data else { + NSLog("Invalid server response data") + completion(false) + return + } + + // If we do have the data, decode + do { + + // POST request returns `["name": recordIdentifier]` - Store the record identifier, not the name + let nameDict = try JSONDecoder().decode([String: String].self, from: data) + // make sure there is an entry for the record ID + guard let name = nameDict["name"] else { + completion(false) // if not, fail and leave + return + } + + // Update record and store that name. POST is now successful and includes the recordIdentifier as part of the item record. + item.recordIdentifier = name // set it to the recordIdentifier + processRequest(method: "PUT", for: item)// PUT it back to the server + completion(true) // complete + + } catch { + + // if error during decoding, log the error, and completion false + NSLog("Error decoding JSON response: \(error)") + completion(false) + return + } + + } + + dataTask.resume() // data task doesn't do anything until we resume + + } + + // Three useful entry points that we will call + // Delete, Save, Fetch + + static func delete(item: Item, completion: @escaping (_ success: Bool) -> Void = { _ in}) { + // call the method DELETE for that item + processRequest(method: "DELETE", for: item, with: completion) + } + + static func save(item: Item, completion: @escaping (_ success: Bool) -> Void = { _ in}) { + // if nothing in the recordIdentifier, POST it + switch item.recordIdentifier.isEmpty { + case true: // POST, new record, "create" + processRequest(method: "POST", for: item, with: completion) + // if there is, replace it instead of store it + case false: // PUT, existing record, "update" + processRequest(method: "PUT", for: item, with: completion) + } + } + + // Fetch all records and pass them to sender via completion handler + static func fetchRecords(completion: @escaping ([Item]?) -> Void) { + // append the path extension json + let requestURL = baseURL.appendingPathExtension("json") + // create a data task, don't need a request because it's a reading method + let dataTask = URLSession.shared.dataTask(with: requestURL) { data, _, error in + + // make sure I don't have an error and there is data for me to use + guard error == nil, let data = data else { + if let error = error { + NSLog("Error fetching entries: \(error)") + } + completion(nil) + return + } + + // if it does work... + do { + + // decode string: item - record ID followed by the item + let recordDict = try JSONDecoder().decode([String: Item].self, from: data) + // extra safety + for (key, value) in recordDict { value.recordIdentifier = key } + // Map - We don't care about the record ID, so take the value out of the pair and that becomes the list of records + // This converts the [[id: item]] array of dictionary entries into an array of items + let records = recordDict.map({ $0.value }) + // call completion with my list of records + completion(records) + } catch { + NSLog("Error decoding received data: \(error)") + // Start it off with an empty list + completion([]) + } + } + + dataTask.resume() + } + +} diff --git a/Table Project - Student & Cohort copy/Table Project/FirebaseItem.swift b/Table Project - Student & Cohort copy/Table Project/FirebaseItem.swift new file mode 100644 index 0000000..1d8ea95 --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project/FirebaseItem.swift @@ -0,0 +1,8 @@ + +import Foundation + +// This is a protocol +// Anything that supplies a record identifier and conforms to the protocol, we can use +protocol FirebaseItem: class { + var recordIdentifier: String { get set } +} diff --git a/Table Project - Student & Cohort copy/Table Project/Info.plist b/Table Project - Student & Cohort copy/Table Project/Info.plist new file mode 100644 index 0000000..16be3b6 --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Table Project - Student & Cohort copy/Table Project/Model.swift b/Table Project - Student & Cohort copy/Table Project/Model.swift new file mode 100644 index 0000000..80eab93 --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project/Model.swift @@ -0,0 +1,95 @@ +import Foundation + +class Model { + // MARK: Singleton + static let shared = Model() + private init() {} + + // MARK: Core Storage + // Store our people + var people: [Person] = [] + + // Basic information that helps our model to manage people and add to our table + + // MARK: - Model Management (CRUD) + + // add person - C + func addNewPerson(person: Person, completion: @escaping () -> Void) { + + // Local: Append it to our devices array + people.append(person) + + // Remote: Save it by sending it to firebase + Firebase.save(item: person) { success in + guard success else { return } + + // otherwise do the completion on the main thread + DispatchQueue.main.async { completion() } + } + } + + func updatePerson(person: Person, completion: @escaping () -> Void ) { + + // Get a person + //let person = people[indexPath.row] + + // Local - change the person string + //person.name = + + // Remote + Firebase.save(item: person) { success in + guard success else { return } + DispatchQueue.main.async { completion() } + + } + } + + // remove person - D + func removePerson(at indexPath: IndexPath, completion: @escaping () -> Void ) { + + // Get a person + let person = people[indexPath.row] + + // Local + people.remove(at: indexPath.row) + + // Remote + Firebase.delete(item: person) { success in + guard success else { return } + DispatchQueue.main.async { completion() } + } + } + + // count - R + func numberOfPeople() -> Int { + return people.count + } + + // move - U + func movePerson(at index: Int, to newIndex: Int) { + // TODO: Implement this + } + + // find which person is at an index - R + func person(at index: Int) -> Person { + return people[index] + } + + + + // MARK: Data Persistence + + // save data + func saveData() { + + // TODO: Implement this + // This shows up in the Table of Contents to the right of Model.swift at the top to show you that you need to do something + } + + // load data + func loadData() { + // TODO: Implement this + } + + +} diff --git a/Table Project - Student & Cohort copy/Table Project/Person.swift b/Table Project - Student & Cohort copy/Table Project/Person.swift new file mode 100644 index 0000000..8916e17 --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project/Person.swift @@ -0,0 +1,16 @@ +import Foundation + +class Person: Codable, FirebaseItem { + var name: String // = "" + var cohort: String + var recordIdentifier = "" + + //initializer + init(name: String, cohort: String) { + self.name = name + self.cohort = cohort + + // Fancy way to intiliaze + //(self.name, self.cohort) = (name, cohort) + } +} diff --git a/Table Project - Student & Cohort copy/Table Project/PersonCell.swift b/Table Project - Student & Cohort copy/Table Project/PersonCell.swift new file mode 100644 index 0000000..238600c --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project/PersonCell.swift @@ -0,0 +1,10 @@ +import UIKit + +class PersonCell: UITableViewCell { + static let reuseIdentifier = "personcell" + + // add labels + @IBOutlet var nameLabel: UILabel! + @IBOutlet var cohortLabel: UILabel! + +} diff --git a/Table Project - Student & Cohort copy/Table Project/PersonTableViewController.swift b/Table Project - Student & Cohort copy/Table Project/PersonTableViewController.swift new file mode 100644 index 0000000..28b1469 --- /dev/null +++ b/Table Project - Student & Cohort copy/Table Project/PersonTableViewController.swift @@ -0,0 +1,131 @@ +import UIKit + +// +class PersonTableViewController: UITableViewController { + + + // reload when we return from the detail view (ensure that the table view reloads whenever we return from the detail view controller) + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + + // Fetch records from Firebase and reload the table view + Firebase.fetchRecords { people in + if let people = people { + Model.shared.people = people + + DispatchQueue.main.async { + self.tableView.reloadData() + + } + } + } + + + } + + // Number of sections + override func numberOfSections(in tableView: UITableView) -> Int { + return 2 + } + + // Number of rows + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + // switch statement + switch section { + case 0: + return 1 // return 1 row in section 0 (the add person section) + case 1: + return Model.shared.numberOfPeople() + default: + fatalError("Invalid section number") + } + } + + // Cell contents + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + if indexPath.section == 0 { + // get a cell + let cell = tableView.dequeueReusableCell(withIdentifier: AddPersonCell.reuseIdentifier, for: indexPath) + return cell + } + + // Handle PersonCell from here forward + // conditional casting because we need to specialize the cell + guard let cell = tableView.dequeueReusableCell(withIdentifier: PersonCell.reuseIdentifier, for: indexPath) as? PersonCell + else { fatalError("Unable to dequeue person cell") } + + // put information into the person cell - populating the person cell + // grab a person + let person = Model.shared.person(at: indexPath.row) + cell.nameLabel.text = person.name + cell.cohortLabel.text = person.cohort + + return cell + } + + // Delete a person, update Firebase, update model, and reload data + override func tableView(_ tableViewPassedToUs: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + + guard editingStyle == .delete else { return } + + Model.shared.removePerson(at: indexPath) { + self.tableView.reloadData() + } + } + + // Add a new person, store it at Firebase, update model, and reload data + @IBAction func add() { + // how many addPersonCells will we have at one time? Always guaranteed that there will be one. So we can grab that single add person cell and cast it to the right type + guard let cell = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? AddPersonCell + else { return } + + // grab text from the nameField, check to make sure it is not empty, and if it's not empty, then continue (ensure that the name is not empty) + guard let name = cell.nameField.text, !name.isEmpty + else { return } + + // If nil, set it to the empty string (ensure that we ahve a string and not nil + let cohort = cell.cohortField.text ?? "" + + // construct a person + let person = Person(name: name, cohort: cohort) + + // Clear the textField after entered and saved + cell.nameField.text = "" + cell.cohortField.text = "" + + // add to the model + Model.shared.addNewPerson(person: person) { + self.tableView.reloadData() + } + + } + + + + // Cell height information - set custom heights for each section + override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + switch indexPath.row { + case 0: + return 150 + default: + return 160 + } + } + + // Prepare for segue allows us to pass forward information that the segue's destination can use to set up its views + // Rule of thumb: NEVER EDIT ANOTHER VIEW CONTROLLER'S FIELDS DIRECTLY, because it might not have enough time to load between screens and you will crash + // Sending information is safe, editing views is unsafe + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + // fetch index path + // fetch view controller + guard + let destination = segue.destination as? DetailViewController, + let indexPath = tableView.indexPathForSelectedRow + else { return } + + let person = Model.shared.person(at: indexPath.row) + destination.person = person + } + +}