diff --git a/Art/Lambda_Logo_Full.pdf b/Art/Lambda_Logo_Full.pdf deleted file mode 100644 index 3a8651d4..00000000 Binary files a/Art/Lambda_Logo_Full.pdf and /dev/null differ diff --git a/Prep/README.md b/Prep/README.md deleted file mode 100644 index bf176dd0..00000000 --- a/Prep/README.md +++ /dev/null @@ -1,87 +0,0 @@ -# Preparing for Day 1 of Lambda School's iOS Curriculum - -Here's an overview of material you should have mastered by day 1 of the iOS program. Feel free to skip any material you already know or you already covered in Lambda's "Intro to iOS" course. - -Make sure you don't skimp on the command line and version control material. You'll need all these skills right from the start but these two in particular are fundamental to working through daily challenges. - -## Preparing Equipment - -Your computer should be ready to begin development on day one: - -* Your Mac should be running the latest operating system. -* Establish an Apple ID. Visit http://appleid.apple.com. -* Install and run the latest version of Xcode. Visit https://developer.apple.com/download/. Make sure to run Xcode, confirm that it works, and allow it to install supplemental command line tools. -* Confirm that Xcode has installed `git`: open a terminal window (/Applications/Utilities/Terminal.app) and entering `git --version` at the command line. You should be running git version 2.18 or later. -* Test the simulator: create a new empty project, compile, and run it. -* Create a free GitHub account. Visit github.com. (https://www.youtube.com/watch?v=GeyDJAwTEO8) -* Set up your git identity (https://www.youtube.com/watch?v=U5flh0sRGBU) and global ignore file on your computer (https://www.youtube.com/watch?v=0fzNSluYjNM). - -## Cross Platform Skills - -Train your essential cross-platform skills: Command line, Version control, Markdown, and Being your own expert. - -### Command Line - -The text-based command line is a common platform for most development environments. Learning how to use this text-based interface enables you to interact directly with your computer's file system and access many developer-facing tools. Lambda's track focuses on the bash shell. - -* **Type and edit text into the bash command line interpreter shell** https://youtu.be/mq2RWXEfJ6s -* **Explain what the command line is and the advantages it offers** https://youtu.be/tCrkdcDycjA -* **Understand, navigate, and use shell history to repeat previous commands** https://youtu.be/urz7sdC556s -* **Navigate through the Unix file system and list directory files.** https://www.youtube.com/watch?v=mFh4vbOH81Q -* **Perform basic file and directory management tasks** https://www.youtube.com/watch?v=wvTTkiPyjCs -* **Command line cheat sheet (via Tower)**: https://www.git-tower.com/blog/command-line-cheat-sheet/ -* **The Unix Family Tree**: https://www.computerworld.com/article/2524660/operating-systems/the-unix-family-tree.html - -## Version Control - -Version control manages the distributed development of software, enabling collaboration, safe feature development, and snapshotted backups. Lambda's track uses the git revision control system and GitHub.com hosting. - -* **Understand and explain the goals of version control** https://www.youtube.com/watch?v=aKmW0oe_aEU&t=0s -* **Create a project starting at GitHub** https://www.youtube.com/watch?v=txRu0pWqR_c -* **Connect an existing project to GitHub** https://www.youtube.com/watch?v=D7ytcVKsSB0 -* **Fork and contribute to a project** https://www.youtube.com/watch?v=ggP3jBpWZwQ -* **Create good commits** https://www.youtube.com/watch?v=0jO9BUQUcsY -* **Git cheat sheet (via Tower)**: https://www.git-tower.com/blog/git-cheat-sheet -* **19 Git Tips for Everday Use**: https://www.alexkras.com/19-git-tips-for-everyday-use/ - -## Markdown - -Markdown defines a plain-text format used across the industry for writing rich text documents. Lambda's track uses the Common Mark standard from commonmark.org. - -* **Learn Markdown Basics**: https://youtu.be/rU7KfaEvYD4 -* **Common Mark**: https://commonmark.org/help/ -* **MacDown open source Markdown editor**: https://macdown.uranusjr.com - -## Being Your Own Expert - -New developers often ask for help, mostly for technical issues, and often for material they are already capable of finding on their own. Finding your own answers and helping yourself is a skill set that nutures your own capabilities. Building these competencies involves insight into understanding what you've already tried, good search skills, and the ability to meticulously self-inspect. Problem solving isn't a human quality one is born with, it is a skill set that is nutured and grown. - -* **Smart Questions** http://www.catb.org/%7Eesr/faqs/smart-questions.html -* **Getting Answers** http://www.mikeash.com/getting_answers.html -* **What Have you Tried** http://whathaveyoutried.com -* **Rubber Duck Problem Solving** https://blog.codinghorror.com/rubber-duck-problem-solving - -## Lambda Skills - -Master your basic Lambda Skills. Understand how the 15-week iOS curriculum works and be able to use Zoom to communicate with PMs and Instructors: - -* **Understand how the 15-week iOS Curriculum works**. https://youtu.be/P7qKjBbDkcY -* **Master the Zoom online meeting tool**. Watch these videos: Join a Zoom meeting: https://support.zoom.us/hc/en-us/articles/201362193-How-Do-I-Join-A-Meeting- and Zoom meeting controls: https://support.zoom.us/hc/en-us/articles/206618765-Zoom-Video-Tutorials. - -## iOS Tools - -Become familiar as possible with your base iOS tool set: Xcode, Playground, Simulator, and IB. - -* **Getting Started with iOS Development**: https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/ -* **I Have This Idea for An App**: https://developer.apple.com/videos/play/wwdc2018/203/ -* **Getting the Most out of Playgrounds in Xcode**: https://developer.apple.com/videos/play/wwdc2018/402/ - -## Swift Language - -Swift is the primary iOS development language. Practice your skills and grow your language mastery with these Swift resources. - -* **The Swift Programming Language** (iBooks, free) -* **Using Swift with Cocoa and Objective C** (iBooks, free) -* **Everyone Can Code: App Development with Swift** (iBooks, free) -* **Learn to Code** (iPad only, Swift Playgrounds for iOS, free) - diff --git a/README.md b/README.md index 47338ac3..46e4b08d 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,66 @@ -# ios-projects +# RPNCalculator-ObjC -This repository contains instructions for Lambda iOS project assignments. +## Introduction -## Sprint 1 - iOS Fundamentals I +The goal of this project is to continue to familarize yourself with the Objective-C language by recreating your RPN Calculator in ObjC. It will help you practice the concepts learned today, including primitives vs. classes, `NSNumber`, and mutability. -### Modules +After completing the lesson material and this project, you should be able to: -1. [UINavigationController and MVC - Collect](https://github.com/LambdaSchool/ios-projects/tree/master/Sprint%201/Module%201%20-%20MVC%20and%20UINavigationController/) -2. Git -3. Intermediate UITableView, Protocols, and Delegates -4. View Controller Containment and UITabBarController +- explain the difference between a class and a primitive, along with common examples of each +- understand and briefly explain the concept of pointers +- use NSNumber to convert between primitive numbers and objects +- understand and explain the difference between immutable and mutable classes +- use copy/mutableCopy to convert from immutable to mutable and vice versa -## Sprint 2 - iOS Fundamentals II +You are welcome and encouraged to use your existing Swift solution to this project as inspiration and help when implementing this project. However, instead of using a custom framework and importing it into your project, you will be creating all the logic in your own project. -### Modules +## Instructions -1. Auto Layout -2. Basic Persistence -3. User Permission and Local Notifications -4. User Defaults and Collection Views +You will need to create your own Xcode project and repository. Commit regularly as you complete the requirements in this project. -## Sprint 3 - Networking +### Part 1 - Storyboard -### Modules +1. Delete the existing ViewController files in your project and in the storyboard. +2. Drag out a new `UIViewController` and add the necessary buttons and textField for your RPN Calculator (Hint: Use the tag property for the numbers rather than creating actions for all of them). + - You can reference the layout of the ViewController in your other project to make sure you have all the buttons laid out correctly + - You could even copy the Main.storyboard from the Swift version and simply change the class of the view controller. +3. Make sure your view controller is the initial view controller. -1. Closures and GCD -2. Enums and REST Basics -3. PUT/POST -4. TBD +### Part 2 - Model/Model Controller -## Sprint 4 - Core Data +1. Create a new file called `XXXStack` (XXX = your class prefix). + - `.h` file: + - Declare the methods to push, pop and peek the stack. + - Declare an `initWith` initalizer that takes an `NSArray`. + - `.m` file: + - Create a private `NSMutableArray` property called `values` that will be used every time your methods are called. + - Implement the methods you declared in your `.h` file to alter your `values` array. + - Inside of your `initWith...` initializer assign a `mutableCopy` of your `array` property to your internal `values` array. +2. Create another file called `XXXCalculator`. + - `.h` file: + - Create an enum for the operations that will be calculated using `typedef NS_ENUM(rawType, nameOfEnum) {};`. Inside the curly braces you will just need to separate each item with a comma. + - Declare three methods `- (void)pushNumber:(double)value;`, `- (void)applyOperator:(YourEnumType)operator;` and `- (void)clear;`. + - Also declare a `topValue` property that will be a computed property in your `.m` file. + - `.m` file: + - Every Calculator should have one `Stack` and only that calculator should know about it's `Stack`. Create a property `XXXStack *stack` in this file that we will use to implement the methods. + - Implement the methods in this file by accessing the methods on your `Stack` model. +3. Create a new file called `XXXDigitAccumulator`. This will be used to accumulate digits as they're entered, then convert the list of digits into a number. + - `.h` file - Your `XXXDigitAccumulator` class should have three methods and one property: + - `-addDigitWithNumericValue:` + - `-addDecimalPoint` + - `-clear` + - `value` - a readonly `double` property + - `.m` file - Implement the digit accumulator. Use the Swift version for inspiration. Note that the Swift version uses an enum with associated values for digits, which you can't do directly in Objective-C. You'll have to come up with another solution! **Note:** For now, for simplicity, you can ignore the error handling that the Swift version does. Ignore repeated decimal digits instead of throwing an error, and assume that values will be in the range 0-9. + -### Modules +### Part 3 - View Controller -1. Core Data I - Basics -2. Core Data II -3. Core Data III - Multiple MOCs +Implement the view controller. Use the Swift version for inspiration. The implementation here will be nearly identical except that it will be in Objective-C. + + +## Go Further + +If you finish early or want to push yourself, here are a few additional features you can implement: + +- Split the logic out into a framework as we did in Swift +- Implement error handling. What should happen if the user enters more than one decimal digit? Note that `@throws` is not available in Objective-C. Objective-C uses a different mechanism for error handling, which we'll learn about in a future lesson. Without knowing that, how would you solve this? \ No newline at end of file diff --git a/RPNCalc-C/RPNCalc-C.xcodeproj/project.pbxproj b/RPNCalc-C/RPNCalc-C.xcodeproj/project.pbxproj new file mode 100644 index 00000000..35226380 --- /dev/null +++ b/RPNCalc-C/RPNCalc-C.xcodeproj/project.pbxproj @@ -0,0 +1,421 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + B5F34702234FC69E00FCD842 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B5F34701234FC69E00FCD842 /* AppDelegate.m */; }; + B5F34705234FC69E00FCD842 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B5F34704234FC69E00FCD842 /* SceneDelegate.m */; }; + B5F3470B234FC69E00FCD842 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B5F34709234FC69E00FCD842 /* Main.storyboard */; }; + B5F3470D234FC6A000FCD842 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B5F3470C234FC6A000FCD842 /* Assets.xcassets */; }; + B5F34710234FC6A000FCD842 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B5F3470E234FC6A000FCD842 /* LaunchScreen.storyboard */; }; + B5F34713234FC6A000FCD842 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B5F34712234FC6A000FCD842 /* main.m */; }; + B5F34724234FD24E00FCD842 /* JSCalculatorVC.m in Sources */ = {isa = PBXBuildFile; fileRef = B5F34723234FD24E00FCD842 /* JSCalculatorVC.m */; }; + B5F34727234FD2D300FCD842 /* JSStack.m in Sources */ = {isa = PBXBuildFile; fileRef = B5F34726234FD2D300FCD842 /* JSStack.m */; }; + B5F3472A234FDE3200FCD842 /* JSCalculator.m in Sources */ = {isa = PBXBuildFile; fileRef = B5F34729234FDE3200FCD842 /* JSCalculator.m */; }; + B5F3472D234FF96100FCD842 /* JSDigitAccumulator.m in Sources */ = {isa = PBXBuildFile; fileRef = B5F3472C234FF96000FCD842 /* JSDigitAccumulator.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + B5F346FD234FC69E00FCD842 /* RPNCalc-C.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RPNCalc-C.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + B5F34700234FC69E00FCD842 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + B5F34701234FC69E00FCD842 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + B5F34703234FC69E00FCD842 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; + B5F34704234FC69E00FCD842 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; + B5F3470A234FC69E00FCD842 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + B5F3470C234FC6A000FCD842 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + B5F3470F234FC6A000FCD842 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + B5F34711234FC6A000FCD842 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B5F34712234FC6A000FCD842 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + B5F34722234FD24E00FCD842 /* JSCalculatorVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSCalculatorVC.h; sourceTree = ""; }; + B5F34723234FD24E00FCD842 /* JSCalculatorVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSCalculatorVC.m; sourceTree = ""; }; + B5F34725234FD2D300FCD842 /* JSStack.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSStack.h; sourceTree = ""; }; + B5F34726234FD2D300FCD842 /* JSStack.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSStack.m; sourceTree = ""; }; + B5F34728234FDE3200FCD842 /* JSCalculator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSCalculator.h; sourceTree = ""; }; + B5F34729234FDE3200FCD842 /* JSCalculator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSCalculator.m; sourceTree = ""; }; + B5F3472B234FF96000FCD842 /* JSDigitAccumulator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSDigitAccumulator.h; sourceTree = ""; }; + B5F3472C234FF96000FCD842 /* JSDigitAccumulator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSDigitAccumulator.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B5F346FA234FC69E00FCD842 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + B5F346F4234FC69E00FCD842 = { + isa = PBXGroup; + children = ( + B5F346FF234FC69E00FCD842 /* RPNCalc-C */, + B5F346FE234FC69E00FCD842 /* Products */, + ); + sourceTree = ""; + }; + B5F346FE234FC69E00FCD842 /* Products */ = { + isa = PBXGroup; + children = ( + B5F346FD234FC69E00FCD842 /* RPNCalc-C.app */, + ); + name = Products; + sourceTree = ""; + }; + B5F346FF234FC69E00FCD842 /* RPNCalc-C */ = { + isa = PBXGroup; + children = ( + B5F3471A234FC6F700FCD842 /* Helpers */, + B5F3471B234FC6F800FCD842 /* Model Controllers */, + B5F3471C234FC6F800FCD842 /* Models */, + B5F3471E234FC6F800FCD842 /* Resources */, + B5F34719234FC6F700FCD842 /* Storyboards */, + B5F3471F234FC6F800FCD842 /* View Controllers */, + B5F3471D234FC6F800FCD842 /* Views */, + ); + path = "RPNCalc-C"; + sourceTree = ""; + }; + B5F34719234FC6F700FCD842 /* Storyboards */ = { + isa = PBXGroup; + children = ( + B5F34709234FC69E00FCD842 /* Main.storyboard */, + B5F3470E234FC6A000FCD842 /* LaunchScreen.storyboard */, + ); + path = Storyboards; + sourceTree = ""; + }; + B5F3471A234FC6F700FCD842 /* Helpers */ = { + isa = PBXGroup; + children = ( + B5F34725234FD2D300FCD842 /* JSStack.h */, + B5F34726234FD2D300FCD842 /* JSStack.m */, + B5F34728234FDE3200FCD842 /* JSCalculator.h */, + B5F34729234FDE3200FCD842 /* JSCalculator.m */, + B5F3472B234FF96000FCD842 /* JSDigitAccumulator.h */, + B5F3472C234FF96000FCD842 /* JSDigitAccumulator.m */, + ); + path = Helpers; + sourceTree = ""; + }; + B5F3471B234FC6F800FCD842 /* Model Controllers */ = { + isa = PBXGroup; + children = ( + ); + path = "Model Controllers"; + sourceTree = ""; + }; + B5F3471C234FC6F800FCD842 /* Models */ = { + isa = PBXGroup; + children = ( + ); + path = Models; + sourceTree = ""; + }; + B5F3471D234FC6F800FCD842 /* Views */ = { + isa = PBXGroup; + children = ( + ); + path = Views; + sourceTree = ""; + }; + B5F3471E234FC6F800FCD842 /* Resources */ = { + isa = PBXGroup; + children = ( + B5F34700234FC69E00FCD842 /* AppDelegate.h */, + B5F34701234FC69E00FCD842 /* AppDelegate.m */, + B5F34703234FC69E00FCD842 /* SceneDelegate.h */, + B5F34704234FC69E00FCD842 /* SceneDelegate.m */, + B5F3470C234FC6A000FCD842 /* Assets.xcassets */, + B5F34711234FC6A000FCD842 /* Info.plist */, + B5F34712234FC6A000FCD842 /* main.m */, + ); + path = Resources; + sourceTree = ""; + }; + B5F3471F234FC6F800FCD842 /* View Controllers */ = { + isa = PBXGroup; + children = ( + B5F34722234FD24E00FCD842 /* JSCalculatorVC.h */, + B5F34723234FD24E00FCD842 /* JSCalculatorVC.m */, + ); + path = "View Controllers"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + B5F346FC234FC69E00FCD842 /* RPNCalc-C */ = { + isa = PBXNativeTarget; + buildConfigurationList = B5F34716234FC6A000FCD842 /* Build configuration list for PBXNativeTarget "RPNCalc-C" */; + buildPhases = ( + B5F346F9234FC69E00FCD842 /* Sources */, + B5F346FA234FC69E00FCD842 /* Frameworks */, + B5F346FB234FC69E00FCD842 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "RPNCalc-C"; + productName = "RPNCalc-C"; + productReference = B5F346FD234FC69E00FCD842 /* RPNCalc-C.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B5F346F5234FC69E00FCD842 /* Project object */ = { + isa = PBXProject; + attributes = { + CLASSPREFIX = JS; + LastUpgradeCheck = 1110; + ORGANIZATIONNAME = Lambda; + TargetAttributes = { + B5F346FC234FC69E00FCD842 = { + CreatedOnToolsVersion = 11.1; + }; + }; + }; + buildConfigurationList = B5F346F8234FC69E00FCD842 /* Build configuration list for PBXProject "RPNCalc-C" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = B5F346F4234FC69E00FCD842; + productRefGroup = B5F346FE234FC69E00FCD842 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B5F346FC234FC69E00FCD842 /* RPNCalc-C */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + B5F346FB234FC69E00FCD842 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B5F34710234FC6A000FCD842 /* LaunchScreen.storyboard in Resources */, + B5F3470D234FC6A000FCD842 /* Assets.xcassets in Resources */, + B5F3470B234FC69E00FCD842 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + B5F346F9234FC69E00FCD842 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B5F34702234FC69E00FCD842 /* AppDelegate.m in Sources */, + B5F34713234FC6A000FCD842 /* main.m in Sources */, + B5F3472A234FDE3200FCD842 /* JSCalculator.m in Sources */, + B5F34705234FC69E00FCD842 /* SceneDelegate.m in Sources */, + B5F34727234FD2D300FCD842 /* JSStack.m in Sources */, + B5F34724234FD24E00FCD842 /* JSCalculatorVC.m in Sources */, + B5F3472D234FF96100FCD842 /* JSDigitAccumulator.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + B5F34709234FC69E00FCD842 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + B5F3470A234FC69E00FCD842 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + B5F3470E234FC6A000FCD842 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + B5F3470F234FC6A000FCD842 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + B5F34714234FC6A000FCD842 /* 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; + 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 = 13.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + B5F34715234FC6A000FCD842 /* 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; + 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 = 13.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + B5F34717234FC6A000FCD842 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = VXUQXR6S56; + INFOPLIST_FILE = "$(SRCROOT)/RPNCalc-C/Resources/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.lambda.RPNCalc-C"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + B5F34718234FC6A000FCD842 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = VXUQXR6S56; + INFOPLIST_FILE = "$(SRCROOT)/RPNCalc-C/Resources/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.lambda.RPNCalc-C"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B5F346F8234FC69E00FCD842 /* Build configuration list for PBXProject "RPNCalc-C" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B5F34714234FC6A000FCD842 /* Debug */, + B5F34715234FC6A000FCD842 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B5F34716234FC6A000FCD842 /* Build configuration list for PBXNativeTarget "RPNCalc-C" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B5F34717234FC6A000FCD842 /* Debug */, + B5F34718234FC6A000FCD842 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B5F346F5234FC69E00FCD842 /* Project object */; +} diff --git a/RPNCalc-C/RPNCalc-C/Helpers/JSCalculator.h b/RPNCalc-C/RPNCalc-C/Helpers/JSCalculator.h new file mode 100644 index 00000000..3eb9b774 --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Helpers/JSCalculator.h @@ -0,0 +1,26 @@ +// +// JSCalculator.h +// RPNCalc-C +// +// Created by Jeffrey Santana on 10/10/19. +// Copyright © 2019 Lambda. All rights reserved. +// + +#import + +@interface JSCalculator : NSObject + +typedef NS_ENUM(NSInteger, CalcOperation) { + add, + subtract, + multiply, + divide +}; + +@property (assign, nonatomic) int topValue; + +- (void)pushNumber:(double)value; +- (void)applyOperator:(CalcOperation)operator; +- (void)clear; + +@end diff --git a/RPNCalc-C/RPNCalc-C/Helpers/JSCalculator.m b/RPNCalc-C/RPNCalc-C/Helpers/JSCalculator.m new file mode 100644 index 00000000..f749d250 --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Helpers/JSCalculator.m @@ -0,0 +1,55 @@ +// +// JSCalculator.m +// RPNCalc-C +// +// Created by Jeffrey Santana on 10/10/19. +// Copyright © 2019 Lambda. All rights reserved. +// + +#import "JSCalculator.h" +#import "JSStack.h" + +@interface JSCalculator () + +@property JSStack *stack; + +@end + +@implementation JSCalculator + +- (void)pushNumber:(double)value { + [self.stack push:value]; +} + +- (void)applyOperator:(CalcOperation)operator { + double result = 0; + +// if ([self.stack count] > 2) { + double rhsOperand = [self.stack pop]; + double lhsOperand = [self.stack pop]; + if (rhsOperand || lhsOperand) { + switch (operator) { + case add: + result = lhsOperand + rhsOperand; + break; + case subtract: + result = lhsOperand - rhsOperand; + break; + case multiply: + result = lhsOperand * rhsOperand; + break; + case divide: + result = lhsOperand / rhsOperand; + break; + } + + [self.stack push:result]; + } +// } +} + +- (void)clear { + self.stack = [[JSStack alloc] init]; +} + +@end diff --git a/RPNCalc-C/RPNCalc-C/Helpers/JSDigitAccumulator.h b/RPNCalc-C/RPNCalc-C/Helpers/JSDigitAccumulator.h new file mode 100644 index 00000000..b918e1ff --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Helpers/JSDigitAccumulator.h @@ -0,0 +1,24 @@ +// +// JSDigitAccumulator.h +// RPNCalc-C +// +// Created by Jeffrey Santana on 10/10/19. +// Copyright © 2019 Lambda. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface JSDigitAccumulator : NSObject + +@property (assign, nonatomic) double value; +@property (assign, nonatomic) NSString *stringValue; + +- (void)addDigitWithNumericValue:(NSInteger)value; +- (void)addDecimalPoint; +- (void)clear; + +@end + +NS_ASSUME_NONNULL_END diff --git a/RPNCalc-C/RPNCalc-C/Helpers/JSDigitAccumulator.m b/RPNCalc-C/RPNCalc-C/Helpers/JSDigitAccumulator.m new file mode 100644 index 00000000..c423ebbe --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Helpers/JSDigitAccumulator.m @@ -0,0 +1,44 @@ +// +// JSDigitAccumulator.m +// RPNCalc-C +// +// Created by Jeffrey Santana on 10/10/19. +// Copyright © 2019 Lambda. All rights reserved. +// + +#import "JSDigitAccumulator.h" + +@interface JSDigitAccumulator () + +@property NSMutableArray *digits; + +@end + +@implementation JSDigitAccumulator + +- (double)value { + return [self.stringValue doubleValue]; +}; + +- (NSString *)stringValue { + return [self.digits componentsJoinedByString:@""]; +} + +- (void)addDigitWithNumericValue:(NSInteger)value { + if (value < 0 || value > 9) { + NSString *digit = [NSString stringWithFormat:@"%li", (long)value]; + [self.digits addObject:digit]; + } +} + +- (void)addDecimalPoint { + if (![self.digits containsObject:@"."]) { + [self.digits addObject:@"."]; + } +} + +- (void)clear { + [self.digits removeAllObjects]; +} + +@end diff --git a/RPNCalc-C/RPNCalc-C/Helpers/JSStack.h b/RPNCalc-C/RPNCalc-C/Helpers/JSStack.h new file mode 100644 index 00000000..fc8ee4ef --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Helpers/JSStack.h @@ -0,0 +1,25 @@ +// +// JSStack.h +// RPNCalc-C +// +// Created by Jeffrey Santana on 10/10/19. +// Copyright © 2019 Lambda. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface JSStack : NSObject + +-(instancetype)initWithArray:(NSArray *)array; + +@property (assign, nonatomic) int count; + +-(void)push:(double)value; +-(double)pop; +-(double)peek; + +@end + +NS_ASSUME_NONNULL_END diff --git a/RPNCalc-C/RPNCalc-C/Helpers/JSStack.m b/RPNCalc-C/RPNCalc-C/Helpers/JSStack.m new file mode 100644 index 00000000..c2951a38 --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Helpers/JSStack.m @@ -0,0 +1,46 @@ +// +// JSStack.m +// RPNCalc-C +// +// Created by Jeffrey Santana on 10/10/19. +// Copyright © 2019 Lambda. All rights reserved. +// + +#import "JSStack.h" + +@interface JSStack () + +@property NSMutableArray *values; + +@end + +@implementation JSStack + +- (int)count { + return (int)self.values.count; +} + +- (instancetype)initWithArray:(NSArray *)array { + self = [super init]; + if (self) { + [_values addObjectsFromArray:array]; + } + return self; +} + +- (void)push:(double)value { + [self.values addObject:[NSNumber numberWithDouble:value]]; +} + +- (double)pop { + double value = [self.values.lastObject doubleValue]; + [self.values removeLastObject]; + + return value; +} + +- (double)peek { + return [self.values.lastObject doubleValue]; +} + +@end diff --git a/RPNCalc-C/RPNCalc-C/Resources/AppDelegate.h b/RPNCalc-C/RPNCalc-C/Resources/AppDelegate.h new file mode 100644 index 00000000..86963353 --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Resources/AppDelegate.h @@ -0,0 +1,15 @@ +// +// AppDelegate.h +// RPNCalc-C +// +// Created by Jeffrey Santana on 10/10/19. +// Copyright © 2019 Lambda. All rights reserved. +// + +#import + +@interface AppDelegate : UIResponder + + +@end + diff --git a/RPNCalc-C/RPNCalc-C/Resources/AppDelegate.m b/RPNCalc-C/RPNCalc-C/Resources/AppDelegate.m new file mode 100644 index 00000000..a8b517a1 --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Resources/AppDelegate.m @@ -0,0 +1,41 @@ +// +// AppDelegate.m +// RPNCalc-C +// +// Created by Jeffrey Santana on 10/10/19. +// Copyright © 2019 Lambda. All rights reserved. +// + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + return YES; +} + + +#pragma mark - UISceneSession lifecycle + + +- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; +} + + +- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { + // 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. +} + + +@end diff --git a/RPNCalc-C/RPNCalc-C/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/RPNCalc-C/RPNCalc-C/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d8db8d65 --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Resources/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/RPNCalc-C/RPNCalc-C/Resources/Assets.xcassets/Contents.json b/RPNCalc-C/RPNCalc-C/Resources/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Resources/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/RPNCalc-C/RPNCalc-C/Resources/Info.plist b/RPNCalc-C/RPNCalc-C/Resources/Info.plist new file mode 100644 index 00000000..3f1711b6 --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Resources/Info.plist @@ -0,0 +1,62 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + SceneDelegate + UISceneStoryboardFile + Main + + + + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/RPNCalc-C/RPNCalc-C/Resources/SceneDelegate.h b/RPNCalc-C/RPNCalc-C/Resources/SceneDelegate.h new file mode 100644 index 00000000..8c4a939e --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Resources/SceneDelegate.h @@ -0,0 +1,16 @@ +// +// SceneDelegate.h +// RPNCalc-C +// +// Created by Jeffrey Santana on 10/10/19. +// Copyright © 2019 Lambda. All rights reserved. +// + +#import + +@interface SceneDelegate : UIResponder + +@property (strong, nonatomic) UIWindow * window; + +@end + diff --git a/RPNCalc-C/RPNCalc-C/Resources/SceneDelegate.m b/RPNCalc-C/RPNCalc-C/Resources/SceneDelegate.m new file mode 100644 index 00000000..823a1111 --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Resources/SceneDelegate.m @@ -0,0 +1,50 @@ +#import "SceneDelegate.h" + +@interface SceneDelegate () + +@end + +@implementation SceneDelegate + + +- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). +} + + +- (void)sceneDidDisconnect:(UIScene *)scene { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). +} + + +- (void)sceneDidBecomeActive:(UIScene *)scene { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. +} + + +- (void)sceneWillResignActive:(UIScene *)scene { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). +} + + +- (void)sceneWillEnterForeground:(UIScene *)scene { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. +} + + +- (void)sceneDidEnterBackground:(UIScene *)scene { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. +} + + +@end diff --git a/RPNCalc-C/RPNCalc-C/Resources/main.m b/RPNCalc-C/RPNCalc-C/Resources/main.m new file mode 100644 index 00000000..2533ee6d --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Resources/main.m @@ -0,0 +1,19 @@ +// +// main.m +// RPNCalc-C +// +// Created by Jeffrey Santana on 10/10/19. +// Copyright © 2019 Lambda. All rights reserved. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + NSString * appDelegateClassName; + @autoreleasepool { + // Setup code that might create autoreleased objects goes here. + appDelegateClassName = NSStringFromClass([AppDelegate class]); + } + return UIApplicationMain(argc, argv, nil, appDelegateClassName); +} diff --git a/RPNCalc-C/RPNCalc-C/Storyboards/Base.lproj/LaunchScreen.storyboard b/RPNCalc-C/RPNCalc-C/Storyboards/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Storyboards/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RPNCalc-C/RPNCalc-C/Storyboards/Base.lproj/Main.storyboard b/RPNCalc-C/RPNCalc-C/Storyboards/Base.lproj/Main.storyboard new file mode 100644 index 00000000..3ad646f9 --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/Storyboards/Base.lproj/Main.storyboard @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RPNCalc-C/RPNCalc-C/View Controllers/JSCalculatorVC.h b/RPNCalc-C/RPNCalc-C/View Controllers/JSCalculatorVC.h new file mode 100644 index 00000000..1c2bbcfd --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/View Controllers/JSCalculatorVC.h @@ -0,0 +1,17 @@ +// +// JSCalculatorVC.h +// RPNCalc-C +// +// Created by Jeffrey Santana on 10/10/19. +// Copyright © 2019 Lambda. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface JSCalculatorVC : UIViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/RPNCalc-C/RPNCalc-C/View Controllers/JSCalculatorVC.m b/RPNCalc-C/RPNCalc-C/View Controllers/JSCalculatorVC.m new file mode 100644 index 00000000..a5cbe660 --- /dev/null +++ b/RPNCalc-C/RPNCalc-C/View Controllers/JSCalculatorVC.m @@ -0,0 +1,119 @@ +// +// JSCalculatorVC.m +// RPNCalc-C +// +// Created by Jeffrey Santana on 10/10/19. +// Copyright © 2019 Lambda. All rights reserved. +// + +#import "JSCalculatorVC.h" +#import "JSCalculator.h" +#import "JSDigitAccumulator.h" + +@interface JSCalculatorVC () + +@property (weak, nonatomic) IBOutlet UITextField *textField; + +@property (nonatomic) NSNumberFormatter *numberFormatter; +@property JSCalculator *calculator; +@property JSDigitAccumulator * digitAccumulator; + +- (IBAction)numberButtonTapped:(UIButton *)sender; +- (IBAction)clearButtonPressed:(UIButton *)sender; +- (IBAction)decimalButtonTapped:(UIButton *)sender; +- (IBAction)divideButtonTapped:(UIButton *)sender; +- (IBAction)multiplyButtonTapped:(UIButton *)sender; +- (IBAction)plusButtonTapped:(UIButton *)sender; +- (IBAction)subtractButtonTapped:(UIButton *)sender; +- (IBAction)returnButtonTapped:(UIButton *)sender; + +- (void)addNumber; +- (void)updateCalculator; +- (void)updateAccumulator; + +@end + +@implementation JSCalculatorVC + +- (instancetype)initWithCoder:(NSCoder *)coder { + self = [super initWithCoder:coder]; + if (self) { + _digitAccumulator = [[JSDigitAccumulator alloc] init]; + _calculator = [[JSCalculator alloc] init]; + } + return self; +} + +- (IBAction)numberButtonTapped:(UIButton *)sender { + [self.digitAccumulator addDigitWithNumericValue:sender.tag]; + [self updateAccumulator]; +} + +- (IBAction)clearButtonPressed:(UIButton *)sender { + [self.calculator clear]; + [self.digitAccumulator clear]; + self.textField.text = @""; +} + +- (IBAction)divideButtonTapped:(UIButton *)sender { + [self addNumber]; + [self.calculator applyOperator:divide]; + [self updateCalculator]; +} + +- (IBAction)returnButtonTapped:(UIButton *)sender { + [self addNumber]; + [self updateCalculator]; +} + +- (IBAction)plusButtonTapped:(UIButton *)sender { + [self addNumber]; + [self.calculator applyOperator:add]; + [self updateCalculator]; +} + +- (IBAction)subtractButtonTapped:(UIButton *)sender { + [self addNumber]; + [self.calculator applyOperator:subtract]; + [self updateCalculator]; +} + +- (IBAction)multiplyButtonTapped:(UIButton *)sender { + [self addNumber]; + [self.calculator applyOperator:multiply]; + [self updateCalculator]; +} + +- (IBAction)decimalButtonTapped:(UIButton *)sender { + [self.digitAccumulator addDecimalPoint]; + [self updateAccumulator]; +} + +- (NSNumberFormatter *)numberFormatter { + NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; + formatter.maximumIntegerDigits = 20; + formatter.minimumFractionDigits = 0; + formatter.maximumFractionDigits = 20; + [formatter setNumberStyle:NSNumberFormatterDecimalStyle]; + return formatter; +} + +- (void)addNumber { + double value = self.digitAccumulator.value; + if (value) { + [self.calculator pushNumber:value]; + self.textField.text = self.digitAccumulator.stringValue; + [self.digitAccumulator clear]; + } +} + +- (void)updateCalculator { + NSNumber *value = [NSNumber numberWithInt:self.calculator.topValue]; + self.textField.text = [self.numberFormatter stringFromNumber:value]; +} + +- (void)updateAccumulator { + self.textField.text = self.digitAccumulator.stringValue; +} + +@end diff --git a/Sprint 1/Module 1 - MVC and UINavigationController/README.md b/Sprint 1/Module 1 - MVC and UINavigationController/README.md deleted file mode 100644 index bcbeeff5..00000000 --- a/Sprint 1/Module 1 - MVC and UINavigationController/README.md +++ /dev/null @@ -1,107 +0,0 @@ -# Collection - -For this project, you'll build an app that helps manage a collection of items. They could be stamps, records, action figures, or anything else you collect. - -This project will help you practice the concepts learned in the first lesson of Sprint 1 in Lambda's full time iOS course. After completing the lesson material and this project, you should be able to: - -- implement a segue between a table view cell and a detail view controller -- use UINavigationController to display data hierarchically -- use the UINavigationItem API to customize navigation bar -- understand and explain the purpose of the MVC design pattern -- understand and explain the purpose of the model layer in MVC -- understand and explain the purpose of the controller layer in MVC -- understand and explain the purpose of the view layer in MVC -- implement a model controller - -## Part One - Model Types and Model Controller - -#### Model Type: - -You will need just one model type in this project. Your model objects will represent items in a hobby collection. - -1. Create a Swift file using Xcode's File->New File menu option. Name the file "Item.swift". -2. In the newly created file, define an `Item` class with properties for `name`, `value`, and `location`. Use appropriate types for each of these properties. -3. Create an initializer for the `Item` class. It should take arguments for `name`, `value`, and `location`. - -#### Model Controller: - -The model controller is a controller object that is specifically responsible for manipulating model objects. It should have methods to create, read, modify, and delete collection items. - -1. Create a Swift file using Xcode's File->New File menu option. Name the file "ItemController.swift". -2. In the newly created file, define a class called `ItemController`. -3. Add a property called `items` whose type is an array of `Item`s. The property should be marked with `private(set)` to prevent outside code from modifying it directly. -4. Add a method called `createNewItem()`. It should take arguments for each of the properties on `Item`. -5. Implement `createNewItem()` so that it creates a new `Item` instance using in the passed in property values, appends it to the controller's `items` array property, then returns the newly created item. -6. Add another method called `update(item:,...)`. It should take an `Item`, and new values for each of the properties on `Item`. -7. Implement `update(item:...)`. It will simply update the values of the passed-in `Item`'s properties. - -## Part Two - Storyboards - -Set up Main.storyboard so it contains scenes for the view controllers in the app. - -#### Table View Controller - -1. Open Main.storyboard. Delete the existing, default view controller. You should also delete the corresponding ViewController.swift file in the project. -2. Drag out a `UITableViewController`. -3. Embed the table view controller in a navigation controller. -4. Set the navigation controller as the initial view controller for the storyboard. -5. Create a subclass of `UITableViewController` called `CollectionTableViewController`. In the storyboard, change the table view controller's class to `CollectionTableViewController`. -6. Set the style for the table view's cell prototype to "Basic" and set an well-named reuse identifier for it. -7. Add a bar button item to the table view controller's navigation bar. -8. Set the bar button item's "System Item" attribute to "Add" in the attributes inspector. It should look like a "+" sign after you've done this. - -#### Detail View Controller - -1. Drag out another view controller. This will be the detail view controller. -2. Create a subclass of `UIViewController` called `ItemDetailViewController`. In the storyboard, change the detail view controller's class to `ItemDetailViewController `. -3. Create a Show segue from the table view cell in the table view controller to the detail view controller. -4. Create another Show segue from the Add button in the table view's navigation bar to the detail view controller. -4. Give both segues separate, well-named identifiers. -5. Add a navigation item to the detail view controller by dragging one onto it from the Library pane. -6. Add a bar button item to the navigation bar. Make its title "Save". -7. Add three text fields with labels to the view controller. They should be labled: "Item:", "Value:", and "Location:". - -#### Outlets and Actions - -1. Create IBOutlets for each of the text fields on the detail view controller. -2. Create an IBAction called `save(_:)` for the save button on the detail view controller. - -## Part Three - Implement View Controller Code - -Now you're ready to implement the code that makes the application actually do what it's meant to do. - -#### Detail View Controller - -1. Open `ItemDetailViewController.swift`. -2. Create a property called `item`. It should be of type `Item?`. -3. Create a private function called `updateViews()`. It should update the text fields with values from the `item` property. Use one or more guard statements to unwrap `item`, and to ensure that the view is loaded. -4. Call `updateViews()` from `viewDidLoad()`. -5. Add a didSet property observer to the `item` property. Use it to call `updateViews()` when the value of `item` changes. -6. Implement the `save(_:)` IBAction. - - If the `item` property is **not nil**, use the model controller's `update()` method to update the existing item with new values. - - If the `item` property is **nil**, use the model controller's `create()` method to create a new item with values taken from the text fields. - - At the end of the `save(_:)` method, pop back to the table view controller. - -#### Table View Data Controller - -1. Open `CollectionTableViewController.swift`. Delete any commented-out code that you won't need. -2. Add a property called `itemController` and initialize it with an instance of the `ItemController` model controller class. -3. Implement the `tableView(_:, numberOfRowsInSection:)` and `tableView(_:, cellForRowAt:)` methods. -4. In `tableView(_:, cellForRowAt:)` configure the cell so that its `textLabel` displays the item's `name`. -5. Implement `prepare(for segue:, sender:)`. - - If the triggered segue is for showing the detail for an existing item, it should configure the detail view controller with the selected item along with passing the `itemController` to it. - - If the trigger segue is for adding a new item, it should configure the detail view controller with the `itemController` only. - -## Part Four - Testing - -1. Run the project and make sure everything works. -2. If anything doesn't work the way the video shows, spend time debugging it and fixing the problem. -3. As always, if you need help, follow the 20-minute rule, then ask your PM. - -## Go Farther - -If you finish early or want to push yourself, here are a few additional features you can implement: - -- Add a favorite button to the detail view controller. When the button is tapped, it should toggle a `isFavorite` property on the item. -- Make it so that favorite items appear at the top of the items table. -- Implement support for deleting items. \ No newline at end of file diff --git a/Sprint 1/Module 1 Beta - Response Chain/README.md b/Sprint 1/Module 1 Beta - Response Chain/README.md deleted file mode 100644 index 40e9f2f2..00000000 --- a/Sprint 1/Module 1 Beta - Response Chain/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# Sprint 1.1: Responder Chain (Touch and Gesture Recognizers) - -In today's challenge, you'll create a simple application that allows users to add and remove new views by touch. You'll explore some gesture recognizers and practice customizing them. - -Preview your project here: https://youtu.be/KxOZLtVLWU4 - -## Preparation - -Follow these steps to create a new single-view application project in Xcode: - -1. Select File > New > Project (Command-Shift-N). -2. Select the iOS > Single View App template. Click Next. -3. Name the product, set the language to Swift, and leave the other boxes unchecked (core data, unit tests, UI tests). Click Next. -4. Navigate to a location where you want to save your new project. Click Create. - -## Create a touchable view subclass - -1. Add a new Swift file to your project named TouchableView.swift. Select File > New > File (Command-N). Select "Swift File", click Next, and edit the file name. Ensure that your project is checked in the Targets box and click Create. -2. Import UIKit and create a class `TouchableView` that is subclassed from UIView. - -## Add gesture recognition - -1. Create a new method to handle long presses: `@objc func handleLongPress(_ recognizer: UILongPressGestureRecognizer)`. -2. Create another method to handle double taps: `@objc func handleSwipe(_ recognizer: UITapGestureRecognizer)`. -3. Add a `coder`-style initializer to your class: `required init?(coder aDecoder: NSCoder)`. -4. Add a `frame`-style initializer to your class: `override init(frame: CGRect)`. Have your initializer call the superclass's implementation with `super.init(frame: frame)` before doing your setup work. -5. In this initializer, create two gesture recognizers: a long press recognizer and a swipe recognizer. Set the target and selector for each recognizer and add them to the view. Set the swipe recognizer's direction to `.right`. - -## Implement the gesture behaviors - -1. The swipe recognizer removes a view. Add `self.removeFromSuperview()` to the appropriate method. -2. The long press will toggle a view between active and inactive by enabling or disabling its `isUserInteractionEnabled` property. When enabled, set the view's background color to blue. When disabled, set it to gray. -3. The long press should only activate when its state is .began. - -## Add views by tapping the view controller - -1. In your ViewController.swift file, add two new handler methods, `@objc func createView(_ recognizer: UITapGestureRecognizer)` and `@objc func restoreInteraction(_ recognizer: UILongPressGestureRecognizer)`. -2. Create a double-tap gesture recognizer and a long-press gesture recognizer in `viewDidLoad` that call these methods and add them to your view controller's view. -3. In the create view handler, fetch the touch point using `recognizer.location(in:)`. -4. Create a new TouchableView using `TouchableView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))`. Set its background color to blue and set its center to the touchPoint. -5. Add it as a subview to the View Controller's view. -6. In the restore interaction handler, check that the recognizer state is began. If it is, iterate through the view controller's subviews and set their `isUserInteractionEnabled` states to true and their background colors to blue. - -## Stretch Goals - -* Instead of changing colors from blue to gray, change the view's alpha value from 1.0 to 0.5 and back. -* Remove the swipe recognizer and use touches moved to move the view with your finger. - -## Compile and Test Your Work - -In your completed project, you should be able to double tap in your view controller to add new views. You can disable the views with a long press and reactivate them by performing a long press on the background. Swiping an active view removes it. - -Run the project and make sure everything works. -* If anything doesn't work the way the video shows, go back and debug your issues. -* As always, if you need help, follow the 20-minute rule, then ask your PM. - -## References - -Here are some helpful resources for your project: - -* Xcode docs: `UIGestureRecognizer`, `UIView` -* [Touches, presses, and gestures](https://developer.apple.com/documentation/uikit/touches_presses_and_gestures) -* [Using responders and the responder chain to handle events](https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/using_responders_and_the_responder_chain_to_handle_events) - diff --git a/Sprint 11/Module 4/README.md b/Sprint 11/Module 4/README.md deleted file mode 100644 index 46e4b08d..00000000 --- a/Sprint 11/Module 4/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# RPNCalculator-ObjC - -## Introduction - -The goal of this project is to continue to familarize yourself with the Objective-C language by recreating your RPN Calculator in ObjC. It will help you practice the concepts learned today, including primitives vs. classes, `NSNumber`, and mutability. - -After completing the lesson material and this project, you should be able to: - -- explain the difference between a class and a primitive, along with common examples of each -- understand and briefly explain the concept of pointers -- use NSNumber to convert between primitive numbers and objects -- understand and explain the difference between immutable and mutable classes -- use copy/mutableCopy to convert from immutable to mutable and vice versa - -You are welcome and encouraged to use your existing Swift solution to this project as inspiration and help when implementing this project. However, instead of using a custom framework and importing it into your project, you will be creating all the logic in your own project. - -## Instructions - -You will need to create your own Xcode project and repository. Commit regularly as you complete the requirements in this project. - -### Part 1 - Storyboard - -1. Delete the existing ViewController files in your project and in the storyboard. -2. Drag out a new `UIViewController` and add the necessary buttons and textField for your RPN Calculator (Hint: Use the tag property for the numbers rather than creating actions for all of them). - - You can reference the layout of the ViewController in your other project to make sure you have all the buttons laid out correctly - - You could even copy the Main.storyboard from the Swift version and simply change the class of the view controller. -3. Make sure your view controller is the initial view controller. - -### Part 2 - Model/Model Controller - -1. Create a new file called `XXXStack` (XXX = your class prefix). - - `.h` file: - - Declare the methods to push, pop and peek the stack. - - Declare an `initWith` initalizer that takes an `NSArray`. - - `.m` file: - - Create a private `NSMutableArray` property called `values` that will be used every time your methods are called. - - Implement the methods you declared in your `.h` file to alter your `values` array. - - Inside of your `initWith...` initializer assign a `mutableCopy` of your `array` property to your internal `values` array. -2. Create another file called `XXXCalculator`. - - `.h` file: - - Create an enum for the operations that will be calculated using `typedef NS_ENUM(rawType, nameOfEnum) {};`. Inside the curly braces you will just need to separate each item with a comma. - - Declare three methods `- (void)pushNumber:(double)value;`, `- (void)applyOperator:(YourEnumType)operator;` and `- (void)clear;`. - - Also declare a `topValue` property that will be a computed property in your `.m` file. - - `.m` file: - - Every Calculator should have one `Stack` and only that calculator should know about it's `Stack`. Create a property `XXXStack *stack` in this file that we will use to implement the methods. - - Implement the methods in this file by accessing the methods on your `Stack` model. -3. Create a new file called `XXXDigitAccumulator`. This will be used to accumulate digits as they're entered, then convert the list of digits into a number. - - `.h` file - Your `XXXDigitAccumulator` class should have three methods and one property: - - `-addDigitWithNumericValue:` - - `-addDecimalPoint` - - `-clear` - - `value` - a readonly `double` property - - `.m` file - Implement the digit accumulator. Use the Swift version for inspiration. Note that the Swift version uses an enum with associated values for digits, which you can't do directly in Objective-C. You'll have to come up with another solution! **Note:** For now, for simplicity, you can ignore the error handling that the Swift version does. Ignore repeated decimal digits instead of throwing an error, and assume that values will be in the range 0-9. - - -### Part 3 - View Controller - -Implement the view controller. Use the Swift version for inspiration. The implementation here will be nearly identical except that it will be in Objective-C. - - -## Go Further - -If you finish early or want to push yourself, here are a few additional features you can implement: - -- Split the logic out into a framework as we did in Swift -- Implement error handling. What should happen if the user enters more than one decimal digit? Note that `@throws` is not available in Objective-C. Objective-C uses a different mechanism for error handling, which we'll learn about in a future lesson. Without knowing that, how would you solve this? \ No newline at end of file diff --git a/Sprint 12/Module 4/README.md b/Sprint 12/Module 4/README.md deleted file mode 100644 index a0ed795a..00000000 --- a/Sprint 12/Module 4/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Contacts MRC - -## Introduction - -The goal of this project is to solidify your understanding of the reference counting memory system by writing a project using Objective-C and manual reference counting (MRC). - -## Instructions - -You will need to create your own Xcode project and repository. Commit regularly as you complete the requirements in this project. - -You will be implementing a basic contacts manager app. It should include: - -- A main view with a table view displaying contact names -- A plus button used to create a new contact -- A detail view controller used to display, enter, and edit more information for each contact, including at least: **name, email address, phone number**. -- Tapping a contact should show the detail view controller and allow you to **edit it**. - -### Part 0 - Preparation - -When you create a new project, by default, ARC is enabled. You'll need to disable ARC so you can use manual reference counting. To do so, follow these steps: - -1. Create the project -2. Select the project itself in the files navigator -3. Select the "Build Settings" tab -4. Select the Project itself under "PROJECT", not the app target -5. Search for "objective-c automatic" which will bring up the "Objective-C Automatic Reference Counting" setting. -6. Change the setting to `NO` to disable automatic reference counting. - -### Part 1 - Storyboard - -Build your storyboard as you normally would. MRC makes no difference in Interface Builder, and you'll build things as you always have. - -### Part 2 - Write the Code - -Implement the app code. At a high level, this code will be identical to what you're used to, except that you will be responsible for memory management. As you work, remember the 5 rules of MRC: - -1. If you get an object from a method that starts with alloc/init, new, copy, or mutableCopy, you own it. -2. Otherwise, call retain to take ownership of an object. -3. If you own an object you must release it (or autorelease it) when you’re done. -4. If you don’t own an object you must not release it. -5. If you need an object to stick around longer than the current method, you must own it. - -### Part 3 - Testing and Analysis - -Run the static analyzer using Product->Analyze in the menu (or command-shift-B). If the analyzer finds any problems, fix them. When you're done, the static analyzer **should not report any problems**. - -Run the app and test to make sure it functions correctly. - -## Go Further - -If you finish early or want to push yourself, here are a few additional features you can implement: - -- Implement persistence using Core Data. -- Add support for contacts having a photo. Does this effect memory usage as the app runs? \ No newline at end of file diff --git a/Sprint 12/Modules 2&3/README.md b/Sprint 12/Modules 2&3/README.md deleted file mode 100644 index d57613d9..00000000 --- a/Sprint 12/Modules 2&3/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# Astronomy - ObjC - -## Introduction - -For this project, you'll once again build Astronomy, an app for viewing photos taken by NASA's Mars rovers. You will build the app primarily in Objective-C with some Swift components. The goal of this project is to practice using Swift and Objective-C together, including using features like nullability, lightweight generics, and name customization. - -This is a two-day project. The first half of the project is focused on Swift<->Objective-C interoperability, while the second half, you'll do some refactoring and add additional features. - -After completing the lesson material and this project, you should be able to: - -- use a bridging header to expose Objective-C to Swift -- import a Swift module header to use Swift code from Objective-C -- annotate Objective-C class interfaces for nullability -- annotate Objective-C class interfaces with lightweight generics -- call Objective-C methods from Swift -- call Swift methods from Objective-C - -You are welcome and encouraged to use existing Swift solutions to this project as inspiration and help when implementing this project. However, you are asked to write all the code, including the Swift parts, from scratch. - -## Instructions - -You will need to create your own Xcode project and repository. Commit regularly as you complete the requirements in this project. - -The instructions for this project are intentionally sparse. At this point in the course, you are familiar with most of the concepts used in building an app like this, and should be able to implement it with minimal guidance. This also means that you have the freedom to implement most parts of the app as you see fit. However, please make sure you follow the instructions that _are_ here. - -### Part 1 - Swift <-> Objective-C Interoperability - -For part 1 of the project, implement a client for the NASA Mars rover API. You will find API documentation here: https://api.nasa.gov/api.html#MarsPhotos . You'll need an API key, which you can get by signing up here: https://api.nasa.gov/index.html#apply-for-an-api-key - -#### Guidelines and Requirements - -1. Implement the app in Objective-C with the exception of the main collection view controller, the detail view controller, and one model type of your choice. The rest of the model, networking, and view components must be written in Objective-C. -2. Your Objective-C code must be thoroughly annotated for nullability and generics. -3. You must cache images using a custom cache class written in Objective-C. It should be a generic type so it can be used with any type. - -### Part 2 - Additional Enhancements - -For part 2 of the project, you'll add additional features to continue your practice of Objective-C. - -#### Guidelines and Requirements - -1. If you haven't already done so, use the `NSOperation`-based image loading system coverred in the Concurrency modules. If you do this, you must write the code in Objective-C. Note that the `ConcurrentOperation` subclass is not required. Your Objective-C `NSOperation` subclass should override `isAsynchronous` to return `YES`, then directly set the `ready`, `executing` and `finished` properties as appropriate during its execution. These properties are observed by the `NSOperationQueue` machinery using KVO, and therefore your use of them must be KVO-compliant. -2. Your app should allow the user to move between sols. - -## Go Further - -If you finish early or want to push yourself, here are a few additional features you can implement: - -- Create a framework for the model and networking code, which is platform-independent (ie. should work fine on macOS and tvOS) -- Implement UI to allow the user to see all available rovers and select them -- Modify your `Cache` class so that it can be configured to evict old entries if its size gets too big. This prevents the app from using too much memory due to a lot of cached data. -- Investigate and implement the use of the system-provided `URLCache` API for caching instead of your own cache class. What benefit does this provide? Downsides? -- Support pinch-to-zoom on the detail view controller. \ No newline at end of file diff --git a/Sprint 6/Module 2-Custom Controls/README.md b/Sprint 6/Module 2-Custom Controls/README.md deleted file mode 100644 index a758563e..00000000 --- a/Sprint 6/Module 2-Custom Controls/README.md +++ /dev/null @@ -1,132 +0,0 @@ -# Custom `UIControl` - -Your project creates a new custom control that allows users to rate items by swiping a finger along a row of stars. This project helps you practice the concepts learned in Lambda's iOS module 6.2. - -After completing the lesson material and ht is project, you'll be able to subclass UIControl to develop custom interactions. - -Preview your project: https://youtu.be/kWtJLhX8-gw - -## Set up the Project - -Follow these steps to set up your project skeleton: - -1. Create a new single-view project. -1. Drag a navigation controller into the `Main.storyboard`. -1. Delete the default table view controller. -1. Make your view controller the root view for the navigation controller. -1. Make your navigation controller the entry point for the project by setting it as the initial view controller. - -## Create a new custom control class - -These steps walk you through creating the new control class and adding an instance in Interface Builder: - -1. Create a new Swift file using File > New File. Name it CustomControl.swift. -2. In the file, import UIKit and create a new type (called `CustomControl`) that you subclass from `UIControl`. -3. Add a new Int-typed variable property called `value` to your class. It's initial value should be 1. This property is API-facing, so clients will be able to see it. It establishes your control as a value-providing (and value-changing) type. -3. In Interface builder, add a new view. Change the background color in the Attributes Inspector so it's visible. Don't worry about the color you pick. This is just to make it easier to work with. -4. Use the Identity Inspector to set the class to Custom Control. -5. Center it with Auto Layout but don't set any rules about size. Instead, your type will use an intrinsic size to tell Auto Layout how big it will be. -6. In the Size Inspector, select Ambiguity > Verify Position Only. This supports your "no-size" layout. -7. Use Ctrl-drag to connect your view to ViewController.swift with an IBAction. Note the "Event" pop-up currently set to "Value Changed". Look at the other options in the pop-up (like "Touch Down" and "Touch Drag Inside") but keep the event set to "Value Changed". Name your IBAction `updateRating`. This method allows the control's client (in this case your view controller) to receive updates about changes in the rating control. -8. Edit the new method's signature to: `@IBAction func updateRating(_ ratingControl: CustomControl)`. This keeps you from having to cast the `sender` to the right class. -9. Implement `updateRating`. Set the view controller's title to the string `"User Rating: N stars"` where N is the number of stars. This number is the control's visible `value` property. -10. **Stretch**: Fix the title so it's correct for 1 ("star" not "stars") as well as 2-5. - -Target-action associates callbacks with an event. IB handles that for you by calling `func addTarget(_ target: Any?, action: Selector, for controlEvents: UIControlEvents)` on your behalf. (You can add this by hand but it's so much easier to use IB.) This callback is a combination of an instance being called (the "target") and the method on the instance (the "action"). - -## Build the control view - -Custom controls are often fixed size (although they may use different sizes on iPhone and iPad targets). You'll create a control that consists of five labels. These are built without Auto Layout using a view's `frame` property to specify where each subview lies. Since the control will never change size, it's safe to use absolute numbers for each frame. - -All this work takes place in your CustomControl.swift file. - -1. Create the following private constants for your class: `componentDimension`, a CGFloat equal to 40.0, `componentCount`, equal to 5, `componentActiveColor`, which is `UIColor.black`, and `componentInactiveColor`, which is `UIColor.gray`. -2. Add an `init?(coder aCoder: NSCoder)` initializer. After calling `super`, have it call a `setup()` function, which is where you'll perform your setup work. -3. In `setup` use a loop to create five labels (using the `UILabel()` constructor). Add each one as a subview. Store each label into a local array with `append`. -4. In your loop, add a tag for each view that represents which star it is. The first star is tag 1. The fifth is tag 5. The tags let you quickly update the control's value. -5. Set each label's frame to size `componentDimension` by `componentDimension`. (Yes, they are all square). Lay out the labels in a row with 8 points of space between each one. The first label should be at (8.0, 0), which allows a small pad between it and the edge of the control. The next one starts at (componentDimension + 16.0, 0.0), and so forth. -6. Set the font (bold system font, size 32.0), text (pick your favorite Unicode star from the character picker), and alignment (center) for your label. -7. Set the label's `textColor` to either the active (for the first) or inactive (for the others) component color. -8. Add the following method to your type. This method tells Auto Layout how big your custom control should be. - -``` -override var intrinsicContentSize: CGSize { - let componentsWidth = CGFloat(componentCount) * componentDimension - let componentsSpacing = CGFloat(componentCount + 1) * 8.0 - let width = componentsWidth + componentsSpacing - return CGSize(width: width, height: componentDimension) -} -``` - -Test and run your application to make sure your control is visible and that the stars are laid out as expected. - -## Add touch handlers. - -Follow these steps to add touch to your control class. These handler implementations are almost identical to the ones in your guided project. You'll be adding: - -* `beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool` -* `continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool` -* `endTracking(_ touch: UITouch?, with event: UIEvent?)` -* `cancelTracking(with event: UIEvent?)` - -The `sendActions(for:)` method you'll call from these handlers is what makes a control a control. Sending actions generates the events that you use in IB with target-action. If you want to subscribe, say, to a `touchUpInside`, you'd control-drag and then choose _that_ event from the IBAction `Event` popup. - -Your code will demonstrate the kinds of events that can be subscribed to and are typical for UIControls. As a rule, always produce as many sent actions as possible because you don't know how your controls will be used in the future. The more exhaustive you are, the better the shelf life of your controls. Plus it's (1) minimal code and (2) almost boilerplate. You can reuse this code between controls with little change. - -1. Add skeletons for `begin`, `continue`, `end`, and `cancel` tracking methods. The `begin` and `continue` methods should just return true. -2. Add a skeleton for an `updateValue(at touch: UITouch)` method. -3. In `cancel`, send an action for `.touchCancel` -4. In `begin`, add `updateValue()` to respond to the start of your user's touch. -5. In `continue`, check whether the touch is inside view bounds. Send `.touchDragInside` or `.touchDragOutside`. If the touch is inside, update your value with `updateValue()`. -6. In `end`, make sure you still have a valid touch and return if not. Otherwise, check whether the touch is inside view bounds and duplicate the logic for `continue`. Replace the two actions with `touchUpInside` and `touchUpOutside` instead of the drag forms you used for `continue`. - -In your code, the `end` handler generates a value update. It provides a little safety net in case the lift event has moved the finger in an untracked movement. You can omit it if desired or keep it if you feel cautious. - -With value controls, there's no penalty for spawning extra `.valueChanged` events with `sendActions(for:)`. Be far more cautious with trigger controls. A button or other trigger should only send *one* primary action. For these, keep a "wasTriggered" Boolean variable on hand. Do not `sendActions` after the first control trigger. Otherwise, your angry customers may end up authorizing multiple payments or using up all the arrows in their quiver when they only intended to pay once or shoot once. - -## Respond to touches. - -Follow these steps to finish up your control and add user feedback. - -1. In `updateValue`, you handle touches by checking to see whether they intersect with any of your stored label subviews. Implement a loop that iterates through your component labels and detect whether each touch's location (`touch.location(in: self)`) is contained in each label's frame. -2. When a touch overlaps a label, set the control's value to that tag, update the label colors to reflect the current touch, and send an action for `valueChanged`. -3. **Stretch** It's better to store the old value before changing it and only send an update when the value has changed. -4. **Stretch** Add the following `UIView` animation to flare the view a little when selected. - -``` -extension UIView { - // "Flare view" animation sequence - func performFlare() { - func flare() { transform = CGAffineTransform(scaleX: 1.6, y: 1.6) } - func unflare() { transform = .identity } - - UIView.animate(withDuration: 0.3, - animations: { flare() }, - completion: { _ in UIView.animate(withDuration: 0.1) { unflare() }}) - } -} -``` - -This method adjusts your view's `transform`, telling it to inflate to 60% larger than its normal size and then shrink back down to its default size (using the "no change" `identity` transform). - -## Test -1. Run the project and make sure everything works. -2. If anything doesn't work the way the video shows (outside of the two stretch goals), go back and debug your issues. -3. As always, if you need help, follow the 20-minute rule, then ask your PM. - -## Go Farther -Time allowing, here are some things you can try. - -1. Change the number of buttons to allow 4 or 6 star rating controls. Does everything work right with your change being in a single line of code? -2. Flip the control for use in right-to-left language countries, where you slide from right to left. -3. Create different flare animations. Things you can animate include background color, translucency (the view's `alpha` property), and rotation. You'll be able to find examples of these with simple web searches. - -## References - -Here are some helpful resources for your project: - -* `UIControl` documentation (Xcode) -* `UIControlEvents` documentation (Xcode): a list of all available control events, including the "value changed" event used in this challenge. -* How to build selectors by hand (apologies for the URL): http://fuckingselectorsyntax.com -* Examples of custom controls: Search the web for `uicontrol site:github.com` -* Custom Cocoa Control repository: https://www.cocoacontrols.com diff --git a/Sprint 6/Module 3 - UIView Animation/README.md b/Sprint 6/Module 3 - UIView Animation/README.md deleted file mode 100644 index a0aadceb..00000000 --- a/Sprint 6/Module 3 - UIView Animation/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# Scatter and Gather Animations - -Your project scatters the letters of the word "Lambda" by animating them to new locations. This project helps you practice the concepts learned in Lambda's iOS module 6.4. - -After completing the lesson material and this project, you'll be able to animate a number of `UIView` and `CALayer` properties. - -Preview your project: https://youtu.be/w8zJVBPrmnY - -## Set up the project - -Follow these steps to set up your project skeleton: - -1. Create a new single-view project. -1. Drag a navigation controller into the `Main.storyboard`. -1. Delete the default table view controller. -1. Make your view controller the root view for the navigation controller. -1. Make your navigation controller the entry point for the project by setting it as the initial view controller. - -## Create a bar button that calls back to your main view controller - -These steps enable you to run your animation with a single tap: - -1. Create a new bar button item in your view controller's navigation item. -2. Name it `toggle` -3. Connect it to your view controller so it will run `toggle(_:)` as an `@IBAction`. -4. Establish a boolean variable called `shouldScramble` that you toggle back and forth. You'll animate your views to either scramble them or gather them together when the toggle button is tapped. - -## Create views - -Build the items you want to animate, either by hand or in Interface Builder. You'll need six labels (one for each letter) and one image view (for the Lambda logo). Your PM will give you the art for the Lambda logo. - -Connect these views to your view controller so they can be animated by your `toggle(_:)` method. - -## Animate - -When scattering: - -* Fade out your logo view -* Move your letters to random locations -* Assign them a random background color and text color -* Use a custom transform to rotate the views (`letter.transform = CGAffineTransform(rotationAngle: random_angle`) -* Incorporate as many other custom animations as you like. -* Your animation should take between 2 and 4 seconds. - -When gathering: - -* Fade in your logo view -* Reset all the custom properties you previously assigned to the letters. -* Animate the letters back to their starting position, either as a line or however you have them set up by default. - -## Stretch - -Here are some stretch goals to investigate: - -* Add 3-D transforms to your layers instead of a simple 2D rotation. -* Animate shadows onto and away from your views. -* Add keyframe sequences to perform multiple tasks when scattering your views. - -## References - -Here are some helpful resources for your project: - -* `UIView` reference (Xcode, also https://developer.apple.com/documentation/uikit/uiview). See the `Animations` section for a list of animatable properties. -* `CALayer` animatable properties: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreAnimation_guide/AnimatableProperties/AnimatableProperties.html -* Core animation: https://developer.apple.com/documentation/quartzcore -* Affine Transforms in iOS: see Xcode docs for `CGAffineTransform` and `CATransform3D`, Quartz 2D: https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_affine/dq_affine.html, Wolfram: http://mathworld.wolfram.com/AffineTransformation.html, Wikipedia: https://en.wikipedia.org/wiki/Affine_transformation, University of Texas: https://www.cs.utexas.edu/users/fussell/courses/cs384g-fall2011/lectures/lecture07-Affine.pdf -* Using `arc4random` and `arc4random_uniform`: https://www.freebsd.org/cgi/man.cgi?query=arc4random, SO: https://stackoverflow.com/questions/32552336/generating-random-numbers-with-swift diff --git a/Sprint 6/Module 4 - View Controller Transitions/README.md b/Sprint 6/Module 4 - View Controller Transitions/README.md deleted file mode 100644 index 07c6c0d2..00000000 --- a/Sprint 6/Module 4 - View Controller Transitions/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Friends - -For this project, you'll build an app that shows a table view of friends including their photo and name. When a row is tapped, a detail view should be shown. The detail view should include a bigger photo, name, and a short bio. Most importantly, implement a custom view controller transition so that the table view cell's image and name appear to smoothly transition to their new sizes and positions on the detail view. Watch the video below to see what your finished application should look like. - -After completing the lesson material and this project, you should be able to: - -- create custom transition animations using the view controller transition animation API -- create interactive transition animations - -[![Demo Video](https://img.youtube.com/vi/eSh93_D_xbE/0.jpg)](https://www.youtube.com/watch?v=eSh93_D_xbE) - -## Part One - Basic App - -Start by implementing the basic app, without the custom transition. It's a simple master/detail interface like you've implemented many times before. For model data, include one or more `Friend` instances just so you have content for the table view. - -## Part Two - Implement Animation Scaffolding - -There are a number of ways to approach this custom transition, but perhaps the most straightforward is to to implement a `UINavigationControllerDelegate` that provides a custom animator to the navigation controller. - -#### Create NavigationControllerDelegate - -1. Create a class called `NavigationControllerDelegate`. It must inherit from `NSObject`, and should conform to the `UINavigationControllerDelegate` protocol. -2. Add `navigationController(_ navigationController:, animationControllerFor:, from:, to:)`. Leave it empty for now. -3. Create a `sourceCell` property so the navigation controller delegate can be given a reference to the table view cell that initiated the transition. - -#### Create an Animator Class - -1. Create a class called `ImageTransitionAnimator`. It must inherit from `NSObject` and should conform to the `UIViewControllerAnimatedTransitioning` protocol. -2. Implement `transitionDuration(using:)`. Return a sane animation duration. 0.5 is a good choice. -3. Add `animateTransition(using:)`. This is the main animation function for your custom transition. Leave it empty for now. -4. Add properties for the views that the animator will need to know about. These are the source and destination photo views, and the source and destination name labels. - -#### Hook Everything Up - -You'll need to provide the navigation controller with a delegate. You'll also need to set the animation up when the table view cell -> detail view segue is triggered. - -1. In your table view controller's `viewDidLoad()`, set the `navigationController`'s delegate to an instance of `NavigationControllerDelegate`. -2. Implement `prepare(for segue:)`. Get the tapped row, and use it to give the navigation controller delegate the source table view cell. -3. In your `NavigationControllerDelegate`'s `navigationController(_ navigationController:, animationControllerFor:, from:, to:)` method, create and configure an `Animator` object, and return it. The animator will need to have its source and destination imageView and label properties set. **Note: You must keep a strong reference to the animator object in your navigation controller delegate. Use a property to hold it instead of creating a temporary instance and referencing it solely in this method.** - -## Part Three - Implement the Animation - -You're ready now to implement the actual animation. - -1. Implement `Animator.animateTransition(using:)` -2. Get the start and end frames for the image view and label. -3. Create a temporary image view and label to be used for the animation and add them to the transitionContext's `containerView`. -4. Set up any properties that should be set up before beginning the animation. For example, hide the destination views by setting their alpha to 0.0. -5. Use `UIView.animate()` to animate the transition. -6. In the animation's completion closure, (re)set the correct properties for the destination views, and remove the temporary animation views. Call `transitionContext.completeTransition()`. - -## Part Four - Testing - -1. Run the project and make sure everything works. -2. If anything doesn't work the way the video shows, spend time debugging it and fixing the problem. -3. As always, if you need help, follow the 20-minute rule, then ask your PM. - -## Go Farther - -If you finish early or want to push yourself, here are a few additional features you can implement: - -- Make it so the back action can be perform using a swipe guesture like a standard navigation controller. The animation should be interactive and should track the user's finger. Hint: You'll need to use a `UIPanGuestureRecognizer`. -- Make the table view cell's image view rounded. Animate the corner radius changing to make it square on the detail view. diff --git a/Sprint 7/Concurrency II (7.3)/README.md b/Sprint 7/Concurrency II (7.3)/README.md deleted file mode 100644 index 2753dcd3..00000000 --- a/Sprint 7/Concurrency II (7.3)/README.md +++ /dev/null @@ -1,98 +0,0 @@ -# Hungry Developers - -For this project, you'll write a program that simulates five developers at a table eating. While this seems like a silly program to write, it will help you explore concurrency, synchronization, and avoiding deadlocks. - -This project will help you practice the concepts learned in the Concurreny II - Fundamentals lesson of Sprint 7 in Lambda's full time iOS course. After completing the lesson material and this project, you should be able to: - -- understand and explain what concurrency means, and common scenarios where it is appropriate -- understand and explain what a race condition is -- understand and use mutexes to prevent race conditions -- understand and explain what a deadlock is - -## Part Zero - Challenge Description - -Imagine a scenario where you have five hungry developers sitting around a round table. On the table between each pair of developers sits a spoon. - -![Table](Table.png) - -Each developer is hungry, and trying to solve a difficult programming challenge. They alternately think and eat soup. However, a developer can only eat soup when they have both the spoon to their left and the spoon to their right. - -After a developer finishes eating, they must put down both spoon, so that other developers can use them. A developer can take the spoon on their left or their right as soon as they're available, but they cannot start eating before they get both spoons. - -Assume that there is an unlimited supply of soup for each developer, and that they have infinitely big stomachs. In other words, they can keep eating and thinking forever. One more rule: the developers can't talk to each other. In other words, they can't know when others are waiting to eat or think. - -Your goal is to write a program that simulates this scenario. - -## Part One - Write A Solution - -This problem is trickier than it seems. However, you should start by writing a simple solution. - -1. Create a class called `Spoon`. It should have two methods, `pickUp()` and `putDown()`. -2. If `pickUp()` is called while the spoon is in use by another developer, `pickUp()` should wait until the other developer calls `putDown()`. You can implement this with a private `lock` property. -3. Create a class called `Developer`. Each `Developer` should have a `leftSpoon` property and a `rightSpoon` property. It should also have `think()`, `eat()`, and `run()` methods. -4. `Developer.run()` should call `think()` then `eat()` over and over again forever. -5. `think()` should pick up both spoons before returning. -6. `eat()` should pause for a random amount of time before putting both spoons down. (Hint: use `usleep()` to pause for a given number of microseconds). -5. Create 5 `Spoon`s and 5 `Developer`s giving each developer a left and right spoon. Note that developers will of course share spoons. Ever developer's right spoon is the next developer's left spoon. -6. Call `run()` on each developer in a different queue/thread. You can do this with the following code assuming you put your developers in an array: - -``` -DispatchQueue.concurrentPerform(iterations: 5) { -developers[$0].run() -} -``` - -To recap the algorithm you're writing here, each developer will: - -- think until the left spoon is available; when it is, pick it up; -- think until the right spoon is available; when it is, pick it up; -- when both spoons are held, eat for a fixed amount of time; -- then, put the right spoon down; -- then, put the left spoon down; -- repeat from the beginning. - -## Part Two - Test and Analyze - -Will this code work OK? Is it safe from race conditions and deadlocks? Add print statements so you can track the progress of your developers. For example, in `eat()`, log when each developer starts eating, and when they finish eating. Run the program. Does it continue indefinitely? Does it hang? Make sure you run the program for a long time. Concurrency problems are non-deterministic, and may take a large number of iterations before appearing. - -If you notice any problems can you explain them? Try to understand what's happening before going on to the next section. - -## Part Three - Fix Any Problems - -This simple solution works **most** of the time, but it can deadlock. Specifically, if each developer picks up their left spoon and is waiting to pick up their right spoon, no progress can be made. While the timing has to be just right, the program _can_ reach this state and deadlock. - -How can you fix this? There are a number of solutions to this problem ranging from simple to quite complex. See if you can think of a solution, and analyze and try it before moving on to the next section. - -## Part Four - Solution - -Fundamentally the problem with the simple solution is that if each developer picks up the spoon to their left, each developer is holding one spoon, and they all are waiting for another spoon to become available. However, another spoon will never become available, because all the other developers are also waiting for a spoon to become available. - -One simple solution to this is to assign an order or ranking to the spoons. For example, number them from 1 to 5. Then, make it so that developers will _always_ pick up the lower-numbered spoon first, before picking up the higher-numbered spoon (from among their two spoon). In this scenario, if four of the five developers pick up their lower-numbered spoon, only the highest numbered spoon (ie. spoon 5) will remain on the table, and the fifth developer will not be able to pick up a spoon, because only their _higher_-numbered spoon will be on the table. This leaves the that spoon free for another developer, thus breaking the deadlock. - -Implement this solution by: - -1. Add an `index` property to `Spoon`. -2. Give each spoon an index from 1 to 5. -3. Refactor `think()` function, so that a developer will always pick up their lower-numbered spoon first. (The order in which they put them down doesn't matter.) -4. Test the app again. In theory, with this solution, the app can't deadlock. Run it until you're confident that that's true. - -## Go Farther - -The simple solution above will prevent deadlocks. However, it has some downsides, including poor performance in real-world scenarios where the number of resources is not known in advance. Can you think of another solution? Some ideas are below. Try implementing them. - -### Waiter (Arbitrator) Solution - -Another solution is to introduce a waiter to the program. In order to pick up the spoons, a developer must ask the waiter's permission. The waiter will give permission to only one developer at a time until the developer has picked up both fo their spoons. The waiter can be implemented using a mutex. - -This approach also has a problem in that it reduces parallelism. In other words, it does not maximize the number of developers simultaneously eating. - -### Chandy/Misra solution (message passing) - -This solution is more complicated, and it actually breaks one of the rules stated above in that it requires the developers to communicate with each other. Howerver, it allows for good concurrency, fixes starvation, and doesn't require a central authority like a waiter. - -1. For every pair of developers contending for a resource, create a spoon and give it to the developer with the lower ID (n for agent Pn). Each spoon can either be dirty or clean. Initially, all spoons are dirty. -2. When a developer wants to use a set of resources (i.e. eat), said developer must obtain the spoons from their contending neighbors. For all such spoons the developer does not have, they send a request message. -3. When a developer with a spoon receives a request message, they keep the spoon if it is clean, but give it up when it is dirty. If the developer sends the spoon over, they clean the spoon before doing so. -4. After a developer is done eating, all their spoons become dirty. If another developer had previously requested one of the spoons, the developer that has just finished eating cleans the spoon and sends it. - -Can you implement this solution? It will require significantly more code. diff --git a/Sprint 7/Concurrency II (7.3)/Table.png b/Sprint 7/Concurrency II (7.3)/Table.png deleted file mode 100644 index 030b056d..00000000 Binary files a/Sprint 7/Concurrency II (7.3)/Table.png and /dev/null differ diff --git a/Sprint 7/Generics (7.2)/README.md b/Sprint 7/Generics (7.2)/README.md deleted file mode 100644 index 854a2825..00000000 --- a/Sprint 7/Generics (7.2)/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# Generics Challenge - -Create a generic `CountedSet` struct that is constrained to `Hashable` elements. A counted set is an unordered collection of unique elements that may appear more than once in the collection. Use a private dictionary as your backing storage for set members and their counts. - -## Set up the project - -Create a new playground or project to develop and test your type. It may be easier to develop a single type in a playground but Xcode is a bit unstable with playgrounds these days. Use whatever tool best works for you. - -## Goals - -* Add insertion and removal (`insert` and `remove`) of one element at a time. -* Support subscripting to look up current values (by implementing `subscript(_ member: Element) -> Int`). Return `0` for any value that is not found. -* Add `count`, returning the number of unique elements in the counted set and `isEmpty` for when `count` is zero. - -## Conform to `ExpressibleByArrayLiteral` - -As demonstrated in class, conform your set to `ExpressibleByArrayLiteral` so you can initialize a counted set using an array of same-type items. - -Your implementation should support the following interaction style: - -``` -enum Arrow { case iron, wooden, elven, dwarvish, magic, silver } -var aCountedSet = CountedSet() -aCountedSet[.iron] // 0 -var myCountedSet: CountedSet = [.iron, .magic, .iron, .silver, .iron, .iron] -myCountedSet[.iron] // 4 -myCountedSet.remove(.iron) // 3 -myCountedSet.remove(.dwarvish) // 0 -myCountedSet.remove(.magic) // 0 -``` - -## Test -1. Run the project and make sure everything works. Create a good suite of tests that check for boundary conditions and many different types. -2. If anything doesn't work the way the above example shows, go back and debug your issues. -3. As always, if you need help, follow the 20-minute rule, then ask your PM. - -## Go Farther - -Time allowing, consider adding the following enhancements to your `CountedSet` type: - -* Conform `CountedSet` to `Sequence` by creating a custom iterator (`DictionaryIterator`). -* Implement `contains` to test whether your set contains at least one of a given item. -* Implement `union` with another set, returning all members and their combined sums. For extra karma, implement a mutating version (change in place) and a non-mutating version (return a copy). -* Implement `intersection` and/or `subtraction` using the same logic. Intersection returns all members and counts appearing in both sets. Subtraction removes all counts found in the second set from the first. -* Implement `isDisjoint`, testing that there's no overlap between elements. -* Conform to `Equatable` and implement `==` to test if two counted sets are the same. - -## References - -* Swift Generics WWDC 2018: https://developer.apple.com/videos/play/wwdc2018/406/ -* https://docs.swift.org/swift-book/LanguageGuide/Generics.html -* Search the web for: *swift generics* - diff --git a/Sprint 9/Creating Frameworks/IndeterminateLoadingView.swift b/Sprint 9/Creating Frameworks/IndeterminateLoadingView.swift deleted file mode 100644 index 18a7145d..00000000 --- a/Sprint 9/Creating Frameworks/IndeterminateLoadingView.swift +++ /dev/null @@ -1,107 +0,0 @@ -// -// LoadingView.swift -// LoadingUI -// -// Created by Andrew R Madsen on 9/18/18. -// Copyright © 2018 Lambda School. All rights reserved. -// - -import UIKit - -class IndeterminateLoadingView: UIView, CAAnimationDelegate { - - override init(frame: CGRect) { - super.init(frame: frame) - - setupShapeLayer() - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - - setupShapeLayer() - } - - func startAnimating() { - guard !isAnimating else { return } - defer { isAnimating = true } - - startAnimation() - } - - func stopAnimating() { - guard isAnimating else { return } - - shouldStopAnimationOnNextCycle = true - } - - // MARK: - Private - - private func setupShapeLayer() { - let thickness: CGFloat = 10.0 - - shapeLayer.frame = layer.bounds - shapeLayer.strokeColor = UIColor.black.cgColor - shapeLayer.fillColor = UIColor.clear.cgColor - shapeLayer.lineWidth = thickness - shapeLayer.strokeStart = 0.0 - shapeLayer.strokeEnd = 0.0 - layer.addSublayer(shapeLayer) - - let radius = min(bounds.width, bounds.height) / 2.0 - thickness/2.0 - let rect = CGRect(x: bounds.midX - radius/2.0, y: bounds.midY - radius/2.0, width: radius, height: radius) - let path = UIBezierPath(ovalIn: rect) - - shapeLayer.path = path.cgPath - } - - private func startAnimation() { - shouldStopAnimationOnNextCycle = false - shapeLayer.strokeStart = 0.0 - shapeLayer.strokeEnd = 0.0 - startAnimation(for: "strokeEnd", timing: .easeIn) - } - - private func startAnimation(for keyPath: String, timing: CAMediaTimingFunctionName) { - let animation = CABasicAnimation(keyPath: keyPath) - animation.fromValue = 0.0 - animation.toValue = 1.0 - animation.duration = duration - animation.delegate = self - animation.isRemovedOnCompletion = false - animation.timingFunction = CAMediaTimingFunction(name: timing) - shapeLayer.add(animation, forKey: keyPath) - } - - // MARK: - CAAnimationDelegate - - func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { - guard !shouldStopAnimationOnNextCycle else { - shouldStopAnimationOnNextCycle = false - isAnimating = false - return - } - - if let anim = anim as? CABasicAnimation, anim.keyPath == "strokeEnd" { - shapeLayer.strokeStart = 0.0 - shapeLayer.strokeEnd = 1.0 - shapeLayer.removeAllAnimations() - startAnimation(for: "strokeStart", timing: .easeOut) - } - - if let anim = anim as? CABasicAnimation, anim.keyPath == "strokeStart" { - shapeLayer.strokeStart = 0.0 - shapeLayer.strokeEnd = 0.0 - shapeLayer.removeAllAnimations() - startAnimation(for: "strokeEnd", timing: .easeIn) - } - } - - // MARK: - Properties - - private(set) var isAnimating = false - - private let shapeLayer = CAShapeLayer() - private let duration = 1.0 - private var shouldStopAnimationOnNextCycle = false -} diff --git a/Sprint 9/Creating Frameworks/LoadingUI.gif b/Sprint 9/Creating Frameworks/LoadingUI.gif deleted file mode 100644 index 708fa94c..00000000 Binary files a/Sprint 9/Creating Frameworks/LoadingUI.gif and /dev/null differ diff --git a/Sprint 9/Creating Frameworks/PolarPoint.swift b/Sprint 9/Creating Frameworks/PolarPoint.swift deleted file mode 100644 index 5964e734..00000000 --- a/Sprint 9/Creating Frameworks/PolarPoint.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// PolarPoint.swift -// -// Created by Andrew Madsen on 2/26/15. -// Copyright (c) 2015 Open Reel Software. All rights reserved. -// - -import Foundation - -struct PolarPoint { - - var radius: CGFloat - var theta: CGFloat - - init(radius: Int, theta: Int) { - self.radius = CGFloat(radius) - self.theta = CGFloat(theta) - } - - init(radius: T, theta: T) { - self.radius = CGFloat(radius) - self.theta = CGFloat(theta) - } - - init(point: CGPoint, centerPoint: CGPoint) { - let translatedPoint = CGPoint(x: point.x - centerPoint.x, y: point.y - centerPoint.y) - let xDistance = abs(point.x - centerPoint.x) - let yDistance = abs(point.y - centerPoint.y) - - self.radius = (pow(xDistance, 2.0) + pow(yDistance, 2.0)).squareRoot() - self.theta = atan2(translatedPoint.y, translatedPoint.x) - } - - static func degreesToRadians(_ degrees: T) -> T { - return degrees * T.pi / T(180.0) - } - - static func radiansToDegrees(_ radians: T) -> T { - return radians * T(180.0) / T.pi - } -} - -extension CGPoint { - init(polarPoint: PolarPoint, centerPoint: CGPoint) { - let x = CGFloat(polarPoint.radius * cos(polarPoint.theta)) + centerPoint.x - let y = CGFloat(polarPoint.radius * sin(polarPoint.theta)) + centerPoint.y - self.init(x: x, y: y) - } -} diff --git a/Sprint 9/Creating Frameworks/README.md b/Sprint 9/Creating Frameworks/README.md deleted file mode 100644 index 2d79c67b..00000000 --- a/Sprint 9/Creating Frameworks/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# Loading UI Framework - -The goal of this project is practice creating a framework, including thinking about the public API you want to expose. This project covers the concepts covered in the Creating Frameworks module. After completing the lesson material and this project, you should be able to: - -- create a framework for sharing code between multiple apps -- include sub-projects with a framework in an Xcode app project -- use Swift’s access control modifiers appropriately to make code private, internal, or public -- make good decisions about abstraction, API surface, and modularity when creating a framework - -Your assignment is to write a framework called "Loading UI" or similar. At a minimum, the framework should include a view controller that can be presented to show a loading animation like the one in the animated GIF below: - -![Animated GIF](LoadingUI.gif) - -You can expand on this by completing the tasks in the Go Farther section. As you work on this project, think about the API you want to expose to users of the framework. It should be as simple as possible to use the framework in an app for various scenarios. - -## Part 0 - Animation Help - -You're encouraged to figure out how to implement the animation itself on your own using the concepts you learned in Sprint 6 - User Interface. However, an animation like this will take a little time to get right, and will stretch you beyond what you've already learned. This project is focused on creating a framework, and if you feel it would be a better use of your time to focus on that, you are welcome to use the `IndeterminateLoadingView` class in `IndeterminateLoadingView.swift` in this repo. If you decide to write the animation on your own, here are some hints: - -- There are a number of ways to do this, but [`CAShapeLayer`](https://developer.apple.com/documentation/quartzcore/cashapelayer) is a good one. Note the `strokeStart` and `strokeEnd` properties which are animatable. -- You can create a circular `UIBezierPath` using `UIBezierPath(ovalIn: rect)` where `rect` is a square `CGRect`. -- `CAAnimation` has a delegate property that allows you to be notified when an animation finishes. You can use this to trigger another animation. -- You may find the `PolarPoint` struct found in this repo helpful. It makes circular geometry and drawing code easier. Note that I didn't use it in my solution to this project, but depending on the approach to drawing you take, you might find it useful. - -## Part 1 - Create a Framework and Setup a Test App - -Start by creating a new framework project. You should also set up a test app so you can excersise, test, and debug your framework code. - -1. Create a parent directory for this project. Call it e.g. `LoadingUIFrameworkDevelopment` -2. Inside `LoadingUIFrameworkDevelopment`, create a new Cocoa Touch Framework project called `LoadingUI` (or whatever name you'd prefer). -3. Inside `LoadingUIFrameworkDevelopment`, create another app project called `LoadingUITest` (or similar). The folder for `LoadingUITest` should be a sibling to the `LoadingUI` folder. -4. Open `LoadingUITest`, and add the `LoadingUI` framework project as a subproject. -5. Add the LoadingUI.framework as an embedded binary in the target settings for `LoadingUITest`. -6. `import LoadingUI` in `ViewController`. Make sure the project builds. - -## Part 2 - Define the Interface - -When creating a new API component for a framework, or even in app code, it's helpful to start by figuring out the external interface you want to expose _before_ you start implementing the API's functionality. - -1. Create a subclass of `UIViewController` called `LoadingViewController`. Should this be public, internal, or private? -2. Add stubs for the `public` methods you want to expose on `LoadingViewController`. These need not be final. You can always refine the interface as you go, but you want to start with something to guide you -3. Are there any other types, functions, or methods you should expose publicly in your framework? -4. Write code in your app (`LoadingUITest`) to use `LoadingViewController`. How you do this is up to you, but you might make it so that a button on the view controller modally presents a `LoadingViewController` and starts animating it. Your goal here is to allow you to test your framework code, but also to get an idea of how the API you're creating feels in practice. - -## Part 3 - Implement `LoadingViewController` - -Fill out the stub methods you created for `LoadingViewController`. Mark any methods and properties that shouldn't be exposed to users of the framework using either `private` or `internal` as appropriate. As you develop this code, build and run your `LoadingUITest` app to test and debug it. - -## Go Farther - -If you finish early or want to challenge yourself, think about completing one or more of the following tasks: - -- Add the ability for the loading animation to show _determinate_ progress. That is, instead of spinning until you tell it to stop, make it so the circle can be filled in from 0-100% as the progress of a task completes. -- Building on the last task, add support for [`Progress`](https://developer.apple.com/documentation/foundation/progress), which is a Foundation class for doing progress reporting. -- Implement a more complicated loading animation. The sky's the limit. Any relatively simple, interesting, repeating animation can be used. Take a look at [`CAReplicatorLayer`](https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CAReplicatorLayer_class) which can do some very interesting things. -- Add support for installing your framework using Carthage. Hint: This is likely easier than you think. -- Add support for installing your framework using CocoaPods. Hint: This is likely harder than you think 😉. \ No newline at end of file