├── .gitignore ├── Help.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── sergeylukaschuk.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── Help ├── CleanCode │ ├── CleanClass.swift │ ├── DeadCode.swift │ ├── Extension.swift │ ├── LifeCycle.swift │ ├── Lifecycle.swift │ ├── LogicallyRelatedElements.swift │ └── MARK.swift ├── DesignPatterns │ └── FactoryMethod.swift ├── Extensions │ ├── Int+Random.swift │ └── String+CompareNumbers.swift ├── Features │ ├── CustomView.swift │ ├── LaunchScreen.swift │ └── SwipeScreen.swift ├── LeetCode │ ├── ContainerWithMostWater.swift │ ├── Pascal'sTriangle.swift │ └── SpiralMatrix.swift ├── Protocols │ ├── Comparable.swift │ ├── CustomStringConvertible.swift │ └── Equatable.swift ├── SupportingFiles │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── SceneDelegate.swift │ └── ViewController.swift ├── UIKit │ ├── AutoLayoutConstraintsProgrammatically.swift │ ├── DataManager │ │ └── SaveAndLoadDataOnDevice.swift │ ├── Destination.swift │ ├── PassDataClosure.swift │ ├── PassDataDelegate.swift │ ├── PerformSegue.swift │ ├── PrepareSegue.swift │ ├── Source.swift │ ├── TableView │ │ ├── DeleteRow.swift │ │ └── MoveRow.swift │ ├── UITextField │ │ └── DismissKeyboard.swift │ └── UnwindSegue.swift └── WorkingCode │ ├── ArrayEnumerated.swift │ ├── ArrayZip.swift │ ├── Dictionary.swift │ ├── EnumDataModel.swift │ ├── ErrorHandling.swift │ ├── FailableInitializers.swift │ ├── Guard.swift │ └── TypeCasting.swift ├── License ├── README.md └── Screenshots ├── ContainerWithMostWater.jpg ├── PascalTriangleAnimated2.gif ├── home-screen-quick-actions.jpeg ├── segue-001.png ├── segue-002.png ├── spiral.jpg └── spiral1.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/swift 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=swift 3 | 4 | ### Swift ### 5 | # Xcode 6 | # 7 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 8 | 9 | ## User settings 10 | xcuserdata/ 11 | 12 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 13 | *.xcscmblueprint 14 | *.xccheckout 15 | 16 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 17 | build/ 18 | DerivedData/ 19 | *.moved-aside 20 | *.pbxuser 21 | !default.pbxuser 22 | *.mode1v3 23 | !default.mode1v3 24 | *.mode2v3 25 | !default.mode2v3 26 | *.perspectivev3 27 | !default.perspectivev3 28 | 29 | ## Obj-C/Swift specific 30 | *.hmap 31 | 32 | ## App packaging 33 | *.ipa 34 | *.dSYM.zip 35 | *.dSYM 36 | 37 | ## Playgrounds 38 | timeline.xctimeline 39 | playground.xcworkspace 40 | 41 | # Swift Package Manager 42 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 43 | # Packages/ 44 | # Package.pins 45 | # Package.resolved 46 | # *.xcodeproj 47 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 48 | # hence it is not needed unless you have added a package configuration file to your project 49 | # .swiftpm 50 | 51 | .build/ 52 | 53 | # CocoaPods 54 | # We recommend against adding the Pods directory to your .gitignore. However 55 | # you should judge for yourself, the pros and cons are mentioned at: 56 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 57 | # Pods/ 58 | # Add this line if you want to avoid checking in source code from the Xcode workspace 59 | # *.xcworkspace 60 | 61 | # Carthage 62 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 63 | # Carthage/Checkouts 64 | 65 | Carthage/Build/ 66 | 67 | # Accio dependency management 68 | Dependencies/ 69 | .accio/ 70 | 71 | # fastlane 72 | # It is recommended to not store the screenshots in the git repo. 73 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 74 | # For more information about the recommended setup visit: 75 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 76 | 77 | fastlane/report.xml 78 | fastlane/Preview.html 79 | fastlane/screenshots/**/*.png 80 | fastlane/test_output 81 | 82 | # Code Injection 83 | # After new code Injection tools there's a generated folder /iOSInjectionProject 84 | # https://github.com/johnno1962/injectionforxcode 85 | 86 | iOSInjectionProject/ 87 | 88 | # End of https://www.toptal.com/developers/gitignore/api/swift 89 | 90 | -------------------------------------------------------------------------------- /Help.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5E023E89268364C3002465D8 /* ArrayEnumerated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E023E88268364C3002465D8 /* ArrayEnumerated.swift */; }; 11 | 5E023E8B2683692F002465D8 /* ArrayZip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E023E8A2683692F002465D8 /* ArrayZip.swift */; }; 12 | 5E023E8F26863070002465D8 /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E023E8E26863070002465D8 /* Dictionary.swift */; }; 13 | 5E1F2F4927873121009446ED /* FactoryMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E1F2F4827873121009446ED /* FactoryMethod.swift */; }; 14 | 5E1F30D6278C9C5E009446ED /* Pascal'sTriangle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E1F30D5278C9C5E009446ED /* Pascal'sTriangle.swift */; }; 15 | 5E2CF7C1266E20AE007BB2EB /* UnwindSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E2CF7C0266E20AE007BB2EB /* UnwindSegue.swift */; }; 16 | 5E2CF7C5266E242E007BB2EB /* segue-002.png in Resources */ = {isa = PBXBuildFile; fileRef = 5E2CF7C3266E242E007BB2EB /* segue-002.png */; }; 17 | 5E2CF7C6266E242E007BB2EB /* segue-001.png in Resources */ = {isa = PBXBuildFile; fileRef = 5E2CF7C4266E242E007BB2EB /* segue-001.png */; }; 18 | 5E2CF7C8266E61A0007BB2EB /* PrepareSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E2CF7C7266E61A0007BB2EB /* PrepareSegue.swift */; }; 19 | 5E2CF7CA266EB745007BB2EB /* PerformSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E2CF7C9266EB745007BB2EB /* PerformSegue.swift */; }; 20 | 5E4D3FCB278FA2F800A0861A /* SpiralMatrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E4D3FCA278FA2F800A0861A /* SpiralMatrix.swift */; }; 21 | 5E4D3FCE278FA3FB00A0861A /* spiral1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 5E4D3FCC278FA3FB00A0861A /* spiral1.jpg */; }; 22 | 5E4D3FCF278FA3FB00A0861A /* spiral.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 5E4D3FCD278FA3FB00A0861A /* spiral.jpg */; }; 23 | 5E5974F526AAAA46006DD0DC /* CustomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E5974F426AAAA46006DD0DC /* CustomView.swift */; }; 24 | 5E5FE82426959E5100C5A396 /* Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E5FE82326959E5100C5A396 /* Equatable.swift */; }; 25 | 5E5FE8262695A27B00C5A396 /* Comparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E5FE8252695A27B00C5A396 /* Comparable.swift */; }; 26 | 5E66D43626694663001CB8F0 /* FailableInitializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E66D43526694663001CB8F0 /* FailableInitializers.swift */; }; 27 | 5E709213278CA0BA0060FB31 /* PascalTriangleAnimated2.gif in Resources */ = {isa = PBXBuildFile; fileRef = 5E709212278CA0BA0060FB31 /* PascalTriangleAnimated2.gif */; }; 28 | 5E72DD5E266A76EE00A8E543 /* Guard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E72DD5D266A76EE00A8E543 /* Guard.swift */; }; 29 | 5E8385532667635000ACA770 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E8385522667635000ACA770 /* AppDelegate.swift */; }; 30 | 5E8385552667635000ACA770 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E8385542667635000ACA770 /* SceneDelegate.swift */; }; 31 | 5E8385572667635000ACA770 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E8385562667635000ACA770 /* ViewController.swift */; }; 32 | 5E83855A2667635000ACA770 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E8385582667635000ACA770 /* Main.storyboard */; }; 33 | 5E83855C2667635400ACA770 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E83855B2667635400ACA770 /* Assets.xcassets */; }; 34 | 5E83855F2667635400ACA770 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E83855D2667635400ACA770 /* LaunchScreen.storyboard */; }; 35 | 5E83856B2667652700ACA770 /* CleanClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E83856A2667652700ACA770 /* CleanClass.swift */; }; 36 | 5E83856F266767D600ACA770 /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = 5E83856E266767D600ACA770 /* .gitignore */; }; 37 | 5E8385712667681600ACA770 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 5E8385702667681600ACA770 /* README.md */; }; 38 | 5E838575266775E300ACA770 /* LifeCycle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E838574266775E300ACA770 /* LifeCycle.swift */; }; 39 | 5E83857926677C7600ACA770 /* Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E83857826677C7600ACA770 /* Extension.swift */; }; 40 | 5E83857B2667814000ACA770 /* LogicallyRelatedElements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E83857A2667814000ACA770 /* LogicallyRelatedElements.swift */; }; 41 | 5E83857E2667B2A400ACA770 /* DeadCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E83857D2667B2A400ACA770 /* DeadCode.swift */; }; 42 | 5E8385802667B63900ACA770 /* MARK.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E83857F2667B63900ACA770 /* MARK.swift */; }; 43 | 5E8385832667B92900ACA770 /* CustomStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E8385822667B92900ACA770 /* CustomStringConvertible.swift */; }; 44 | 5E8385862667BE7500ACA770 /* Int+Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E8385852667BE7500ACA770 /* Int+Random.swift */; }; 45 | 5E83858B2667C5CB00ACA770 /* ErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E83858A2667C5CB00ACA770 /* ErrorHandling.swift */; }; 46 | 5E83858D2667D3C400ACA770 /* TypeCasting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E83858C2667D3C400ACA770 /* TypeCasting.swift */; }; 47 | 5E84F23B26FC424B00BA06D3 /* home-screen-quick-actions.jpeg in Resources */ = {isa = PBXBuildFile; fileRef = 5E84F23A26FC424B00BA06D3 /* home-screen-quick-actions.jpeg */; }; 48 | 5E88DEA9273C85D700363BF6 /* DismissKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E88DEA8273C85D700363BF6 /* DismissKeyboard.swift */; }; 49 | 5E88DEAC273D3C5800363BF6 /* String+CompareNumbers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E88DEAB273D3C5800363BF6 /* String+CompareNumbers.swift */; }; 50 | 5E9DF0BD2812EB520083DD6A /* AutoLayoutConstraintsProgrammatically.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E9DF0BC2812EB520083DD6A /* AutoLayoutConstraintsProgrammatically.swift */; }; 51 | 5EAC17F826807D1C006EC7CD /* PassDataDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EAC17F726807D1C006EC7CD /* PassDataDelegate.swift */; }; 52 | 5EAC17FA26808458006EC7CD /* PassDataClosure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EAC17F926808458006EC7CD /* PassDataClosure.swift */; }; 53 | 5EAC92BA26BA8D0D00A6198C /* MoveRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EAC92B926BA8D0D00A6198C /* MoveRow.swift */; }; 54 | 5EAC92BC26BAA06300A6198C /* DeleteRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EAC92BB26BAA06300A6198C /* DeleteRow.swift */; }; 55 | 5EAC92BE26BC0BCD00A6198C /* Source.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EAC92BD26BC0BCD00A6198C /* Source.swift */; }; 56 | 5EAC92C026BC0E4D00A6198C /* Destination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EAC92BF26BC0E4D00A6198C /* Destination.swift */; }; 57 | 5EAC92C726BD35E700A6198C /* SaveAndLoadDataOnDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EAC92C626BD35E700A6198C /* SaveAndLoadDataOnDevice.swift */; }; 58 | 5EAF0B81268C5F4300D92322 /* LaunchScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EAF0B80268C5F4300D92322 /* LaunchScreen.swift */; }; 59 | 5ED4FE63270920F700E3B850 /* License in Resources */ = {isa = PBXBuildFile; fileRef = 5ED4FE62270920F700E3B850 /* License */; }; 60 | 5EDBDABA279B5C5B006DF495 /* ContainerWithMostWater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EDBDAB9279B5C5B006DF495 /* ContainerWithMostWater.swift */; }; 61 | 5EDBDABC279B5DFB006DF495 /* ContainerWithMostWater.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 5EDBDABB279B5DFB006DF495 /* ContainerWithMostWater.jpg */; }; 62 | 5EDE65CE26E29E0000DB71C0 /* SwipeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EDE65CD26E29E0000DB71C0 /* SwipeScreen.swift */; }; 63 | 5EF5E01526F72FE40068A717 /* EnumDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EF5E01426F72FE40068A717 /* EnumDataModel.swift */; }; 64 | /* End PBXBuildFile section */ 65 | 66 | /* Begin PBXFileReference section */ 67 | 5E023E88268364C3002465D8 /* ArrayEnumerated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayEnumerated.swift; sourceTree = ""; }; 68 | 5E023E8A2683692F002465D8 /* ArrayZip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayZip.swift; sourceTree = ""; }; 69 | 5E023E8E26863070002465D8 /* Dictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dictionary.swift; sourceTree = ""; }; 70 | 5E1F2F4827873121009446ED /* FactoryMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FactoryMethod.swift; sourceTree = ""; }; 71 | 5E1F30D5278C9C5E009446ED /* Pascal'sTriangle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Pascal'sTriangle.swift"; sourceTree = ""; }; 72 | 5E2CF7C0266E20AE007BB2EB /* UnwindSegue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnwindSegue.swift; sourceTree = ""; }; 73 | 5E2CF7C3266E242E007BB2EB /* segue-002.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "segue-002.png"; sourceTree = ""; }; 74 | 5E2CF7C4266E242E007BB2EB /* segue-001.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "segue-001.png"; sourceTree = ""; }; 75 | 5E2CF7C7266E61A0007BB2EB /* PrepareSegue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrepareSegue.swift; sourceTree = ""; }; 76 | 5E2CF7C9266EB745007BB2EB /* PerformSegue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerformSegue.swift; sourceTree = ""; }; 77 | 5E4D3FCA278FA2F800A0861A /* SpiralMatrix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpiralMatrix.swift; sourceTree = ""; }; 78 | 5E4D3FCC278FA3FB00A0861A /* spiral1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = spiral1.jpg; sourceTree = ""; }; 79 | 5E4D3FCD278FA3FB00A0861A /* spiral.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = spiral.jpg; sourceTree = ""; }; 80 | 5E5974F426AAAA46006DD0DC /* CustomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomView.swift; sourceTree = ""; }; 81 | 5E5FE82326959E5100C5A396 /* Equatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Equatable.swift; sourceTree = ""; }; 82 | 5E5FE8252695A27B00C5A396 /* Comparable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comparable.swift; sourceTree = ""; }; 83 | 5E66D43526694663001CB8F0 /* FailableInitializers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailableInitializers.swift; sourceTree = ""; }; 84 | 5E709212278CA0BA0060FB31 /* PascalTriangleAnimated2.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = PascalTriangleAnimated2.gif; sourceTree = ""; }; 85 | 5E72DD5D266A76EE00A8E543 /* Guard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Guard.swift; sourceTree = ""; }; 86 | 5E83854F2667635000ACA770 /* Help.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Help.app; sourceTree = BUILT_PRODUCTS_DIR; }; 87 | 5E8385522667635000ACA770 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 88 | 5E8385542667635000ACA770 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 89 | 5E8385562667635000ACA770 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 90 | 5E8385592667635000ACA770 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 91 | 5E83855B2667635400ACA770 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 92 | 5E83855E2667635400ACA770 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 93 | 5E8385602667635400ACA770 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 94 | 5E83856A2667652700ACA770 /* CleanClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CleanClass.swift; sourceTree = ""; }; 95 | 5E83856E266767D600ACA770 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; 96 | 5E8385702667681600ACA770 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 97 | 5E838574266775E300ACA770 /* LifeCycle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LifeCycle.swift; sourceTree = ""; }; 98 | 5E83857826677C7600ACA770 /* Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extension.swift; sourceTree = ""; }; 99 | 5E83857A2667814000ACA770 /* LogicallyRelatedElements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogicallyRelatedElements.swift; sourceTree = ""; }; 100 | 5E83857D2667B2A400ACA770 /* DeadCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeadCode.swift; sourceTree = ""; }; 101 | 5E83857F2667B63900ACA770 /* MARK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MARK.swift; sourceTree = ""; }; 102 | 5E8385822667B92900ACA770 /* CustomStringConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomStringConvertible.swift; sourceTree = ""; }; 103 | 5E8385852667BE7500ACA770 /* Int+Random.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Random.swift"; sourceTree = ""; }; 104 | 5E83858A2667C5CB00ACA770 /* ErrorHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandling.swift; sourceTree = ""; }; 105 | 5E83858C2667D3C400ACA770 /* TypeCasting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeCasting.swift; sourceTree = ""; }; 106 | 5E84F23A26FC424B00BA06D3 /* home-screen-quick-actions.jpeg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "home-screen-quick-actions.jpeg"; sourceTree = ""; }; 107 | 5E88DEA8273C85D700363BF6 /* DismissKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DismissKeyboard.swift; sourceTree = ""; }; 108 | 5E88DEAB273D3C5800363BF6 /* String+CompareNumbers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+CompareNumbers.swift"; sourceTree = ""; }; 109 | 5E9DF0BC2812EB520083DD6A /* AutoLayoutConstraintsProgrammatically.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoLayoutConstraintsProgrammatically.swift; sourceTree = ""; }; 110 | 5EAC17F726807D1C006EC7CD /* PassDataDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassDataDelegate.swift; sourceTree = ""; }; 111 | 5EAC17F926808458006EC7CD /* PassDataClosure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassDataClosure.swift; sourceTree = ""; }; 112 | 5EAC92B926BA8D0D00A6198C /* MoveRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveRow.swift; sourceTree = ""; }; 113 | 5EAC92BB26BAA06300A6198C /* DeleteRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteRow.swift; sourceTree = ""; }; 114 | 5EAC92BD26BC0BCD00A6198C /* Source.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Source.swift; sourceTree = ""; }; 115 | 5EAC92BF26BC0E4D00A6198C /* Destination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Destination.swift; sourceTree = ""; }; 116 | 5EAC92C626BD35E700A6198C /* SaveAndLoadDataOnDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveAndLoadDataOnDevice.swift; sourceTree = ""; }; 117 | 5EAF0B80268C5F4300D92322 /* LaunchScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchScreen.swift; sourceTree = ""; }; 118 | 5ED4FE62270920F700E3B850 /* License */ = {isa = PBXFileReference; lastKnownFileType = text; path = License; sourceTree = ""; }; 119 | 5EDBDAB9279B5C5B006DF495 /* ContainerWithMostWater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerWithMostWater.swift; sourceTree = ""; }; 120 | 5EDBDABB279B5DFB006DF495 /* ContainerWithMostWater.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = ContainerWithMostWater.jpg; sourceTree = ""; }; 121 | 5EDE65CD26E29E0000DB71C0 /* SwipeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeScreen.swift; sourceTree = ""; }; 122 | 5EF5E01426F72FE40068A717 /* EnumDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumDataModel.swift; sourceTree = ""; }; 123 | /* End PBXFileReference section */ 124 | 125 | /* Begin PBXFrameworksBuildPhase section */ 126 | 5E83854C2667635000ACA770 /* Frameworks */ = { 127 | isa = PBXFrameworksBuildPhase; 128 | buildActionMask = 2147483647; 129 | files = ( 130 | ); 131 | runOnlyForDeploymentPostprocessing = 0; 132 | }; 133 | /* End PBXFrameworksBuildPhase section */ 134 | 135 | /* Begin PBXGroup section */ 136 | 5E1F2F4727872F9B009446ED /* DesignPatterns */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 5E1F2F4827873121009446ED /* FactoryMethod.swift */, 140 | ); 141 | path = DesignPatterns; 142 | sourceTree = ""; 143 | }; 144 | 5E1F30D4278C9BF9009446ED /* LeetCode */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 5E1F30D5278C9C5E009446ED /* Pascal'sTriangle.swift */, 148 | 5E4D3FCA278FA2F800A0861A /* SpiralMatrix.swift */, 149 | 5EDBDAB9279B5C5B006DF495 /* ContainerWithMostWater.swift */, 150 | ); 151 | path = LeetCode; 152 | sourceTree = ""; 153 | }; 154 | 5E2CF7BF266E2087007BB2EB /* UIKit */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 5E88DEAA273C869000363BF6 /* UITextField */, 158 | 5EAC92C326BD317800A6198C /* DataManager */, 159 | 5EAC92B826BA8CBC00A6198C /* TableView */, 160 | 5E2CF7C0266E20AE007BB2EB /* UnwindSegue.swift */, 161 | 5E2CF7C7266E61A0007BB2EB /* PrepareSegue.swift */, 162 | 5E2CF7C9266EB745007BB2EB /* PerformSegue.swift */, 163 | 5EAC92BD26BC0BCD00A6198C /* Source.swift */, 164 | 5EAC92BF26BC0E4D00A6198C /* Destination.swift */, 165 | 5EAC17F726807D1C006EC7CD /* PassDataDelegate.swift */, 166 | 5EAC17F926808458006EC7CD /* PassDataClosure.swift */, 167 | 5E9DF0BC2812EB520083DD6A /* AutoLayoutConstraintsProgrammatically.swift */, 168 | ); 169 | path = UIKit; 170 | sourceTree = ""; 171 | }; 172 | 5E2CF7C2266E23D7007BB2EB /* Screenshots */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | 5E4D3FCD278FA3FB00A0861A /* spiral.jpg */, 176 | 5E4D3FCC278FA3FB00A0861A /* spiral1.jpg */, 177 | 5E2CF7C4266E242E007BB2EB /* segue-001.png */, 178 | 5E2CF7C3266E242E007BB2EB /* segue-002.png */, 179 | 5E84F23A26FC424B00BA06D3 /* home-screen-quick-actions.jpeg */, 180 | 5E709212278CA0BA0060FB31 /* PascalTriangleAnimated2.gif */, 181 | 5EDBDABB279B5DFB006DF495 /* ContainerWithMostWater.jpg */, 182 | ); 183 | path = Screenshots; 184 | sourceTree = ""; 185 | }; 186 | 5E8385462667634F00ACA770 = { 187 | isa = PBXGroup; 188 | children = ( 189 | 5E2CF7C2266E23D7007BB2EB /* Screenshots */, 190 | 5E8385702667681600ACA770 /* README.md */, 191 | 5E83856E266767D600ACA770 /* .gitignore */, 192 | 5ED4FE62270920F700E3B850 /* License */, 193 | 5E8385512667635000ACA770 /* Help */, 194 | 5E8385502667635000ACA770 /* Products */, 195 | ); 196 | sourceTree = ""; 197 | }; 198 | 5E8385502667635000ACA770 /* Products */ = { 199 | isa = PBXGroup; 200 | children = ( 201 | 5E83854F2667635000ACA770 /* Help.app */, 202 | ); 203 | name = Products; 204 | sourceTree = ""; 205 | }; 206 | 5E8385512667635000ACA770 /* Help */ = { 207 | isa = PBXGroup; 208 | children = ( 209 | 5E1F30D4278C9BF9009446ED /* LeetCode */, 210 | 5EAF0B7F268C5E9200D92322 /* Features */, 211 | 5E83856C2667679B00ACA770 /* SupportingFiles */, 212 | 5E83857726677A6100ACA770 /* CleanCode */, 213 | 5E1F2F4727872F9B009446ED /* DesignPatterns */, 214 | 5E2CF7BF266E2087007BB2EB /* UIKit */, 215 | 5E8385872667C57600ACA770 /* WorkingCode */, 216 | 5E8385842667BCAB00ACA770 /* Extensions */, 217 | 5E8385812667B91000ACA770 /* Protocols */, 218 | ); 219 | path = Help; 220 | sourceTree = ""; 221 | }; 222 | 5E83856C2667679B00ACA770 /* SupportingFiles */ = { 223 | isa = PBXGroup; 224 | children = ( 225 | 5E8385562667635000ACA770 /* ViewController.swift */, 226 | 5E8385582667635000ACA770 /* Main.storyboard */, 227 | 5E8385602667635400ACA770 /* Info.plist */, 228 | 5E8385522667635000ACA770 /* AppDelegate.swift */, 229 | 5E8385542667635000ACA770 /* SceneDelegate.swift */, 230 | 5E83855B2667635400ACA770 /* Assets.xcassets */, 231 | 5E83855D2667635400ACA770 /* LaunchScreen.storyboard */, 232 | ); 233 | path = SupportingFiles; 234 | sourceTree = ""; 235 | }; 236 | 5E83857726677A6100ACA770 /* CleanCode */ = { 237 | isa = PBXGroup; 238 | children = ( 239 | 5E83856A2667652700ACA770 /* CleanClass.swift */, 240 | 5E838574266775E300ACA770 /* LifeCycle.swift */, 241 | 5E83857826677C7600ACA770 /* Extension.swift */, 242 | 5E83857A2667814000ACA770 /* LogicallyRelatedElements.swift */, 243 | 5E83857D2667B2A400ACA770 /* DeadCode.swift */, 244 | 5E83857F2667B63900ACA770 /* MARK.swift */, 245 | ); 246 | path = CleanCode; 247 | sourceTree = ""; 248 | }; 249 | 5E8385812667B91000ACA770 /* Protocols */ = { 250 | isa = PBXGroup; 251 | children = ( 252 | 5E8385822667B92900ACA770 /* CustomStringConvertible.swift */, 253 | 5E5FE82326959E5100C5A396 /* Equatable.swift */, 254 | 5E5FE8252695A27B00C5A396 /* Comparable.swift */, 255 | ); 256 | path = Protocols; 257 | sourceTree = ""; 258 | }; 259 | 5E8385842667BCAB00ACA770 /* Extensions */ = { 260 | isa = PBXGroup; 261 | children = ( 262 | 5E8385852667BE7500ACA770 /* Int+Random.swift */, 263 | 5E88DEAB273D3C5800363BF6 /* String+CompareNumbers.swift */, 264 | ); 265 | path = Extensions; 266 | sourceTree = ""; 267 | }; 268 | 5E8385872667C57600ACA770 /* WorkingCode */ = { 269 | isa = PBXGroup; 270 | children = ( 271 | 5E83858A2667C5CB00ACA770 /* ErrorHandling.swift */, 272 | 5E83858C2667D3C400ACA770 /* TypeCasting.swift */, 273 | 5E66D43526694663001CB8F0 /* FailableInitializers.swift */, 274 | 5E72DD5D266A76EE00A8E543 /* Guard.swift */, 275 | 5E023E88268364C3002465D8 /* ArrayEnumerated.swift */, 276 | 5E023E8A2683692F002465D8 /* ArrayZip.swift */, 277 | 5E023E8E26863070002465D8 /* Dictionary.swift */, 278 | 5EF5E01426F72FE40068A717 /* EnumDataModel.swift */, 279 | ); 280 | path = WorkingCode; 281 | sourceTree = ""; 282 | }; 283 | 5E88DEAA273C869000363BF6 /* UITextField */ = { 284 | isa = PBXGroup; 285 | children = ( 286 | 5E88DEA8273C85D700363BF6 /* DismissKeyboard.swift */, 287 | ); 288 | path = UITextField; 289 | sourceTree = ""; 290 | }; 291 | 5EAC92B826BA8CBC00A6198C /* TableView */ = { 292 | isa = PBXGroup; 293 | children = ( 294 | 5EAC92B926BA8D0D00A6198C /* MoveRow.swift */, 295 | 5EAC92BB26BAA06300A6198C /* DeleteRow.swift */, 296 | ); 297 | path = TableView; 298 | sourceTree = ""; 299 | }; 300 | 5EAC92C326BD317800A6198C /* DataManager */ = { 301 | isa = PBXGroup; 302 | children = ( 303 | 5EAC92C626BD35E700A6198C /* SaveAndLoadDataOnDevice.swift */, 304 | ); 305 | path = DataManager; 306 | sourceTree = ""; 307 | }; 308 | 5EAF0B7F268C5E9200D92322 /* Features */ = { 309 | isa = PBXGroup; 310 | children = ( 311 | 5EAF0B80268C5F4300D92322 /* LaunchScreen.swift */, 312 | 5E5974F426AAAA46006DD0DC /* CustomView.swift */, 313 | 5EDE65CD26E29E0000DB71C0 /* SwipeScreen.swift */, 314 | ); 315 | path = Features; 316 | sourceTree = ""; 317 | }; 318 | /* End PBXGroup section */ 319 | 320 | /* Begin PBXNativeTarget section */ 321 | 5E83854E2667635000ACA770 /* Help */ = { 322 | isa = PBXNativeTarget; 323 | buildConfigurationList = 5E8385632667635400ACA770 /* Build configuration list for PBXNativeTarget "Help" */; 324 | buildPhases = ( 325 | 5E83854B2667635000ACA770 /* Sources */, 326 | 5E83854C2667635000ACA770 /* Frameworks */, 327 | 5E83854D2667635000ACA770 /* Resources */, 328 | ); 329 | buildRules = ( 330 | ); 331 | dependencies = ( 332 | ); 333 | name = Help; 334 | productName = Help; 335 | productReference = 5E83854F2667635000ACA770 /* Help.app */; 336 | productType = "com.apple.product-type.application"; 337 | }; 338 | /* End PBXNativeTarget section */ 339 | 340 | /* Begin PBXProject section */ 341 | 5E8385472667634F00ACA770 /* Project object */ = { 342 | isa = PBXProject; 343 | attributes = { 344 | LastSwiftUpdateCheck = 1250; 345 | LastUpgradeCheck = 1250; 346 | TargetAttributes = { 347 | 5E83854E2667635000ACA770 = { 348 | CreatedOnToolsVersion = 12.5; 349 | }; 350 | }; 351 | }; 352 | buildConfigurationList = 5E83854A2667634F00ACA770 /* Build configuration list for PBXProject "Help" */; 353 | compatibilityVersion = "Xcode 9.3"; 354 | developmentRegion = en; 355 | hasScannedForEncodings = 0; 356 | knownRegions = ( 357 | en, 358 | Base, 359 | ); 360 | mainGroup = 5E8385462667634F00ACA770; 361 | productRefGroup = 5E8385502667635000ACA770 /* Products */; 362 | projectDirPath = ""; 363 | projectRoot = ""; 364 | targets = ( 365 | 5E83854E2667635000ACA770 /* Help */, 366 | ); 367 | }; 368 | /* End PBXProject section */ 369 | 370 | /* Begin PBXResourcesBuildPhase section */ 371 | 5E83854D2667635000ACA770 /* Resources */ = { 372 | isa = PBXResourcesBuildPhase; 373 | buildActionMask = 2147483647; 374 | files = ( 375 | 5EDBDABC279B5DFB006DF495 /* ContainerWithMostWater.jpg in Resources */, 376 | 5E2CF7C5266E242E007BB2EB /* segue-002.png in Resources */, 377 | 5E83856F266767D600ACA770 /* .gitignore in Resources */, 378 | 5E84F23B26FC424B00BA06D3 /* home-screen-quick-actions.jpeg in Resources */, 379 | 5E4D3FCE278FA3FB00A0861A /* spiral1.jpg in Resources */, 380 | 5E83855F2667635400ACA770 /* LaunchScreen.storyboard in Resources */, 381 | 5E8385712667681600ACA770 /* README.md in Resources */, 382 | 5E83855C2667635400ACA770 /* Assets.xcassets in Resources */, 383 | 5E709213278CA0BA0060FB31 /* PascalTriangleAnimated2.gif in Resources */, 384 | 5E4D3FCF278FA3FB00A0861A /* spiral.jpg in Resources */, 385 | 5ED4FE63270920F700E3B850 /* License in Resources */, 386 | 5E83855A2667635000ACA770 /* Main.storyboard in Resources */, 387 | 5E2CF7C6266E242E007BB2EB /* segue-001.png in Resources */, 388 | ); 389 | runOnlyForDeploymentPostprocessing = 0; 390 | }; 391 | /* End PBXResourcesBuildPhase section */ 392 | 393 | /* Begin PBXSourcesBuildPhase section */ 394 | 5E83854B2667635000ACA770 /* Sources */ = { 395 | isa = PBXSourcesBuildPhase; 396 | buildActionMask = 2147483647; 397 | files = ( 398 | 5EDE65CE26E29E0000DB71C0 /* SwipeScreen.swift in Sources */, 399 | 5E4D3FCB278FA2F800A0861A /* SpiralMatrix.swift in Sources */, 400 | 5E8385862667BE7500ACA770 /* Int+Random.swift in Sources */, 401 | 5E1F2F4927873121009446ED /* FactoryMethod.swift in Sources */, 402 | 5EAC92C726BD35E700A6198C /* SaveAndLoadDataOnDevice.swift in Sources */, 403 | 5E2CF7C8266E61A0007BB2EB /* PrepareSegue.swift in Sources */, 404 | 5E88DEAC273D3C5800363BF6 /* String+CompareNumbers.swift in Sources */, 405 | 5E9DF0BD2812EB520083DD6A /* AutoLayoutConstraintsProgrammatically.swift in Sources */, 406 | 5E83857B2667814000ACA770 /* LogicallyRelatedElements.swift in Sources */, 407 | 5EAC92C026BC0E4D00A6198C /* Destination.swift in Sources */, 408 | 5EAC92BA26BA8D0D00A6198C /* MoveRow.swift in Sources */, 409 | 5E83856B2667652700ACA770 /* CleanClass.swift in Sources */, 410 | 5E83857E2667B2A400ACA770 /* DeadCode.swift in Sources */, 411 | 5EDBDABA279B5C5B006DF495 /* ContainerWithMostWater.swift in Sources */, 412 | 5E8385802667B63900ACA770 /* MARK.swift in Sources */, 413 | 5EAC17FA26808458006EC7CD /* PassDataClosure.swift in Sources */, 414 | 5EAC92BE26BC0BCD00A6198C /* Source.swift in Sources */, 415 | 5E023E8F26863070002465D8 /* Dictionary.swift in Sources */, 416 | 5EAF0B81268C5F4300D92322 /* LaunchScreen.swift in Sources */, 417 | 5E8385572667635000ACA770 /* ViewController.swift in Sources */, 418 | 5E66D43626694663001CB8F0 /* FailableInitializers.swift in Sources */, 419 | 5E88DEA9273C85D700363BF6 /* DismissKeyboard.swift in Sources */, 420 | 5EAC92BC26BAA06300A6198C /* DeleteRow.swift in Sources */, 421 | 5E023E8B2683692F002465D8 /* ArrayZip.swift in Sources */, 422 | 5E1F30D6278C9C5E009446ED /* Pascal'sTriangle.swift in Sources */, 423 | 5E8385532667635000ACA770 /* AppDelegate.swift in Sources */, 424 | 5E5FE8262695A27B00C5A396 /* Comparable.swift in Sources */, 425 | 5E5FE82426959E5100C5A396 /* Equatable.swift in Sources */, 426 | 5E83857926677C7600ACA770 /* Extension.swift in Sources */, 427 | 5E83858D2667D3C400ACA770 /* TypeCasting.swift in Sources */, 428 | 5EAC17F826807D1C006EC7CD /* PassDataDelegate.swift in Sources */, 429 | 5E2CF7CA266EB745007BB2EB /* PerformSegue.swift in Sources */, 430 | 5E2CF7C1266E20AE007BB2EB /* UnwindSegue.swift in Sources */, 431 | 5EF5E01526F72FE40068A717 /* EnumDataModel.swift in Sources */, 432 | 5E8385832667B92900ACA770 /* CustomStringConvertible.swift in Sources */, 433 | 5E5974F526AAAA46006DD0DC /* CustomView.swift in Sources */, 434 | 5E023E89268364C3002465D8 /* ArrayEnumerated.swift in Sources */, 435 | 5E72DD5E266A76EE00A8E543 /* Guard.swift in Sources */, 436 | 5E83858B2667C5CB00ACA770 /* ErrorHandling.swift in Sources */, 437 | 5E838575266775E300ACA770 /* LifeCycle.swift in Sources */, 438 | 5E8385552667635000ACA770 /* SceneDelegate.swift in Sources */, 439 | ); 440 | runOnlyForDeploymentPostprocessing = 0; 441 | }; 442 | /* End PBXSourcesBuildPhase section */ 443 | 444 | /* Begin PBXVariantGroup section */ 445 | 5E8385582667635000ACA770 /* Main.storyboard */ = { 446 | isa = PBXVariantGroup; 447 | children = ( 448 | 5E8385592667635000ACA770 /* Base */, 449 | ); 450 | name = Main.storyboard; 451 | sourceTree = ""; 452 | }; 453 | 5E83855D2667635400ACA770 /* LaunchScreen.storyboard */ = { 454 | isa = PBXVariantGroup; 455 | children = ( 456 | 5E83855E2667635400ACA770 /* Base */, 457 | ); 458 | name = LaunchScreen.storyboard; 459 | sourceTree = ""; 460 | }; 461 | /* End PBXVariantGroup section */ 462 | 463 | /* Begin XCBuildConfiguration section */ 464 | 5E8385612667635400ACA770 /* Debug */ = { 465 | isa = XCBuildConfiguration; 466 | buildSettings = { 467 | ALWAYS_SEARCH_USER_PATHS = NO; 468 | CLANG_ANALYZER_NONNULL = YES; 469 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 470 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 471 | CLANG_CXX_LIBRARY = "libc++"; 472 | CLANG_ENABLE_MODULES = YES; 473 | CLANG_ENABLE_OBJC_ARC = YES; 474 | CLANG_ENABLE_OBJC_WEAK = YES; 475 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 476 | CLANG_WARN_BOOL_CONVERSION = YES; 477 | CLANG_WARN_COMMA = YES; 478 | CLANG_WARN_CONSTANT_CONVERSION = YES; 479 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 480 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 481 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 482 | CLANG_WARN_EMPTY_BODY = YES; 483 | CLANG_WARN_ENUM_CONVERSION = YES; 484 | CLANG_WARN_INFINITE_RECURSION = YES; 485 | CLANG_WARN_INT_CONVERSION = YES; 486 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 487 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 488 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 489 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 490 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 491 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 492 | CLANG_WARN_STRICT_PROTOTYPES = YES; 493 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 494 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 495 | CLANG_WARN_UNREACHABLE_CODE = YES; 496 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 497 | COPY_PHASE_STRIP = NO; 498 | DEBUG_INFORMATION_FORMAT = dwarf; 499 | ENABLE_STRICT_OBJC_MSGSEND = YES; 500 | ENABLE_TESTABILITY = YES; 501 | GCC_C_LANGUAGE_STANDARD = gnu11; 502 | GCC_DYNAMIC_NO_PIC = NO; 503 | GCC_NO_COMMON_BLOCKS = YES; 504 | GCC_OPTIMIZATION_LEVEL = 0; 505 | GCC_PREPROCESSOR_DEFINITIONS = ( 506 | "DEBUG=1", 507 | "$(inherited)", 508 | ); 509 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 510 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 511 | GCC_WARN_UNDECLARED_SELECTOR = YES; 512 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 513 | GCC_WARN_UNUSED_FUNCTION = YES; 514 | GCC_WARN_UNUSED_VARIABLE = YES; 515 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 516 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 517 | MTL_FAST_MATH = YES; 518 | ONLY_ACTIVE_ARCH = YES; 519 | SDKROOT = iphoneos; 520 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 521 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 522 | }; 523 | name = Debug; 524 | }; 525 | 5E8385622667635400ACA770 /* Release */ = { 526 | isa = XCBuildConfiguration; 527 | buildSettings = { 528 | ALWAYS_SEARCH_USER_PATHS = NO; 529 | CLANG_ANALYZER_NONNULL = YES; 530 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 531 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 532 | CLANG_CXX_LIBRARY = "libc++"; 533 | CLANG_ENABLE_MODULES = YES; 534 | CLANG_ENABLE_OBJC_ARC = YES; 535 | CLANG_ENABLE_OBJC_WEAK = YES; 536 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 537 | CLANG_WARN_BOOL_CONVERSION = YES; 538 | CLANG_WARN_COMMA = YES; 539 | CLANG_WARN_CONSTANT_CONVERSION = YES; 540 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 541 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 542 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 543 | CLANG_WARN_EMPTY_BODY = YES; 544 | CLANG_WARN_ENUM_CONVERSION = YES; 545 | CLANG_WARN_INFINITE_RECURSION = YES; 546 | CLANG_WARN_INT_CONVERSION = YES; 547 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 548 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 549 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 550 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 551 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 552 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 553 | CLANG_WARN_STRICT_PROTOTYPES = YES; 554 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 555 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 556 | CLANG_WARN_UNREACHABLE_CODE = YES; 557 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 558 | COPY_PHASE_STRIP = NO; 559 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 560 | ENABLE_NS_ASSERTIONS = NO; 561 | ENABLE_STRICT_OBJC_MSGSEND = YES; 562 | GCC_C_LANGUAGE_STANDARD = gnu11; 563 | GCC_NO_COMMON_BLOCKS = YES; 564 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 565 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 566 | GCC_WARN_UNDECLARED_SELECTOR = YES; 567 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 568 | GCC_WARN_UNUSED_FUNCTION = YES; 569 | GCC_WARN_UNUSED_VARIABLE = YES; 570 | IPHONEOS_DEPLOYMENT_TARGET = 14.5; 571 | MTL_ENABLE_DEBUG_INFO = NO; 572 | MTL_FAST_MATH = YES; 573 | SDKROOT = iphoneos; 574 | SWIFT_COMPILATION_MODE = wholemodule; 575 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 576 | VALIDATE_PRODUCT = YES; 577 | }; 578 | name = Release; 579 | }; 580 | 5E8385642667635400ACA770 /* Debug */ = { 581 | isa = XCBuildConfiguration; 582 | buildSettings = { 583 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 584 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 585 | CODE_SIGN_STYLE = Automatic; 586 | DEVELOPMENT_TEAM = LGVM9RAH4N; 587 | INFOPLIST_FILE = Help/SupportingFiles/Info.plist; 588 | LD_RUNPATH_SEARCH_PATHS = ( 589 | "$(inherited)", 590 | "@executable_path/Frameworks", 591 | ); 592 | PRODUCT_BUNDLE_IDENTIFIER = "com.yahoo-s.lukaschuk.Help"; 593 | PRODUCT_NAME = "$(TARGET_NAME)"; 594 | SWIFT_VERSION = 5.0; 595 | TARGETED_DEVICE_FAMILY = "1,2"; 596 | }; 597 | name = Debug; 598 | }; 599 | 5E8385652667635400ACA770 /* Release */ = { 600 | isa = XCBuildConfiguration; 601 | buildSettings = { 602 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 603 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 604 | CODE_SIGN_STYLE = Automatic; 605 | DEVELOPMENT_TEAM = LGVM9RAH4N; 606 | INFOPLIST_FILE = Help/SupportingFiles/Info.plist; 607 | LD_RUNPATH_SEARCH_PATHS = ( 608 | "$(inherited)", 609 | "@executable_path/Frameworks", 610 | ); 611 | PRODUCT_BUNDLE_IDENTIFIER = "com.yahoo-s.lukaschuk.Help"; 612 | PRODUCT_NAME = "$(TARGET_NAME)"; 613 | SWIFT_VERSION = 5.0; 614 | TARGETED_DEVICE_FAMILY = "1,2"; 615 | }; 616 | name = Release; 617 | }; 618 | /* End XCBuildConfiguration section */ 619 | 620 | /* Begin XCConfigurationList section */ 621 | 5E83854A2667634F00ACA770 /* Build configuration list for PBXProject "Help" */ = { 622 | isa = XCConfigurationList; 623 | buildConfigurations = ( 624 | 5E8385612667635400ACA770 /* Debug */, 625 | 5E8385622667635400ACA770 /* Release */, 626 | ); 627 | defaultConfigurationIsVisible = 0; 628 | defaultConfigurationName = Release; 629 | }; 630 | 5E8385632667635400ACA770 /* Build configuration list for PBXNativeTarget "Help" */ = { 631 | isa = XCConfigurationList; 632 | buildConfigurations = ( 633 | 5E8385642667635400ACA770 /* Debug */, 634 | 5E8385652667635400ACA770 /* Release */, 635 | ); 636 | defaultConfigurationIsVisible = 0; 637 | defaultConfigurationName = Release; 638 | }; 639 | /* End XCConfigurationList section */ 640 | }; 641 | rootObject = 5E8385472667634F00ACA770 /* Project object */; 642 | } 643 | -------------------------------------------------------------------------------- /Help.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Help.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Help.xcodeproj/xcuserdata/sergeylukaschuk.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Help.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Help/CleanCode/CleanClass.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CleanClass.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 02.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: Clean Class 11 | /// The code has a clear structure, due to which the logic is more obvious, the code is easy to read, you can quickly find what you are looking for, and besides, it is just pleasant to look at it. 12 | 13 | /* 14 | final class CleanViewController: UIViewController { 15 | 16 | // MARK: - IBOutlets 17 | @IBOutlet private weak var searchBar: UISearchBar! 18 | @IBOutlet private weak var tableView: UITableView! 19 | 20 | 21 | // MARK: - Public Properties 22 | var userID: String? 23 | weak var delegate: SomeDelegate? 24 | 25 | 26 | // MARK: - Private Properties 27 | private let userService = UserService() 28 | private var userList: [User]? 29 | 30 | 31 | // MARK: - Lifecycle 32 | override func viewDidLoad() { 33 | super.viewDidLoad() 34 | 35 | setupNavigationBar() 36 | } 37 | 38 | 39 | // MARK: - Private Methods 40 | private func setupNavigationBar() { 41 | navigationController?.navigationBar.backgroundColor = .red 42 | navigationItem.title = "Some" 43 | } 44 | 45 | 46 | // MARK: - IBActions 47 | @IBAction private func cancelButtonPressed(_ sender: UIBarButtonItem) { 48 | dismiss(animated: true, completion: nil) 49 | } 50 | } 51 | 52 | */ 53 | 54 | 55 | -------------------------------------------------------------------------------- /Help/CleanCode/DeadCode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeadCode.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 02.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | // MARK: Dead Code 12 | /// Do not leave unnecessary comments (default), empty methods or dead functionality - it makes code dirty. Attention to the AppDelegate class, most likely you will find empty methods there with comments inside. 13 | 14 | /* 15 | ❌ NOT Preferred 16 | 17 | @UIApplicationMain 18 | class AppDelegate: UIResponder, UIApplicationDelegate { 19 | 20 | var window: UIWindow? 21 | 22 | func application( 23 | _ application: UIApplication, 24 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 25 | // Override point for customization after application launch. 26 | return true 27 | } 28 | // 29 | // func someFunc() { 30 | // print("Some") 31 | // } 32 | 33 | func applicationWillResignActive(_ application: UIApplication) { 34 | // Sent when the application is about to move from active to inactive state. This can occur for certain 35 | //types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits 36 | //the application and it begins the transition to the background state. 37 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 38 | } 39 | 40 | 41 | ✅ Preferred 42 | 43 | @UIApplicationMain 44 | class AppDelegate: UIResponder, UIApplicationDelegate { 45 | 46 | var window: UIWindow? 47 | 48 | func application( 49 | _ application: UIApplication, 50 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 51 | 52 | return true 53 | } 54 | 55 | } 56 | */ 57 | 58 | 59 | -------------------------------------------------------------------------------- /Help/CleanCode/Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extension.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 02.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: Extension 11 | /// Using an extension to implement protocols. Move protocols implementation into extensions with mark // MARK: — SomeProtocol 12 | 13 | /* 14 | ❌ NOT Preferred 15 | 16 | final class CleanViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { 17 | 18 | // all methods 19 | } 20 | 21 | 22 | ✅ Preferred 23 | 24 | final class CleanViewController: UIViewController { 25 | 26 | // class stuff here 27 | 28 | } 29 | 30 | 31 | // MARK: - Table View Data Source 32 | extension CleanViewController: UITableViewDataSource { 33 | 34 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 35 | 36 | return userList?.count ?? 0 37 | } 38 | 39 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 40 | 41 | let cell = UITableViewCell() 42 | return cell 43 | } 44 | 45 | } 46 | */ 47 | 48 | 49 | -------------------------------------------------------------------------------- /Help/CleanCode/LifeCycle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Lifecycle.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 02.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | // MARK: Life Cycle 12 | /// We move the logic out of the lifecycle methods into separate methods. The logic inside the methods of the ViewController lifecycle should be moved into separate methods, even if you have to create a method with one line of code. Today one, and tomorrow ten. 13 | 14 | /* 15 | ❌ NOT Preferred 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | navigationController?.navigationBar.backgroundColor = .red 21 | someButton.layer.cornerRadius = 10 22 | someButton.layer.masksToBounds = true 23 | navigationItem.title = "Some" 24 | print("Some") 25 | } 26 | 27 | 28 | ✅ Preferred 29 | 30 | // MARK: - Lifecycle 31 | override func viewDidLoad() { 32 | super.viewDidLoad() 33 | 34 | setupNavigationBar() 35 | setupSomeButton() 36 | printSome() 37 | } 38 | 39 | 40 | // MARK: - Private Methods 41 | private func setupNavigationBar() { 42 | navigationController?.navigationBar.backgroundColor = .red 43 | navigationItem.title = "Some" 44 | } 45 | 46 | private func setupSomeButton() { 47 | someButton.layer.cornerRadius = 10 48 | someButton.layer.masksToBounds = true 49 | } 50 | 51 | private func printSome() { 52 | print("Some") 53 | } 54 | */ 55 | 56 | 57 | -------------------------------------------------------------------------------- /Help/CleanCode/Lifecycle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Lifecycle.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 02.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | // MARK: Life Cycle 12 | /// We move the logic out of the lifecycle methods into separate methods. The logic inside the methods of the ViewController lifecycle should be moved into separate methods, even if you have to create a method with one line of code. Today one, and tomorrow ten. 13 | 14 | /* 15 | ❌ NOT Preferred 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | navigationController?.navigationBar.backgroundColor = .red 21 | someButton.layer.cornerRadius = 10 22 | someButton.layer.masksToBounds = true 23 | navigationItem.title = "Some" 24 | print("Some") 25 | } 26 | 27 | 28 | ✅ Preferred 29 | 30 | // MARK: - Lifecycle 31 | override func viewDidLoad() { 32 | super.viewDidLoad() 33 | 34 | setupNavigationBar() 35 | setupSomeButton() 36 | printSome() 37 | } 38 | 39 | 40 | // MARK: - Private Methods 41 | private func setupNavigationBar() { 42 | navigationController?.navigationBar.backgroundColor = .red 43 | navigationItem.title = "Some" 44 | } 45 | 46 | private func setupSomeButton() { 47 | someButton.layer.cornerRadius = 10 48 | someButton.layer.masksToBounds = true 49 | } 50 | 51 | private func printSome() { 52 | print("Some") 53 | } 54 | */ 55 | 56 | 57 | -------------------------------------------------------------------------------- /Help/CleanCode/LogicallyRelatedElements.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LogicallyRelatedElements.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 02.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | // MARK: Logically Related Elements 12 | /// Selecting logically related elements. To improve clarity, you need to highlight logically related elements using an empty string. 13 | 14 | /* 15 | ❌ NOT Preferred 16 | 17 | private func showActivityIndicator(on viewController: UIViewController) { 18 | activityIndicator.center = viewController.view.center 19 | loadingView.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) 20 | loadingView.alpha = 0.5 21 | activityIndicator.hidesWhenStopped = true 22 | activityIndicator.style = .whiteLarge 23 | loadingView.center = viewController.view.center 24 | loadingView.clipsToBounds = true 25 | loadingView.layer.cornerRadius = 15 26 | viewController.view.addSubview(loadingView) 27 | viewController.view.addSubview(activityIndicator) 28 | activityIndicator.startAnimating() 29 | } 30 | 31 | 32 | ✅ Preferred 33 | 34 | private func showActivityIndicator(on viewController: UIViewController) { 35 | activityIndicator.center = viewController.view.center 36 | activityIndicator.hidesWhenStopped = true 37 | activityIndicator.style = .whiteLarge 38 | 39 | loadingView.center = viewController.view.center 40 | loadingView.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) 41 | loadingView.alpha = 0.5 42 | loadingView.clipsToBounds = true 43 | loadingView.layer.cornerRadius = 15 44 | 45 | viewController.view.addSubview(loadingView) 46 | viewController.view.addSubview(activityIndicator) 47 | 48 | activityIndicator.startAnimating() 49 | } 50 | */ 51 | 52 | 53 | -------------------------------------------------------------------------------- /Help/CleanCode/MARK.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MARK.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 02.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | // MARK: MARK 12 | /// The main MARKs for splitting the code into logically related blocks and their sequence 13 | 14 | // MARK: - IBOutlets 15 | 16 | // MARK: - Public Properties 17 | 18 | // MARK: - Private Properties 19 | 20 | // MARK: - Initializers 21 | 22 | // MARK: - Lifecycle 23 | 24 | // MARK: - View life cycle 25 | 26 | // MARK: - Object life cycle 27 | 28 | // MARK: - Public Methods 29 | 30 | // MARK: - Private Methods 31 | 32 | // MARK: - IBActions 33 | 34 | // MARK: - Actions 35 | 36 | // MARK: - Application Shortcut Support 37 | 38 | // MARK: - Segue preparation 39 | 40 | // MARK: - Table view data source 41 | -------------------------------------------------------------------------------- /Help/DesignPatterns/FactoryMethod.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FactoryMethod.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 06.01.2022. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | // structure Factory Method 12 | // MARK: Creator 13 | class FactoryProducts { 14 | 15 | static let defaultFactory = FactoryProducts() 16 | 17 | func createProduct(_ product: Products) -> Product_ { 18 | switch product { 19 | case .productA: return ConcreteProductA() 20 | case .productB: return ConcreteProductB() 21 | } 22 | } 23 | 24 | private init() {} 25 | } 26 | 27 | enum Products { 28 | case productA 29 | case productB 30 | } 31 | 32 | // MARK: Product Interface 33 | protocol Product_ { 34 | var id: String { get } 35 | var name: String { get } 36 | 37 | func printProduct() 38 | } 39 | 40 | 41 | // MARK: Product A 42 | class ConcreteProductA: Product_ { 43 | 44 | var name: String = "Product A" 45 | var id: String = "1" 46 | 47 | func printProduct() { 48 | print("id: \(id), name: \(name)") 49 | } 50 | } 51 | 52 | // MARK: Product B 53 | class ConcreteProductB: Product_ { 54 | 55 | var name: String = "Product B" 56 | var id: String = "2" 57 | 58 | func printProduct() { 59 | print("id: \(id), name: \(name)") 60 | } 61 | } 62 | 63 | 64 | // MARK: Implementation 65 | class SomeViewController: UIViewController { 66 | 67 | var products: [Product_] = [] 68 | 69 | override func viewDidLoad() { 70 | super.viewDidLoad() 71 | addProduct(.productA) 72 | addProduct(.productB) 73 | allProducts() 74 | } 75 | 76 | func addProduct(_ product: Products) { 77 | let newProduct = FactoryProducts.defaultFactory.createProduct(product) 78 | products.append(newProduct) 79 | } 80 | 81 | func allProducts() { 82 | products.forEach { $0.printProduct() } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Help/Extensions/Int+Random.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Int+Random.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 02.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | // MARK: Int + Random 12 | /// An extension for Int that returns a random number works with range of numbers. Negative numbers converts to positive. 13 | /// Example: 17.random will return 0 to 17 (not including 17) 14 | /// Example: -5.random, -5 convert to 5, will return 0 to 5 (not including 5). 15 | 16 | extension Int { 17 | var random: Int { 18 | if self > 0 { 19 | return Int.random(in: 0.. Bool { 13 | return lhs.compare(rhs, options: .numeric) == .orderedSame 14 | } 15 | 16 | static func <(lhs: String, rhs: String) -> Bool { 17 | return lhs.compare(rhs, options: .numeric) == .orderedAscending 18 | } 19 | 20 | static func <=(lhs: String, rhs: String) -> Bool { 21 | return lhs.compare(rhs, options: .numeric) == .orderedAscending || lhs.compare(rhs, options: .numeric) == .orderedSame 22 | } 23 | 24 | static func >(lhs: String, rhs: String) -> Bool { 25 | return lhs.compare(rhs, options: .numeric) == .orderedDescending 26 | } 27 | 28 | static func >=(lhs: String, rhs: String) -> Bool { 29 | return lhs.compare(rhs, options: .numeric) == .orderedDescending || lhs.compare(rhs, options: .numeric) == .orderedSame 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /Help/Features/CustomView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomView.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 23.07.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | /// Article 11 | /// https://medium.com/@umairhassanbaig/ios-swift-creating-a-custom-view-with-xib-ace878cd41c5 12 | 13 | class PersonView: UIView { 14 | 15 | @IBOutlet var contentView: UIView! 16 | @IBOutlet var firstNameLabel: UILabel! 17 | @IBOutlet var lastNameLabel: UILabel! 18 | 19 | let contentXibName = "PersonView" 20 | 21 | override init(frame: CGRect) { 22 | super.init(frame: frame) 23 | commonInit() 24 | } 25 | 26 | required init?(coder aDecoder: NSCoder) { 27 | super.init(coder: aDecoder) 28 | commonInit() 29 | } 30 | 31 | func commonInit() { 32 | Bundle.main.loadNibNamed(contentXibName, owner: self, options: nil) 33 | contentView.fixInView(self) 34 | } 35 | } 36 | 37 | 38 | extension UIView 39 | { 40 | func fixInView(_ container: UIView!) -> Void{ 41 | self.translatesAutoresizingMaskIntoConstraints = false; 42 | self.frame = container.frame; 43 | container.addSubview(self); 44 | NSLayoutConstraint(item: self, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true 45 | NSLayoutConstraint(item: self, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true 46 | NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1.0, constant: 0).isActive = true 47 | NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1.0, constant: 0).isActive = true 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Help/Features/LaunchScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LaunchScreen.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 30.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | /* 11 | class LogoViewController: UIViewController { 12 | 13 | // MARK: - Private Properties 14 | private var imageView: UIImageView = { 15 | let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 150, height: 150)) 16 | imageView.image = UIImage(named: "logo-icon") 17 | return imageView 18 | }() 19 | 20 | 21 | // MARK: - Lifecycle 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | view.addSubview(imageView) 25 | DispatchQueue.main.asyncAfter(deadline: .now()+1.5) { 26 | self.performSegue(withIdentifier: "welcomeVC", sender: nil) 27 | } 28 | } 29 | 30 | override func viewDidLayoutSubviews() { 31 | super.viewDidLayoutSubviews() 32 | imageView.center = view.center 33 | DispatchQueue.main.asyncAfter(deadline: .now()+0.5) { 34 | self.animate() 35 | } 36 | } 37 | 38 | 39 | // MARK: - Private Methods 40 | private func animate() { 41 | UIView.animate(withDuration: 1.7) { 42 | let size = self.view.frame.size.width * 2.5 43 | let diffX = size - self.view.frame.width 44 | let diffY = self.view.frame.height - size 45 | 46 | self.imageView.frame = CGRect( 47 | x: -(diffX/2), 48 | y: diffY/2, 49 | width: size, 50 | height: size) 51 | self.imageView.alpha = 0 52 | } 53 | } 54 | } 55 | 56 | 57 | class WelcomeViewController: UIViewController { 58 | 59 | override func viewDidLoad() { 60 | super.viewDidLoad() 61 | 62 | } 63 | } 64 | 65 | */ 66 | -------------------------------------------------------------------------------- /Help/Features/SwipeScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwipeScreen.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 03.09.2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | class SwipeScreen: UIViewController { 12 | 13 | private func configureSwipe() { 14 | let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(respondToSwipeGesture)) 15 | swipeRight.direction = .right 16 | self.view.addGestureRecognizer(swipeRight) 17 | 18 | let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(respondToSwipeGesture)) 19 | swipeLeft.direction = .left 20 | self.view.addGestureRecognizer(swipeLeft) 21 | } 22 | 23 | @objc 24 | private func respondToSwipeGesture(gesture: UIGestureRecognizer) { 25 | if let swipeGesture = gesture as? UISwipeGestureRecognizer { 26 | switch swipeGesture.direction { 27 | case .right: 28 | print("right swipe") 29 | // add action! 30 | case .left: 31 | print("left swipe") 32 | // add action! 33 | default: 34 | break 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Help/LeetCode/ContainerWithMostWater.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContainerWithMostWater.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 21.01.2022. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: Container With Most Water 11 | 12 | /* 13 | You are given an integer array `height` of length `n`. There are n vertical lines drawn such that the two endpoints of the `ith` line are `(i, 0)` and `(i, height[i])`. 14 | 15 | Find two lines that together with the x-axis form a container, such that the container contains the most water. 16 | 17 | Return the maximum amount of water a container can store. 18 | 19 | **Notice** that you may not slant the container. 20 | 21 | **Example 1:**
22 | *Input:* `height = [1,8,6,2,5,4,8,3,7]`
23 | *Output:* `49`
24 | *Explanation:* The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section) the container can contain is 49. 25 | 26 | **Example 2:**
27 | *Input:* `height = [1,1]`
28 | *Output:* `1`
29 | 30 | */ 31 | 32 | func maxArea(_ height: [Int]) -> Int { 33 | guard !height.isEmpty else { return -1 } 34 | 35 | var maxArea = 0 36 | var left = 0 37 | var right = height.count - 1 38 | 39 | while left < right { 40 | // Re-calc maxArea 41 | let minHeight = min(height[left], height[right]) 42 | let currentHeight = minHeight * (right - left) 43 | maxArea = max(maxArea, currentHeight) 44 | 45 | // Move pointers 46 | if height[left] < height[right] { 47 | left += 1 48 | } else { 49 | right -= 1 50 | } 51 | } 52 | return maxArea 53 | } 54 | 55 | let input = [1, 8, 6, 2, 5, 4, 8, 3, 7] 56 | let result = maxArea(input) 57 | //print("Result: \(result)") 58 | -------------------------------------------------------------------------------- /Help/LeetCode/Pascal'sTriangle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Pascal'sTriangle.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 10.01.2022. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | // MARK: Pascal's Triangle 12 | /* 13 | 14 | Given an integer numRows, return the first numRows of Pascal's triangle. 15 | In Pascal's triangle, each number is the sum of the two numbers directly above it as shown: 16 | 17 | Example 1: 18 | Input: numRows = 5 19 | Output: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] 20 | 21 | Example 2: 22 | Input: numRows = 1 23 | Output: [[1]] 24 | 25 | */ 26 | 27 | 28 | 29 | func generate(_ numRows: Int) -> [[Int]] { 30 | 31 | guard numRows > 0 else { return [] } 32 | if numRows == 1 { return [[1]] } 33 | 34 | var results = [[Int]]() 35 | results.append([1]) 36 | 37 | for x in 1.. [Int] { 26 | var result = [Int]() 27 | 28 | if matrix.count == 0 || matrix[0].count == 0 { 29 | return result 30 | } 31 | 32 | var left = 0 33 | var bottom = matrix.count - 1 34 | var top = 0 35 | var right = matrix[0].count - 1 36 | 37 | while (left <= bottom && top <= right) { 38 | // Go left to right 39 | for col in stride(from: top, through: right, by: 1) { 40 | result.append(matrix[left][col]) 41 | } 42 | // Go top to down 43 | left += 1 44 | for row in stride(from: left, through: bottom, by: 1) { 45 | result.append(matrix[row][right]) 46 | } 47 | // Go right to left 48 | right -= 1 49 | if left <= bottom { 50 | for col in stride(from: right, through: top, by: -1) { 51 | result.append(matrix[bottom][col]) 52 | } 53 | bottom -= 1 54 | } 55 | // Go up 56 | if top <= right { 57 | for row in stride(from: bottom, through: left, by: -1) { 58 | result.append(matrix[row][top]) 59 | } 60 | top += 1 61 | } 62 | } 63 | return result 64 | } 65 | -------------------------------------------------------------------------------- /Help/Protocols/Comparable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Comparable.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 07.07.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | /* 13 | 14 | struct User: Comparable { 15 | 16 | let name: String 17 | let money: Int 18 | 19 | static func < (lhs: User, rhs: User) -> Bool { 20 | return lhs.money < rhs.money 21 | } 22 | } 23 | 24 | let mike = User(name: "Mike", money: 50 ) 25 | let bob = User(name: "Bob", money: 40 ) 26 | 27 | mike < bob // false 28 | mike > bob // true 29 | 30 | */ 31 | -------------------------------------------------------------------------------- /Help/Protocols/CustomStringConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomStringConvertible.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 02.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | // MARK: - Custom String Convertible 12 | /// A type with a customized textual representation. 13 | /// Types that conform to the CustomStringConvertible protocol can provide their own representation to be used when converting an instance to a string. The String(describing:) initializer is the preferred way to convert an instance of any type to a string. If the passed instance conforms to CustomStringConvertible, the String(describing:) initializer and the print(_:) function use the instance’s custom description property. Accessing a type’s description property directly or using CustomStringConvertible as a generic constraint is discouraged. 14 | 15 | struct Card: CustomStringConvertible { 16 | 17 | var description: String { return "\(rank) \(suit)"} 18 | 19 | var suit: Suit 20 | var rank: Rank 21 | 22 | enum Suit: String, CustomStringConvertible { 23 | var description: String { return self.rawValue } 24 | case spades = "♠️" 25 | case hearts = "♥️" 26 | case clubs = "♣️" 27 | case diamonds = "♦️" 28 | } 29 | 30 | enum Rank: Int, CustomStringConvertible { 31 | case jack = 11 32 | case lady = 12 33 | case king = 13 34 | case ace = 14 35 | var description: String { return "\(self.rawValue)" } 36 | } 37 | } 38 | 39 | let card = Card(suit: .hearts, rank: .king) 40 | //print(card) // 13 ♥️ nice print 41 | 42 | 43 | -------------------------------------------------------------------------------- /Help/Protocols/Equatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Equatable.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 07.07.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | /* 12 | 13 | struct User: Equatable { 14 | let login: String 15 | let password: Int 16 | 17 | static func == (lhs: User, rhs: User) -> Bool { 18 | return lhs.password == rhs.password 19 | } 20 | } 21 | 22 | let userOne = User(login: "mike", password: 123 ) 23 | let userTwo = User(login: "mikeeeee", password: 123 ) 24 | 25 | userOne == userTwo ? print("login") : print("not login") 26 | // login 27 | 28 | */ 29 | -------------------------------------------------------------------------------- /Help/SupportingFiles/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 02.06.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /Help/SupportingFiles/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Help/SupportingFiles/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Help/SupportingFiles/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Help/SupportingFiles/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Help/SupportingFiles/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Help/SupportingFiles/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UIApplicationSupportsIndirectInputEvents 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationPortraitUpsideDown 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Help/SupportingFiles/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 02.06.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Help/SupportingFiles/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 02.06.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class ViewController: UIViewController { 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | // Do any additional setup after loading the view. 15 | } 16 | 17 | } 18 | 19 | 20 | -------------------------------------------------------------------------------- /Help/UIKit/AutoLayoutConstraintsProgrammatically.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AutoLayoutConstraintsProgrammatically.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 22.04.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | class AutoLayoutConstraintsProgrammatically: UIViewController { 11 | 12 | private let blueView: UIView = { 13 | let view = UIView() 14 | view.translatesAutoresizingMaskIntoConstraints = false 15 | view.backgroundColor = .link 16 | return view 17 | }() 18 | 19 | private let redView: UIView = { 20 | let view = UIView() 21 | view.translatesAutoresizingMaskIntoConstraints = false 22 | view.backgroundColor = .red 23 | return view 24 | }() 25 | 26 | private let imageView_1: UIImageView = { 27 | let image = UIImage(named: "github") 28 | let view = UIImageView(image: image) 29 | view.translatesAutoresizingMaskIntoConstraints = false 30 | return view 31 | }() 32 | 33 | private let imageView_2: UIImageView = { 34 | let image = UIImage(named: "github") 35 | let view = UIImageView(image: image) 36 | view.translatesAutoresizingMaskIntoConstraints = false 37 | return view 38 | }() 39 | 40 | override func viewDidLoad() { 41 | super.viewDidLoad() 42 | 43 | addConstraints() 44 | } 45 | } 46 | 47 | extension AutoLayoutConstraintsProgrammatically { 48 | 49 | private func addConstraints() { 50 | var constraints = [NSLayoutConstraint]() 51 | 52 | view.addSubview(blueView) 53 | // Offset from the edges 54 | constraints.append(blueView.leadingAnchor.constraint(equalTo: view.leadingAnchor)) 55 | constraints.append(blueView.trailingAnchor.constraint(equalTo: view.trailingAnchor)) 56 | constraints.append(blueView.topAnchor.constraint(equalTo: view.topAnchor)) 57 | constraints.append(blueView.bottomAnchor.constraint(equalTo: view.bottomAnchor)) 58 | 59 | // Safe Area 60 | // constraints.append(blueView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor)) 61 | // constraints.append(blueView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)) 62 | // constraints.append(blueView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor)) 63 | // constraints.append(blueView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)) 64 | 65 | // With constant 66 | // constraints.append(blueView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 100)) 67 | // constraints.append(blueView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -100)) 68 | // constraints.append(blueView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100)) 69 | // constraints.append(blueView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -100)) 70 | 71 | view.addSubview(redView) 72 | // Size Height and Width 73 | constraints.append(redView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.3)) 74 | constraints.append(redView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.3)) 75 | 76 | // Center alignment 77 | constraints.append(redView.centerXAnchor.constraint(equalTo: view.centerXAnchor)) 78 | constraints.append(redView.centerYAnchor.constraint(equalTo: view.centerYAnchor)) 79 | 80 | // Image 1 81 | view.addSubview(imageView_1) 82 | constraints.append(imageView_1.heightAnchor.constraint(equalToConstant: 50)) 83 | constraints.append(imageView_1.widthAnchor.constraint(equalToConstant: 50)) 84 | constraints.append(imageView_1.centerXAnchor.constraint(equalTo: view.centerXAnchor)) 85 | constraints.append(imageView_1.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20)) 86 | 87 | // Image 2 88 | view.addSubview(imageView_2) 89 | imageView_2.heightAnchor.constraint(equalToConstant: 70).isActive = true 90 | imageView_2.widthAnchor.constraint(equalToConstant: 70).isActive = true 91 | imageView_2.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 92 | imageView_2.topAnchor.constraint(equalTo: imageView_1.bottomAnchor, constant: 20).isActive = true 93 | 94 | NSLayoutConstraint.activate(constraints) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Help/UIKit/DataManager/SaveAndLoadDataOnDevice.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SaveAndLoadDataOnDevice.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 06.08.2021. 6 | // 7 | // add 8 | 9 | import UIKit 10 | 11 | class SaveAndLoadDataOnDevice { 12 | 13 | // Create an archive file 14 | var archiveURL: URL? { 15 | guard let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { fatalError() } 16 | return documentDirectory.appendingPathComponent("nameFile").appendingPathExtension("plist") 17 | } 18 | 19 | // Decoding an object from an archive 20 | func loadAll() -> [SameObject]? { 21 | let decoder = PropertyListDecoder() 22 | guard let archiveURL = archiveURL else { fatalError() } 23 | guard let decoderSameObject = try? Data(contentsOf: archiveURL) else { fatalError() } 24 | return try? decoder.decode([SameObject].self, from: decoderSameObject) 25 | } 26 | 27 | // Encoder the received object into the archive file 28 | func saveEmojis(_ object: [SameObject]) { 29 | let encoder = PropertyListEncoder() 30 | guard let archiveURL = archiveURL else { fatalError() } 31 | guard let encoderSameObject = try? encoder.encode(object) else { fatalError() } 32 | try? encoderSameObject.write(to: archiveURL, options: .noFileProtection) 33 | } 34 | } 35 | 36 | // Class have to protocol 'Codable' 37 | class SameObject: Codable { 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Help/UIKit/Destination.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Destination.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 05.08.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class Destination: UIViewController { 11 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 12 | let destination = segue.destination as! OtherDestination 13 | destination.property = 1 14 | } 15 | } 16 | 17 | class OtherDestination: UIViewController { 18 | var property = 0 19 | } 20 | -------------------------------------------------------------------------------- /Help/UIKit/PassDataClosure.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PassDataClosure.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 21.06.2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | 12 | /* 13 | 14 | class FirstViewController: UIViewController { 15 | 16 | @IBOutlet weak var textLabel: UILabel! 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | } 22 | 23 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 24 | guard let secondVC = segue.destination as? SecondViewController else { fatalError() } 25 | secondVC.closure = { [weak self] text in 26 | self?.textLabel.text = text 27 | } 28 | } 29 | } 30 | 31 | 32 | class SecondViewController: UIViewController { 33 | 34 | @IBOutlet weak var textLabel: UILabel! 35 | 36 | var closure: ((String) -> ())? 37 | 38 | override func viewDidLoad() { 39 | super.viewDidLoad() 40 | 41 | } 42 | 43 | @IBAction func actionButton(_ sender: UIButton) { 44 | closure?("I can pass data by closure") 45 | dismiss(animated: true, completion: nil) 46 | } 47 | } 48 | 49 | */ 50 | -------------------------------------------------------------------------------- /Help/UIKit/PassDataDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PassDataDelegate.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 21.06.2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | /* 12 | 13 | Пишем протокол (правило), в котором описываем нужный нам метод, который принимает необходимые аргументы и, если нужно, что-то выдает. 14 | 15 | Подписываем «порождающий» контроллер под этот протокол и пишем в нем этот метод. 16 | 17 | У дочернего контроллера (например, у ячейки) добавляем опциональное свойство делегата, являющееся типом нашего протокола (то есть поддерживающего тип нашего протокола) 18 | 19 | При создании дочернего контроллера ставим в его свойстве делегата себя, то есть self «порождающего» контроллера. 20 | 21 | https://habr.com/ru/post/510882/ 22 | 23 | */ 24 | 25 | 26 | protocol FirstViewControllerDelegate: AnyObject { 27 | func update(text: String) 28 | } 29 | 30 | /// - Implementation option through extensions 31 | //extension FirstViewController: FirstViewControllerDelegate { 32 | // func update(text: String) { 33 | // textLabel.text = text 34 | // } 35 | //} 36 | 37 | class FirstViewController: UIViewController, FirstViewControllerDelegate { 38 | 39 | @IBOutlet weak var textLabel: UILabel! 40 | 41 | override func viewDidLoad() { 42 | super.viewDidLoad() 43 | 44 | } 45 | 46 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 47 | guard let secondVC = segue.destination as? SecondViewController else { fatalError() } 48 | secondVC.delegate = self 49 | } 50 | 51 | func update(text: String) { 52 | textLabel.text = text 53 | } 54 | } 55 | 56 | 57 | class SecondViewController: UIViewController { 58 | 59 | @IBOutlet weak var textLabel: UILabel! 60 | 61 | weak var delegate: FirstViewControllerDelegate? 62 | 63 | override func viewDidLoad() { 64 | super.viewDidLoad() 65 | 66 | } 67 | 68 | @IBAction func actionButton(_ sender: UIButton) { 69 | delegate?.update(text: "Text was changed") 70 | dismiss(animated: true, completion: nil) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Help/UIKit/PerformSegue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PerformSegue.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 07.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | // MARK: Perform Segue 12 | /// Initiates the segue with the specified identifier from the current view controller's storyboard file. 13 | 14 | // func performSegue(withIdentifier identifier: String, sender: Any?) 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Help/UIKit/PrepareSegue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrepareSegue.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 07.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | // MARK: - Prepare Segue 12 | 13 | /// Notifies the view controller that a segue is about to be performed. 14 | 15 | /* 16 | 17 | func prepare(for segue: UIStoryboardSegue, sender: Any?) { 18 | guard segue.identifier == "identifier segue" else { fatalError() } 19 | guard let secondVC = segue.destination as? SecondViewController else { fatalError() } 20 | secondVC?.property = "text" 21 | 22 | } 23 | 24 | */ 25 | -------------------------------------------------------------------------------- /Help/UIKit/Source.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Source.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 05.08.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class Source: UIViewController { 11 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 12 | let source = segue.source as! OtherSource 13 | source.property = 1 14 | } 15 | } 16 | 17 | class OtherSource: UIViewController { 18 | var property = 0 19 | } 20 | -------------------------------------------------------------------------------- /Help/UIKit/TableView/DeleteRow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeleteRow.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 04.08.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class DeleteRow: UITableViewController { 11 | 12 | var arrayElement = [String]() 13 | 14 | override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { 15 | true 16 | } 17 | 18 | override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { 19 | if editingStyle == .delete { 20 | arrayElement.remove(at: indexPath.row) 21 | tableView.deleteRows(at: [indexPath], with: .automatic) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Help/UIKit/TableView/MoveRow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MoveRowAtTo.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 04.08.2021. 6 | // 7 | 8 | import UIKit 9 | 10 | class MoveRow: UITableViewController { 11 | 12 | var arrayElement = [String]() 13 | 14 | override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { 15 | let someRow = arrayElement.remove(at: sourceIndexPath.row) 16 | arrayElement.insert(someRow, at: destinationIndexPath.row) 17 | } 18 | } 19 | 20 | 21 | -------------------------------------------------------------------------------- /Help/UIKit/UITextField/DismissKeyboard.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DismissKeyboard.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 11.11.2021. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | 12 | 13 | class DismissKeyboard: UIViewController { 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | 18 | // When user touch on screen 19 | let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) 20 | self.view.addGestureRecognizer(tap) 21 | } 22 | 23 | // Dismiss keyboard 24 | @objc private func dismissKeyboard() { 25 | self.view.endEditing(true) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Help/UIKit/UnwindSegue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnwindSegue.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 07.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | // MARK: - Unwind Segue 12 | 13 | /// Article https://developer.apple.com/documentation/uikit/resource_management/dismissing_a_view_controller_with_an_unwind_segue 14 | 15 | /// Dismissing a View Controller with an Unwind Segue 16 | /// Configure an unwind segue in your storyboard file that dynamically chooses the most appropriate view controller to display next. 17 | /// To handle the dismissal of a view controller, create an unwind segue. 18 | /// Unlike the segues that you use to present view controllers, an unwind segue promises the dismissal of the current view controller without promising a specific target for the resulting transition. Instead, UIKit determines the target of an unwind segue programmatically at runtime. 19 | /// UIKit determines the target of an unwind segue at runtime, so you aren’t restricted in how you set up your view controller hierarchies. 20 | /// Consider a scenario where two view controllers present the same child view controller, as shown in the following figure. You could add complicated logic to determine which view controller to display next, but such a solution wouldn’t scale well. Instead, UIKit provides a simple programmatic solution that scales to any number of view controllers with minimal effort. 21 | 22 | /* 23 | @IBAction func myUnwindAction(unwindSegue: UIStoryboardSegue) { 24 | // Connect a Triggering Object to the Exit Control 25 | } 26 | */ 27 | -------------------------------------------------------------------------------- /Help/WorkingCode/ArrayEnumerated.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ArrayEnumerated.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 23.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | // MARK: - Array Enumerated 12 | 13 | 14 | /* 15 | 16 | let emojiArray = ["🥵", "😰", "😇", "😱", "🥶"] 17 | 18 | for (index, emoji) in emojiArray.enumerated() { 19 | print(index, emoji) 20 | } 21 | 22 | //0 🥵 23 | //1 😰 24 | //2 😇 25 | //3 😱 26 | //4 🥶 27 | 28 | */ 29 | 30 | 31 | -------------------------------------------------------------------------------- /Help/WorkingCode/ArrayZip.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ArrayZip.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 23.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | /* 11 | 12 | let emojiArray = ["🥵", "🥶", "😎", "😱", "😡"] 13 | let strArray = ["Hot", "Cold", "Good"] 14 | var newArray = [(String, String)]() 15 | 16 | for value in zip(emojiArray, strArray) { 17 | newArray.append(value) 18 | } 19 | 20 | print(newArray) 21 | 22 | // [("🥵", "Hot"), ("🥶", "Cold"), ("😎", "Good")] // Array of Tuple 23 | 24 | */ 25 | -------------------------------------------------------------------------------- /Help/WorkingCode/Dictionary.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dictionary.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 25.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | // MARK: Dictionary Reduce 12 | let fruits = ["🍏", "🍓", "🍒", "🍌", "🍏", "🍒", "🍌", "🍌", "🍌", "🍒", "🍒", "🍌", "🍓", "🍓"] 13 | 14 | let fruitsCount = fruits.reduce(into: [:]) { counts, fruit in 15 | counts[fruit, default: 0] += 1 16 | } 17 | 18 | // fruitsCount 19 | // ["🍒": 4, "🍏": 2, "🍓": 3, "🍌": 5] 20 | // [String : Int] 21 | 22 | 23 | 24 | // MARK: Dictionary Search List 25 | struct Product: Hashable { 26 | let id: String; // unique identifier 27 | let name: String; 28 | let producer: String; 29 | 30 | static func ==(lhs: Product, rhs: Product) -> Bool { 31 | return lhs.id == rhs.id 32 | } 33 | } 34 | 35 | protocol Library { 36 | /** 37 | Adds a new product object to the Library. 38 | - Parameter product: product to add to the Library 39 | - Returns: false if the product with same id already exists in the Library, true – otherwise. 40 | */ 41 | func addNewProduct(product: Product) -> Bool; 42 | 43 | /** 44 | Deletes the product with the specified id from the Library. 45 | - Returns: true if the product with same id existed in the Library, false – otherwise. 46 | */ 47 | func deleteProduct(id: String) -> Bool; 48 | 49 | /** 50 | - Returns: 10 product names containing the specified string. If there are several products with the same name, producer's name is appended to product's name. 51 | */ 52 | func listProductsByName(searchString: String) -> Set; 53 | 54 | /** 55 | - Returns: 10 product names whose producer contains the specified string, ordered by producers. 56 | */ 57 | func listProductsByProducer(searchString: String) -> [String]; 58 | } 59 | 60 | class LibraryRepository: Library { 61 | private var products = [String: Product]() 62 | 63 | func addNewProduct(product: Product) -> Bool { 64 | return self.products.updateValue(product, forKey: product.id) == nil 65 | } 66 | 67 | func deleteProduct(id: String) -> Bool { 68 | return self.products.removeValue(forKey: id) != nil 69 | } 70 | 71 | func listProductsByName(searchString: String) -> Set { 72 | return self.products 73 | .filter({ $1.name.contains(searchString) }) 74 | .prefix(10) 75 | .reduce(into: Set()) { (productNames, product) in 76 | if products.filter({ $1.name == product.value.name }).count > 1 { 77 | productNames.insert("\(product.value.producer) - \(product.value.name)") 78 | return 79 | } 80 | productNames.insert(product.value.name) 81 | } 82 | } 83 | 84 | func listProductsByProducer(searchString: String) -> [String] { 85 | return self.products 86 | .filter({ $1.producer.contains(searchString) }) 87 | .sorted(by: { $0.value.producer > $1.value.producer }) 88 | .prefix(10) 89 | .map { $0.value.name } 90 | } 91 | } 92 | 93 | func test(lib: Library) { 94 | assert(!lib.deleteProduct(id: "1")) 95 | assert(lib.addNewProduct(product: Product(id: "1", name: "1", producer: "Lex"))) 96 | assert(!lib.addNewProduct(product: Product(id: "1", name: "any name because we check id only", producer: "any producer"))) 97 | assert(lib.deleteProduct(id: "1")) 98 | assert(lib.addNewProduct(product: Product(id: "3", name: "Some Product3", producer: "Some Producer2"))) 99 | assert(lib.addNewProduct(product: Product(id: "4", name: "Some Product1", producer: "Some Producer3"))) 100 | assert(lib.addNewProduct(product: Product(id: "2", name: "Some Product2", producer: "Some Producer2"))) 101 | assert(lib.addNewProduct(product: Product(id: "1", name: "Some Product1", producer: "Some Producer1"))) 102 | assert(lib.addNewProduct(product: Product(id: "5", name: "Other Product5", producer: "Other Producer4"))) 103 | assert(lib.addNewProduct(product: Product(id: "6", name: "Other Product6", producer: "Other Producer4"))) 104 | assert(lib.addNewProduct(product: Product(id: "7", name: "Other Product7", producer: "Other Producer4"))) 105 | assert(lib.addNewProduct(product: Product(id: "8", name: "Other Product8", producer: "Other Producer4"))) 106 | assert(lib.addNewProduct(product: Product(id: "9", name: "Other Product9", producer: "Other Producer4"))) 107 | assert(lib.addNewProduct(product: Product(id: "10", name: "Other Product10", producer: "Other Producer4"))) 108 | assert(lib.addNewProduct(product: Product(id: "11", name: "Other Product11", producer: "Other Producer4"))) 109 | 110 | var byNames: Set = lib.listProductsByName(searchString: "Product") 111 | assert(byNames.count == 10) 112 | 113 | byNames = lib.listProductsByName(searchString: "Some Product") 114 | assert(byNames.count == 4) 115 | assert(byNames.contains("Some Producer3 - Some Product1")) 116 | assert(byNames.contains("Some Product2")) 117 | assert(byNames.contains("Some Product3")) 118 | assert(!byNames.contains("Some Product1")) 119 | assert(byNames.contains("Some Producer1 - Some Product1")) 120 | 121 | var byProducer: [String] = lib.listProductsByProducer(searchString: "Producer") 122 | assert(byProducer.count == 10) 123 | 124 | byProducer = lib.listProductsByProducer(searchString: "Some Producer") 125 | assert(byProducer.count == 4) 126 | assert(byProducer[0] == "Some Product1") 127 | assert(byProducer[1] == "Some Product2" || byProducer[1] == "Some Product3") 128 | assert(byProducer[2] == "Some Product2" || byProducer[2] == "Some Product3") 129 | assert(byProducer[3] == "Some Product1") 130 | } 131 | 132 | //test(lib: LibraryRepository()) 133 | -------------------------------------------------------------------------------- /Help/WorkingCode/EnumDataModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnumDataModel.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 19.09.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | enum Difficulty: String { 11 | typealias DifficultyContent = (title: String, description: String, levels: Int) 12 | 13 | case novice 14 | case warrior 15 | case master 16 | case unknown 17 | 18 | var content: DifficultyContent { 19 | switch self { 20 | case .novice: 21 | return (title: "Novice", description: "All Too Easy!", levels: 8) 22 | case .warrior: 23 | return (title: "Warrior", description: "You Will Die Mortal!", levels: 10) 24 | case .master: 25 | return (title: "Master", description: "You Will Never Win!", levels: 12) 26 | case .unknown: 27 | return (title: "Unknown", description: "Unknown!", levels: -1) 28 | } 29 | } 30 | } 31 | 32 | func showTower(for difficulty: Difficulty) { 33 | print(difficulty.content.title) 34 | print(difficulty.content.description) 35 | print(difficulty.content.levels) 36 | } 37 | 38 | let difficulty = Difficulty(rawValue: "novice") 39 | 40 | // showTower(for: difficulty ?? .unknown) 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Help/WorkingCode/ErrorHandling.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorHandling.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 02.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | // MARK: - Error Handling 12 | /// Error handler, using the 'throws' keyword we indicate that the function can receive an error. 13 | /// Using the 'throw' key we throw an error into the 'NSError' instance. 14 | /// do catch - this error handler helps to catch the error. 15 | /// In this example, we are trying to call the function that we marked with the keyword 'throws'. 16 | /// Before calling the function, put the 'try' keyword. 17 | /// If the function gets an error, we catch it 'catch'. 18 | 19 | /* 20 | 21 | func divide(_ a: Int, by b: Int) throws -> Double { 22 | guard b != 0 else { throw NSError(domain: "The number 'b' must not be zero", code: 100) } 23 | return Double(a / b) 24 | } 25 | 26 | /// Checks in 'do catch' 27 | do { 28 | try divide(10, by: 0) 29 | } catch let error { 30 | error.localizedDescription 31 | } 32 | 33 | /// Checks in 'try!' or 'try?' 34 | let myError = try! divide(10, by: 0) 35 | let myError = try? divide(10, by: 0) 36 | 37 | /// Example with user data 38 | enum NetworkError: Error { 39 | case notPage 40 | case networkError 41 | } 42 | 43 | class Network { 44 | static let responses = [200, 404, 500] 45 | static func request() -> Int { 46 | return responses.randomElement() ?? 0 47 | } 48 | } 49 | 50 | class NetworkManager { 51 | func userRequest(text: String) throws -> String { 52 | let statusCode = Network.request() 53 | guard statusCode != 404 else { throw NetworkError.notPage } 54 | guard statusCode != 500 else { throw NetworkError.networkError } 55 | return "\(statusCode): image \(text)" 56 | } 57 | } 58 | 59 | class Browser { 60 | let networkManager = NetworkManager() 61 | 62 | func getImage(text: String) { 63 | do { 64 | let result = try networkManager.userRequest(text: text) 65 | print(result) 66 | } catch { 67 | switch error { 68 | case NetworkError.notPage: 69 | print("Not Page") 70 | case NetworkError.networkError: 71 | print("Network Error") 72 | default: 73 | print(error.localizedDescription) 74 | } 75 | } 76 | } 77 | } 78 | 79 | let safari = Browser() 80 | safari.getImage(text: "page 1") 81 | safari.getImage(text: "page 2") 82 | safari.getImage(text: "page 3") 83 | 84 | */ 85 | 86 | 87 | -------------------------------------------------------------------------------- /Help/WorkingCode/FailableInitializers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FailableInitializers.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 03.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: Failable Initializers 11 | 12 | /// t’s sometimes useful to define a class, structure, or enumeration for which initialization can fail. This failure might be triggered by invalid initialization parameter values, the absence of a required external resource, or some other condition that prevents initialization from succeeding. 13 | 14 | /// To cope with initialization conditions that can fail, define one or more failable initializers as part of a class, structure, or enumeration definition. You write a failable initializer by placing a question mark after the init keyword (init?). 15 | 16 | 17 | struct Animal { 18 | let species: String 19 | init?(species: String) { 20 | if species.isEmpty { return nil } 21 | self.species = species 22 | } 23 | } 24 | 25 | let cat = Animal(species: "") // nil 26 | let dog = Animal(species: "Mammal") // Animal 27 | 28 | 29 | -------------------------------------------------------------------------------- /Help/WorkingCode/Guard.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Guard.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 04.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: Guard Statement 11 | /// A guard statement is used to transfer program control out of a scope if one or more conditions aren’t met. 12 | /// A guard statement has the following form: 13 | 14 | /* 15 | guard condition else { 16 | statements 17 | } 18 | */ 19 | 20 | /// The value of any condition in a guard statement must be of type Bool or a type bridged to Bool. The condition can also be an optional binding declaration, as discussed in Optional Binding. 21 | /// Any constants or variables assigned a value from an optional binding declaration in a guard statement condition can be used for the rest of the guard statement’s enclosing scope. 22 | /// The else clause of a guard statement is required, and must either call a function with the Never return type or transfer program control outside the guard statement’s enclosing scope using one of the following statements: 23 | 24 | /// return 25 | /// break 26 | /// continue 27 | /// throw 28 | 29 | /// Example 1 30 | struct MyFood { 31 | var name: String 32 | var calories: Int 33 | 34 | init?(name: String, calories: Int) { 35 | guard name != "", calories != 0 else { return nil } 36 | self.name = name 37 | self.calories = calories 38 | } 39 | } 40 | 41 | let breakfast = MyFood(name: "banana", calories: 50) // return object 42 | let dinner = MyFood(name: "", calories: 10) // return object 43 | let lunch = MyFood(name: "water", calories: 0) // return object 44 | 45 | 46 | /// Example 2 47 | func printMyFood(eating: MyFood?) { 48 | guard let name = eating?.name, let calories = eating?.calories else { fatalError() } 49 | print(name, calories) 50 | } 51 | 52 | //printMyFood(eating: breakfast) // print object 53 | //printMyFood(eating: lunch) // error 54 | -------------------------------------------------------------------------------- /Help/WorkingCode/TypeCasting.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TypeCasting.swift 3 | // Help 4 | // 5 | // Created by Sergey Lukaschuk on 02.06.2021. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | // MARK: - Type Casting 12 | 13 | /* 14 | 15 | /// Type Casting 16 | let unknown: Any = "Steve" 17 | 18 | if let name = unknown as? String { 19 | print(name) 20 | } else { 21 | print("'unknown' is not type String") 22 | } 23 | 24 | /// Checking Type 25 | unknown is Int ? print(unknown) : print("'unknown' is not type Int") 26 | 27 | /// Downcasting 28 | /// The variable `tom` of the type `Superclass` assigns an instance of `Subclass`. 29 | /// If we want to access the properties of the subclass `Subclass` then we need to do Downcasting. 30 | /// We cannot directly access the property of the `Subclass`, since the variable `tom` has the type `Superclass`. 31 | class Superclass { 32 | var name: String = "Tom" 33 | } 34 | 35 | class Subclass: Superclass { 36 | var age: Int = 15 37 | } 38 | 39 | let tom: Superclass = Subclass() // The variable type of 'Superclass' assigns an instance of 'Subclass' 40 | 41 | tom.age // error 42 | if let value = tom as? Subclass { 43 | print(value.age) 44 | } 45 | 46 | */ 47 | 48 | 49 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Sergey Lukaschuk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # **Help** 3 | 4 | In this project, I have collected various best practices and iOS development tips. This is my personal development assistant. 5 | 6 | ### Index 7 | 8 | - [**Clean Code**](#clean-code) 9 | - [Clean Class](#clean-class) 10 | - [Life Cycle](#life-cycle) 11 | - [Extension](#extension) 12 | - [Logically Related Elements](#logically-related-elements) 13 | - [Dead Code](#dead-code) 14 | - [Main MARKs](#main-marks) 15 | - [Semantic Commit](#semantic-commit) 16 | - [Shortcuts](#shortcut) 17 | 18 | - [**Protocols**](#protocols) 19 | - [CustomStringConvertible](#customstringconvertible) 20 | - [Equatable](#equatable) 21 | - [Comparable](#comparable) 22 | 23 | - [**Extensions**](#extensions) 24 | - [Int+Random](#intᐩrandom) 25 | - [String+CompareNumbers](#stringᐩcompareNumbers) 26 | 27 | - [**Working Code**](#working-code) 28 | - [Error Handling](#error-handling) 29 | - [Type Casting](#type-casting) 30 | - [Failable Initializers](#failable-initializers) 31 | - [Guard](#guard) 32 | - [Array Enumerated](#array-enumerated) 33 | - [Array Zip](#array-zip) 34 | - [Dictionary](#dictionary) 35 | - [Enum Data Model](#enum-data-model) 36 | 37 | 38 | - [**UIKit**](#uikit) 39 | - [UITextField](#uitextfield) 40 | - [Dismiss Keyboard](#dismiss-keyboard) 41 | - [Data Manager](#data-manager) 42 | - [Save and Load Data on Device](#save-and-load-data-on-device) 43 | - [Table View](#table-view) 44 | - [Move Row](#move-row) 45 | - [Delete Row](#delete-row) 46 | - [Unwind Segue](#unwind-segue) 47 | - [Prepare Segue](#prepare-segue) 48 | - [Perform Segue](#perform-segue) 49 | - [Source](#source) 50 | - [Destination](#destination) 51 | - [Pass Data Delegate](#pass-data-delegate) 52 | - [Pass Data Closure](#pass-data-closure) 53 | - [AutoLayout Constraints Programmatically](#autolayout-constraints-programmatically) 54 | 55 | 56 | - [**Features**](#features) 57 | - [Launch Screen](#launch-screen) 58 | - [Custom View](#custom-view) 59 | - [Swipe Screen](#swipe-screen) 60 | - [Add Home Screen Quick Actions](#add-home-screen-quick-actions) 61 | 62 | 63 | - [**Design Patterns**](#design-patterns) 64 | - [Factory Method](#factory-method) 65 | 66 | 67 | - [**Leet Code**](#leet-code) 68 | - [Pascal Triangle](#pascal-triangle) 69 | - [Spiral Matrix](#spiral-matrix) 70 | - [Container With Most Water](#container-with-most-water) 71 | 72 | 73 | ## **Clean Code** 74 | Raising code readability in iOS development. Thanks to the application of these tips, your code will become readable, which will further ensure convenience and speed of working with it. 75 | 76 | 77 | ### [Clean Class](https://github.com/lgreydev/Help/blob/master/Help/CleanCode/CleanClass.swift) 78 | The code has a clear structure, due to which the logic is more obvious, the code is easy to read, you can quickly find what you are looking for, and besides, it is just pleasant to look at it. 79 | 80 | ```swift 81 | 82 | final class CleanViewController: UIViewController { 83 | 84 | // MARK: - IBOutlets 85 | @IBOutlet private weak var searchBar: UISearchBar! 86 | @IBOutlet private weak var tableView: UITableView! 87 | 88 | 89 | // MARK: - Public Properties 90 | var userID: String? 91 | weak var delegate: SomeDelegate? 92 | 93 | 94 | // MARK: - Private Properties 95 | private let userService = UserService() 96 | private var userList: [User]? 97 | 98 | 99 | // MARK: - Lifecycle 100 | override func viewDidLoad() { 101 | super.viewDidLoad() 102 | 103 | setupNavigationBar() 104 | } 105 | 106 | 107 | // MARK: - Private Methods 108 | private func setupNavigationBar() { 109 | navigationController?.navigationBar.backgroundColor = .red 110 | navigationItem.title = "Some" 111 | } 112 | 113 | 114 | // MARK: - IBActions 115 | @IBAction private func cancelButtonPressed(_ sender: UIBarButtonItem) { 116 | dismiss(animated: true, completion: nil) 117 | } 118 | 119 | } 120 | 121 | ``` 122 | 123 | 124 | ### [Life Cycle](https://github.com/lgreydev/Help/blob/master/Help/CleanCode/Lifecycle.swift) 125 | We move the logic out of the lifecycle methods into separate methods. The logic inside the methods of the ViewController lifecycle should be moved into separate methods, even if you have to create a method with one line of code. Today one, and tomorrow ten. 126 | 127 | ```swift 128 | 129 | ❌ NOT Preferred 130 | 131 | override func viewDidLoad() { 132 | super.viewDidLoad() 133 | 134 | navigationController?.navigationBar.backgroundColor = .red 135 | someButton.layer.cornerRadius = 10 136 | someButton.layer.masksToBounds = true 137 | navigationItem.title = "Some" 138 | print("Some") 139 | } 140 | 141 | 142 | ✅ Preferred 143 | 144 | // MARK: - Lifecycle 145 | override func viewDidLoad() { 146 | super.viewDidLoad() 147 | 148 | setupNavigationBar() 149 | setupSomeButton() 150 | printSome() 151 | } 152 | 153 | 154 | // MARK: - Private Methods 155 | private func setupNavigationBar() { 156 | navigationController?.navigationBar.backgroundColor = .red 157 | navigationItem.title = "Some" 158 | } 159 | 160 | private func setupSomeButton() { 161 | someButton.layer.cornerRadius = 10 162 | someButton.layer.masksToBounds = true 163 | } 164 | 165 | private func printSome() { 166 | print("Some") 167 | } 168 | 169 | ``` 170 | 171 | 172 | ### [Extension](https://github.com/lgreydev/Help/blob/master/Help/CleanCode/Extension.swift) 173 | Using an extension to implement protocols. Move protocols implementation into extensions with mark // MARK: — SomeProtocol 174 | 175 | ```swift 176 | 177 | ❌ NOT Preferred 178 | 179 | final class CleanViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { 180 | 181 | // all methods 182 | } 183 | 184 | 185 | ✅ Preferred 186 | 187 | final class CleanViewController: UIViewController { 188 | 189 | // class stuff here 190 | 191 | } 192 | 193 | 194 | // MARK: - Table View Data Source 195 | extension CleanViewController: UITableViewDataSource { 196 | 197 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 198 | 199 | return userList?.count ?? 0 200 | } 201 | 202 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 203 | 204 | let cell = UITableViewCell() 205 | return cell 206 | } 207 | 208 | } 209 | 210 | ``` 211 | 212 | 213 | ### [Logically Related Elements](https://github.com/lgreydev/Help/blob/master/Help/CleanCode/LogicallyRelatedElements.swift) 214 | To improve clarity, you need to highlight logically related elements using an empty string. 215 | 216 | ```swift 217 | 218 | ❌ NOT Preferred 219 | 220 | private func showActivityIndicator(on viewController: UIViewController) { 221 | activityIndicator.center = viewController.view.center 222 | loadingView.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) 223 | loadingView.alpha = 0.5 224 | activityIndicator.hidesWhenStopped = true 225 | activityIndicator.style = .whiteLarge 226 | loadingView.center = viewController.view.center 227 | loadingView.clipsToBounds = true 228 | loadingView.layer.cornerRadius = 15 229 | viewController.view.addSubview(loadingView) 230 | viewController.view.addSubview(activityIndicator) 231 | activityIndicator.startAnimating() 232 | } 233 | 234 | 235 | ✅ Preferred 236 | 237 | private func showActivityIndicator(on viewController: UIViewController) { 238 | activityIndicator.center = viewController.view.center 239 | activityIndicator.hidesWhenStopped = true 240 | activityIndicator.style = .whiteLarge 241 | 242 | loadingView.center = viewController.view.center 243 | loadingView.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) 244 | loadingView.alpha = 0.5 245 | loadingView.clipsToBounds = true 246 | loadingView.layer.cornerRadius = 15 247 | 248 | viewController.view.addSubview(loadingView) 249 | viewController.view.addSubview(activityIndicator) 250 | 251 | activityIndicator.startAnimating() 252 | } 253 | 254 | ``` 255 | 256 | 257 | ### [Dead Code](https://github.com/lgreydev/Help/blob/master/Help/CleanCode/DeadCode.swift) 258 | Do not leave unnecessary comments (default), empty methods or dead functionality - it makes code dirty. Attention to the AppDelegate class, most likely you will find empty methods there with comments inside. 259 | 260 | ```swift 261 | 262 | ❌ NOT Preferred 263 | 264 | @UIApplicationMain 265 | class AppDelegate: UIResponder, UIApplicationDelegate { 266 | 267 | var window: UIWindow? 268 | 269 | func application( 270 | _ application: UIApplication, 271 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 272 | // Override point for customization after application launch. 273 | return true 274 | } 275 | // 276 | // func someFunc() { 277 | // print("Some") 278 | // } 279 | 280 | func applicationWillResignActive(_ application: UIApplication) { 281 | // Sent when the application is about to move from active to inactive state. This can occur for certain 282 | //types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits 283 | //the application and it begins the transition to the background state. 284 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 285 | } 286 | 287 | 288 | ✅ Preferred 289 | 290 | @UIApplicationMain 291 | class AppDelegate: UIResponder, UIApplicationDelegate { 292 | 293 | var window: UIWindow? 294 | 295 | func application( 296 | _ application: UIApplication, 297 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 298 | 299 | return true 300 | } 301 | 302 | } 303 | 304 | ``` 305 | 306 | 307 | ### [Main MARKs](https://github.com/lgreydev/Help/blob/master/Help/CleanCode/MARK.swift) 308 | The main MARKs for splitting the code into logically related blocks and their sequence. 309 | 310 | ```swift 311 | 312 | // MARK: - IBOutlets 313 | 314 | // MARK: - Public Properties 315 | 316 | // MARK: - Private Properties 317 | 318 | // MARK: - Initializers 319 | 320 | // MARK: - Lifecycle 321 | 322 | // MARK: - View Life Cycle 323 | 324 | // MARK: - Object Life Cycle 325 | 326 | // MARK: - Navigation 327 | 328 | // MARK: - Public Methods 329 | 330 | // MARK: - Private Methods 331 | 332 | // MARK: - IBActions 333 | 334 | // MARK: - Actions 335 | 336 | // MARK: - Application Shortcut Support 337 | 338 | // MARK: - Segue preparation 339 | 340 | // MARK: - Table view data source 341 | 342 | ``` 343 | 344 | ### Semantic Commit 345 | 346 | - `feat`: (new feature for the user, not a new feature for build script) 347 | - `fix`: (bug fix for the user, not a fix to a build script) 348 | - `docs`: (changes to the documentation) 349 | - `style`: (formatting, missing semi colons, etc; no production code change) 350 | - `refactor`: (refactoring production code, eg. renaming a variable) 351 | - `test`: (adding missing tests, refactoring tests; no production code change) 352 | - `chore`: (updating grunt tasks etc; no production code change) `chore: release v0.5.3'` 353 | 354 | 355 | ### Shortcut 356 | 357 | - Build: `⌘ + B` 358 | - Run: `⌘ + R` 359 | - Test: `⌘ + U` 360 | - Stop: `⌘ + .` 361 | - Clean: `⌘ + ⇧ + K` 362 | - Open quickly: `⇧ + ⌘ + O` 363 | - Code completion: `⌃ + Space` 364 | - Opening a file: `⇧ + ⌘ + O` 365 | - Navigation: `⇧ + ⌘ + J` 366 | - Jump to Definition: `⌃ + ⌘ + J` 367 | - Multiple Cursors: `⌥ + ⌘ + E` 368 | - All Refactor: `⌃ + ⌘ + E` 369 | - Reorder Statements: `⌘ + ⌥ + ( ] or [ )` 370 | - Open Inspectors: `⌥ + ⌘ + 0` 371 | - Open Navigation: `⌘ + 0` 372 | 373 | **[⬆ Back to Index](#index)** 374 | 375 | 376 | 377 | ## **Protocols** 378 | 379 | ### [CustomStringConvertible](https://github.com/lgreydev/Help/blob/master/Help/Protocols/CustomStringConvertible.swift) 380 | A type with a customized textual representation. 381 | Types that conform to the CustomStringConvertible protocol can provide their own representation to be used when converting an instance to a string. The String(describing:) initializer is the preferred way to convert an instance of any type to a string. If the passed instance conforms to CustomStringConvertible, the String(describing:) initializer and the print(_:) function use the instance’s custom description property. Accessing a type’s description property directly or using CustomStringConvertible as a generic constraint is discouraged. 382 | 383 | ```swift 384 | 385 | struct Card: CustomStringConvertible { 386 | 387 | var description: String { return "\(rank) \(suit)"} 388 | 389 | var suit: Suit 390 | var rank: Rank 391 | 392 | enum Suit: String, CustomStringConvertible { 393 | var description: String { return self.rawValue } 394 | case spades = "♠️" 395 | case hearts = "♥️" 396 | case clubs = "♣️" 397 | case diamonds = "♦️" 398 | } 399 | 400 | enum Rank: Int, CustomStringConvertible { 401 | case jack = 11 402 | case lady = 12 403 | case king = 13 404 | case ace = 14 405 | var description: String { return "\(self.rawValue)" } 406 | } 407 | } 408 | 409 | let card = Card(suit: .hearts, rank: .king) 410 | print(card) // 13 ♥️ nice print 411 | 412 | ``` 413 | 414 | 415 | ### [Equatable](https://github.com/lgreydev/Help/blob/master/Help/Protocols/Equatable.swift) 416 | 417 | [Documentation](https://developer.apple.com/documentation/swift/equatable) 418 | 419 | ```swift 420 | 421 | struct User: Equatable { 422 | let login: String 423 | let password: Int 424 | 425 | static func == (lhs: User, rhs: User) -> Bool { 426 | return lhs.password == rhs.password 427 | } 428 | } 429 | 430 | let userOne = User(login: "mike", password: 123 ) 431 | let userTwo = User(login: "mikeeeee", password: 123 ) 432 | 433 | userOne == userTwo ? print("login") : print("not login") 434 | // login 435 | 436 | ``` 437 | 438 | 439 | ### [Comparable](https://github.com/lgreydev/Help/blob/master/Help/Protocols/Comparable.swift) 440 | 441 | [Documentation](https://developer.apple.com/documentation/swift/comparable/) 442 | 443 | ```swift 444 | 445 | struct User: Comparable { 446 | 447 | let name: String 448 | let money: Int 449 | 450 | static func < (lhs: User, rhs: User) -> Bool { 451 | return lhs.money < rhs.money 452 | } 453 | } 454 | 455 | let mike = User(name: "Mike", money: 50 ) 456 | let bob = User(name: "Bob", money: 40 ) 457 | 458 | mike < bob // false 459 | mike > bob // true 460 | 461 | ``` 462 | 463 | **[⬆ Back to Index](#index)** 464 | 465 | 466 | 467 | ## **Extensions** 468 | 469 | ### [IntᐩRandom](https://github.com/lgreydev/Help/blob/master/Help/Extensions/Int+Random.swift) 470 | An extension for Int that returns a random number works with range of numbers. Negative numbers converts to positive. 471 | - **Example:** 17.random will return 0 to 17 (not including 17). 472 | - **Example:** -5.random, -5 convert to 5, will return 0 to 5 (not including 5). 473 | 474 | 475 | ```swift 476 | 477 | extension Int { 478 | var random: Int { 479 | if self > 0 { 480 | return Int.random(in: 0.. Bool { 499 | return lhs.compare(rhs, options: .numeric) == .orderedSame 500 | } 501 | 502 | static func <(lhs: String, rhs: String) -> Bool { 503 | return lhs.compare(rhs, options: .numeric) == .orderedAscending 504 | } 505 | 506 | static func <=(lhs: String, rhs: String) -> Bool { 507 | return lhs.compare(rhs, options: .numeric) == .orderedAscending || lhs.compare(rhs, options: .numeric) == .orderedSame 508 | } 509 | 510 | static func >(lhs: String, rhs: String) -> Bool { 511 | return lhs.compare(rhs, options: .numeric) == .orderedDescending 512 | } 513 | 514 | static func >=(lhs: String, rhs: String) -> Bool { 515 | return lhs.compare(rhs, options: .numeric) == .orderedDescending || lhs.compare(rhs, options: .numeric) == .orderedSame 516 | } 517 | } 518 | 519 | ``` 520 | 521 | **[⬆ Back to Index](#index)** 522 | 523 | 524 | 525 | ## **Working Code** 526 | 527 | 528 | ### [Error Handling](https://github.com/lgreydev/Help/blob/master/Help/WorkingCode/ErrorHandling.swift) 529 | - Error handler, using the `throws` keyword we indicate that the function can receive an error. 530 | - Using the `throw` key we throw an error into the `NSError` instance. 531 | - `do catch` - this error handler helps to catch the error. 532 | - In this example, we are trying to call the function that we marked with the keyword `throws`. 533 | - Before calling the function, put the `try` keyword. 534 | - If the function gets an error, we catch it `catch`. 535 | 536 | ```swift 537 | 538 | func divide(_ a: Int, by b: Int) throws -> Double { 539 | guard b != 0 else { throw NSError(domain: "The number 'b' must not be zero", code: 100) } 540 | return Double(a / b) 541 | } 542 | 543 | /// Checks in 'do catch' 544 | do { 545 | try divide(10, by: 0) 546 | } catch let error { 547 | error.localizedDescription 548 | } 549 | 550 | /// Checks in 'try!' or 'try?' 551 | let myError = try! divide(10, by: 0) 552 | let myError = try? divide(10, by: 0) 553 | 554 | /// Example with user data 555 | enum NetworkError: Error { 556 | case notPage 557 | case networkError 558 | } 559 | 560 | class Network { 561 | static let responses = [200, 404, 500] 562 | static func request() -> Int { 563 | return responses.randomElement() ?? 0 564 | } 565 | } 566 | 567 | class NetworkManager { 568 | func userRequest(text: String) throws -> String { 569 | let statusCode = Network.request() 570 | guard statusCode != 404 else { throw NetworkError.notPage } 571 | guard statusCode != 500 else { throw NetworkError.networkError } 572 | return "\(statusCode): image \(text)" 573 | } 574 | } 575 | 576 | class Browser { 577 | let networkManager = NetworkManager() 578 | 579 | func getImage(text: String) { 580 | do { 581 | let result = try networkManager.userRequest(text: text) 582 | print(result) 583 | } catch { 584 | switch error { 585 | case NetworkError.notPage: 586 | print("Not Page") 587 | case NetworkError.networkError: 588 | print("Network Error") 589 | default: 590 | print(error.localizedDescription) 591 | } 592 | } 593 | } 594 | } 595 | 596 | let safari = Browser() 597 | safari.getImage(text: "page 1") 598 | safari.getImage(text: "page 2") 599 | safari.getImage(text: "page 3") 600 | 601 | ``` 602 | 603 | 604 | 605 | ### [Type Casting](https://github.com/lgreydev/Help/blob/master/Help/WorkingCode/TypeCasting.swift) 606 | Type casting is a way to check the type of an instance, or to treat that instance as a different superclass or subclass from somewhere else in its own class hierarchy. 607 | 608 | Type casting in Swift is implemented with the is and as operators. These two operators provide a simple and expressive way to check the type of a value or cast a value to a different type. 609 | 610 | ```swift 611 | 612 | /// Type Casting 613 | let unknown: Any = "Steve" 614 | 615 | if let name = unknown as? String { 616 | print(name) 617 | } else { 618 | print("'unknown' is not type String") 619 | } 620 | 621 | /// Checking Type 622 | unknown is Int ? print(unknown) : print("'unknown' is not type Int") 623 | 624 | /// Downcasting 625 | /// The variable `tom` of the type `Superclass` assigns an instance of `Subclass`. 626 | /// If we want to access the properties of the subclass `Subclass` then we need to do Downcasting. 627 | /// We cannot directly access the property of the `Subclass`, since the variable `tom` has the type `Superclass`. 628 | class Superclass { 629 | var name: String = "Tom" 630 | } 631 | 632 | class Subclass: Superclass { 633 | var age: Int = 15 634 | } 635 | 636 | let tom: Superclass = Subclass() // The variable type of 'Superclass' assigns an instance of 'Subclass' 637 | 638 | tom.age // error 639 | if let value = tom as? Subclass { 640 | print(value.age) 641 | } 642 | 643 | ``` 644 | 645 | 646 | ### [Failable Initializers](https://github.com/lgreydev/Help/blob/master/Help/WorkingCode/FailableInitializers.swift) 647 | It’s sometimes useful to define a class, structure, or enumeration for which initialization can fail. This failure might be triggered by invalid initialization parameter values, the absence of a required external resource, or some other condition that prevents initialization from succeeding. 648 | 649 | To cope with initialization conditions that can fail, define one or more failable initializers as part of a class, structure, or enumeration definition. You write a failable initializer by placing a question mark after the init keyword (init?). 650 | 651 | 652 | ```swift 653 | 654 | struct Animal { 655 | let species: String 656 | init?(species: String) { 657 | if species.isEmpty { return nil } 658 | self.species = species 659 | } 660 | } 661 | 662 | let cat = Animal(species: "") // nil 663 | let dog = Animal(species: "Mammal") // Animal 664 | 665 | ``` 666 | 667 | ### [Guard](https://github.com/lgreydev/Help/blob/master/Help/WorkingCode/Guard.swift) 668 | A guard statement is used to transfer program control out of a scope if one or more conditions aren’t met. 669 | A guard statement has the following form: 670 | 671 | `guard condition else { 672 | statements 673 | }` 674 | 675 | The value of any condition in a guard statement must be of type Bool or a type bridged to Bool. The condition can also be an optional binding declaration, as discussed in Optional Binding. 676 | Any constants or variables assigned a value from an optional binding declaration in a guard statement condition can be used for the rest of the guard statement’s enclosing scope. 677 | The else clause of a guard statement is required, and must either call a function with the Never return type or transfer program control outside the guard statement’s enclosing scope using one of the following statements: 678 | 679 | `return` 680 | `break` 681 | `continue` 682 | `throw` 683 | 684 | 685 | ```swift 686 | 687 | /// Example 1 688 | struct MyFood { 689 | var name: String 690 | var calories: Int 691 | 692 | init?(name: String, calories: Int) { 693 | guard name != "", calories != 0 else { return nil } 694 | self.name = name 695 | self.calories = calories 696 | } 697 | } 698 | 699 | let breakfast = MyFood(name: "banana", calories: 50) // return object 700 | let dinner = MyFood(name: "", calories: 10) // return object 701 | let lunch = MyFood(name: "water", calories: 0) // return object 702 | 703 | /// Example 2 704 | func printMyFood(eating: MyFood?) { 705 | guard let name = eating?.name, let calories = eating?.calories else { fatalError() } 706 | print(name, calories) 707 | } 708 | 709 | printMyFood(eating: breakfast) // print object 710 | printMyFood(eating: lunch) // error 711 | 712 | ``` 713 | 714 | 715 | ### [Array Enumerated](https://github.com/lgreydev/Help/blob/master/Help/WorkingCode/ArrayEnumerated.swift) 716 | [documentation](https://developer.apple.com/documentation/swift/array/1687832-enumerated) 717 | 718 | Returns a sequence of pairs (n, x), where n represents a consecutive integer starting at zero and x represents an element of the sequence. 719 | 720 | ```swift 721 | 722 | let emojiArray = ["🥵", "😰", "😇", "😱", "🥶"] 723 | 724 | for (index, emoji) in emojiArray.enumerated() { 725 | print(index, emoji) 726 | } 727 | 728 | //0 🥵 729 | //1 😰 730 | //2 😇 731 | //3 😱 732 | //4 🥶 733 | 734 | ``` 735 | 736 | 737 | ### [Array Zip](https://github.com/lgreydev/Help/blob/master/Help/WorkingCode/ArrayZip.swift) 738 | [documentation](https://developer.apple.com/documentation/combine/publishers/autoconnect/zip(_:)/) 739 | 740 | Combines elements from another publisher and deliver pairs of elements as tuples. 741 | 742 | ```swift 743 | 744 | let emojiArray = ["🥵", "🥶", "😎", "😱", "😡"] 745 | let strArray = ["Hot", "Cold", "Good"] 746 | var newArray = [(String, String)]() 747 | 748 | for value in zip(emojiArray, strArray) { 749 | newArray.append(value) 750 | } 751 | 752 | print(newArray) 753 | 754 | // [("🥵", "Hot"), ("🥶", "Cold"), ("😎", "Good")] // Array of Tuple 755 | 756 | ``` 757 | 758 | 759 | ### [Dictionary](https://github.com/lgreydev/Help/blob/master/Help/WorkingCode/Dictionary.swift) 760 | [reduce(into:_:)](https://developer.apple.com/documentation/swift/array/3126956-reduce) 761 | 762 | **Dictionary Reduce** 763 | 764 | Returns the result of combining the elements of the sequence using the given closure. 765 | 766 | ```swift 767 | 768 | let fruits = ["🍏", "🍓", "🍒", "🍌", "🍏", "🍒", "🍌", "🍌", "🍌", "🍒", "🍒", "🍌", "🍓", "🍓"] 769 | 770 | let fruitsCount = fruits.reduce(into: [:]) { counts, fruit in 771 | counts[fruit, default: 0] += 1 772 | } 773 | 774 | fruitsCount // ["🍒": 4, "🍏": 2, "🍓": 3, "🍌": 5] 775 | // [String : Int] 776 | 777 | ``` 778 | 779 | **Dictionary Search List** 780 | [updateValue(_:forKey:)](https://developer.apple.com/documentation/swift/dictionary/3127179-updatevalue) 781 | [removeValue(forKey:)](https://developer.apple.com/documentation/swift/dictionary/1641348-removevalue) 782 | 783 | ```swift 784 | struct Product: Hashable { 785 | let id: String; // unique identifier 786 | let name: String; 787 | let producer: String; 788 | 789 | static func ==(lhs: Product, rhs: Product) -> Bool { 790 | return lhs.id == rhs.id 791 | } 792 | } 793 | 794 | protocol Library { 795 | /** 796 | Adds a new product object to the Library. 797 | - Parameter product: product to add to the Library 798 | - Returns: false if the product with same id already exists in the Library, true – otherwise. 799 | */ 800 | func addNewProduct(product: Product) -> Bool; 801 | 802 | /** 803 | Deletes the product with the specified id from the Library. 804 | - Returns: true if the product with same id existed in the Library, false – otherwise. 805 | */ 806 | func deleteProduct(id: String) -> Bool; 807 | 808 | /** 809 | - Returns: 10 product names containing the specified string. If there are several products with the same name, producer's name is appended to product's name. 810 | */ 811 | func listProductsByName(searchString: String) -> Set; 812 | 813 | /** 814 | - Returns: 10 product names whose producer contains the specified string, ordered by producers. 815 | */ 816 | func listProductsByProducer(searchString: String) -> [String]; 817 | } 818 | 819 | class LibraryRepository: Library { 820 | private var products = [String: Product]() 821 | 822 | func addNewProduct(product: Product) -> Bool { 823 | return self.products.updateValue(product, forKey: product.id) == nil 824 | } 825 | 826 | func deleteProduct(id: String) -> Bool { 827 | return self.products.removeValue(forKey: id) != nil 828 | } 829 | 830 | func listProductsByName(searchString: String) -> Set { 831 | return self.products 832 | .filter({ $1.name.contains(searchString) }) 833 | .prefix(10) 834 | .reduce(into: Set()) { (productNames, product) in 835 | if products.filter({ $1.name == product.value.name }).count > 1 { 836 | productNames.insert("\(product.value.producer) - \(product.value.name)") 837 | return 838 | } 839 | productNames.insert(product.value.name) 840 | } 841 | } 842 | 843 | func listProductsByProducer(searchString: String) -> [String] { 844 | return self.products 845 | .filter({ $1.producer.contains(searchString) }) 846 | .sorted(by: { $0.value.producer > $1.value.producer }) 847 | .prefix(10) 848 | .map { $0.value.name } 849 | } 850 | } 851 | ``` 852 | 853 | 854 | 855 | ### [Enum Data Model](https://github.com/lgreydev/Help/blob/master/Help/WorkingCode/EnumDataModel.swift) 856 | 857 | ```swift 858 | 859 | enum Difficulty: String { 860 | typealias DifficultyContent = (title: String, description: String, levels: Int) 861 | 862 | case novice 863 | case warrior 864 | case master 865 | case unknown 866 | 867 | var content: DifficultyContent { 868 | switch self { 869 | case .novice: 870 | return (title: "Novice", description: "All Too Easy!", levels: 8) 871 | case .warrior: 872 | return (title: "Warrior", description: "You Will Die Mortal!", levels: 10) 873 | case .master: 874 | return (title: "Master", description: "You Will Never Win!", levels: 12) 875 | case .unknown: 876 | return (title: "Unknown", description: "Unknown!", levels: -1) 877 | } 878 | } 879 | } 880 | 881 | func showTower(for difficulty: Difficulty) { 882 | print(difficulty.content.title) 883 | print(difficulty.content.description) 884 | print(difficulty.content.levels) 885 | } 886 | 887 | let difficulty = Difficulty(rawValue: "novice") 888 | 889 | showTower(for: difficulty ?? .unknown) 890 | 891 | ``` 892 | 893 | **[⬆ Back to Index](#index)** 894 | 895 | 896 | 897 | ## **UIKit** 898 | 899 | ## UITextField 900 | 901 | ### [Dismiss Keyboard](https://github.com/lgreydev/Help/blob/master/Help/UIKit/UITextField/DismissKeyboard.swift) 902 | 903 | ```swift 904 | 905 | class DismissKeyboard: UIViewController { 906 | 907 | override func viewDidLoad() { 908 | super.viewDidLoad() 909 | 910 | // When user touch on screen 911 | let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) 912 | self.view.addGestureRecognizer(tap) 913 | } 914 | 915 | // Dismiss keyboard 916 | @objc private func dismissKeyboard() { 917 | self.view.endEditing(true) 918 | } 919 | } 920 | 921 | ``` 922 | 923 | ## Data Manager 924 | 925 | ### [Save and Load Data on Device](https://github.com/lgreydev/Help/blob/master/Help/UIKit/DataManager/SaveAndLoadDataOnDevice.swift) 926 | [Documentation - Data Manager](https://developer.apple.com/documentation/foundation/filemanager)
927 | [Documentation - Codable](https://developer.apple.com/documentation/swift/codable)
928 | [Documentation - Decodable](https://developer.apple.com/documentation/swift/decodable)
929 | [Documentation - Encodable](https://developer.apple.com/documentation/swift/encodable) 930 | 931 | ```swift 932 | 933 | class SaveAndLoadDataOnDevice { 934 | 935 | // Create an archive file 936 | var archiveURL: URL? { 937 | guard let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { fatalError() } 938 | return documentDirectory.appendingPathComponent("nameFile").appendingPathExtension("plist") 939 | } 940 | 941 | // Decoding an object from an archive 942 | func loadAll() -> [SameObject]? { 943 | let decoder = PropertyListDecoder() 944 | guard let archiveURL = archiveURL else { fatalError() } 945 | guard let decoderSameObject = try? Data(contentsOf: archiveURL) else { fatalError() } 946 | return try? decoder.decode([SameObject].self, from: decoderSameObject) 947 | } 948 | 949 | // Encoder the received object into the archive file 950 | func saveEmojis(_ object: [SameObject]) { 951 | let encoder = PropertyListEncoder() 952 | guard let archiveURL = archiveURL else { fatalError() } 953 | guard let encoderSameObject = try? encoder.encode(object) else { fatalError() } 954 | try? encoderSameObject.write(to: archiveURL, options: .noFileProtection) 955 | } 956 | } 957 | 958 | // Class have to protocol 'Codable' 959 | class SameObject: Codable { 960 | 961 | } 962 | 963 | ``` 964 | 965 | **[⬆ Back to Index](#index)** 966 | 967 | 968 | 969 | ## Table View 970 | 971 | ### [Move Row](https://github.com/lgreydev/Help/blob/master/Help/UIKit/TableView/MoveRow.swift) 972 | [Documentation](https://developer.apple.com/documentation/uikit/uitableviewdatasource/1614867-tableview) 973 | 974 | ```swift 975 | 976 | class MoveRow: UITableViewController { 977 | 978 | var arrayElement = [String]() 979 | 980 | override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { 981 | let someRow = arrayElement.remove(at: sourceIndexPath.row) 982 | arrayElement.insert(someRow, at: destinationIndexPath.row) 983 | } 984 | } 985 | 986 | ``` 987 | 988 | ### [Move Row](https://github.com/lgreydev/Help/blob/master/Help/UIKit/TableView/DeleteRow.swift) 989 | [Documentation1](https://developer.apple.com/documentation/uikit/uitableviewdiffabledatasource/3375816-tableview) 990 | [Documentation2](https://developer.apple.com/documentation/uikit/uitableviewdiffabledatasource/3375814-tableview) 991 | 992 | ```swift 993 | 994 | class DeleteRow: UITableViewController { 995 | 996 | var arrayElement = [String]() 997 | 998 | override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { 999 | true 1000 | } 1001 | 1002 | override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { 1003 | if editingStyle == .delete { 1004 | arrayElement.remove(at: indexPath.row) 1005 | tableView.deleteRows(at: [indexPath], with: .automatic) 1006 | } 1007 | } 1008 | } 1009 | 1010 | ``` 1011 | 1012 | ### [Unwind Segue](https://github.com/lgreydev/Help/blob/master/Help/UIKit/UnwindSegue.swift) 1013 | [Articl - Unwind Segue](https://developer.apple.com/documentation/uikit/resource_management/dismissing_a_view_controller_with_an_unwind_segue) 1014 | 1015 | **Dismissing a View Controller with an Unwind Segue** 1016 | 1017 | Configure an unwind segue in your storyboard file that dynamically chooses the most appropriate view controller to display next. 1018 | 1019 | To handle the dismissal of a view controller, create an unwind segue. Unlike the segues that you use to present view controllers, an unwind segue promises the dismissal of the current view controller without promising a specific target for the resulting transition. Instead, UIKit determines the target of an unwind segue programmatically at runtime. 1020 | 1021 | UIKit determines the target of an unwind segue at runtime, so you aren’t restricted in how you set up your view controller hierarchies. Consider a scenario where two view controllers present the same child view controller, as shown in the following figure. You could add complicated logic to determine which view controller to display next, but such a solution wouldn’t scale well. Instead, UIKit provides a simple programmatic solution that scales to any number of view controllers with minimal effort. 1022 | 1023 | ```swift 1024 | 1025 | @IBAction func myUnwindAction(unwindSegue: UIStoryboardSegue) { 1026 | // Connect a Triggering Object to the Exit Control 1027 | } 1028 | 1029 | ``` 1030 | 1031 | 1032 | 1033 | 1034 | ### [Prepare Segue](https://github.com/lgreydev/Help/blob/master/Help/UIKit/PrepareSegue.swift) 1035 | [Documentation](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621490-prepare) 1036 | 1037 | `prepare(for:sender:)` 1038 | Notifies the view controller that a segue is about to be performed. 1039 | 1040 | The default implementation of this method does nothing. Subclasses override this method and use it to configure the new view controller prior to it being displayed. The segue object contains information about the transition, including references to both view controllers that are involved. 1041 | 1042 | Because segues can be triggered from multiple sources, you can use the information in the segue and sender parameters to disambiguate between different logical paths in your app. For example, if the segue originated from a table view, the sender parameter would identify the table view cell that the user tapped. You could then use that information to set the data on the destination view controller. 1043 | 1044 | 1045 | ```swift 1046 | 1047 | func prepare(for segue: UIStoryboardSegue, sender: Any?) { 1048 | guard segue.identifier == "identifier segue" else { fatalError() } 1049 | guard let secondVC = segue.destination as? SecondViewController else { fatalError() } 1050 | secondVC?.property = "text 1051 | } 1052 | 1053 | ``` 1054 | 1055 | **Parameters** 1056 | 1057 | `segue` The segue object containing information about the view controllers involved in the segue. 1058 | 1059 | `sender` The object that initiated the segue. You might use this parameter to perform different actions based on which control (or other object) initiated the segue. 1060 | 1061 | **Accessing the Segue Attributes** 1062 | 1063 | `var source: UIViewController` The source view controller for the segue. 1064 | 1065 | `var destination: UIViewController` The destination view controller for the segue. 1066 | 1067 | `var identifier: String?` The identifier for the segue object. 1068 | 1069 | 1070 | 1071 | ### [Perform Segue](https://github.com/lgreydev/Help/blob/master/Help/UIKit/PerformSegue.swift) 1072 | [Documentation](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621413-performsegue) 1073 | 1074 | Initiates the segue with the specified identifier from the current view controller's storyboard file. 1075 | 1076 | ```swift 1077 | 1078 | func performSegue(withIdentifier identifier: String, sender: Any?) 1079 | 1080 | ``` 1081 | 1082 | **Parameters** 1083 | 1084 | `identifier` The string that identifies the triggered segue. In Interface Builder, you specify the segue’s identifier string in the attributes inspector. 1085 | 1086 | This method throws an Exception handling if there is no segue with the specified identifier. 1087 | 1088 | `sender` The object that you want to use to initiate the segue. This object is made available for informational purposes during the actual segue. 1089 | 1090 | **Discussion** 1091 | 1092 | Normally, segues are initiated automatically and not using this method. However, you can use this method in cases where the segue could not be configured in your storyboard file. For example, you might call it from a custom action handler used in response to shake or accelerometer events. 1093 | 1094 | The current view controller must have been loaded from a storyboard. If its storyboard property is nil, perhaps because you allocated and initialized the view controller yourself, this method throws an exception. 1095 | 1096 | 1097 | ### [Source](https://github.com/lgreydev/Help/blob/master/Help/UIKit/Source.swift) 1098 | [Documentation](https://developer.apple.com/documentation/uikit/uistoryboardsegue/1621918-source) 1099 | 1100 | ```swift 1101 | 1102 | class Source: UIViewController { 1103 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 1104 | let source = segue.source as! OtherSource 1105 | source.property = 1 1106 | } 1107 | } 1108 | 1109 | class OtherSource: UIViewController { 1110 | var property = 0 1111 | } 1112 | 1113 | ``` 1114 | 1115 | 1116 | ### [Destination](https://github.com/lgreydev/Help/blob/master/Help/UIKit/Destination.swift) 1117 | [Documentation](https://developer.apple.com/documentation/uikit/uistoryboardsegue/1621916-destination) 1118 | 1119 | ```swift 1120 | 1121 | class Destination: UIViewController { 1122 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 1123 | let destination = segue.destination as! OtherDestination 1124 | destination.property = 1 1125 | } 1126 | } 1127 | 1128 | class OtherDestination: UIViewController { 1129 | var property = 0 1130 | } 1131 | 1132 | ``` 1133 | 1134 | ### [Pass Data Delegate](https://github.com/lgreydev/Help/blob/master/Help/UIKit/PassDataDelegate.swift) 1135 | 1136 | ```swift 1137 | 1138 | protocol FirstViewControllerDelegate: AnyObject { 1139 | func update(text: String) 1140 | } 1141 | 1142 | /// - Implementation option through extensions 1143 | //extension FirstViewController: FirstViewControllerDelegate { 1144 | // func update(text: String) { 1145 | // textLabel.text = text 1146 | // } 1147 | //} 1148 | 1149 | class FirstViewController: UIViewController, FirstViewControllerDelegate { 1150 | 1151 | @IBOutlet weak var textLabel: UILabel! 1152 | 1153 | override func viewDidLoad() { 1154 | super.viewDidLoad() 1155 | 1156 | } 1157 | 1158 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 1159 | guard let secondVC = segue.destination as? SecondViewController else { fatalError() } 1160 | secondVC.delegate = self 1161 | } 1162 | 1163 | func update(text: String) { 1164 | textLabel.text = text 1165 | } 1166 | } 1167 | 1168 | 1169 | class SecondViewController: UIViewController { 1170 | 1171 | @IBOutlet weak var textLabel: UILabel! 1172 | 1173 | weak var delegate: FirstViewControllerDelegate? 1174 | 1175 | override func viewDidLoad() { 1176 | super.viewDidLoad() 1177 | 1178 | } 1179 | 1180 | @IBAction func actionButton(_ sender: UIButton) { 1181 | delegate?.update(text: "Text was changed") 1182 | dismiss(animated: true, completion: nil) 1183 | } 1184 | } 1185 | 1186 | ``` 1187 | 1188 | 1189 | ### [Pass Data Closure](https://github.com/lgreydev/Help/blob/master/Help/UIKit/PassDataClosure.swift) 1190 | #### Callbacks 1191 | 1192 | ```swift 1193 | 1194 | class FirstViewController: UIViewController { 1195 | 1196 | @IBOutlet weak var textLabel: UILabel! 1197 | 1198 | override func viewDidLoad() { 1199 | super.viewDidLoad() 1200 | 1201 | } 1202 | 1203 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 1204 | guard let secondVC = segue.destination as? SecondViewController else { fatalError() } 1205 | secondVC.closure = { [weak self] text in 1206 | self?.textLabel.text = text 1207 | } 1208 | } 1209 | } 1210 | 1211 | 1212 | class SecondViewController: UIViewController { 1213 | 1214 | @IBOutlet weak var textLabel: UILabel! 1215 | 1216 | var closure: ((String) -> ())? 1217 | 1218 | override func viewDidLoad() { 1219 | super.viewDidLoad() 1220 | 1221 | } 1222 | 1223 | @IBAction func actionButton(_ sender: UIButton) { 1224 | closure?("I can pass data by closure") 1225 | dismiss(animated: true, completion: nil) 1226 | } 1227 | } 1228 | 1229 | ``` 1230 | 1231 | 1232 | 1233 | ### [AutoLayout Constraints Programmatically](https://github.com/lgreydev/Help/blob/master/Help/UIKit/AutoLayoutConstraintsProgrammatically.swift) 1234 | 1235 | ```swift 1236 | 1237 | class AutoLayoutConstraintsProgrammatically: UIViewController { 1238 | 1239 | private let blueView: UIView = { 1240 | let view = UIView() 1241 | view.translatesAutoresizingMaskIntoConstraints = false 1242 | view.backgroundColor = .link 1243 | return view 1244 | }() 1245 | 1246 | private let redView: UIView = { 1247 | let view = UIView() 1248 | view.translatesAutoresizingMaskIntoConstraints = false 1249 | view.backgroundColor = .red 1250 | return view 1251 | }() 1252 | 1253 | private let imageView_1: UIImageView = { 1254 | let image = UIImage(named: "github") 1255 | let view = UIImageView(image: image) 1256 | view.translatesAutoresizingMaskIntoConstraints = false 1257 | return view 1258 | }() 1259 | 1260 | private let imageView_2: UIImageView = { 1261 | let image = UIImage(named: "github") 1262 | let view = UIImageView(image: image) 1263 | view.translatesAutoresizingMaskIntoConstraints = false 1264 | return view 1265 | }() 1266 | 1267 | override func viewDidLoad() { 1268 | super.viewDidLoad() 1269 | 1270 | addConstraints() 1271 | } 1272 | } 1273 | 1274 | extension AutoLayoutConstraintsProgrammatically { 1275 | 1276 | private func addConstraints() { 1277 | var constraints = [NSLayoutConstraint]() 1278 | 1279 | view.addSubview(blueView) 1280 | // Offset from the edges 1281 | constraints.append(blueView.leadingAnchor.constraint(equalTo: view.leadingAnchor)) 1282 | constraints.append(blueView.trailingAnchor.constraint(equalTo: view.trailingAnchor)) 1283 | constraints.append(blueView.topAnchor.constraint(equalTo: view.topAnchor)) 1284 | constraints.append(blueView.bottomAnchor.constraint(equalTo: view.bottomAnchor)) 1285 | 1286 | // Safe Area 1287 | // constraints.append(blueView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor)) 1288 | // constraints.append(blueView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)) 1289 | // constraints.append(blueView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor)) 1290 | // constraints.append(blueView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)) 1291 | 1292 | // With constant 1293 | // constraints.append(blueView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 100)) 1294 | // constraints.append(blueView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -100)) 1295 | // constraints.append(blueView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100)) 1296 | // constraints.append(blueView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -100)) 1297 | 1298 | view.addSubview(redView) 1299 | // Size Height and Width 1300 | constraints.append(redView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.3)) 1301 | constraints.append(redView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.3)) 1302 | 1303 | // Center alignment 1304 | constraints.append(redView.centerXAnchor.constraint(equalTo: view.centerXAnchor)) 1305 | constraints.append(redView.centerYAnchor.constraint(equalTo: view.centerYAnchor)) 1306 | 1307 | // Image 1 1308 | view.addSubview(imageView_1) 1309 | constraints.append(imageView_1.heightAnchor.constraint(equalToConstant: 50)) 1310 | constraints.append(imageView_1.widthAnchor.constraint(equalToConstant: 50)) 1311 | constraints.append(imageView_1.centerXAnchor.constraint(equalTo: view.centerXAnchor)) 1312 | constraints.append(imageView_1.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20)) 1313 | 1314 | // Image 2 1315 | view.addSubview(imageView_2) 1316 | imageView_2.heightAnchor.constraint(equalToConstant: 70).isActive = true 1317 | imageView_2.widthAnchor.constraint(equalToConstant: 70).isActive = true 1318 | imageView_2.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 1319 | imageView_2.topAnchor.constraint(equalTo: imageView_1.bottomAnchor, constant: 20).isActive = true 1320 | 1321 | NSLayoutConstraint.activate(constraints) 1322 | } 1323 | } 1324 | 1325 | ``` 1326 | 1327 | **[⬆ Back to Index](#index)** 1328 | 1329 | 1330 | 1331 | ## **Features** 1332 | 1333 | ### [Launch Screen](https://github.com/lgreydev/Help/blob/master/Help/Features/LaunchScreen.swift) 1334 | 1335 | [Watch video](https://youtu.be/QpShzjq8q8g)
1336 | [Watch project](https://github.com/lgreydev/LaunchScreen) 1337 | 1338 | ```swift 1339 | 1340 | class LogoViewController: UIViewController { 1341 | 1342 | // MARK: - Private Properties 1343 | private var imageView: UIImageView = { 1344 | let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 150, height: 150)) 1345 | imageView.image = UIImage(named: "logo-icon") 1346 | return imageView 1347 | }() 1348 | 1349 | 1350 | // MARK: - Lifecycle 1351 | override func viewDidLoad() { 1352 | super.viewDidLoad() 1353 | view.addSubview(imageView) 1354 | DispatchQueue.main.asyncAfter(deadline: .now()+1.5) { 1355 | self.performSegue(withIdentifier: "welcomeVC", sender: nil) 1356 | } 1357 | } 1358 | 1359 | override func viewDidLayoutSubviews() { 1360 | super.viewDidLayoutSubviews() 1361 | imageView.center = view.center 1362 | DispatchQueue.main.asyncAfter(deadline: .now()+0.5) { 1363 | self.animate() 1364 | } 1365 | } 1366 | 1367 | 1368 | // MARK: - Private Methods 1369 | private func animate() { 1370 | UIView.animate(withDuration: 1.7) { 1371 | let size = self.view.frame.size.width * 2.5 1372 | let diffX = size - self.view.frame.width 1373 | let diffY = self.view.frame.height - size 1374 | 1375 | self.imageView.frame = CGRect( 1376 | x: -(diffX/2), 1377 | y: diffY/2, 1378 | width: size, 1379 | height: size) 1380 | self.imageView.alpha = 0 1381 | } 1382 | } 1383 | } 1384 | 1385 | 1386 | class WelcomeViewController: UIViewController { 1387 | 1388 | override func viewDidLoad() { 1389 | super.viewDidLoad() 1390 | 1391 | } 1392 | } 1393 | 1394 | ``` 1395 | 1396 | ### [Custom View](https://github.com/lgreydev/Help/blob/master/Help/Features/CustomView.swift) 1397 | 1398 | [Article](https://medium.com/@umairhassanbaig/ios-swift-creating-a-custom-view-with-xib-ace878cd41c5) 1399 | 1400 | ``` swift 1401 | 1402 | class PersonView: UIView { 1403 | 1404 | @IBOutlet var contentView: UIView! 1405 | @IBOutlet var firstNameLabel: UILabel! 1406 | @IBOutlet var lastNameLabel: UILabel! 1407 | 1408 | let contentXibName = "PersonView" 1409 | 1410 | override init(frame: CGRect) { 1411 | super.init(frame: frame) 1412 | commonInit() 1413 | } 1414 | 1415 | required init?(coder aDecoder: NSCoder) { 1416 | super.init(coder: aDecoder) 1417 | commonInit() 1418 | } 1419 | 1420 | func commonInit() { 1421 | Bundle.main.loadNibNamed(contentXibName, owner: self, options: nil) 1422 | contentView.fixInView(self) 1423 | } 1424 | } 1425 | 1426 | extension UIView 1427 | { 1428 | func fixInView(_ container: UIView!) -> Void{ 1429 | self.translatesAutoresizingMaskIntoConstraints = false; 1430 | self.frame = container.frame; 1431 | container.addSubview(self); 1432 | NSLayoutConstraint(item: self, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true 1433 | NSLayoutConstraint(item: self, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true 1434 | NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1.0, constant: 0).isActive = true 1435 | NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1.0, constant: 0).isActive = true 1436 | } 1437 | } 1438 | 1439 | ``` 1440 | 1441 | 1442 | ### [Swipe Screen](https://github.com/lgreydev/Help/blob/master/Help/Features/SwipeScreen.swift) 1443 | 1444 | ``` swift 1445 | 1446 | private func configureSwipe() { 1447 | let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(respondToSwipeGesture)) 1448 | swipeRight.direction = .right 1449 | self.view.addGestureRecognizer(swipeRight) 1450 | 1451 | let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(respondToSwipeGesture)) 1452 | swipeLeft.direction = .left 1453 | self.view.addGestureRecognizer(swipeLeft) 1454 | } 1455 | 1456 | @objc 1457 | private func respondToSwipeGesture(gesture: UIGestureRecognizer) { 1458 | if let swipeGesture = gesture as? UISwipeGestureRecognizer { 1459 | switch swipeGesture.direction { 1460 | case .right: 1461 | print("right swipe") 1462 | // add action! 1463 | case .left: 1464 | print("left swipe") 1465 | // add action! 1466 | default: 1467 | break 1468 | } 1469 | } 1470 | } 1471 | 1472 | ``` 1473 | 1474 | **[⬆ Back to Index](#index)** 1475 | 1476 | 1477 | 1478 | ## **Design Patterns** 1479 | [Book Design Patterns](https://en.wikipedia.org/wiki/Design_Patterns) 1480 | 1481 | ### [Factory Method](https://github.com/lgreydev/Help/blob/master/Help/DesignPatterns/FactoryMethod.swift) 1482 | [Factory Method RU](https://refactoring.guru/ru/design-patterns/factory-method) 1483 | 1484 | ``` swift 1485 | 1486 | // structure Factory Method 1487 | // MARK: Creator 1488 | class FactoryProducts { 1489 | 1490 | static let defaultFactory = FactoryProducts() 1491 | 1492 | func createProduct(_ product: Products) -> Product { 1493 | switch product { 1494 | case .productA: return ConcreteProductA() 1495 | case .productB: return ConcreteProductB() 1496 | } 1497 | } 1498 | 1499 | private init() {} 1500 | } 1501 | 1502 | enum Products { 1503 | case productA 1504 | case productB 1505 | } 1506 | 1507 | // MARK: Product Interface 1508 | protocol Product { 1509 | var id: String { get } 1510 | var name: String { get } 1511 | 1512 | func printProduct() 1513 | } 1514 | 1515 | 1516 | // MARK: Product A 1517 | class ConcreteProductA: Product { 1518 | 1519 | var name: String = "Product A" 1520 | var id: String = "1" 1521 | 1522 | func printProduct() { 1523 | print("id: \(id), name: \(name)") 1524 | } 1525 | } 1526 | 1527 | // MARK: Product B 1528 | class ConcreteProductB: Product { 1529 | 1530 | var name: String = "Product B" 1531 | var id: String = "2" 1532 | 1533 | func printProduct() { 1534 | print("id: \(id), name: \(name)") 1535 | } 1536 | } 1537 | 1538 | 1539 | // MARK: Implementation 1540 | class SomeViewController: UIViewController { 1541 | 1542 | var products: [Product] = [] 1543 | 1544 | override func viewDidLoad() { 1545 | super.viewDidLoad() 1546 | addProduct(.productA) 1547 | addProduct(.productB) 1548 | allProducts() 1549 | } 1550 | 1551 | func addProduct(_ product: Products) { 1552 | let newProduct = FactoryProducts.defaultFactory.createProduct(product) 1553 | products.append(newProduct) 1554 | } 1555 | 1556 | func allProducts() { 1557 | products.forEach { $0.printProduct() } 1558 | } 1559 | } 1560 | 1561 | 1562 | ``` 1563 | 1564 | **[⬆ Back to Index](#index)** 1565 | 1566 | 1567 | 1568 | ## **Leet Code** 1569 | 1570 | ### [Pascal Triangle](https://github.com/lgreydev/Help/blob/master/Help/LeetCode/Pascal'sTriangle.swift) 1571 | 1572 | Given an integer numRows, return the first numRows of Pascal's triangle.
1573 | In Pascal's triangle, each number is the sum of the two numbers directly above it as shown:
1574 | 1575 | 1576 | 1577 | **Example 1:**
1578 | *Input:* `numRows = 5`
1579 | *Output:* `[[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]`
1580 | 1581 | **Example 2:**
1582 | *Input:* `numRows = 1`
1583 | *Output:* `[[1]]` 1584 | 1585 | ``` swift 1586 | 1587 | func generate(_ numRows: Int) -> [[Int]] { 1588 | 1589 | guard numRows > 0 else { return [] } 1590 | if numRows == 1 { return [[1]] } 1591 | 1592 | var results = [[Int]]() 1593 | results.append([1]) 1594 | 1595 | for x in 1.. 1617 | 1618 | 1619 | 1620 | **Example 1:**
1621 | > **Input:** `matrix = [[1,2,3],[4,5,6],[7,8,9]]`
1622 | > **Output:** `[1,2,3,6,9,8,7,4,5]`
1623 | 1624 | **Example 2:**
1625 | > **Input:** `matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]`
1626 | > **Output:** `[1,2,3,4,8,12,11,10,9,5,6,7]` 1627 | 1628 | ``` swift 1629 | 1630 | func spiralOrder(_ matrix: [[Int]]) -> [Int] { 1631 | var result = [Int]() 1632 | 1633 | if matrix.count == 0 || matrix[0].count == 0 { 1634 | return result 1635 | } 1636 | 1637 | var left = 0 1638 | var bottom = matrix.count - 1 1639 | var top = 0 1640 | var right = matrix[0].count - 1 1641 | 1642 | while (left <= bottom && top <= right) { 1643 | // Go left to right 1644 | for col in stride(from: top, through: right, by: 1) { 1645 | result.append(matrix[left][col]) 1646 | } 1647 | // Go top to down 1648 | left += 1 1649 | for row in stride(from: left, through: bottom, by: 1) { 1650 | result.append(matrix[row][right]) 1651 | } 1652 | // Go right to left 1653 | right -= 1 1654 | if left <= bottom { 1655 | for col in stride(from: right, through: top, by: -1) { 1656 | result.append(matrix[bottom][col]) 1657 | } 1658 | bottom -= 1 1659 | } 1660 | // Go up 1661 | if top <= right { 1662 | for row in stride(from: bottom, through: left, by: -1) { 1663 | result.append(matrix[row][top]) 1664 | } 1665 | top += 1 1666 | } 1667 | } 1668 | return result 1669 | } 1670 | 1671 | ``` 1672 | 1673 | 1674 | 1675 | ### [Container With Most Water](https://github.com/lgreydev/Help/blob/master/Help/LeetCode/ContainerWithMostWater.swift) 1676 | 1677 | > *Amazon iOS Interview Question* 1678 | 1679 | You are given an integer array `height` of length `n`. There are n vertical lines drawn such that the two endpoints of the `ith` line are `(i, 0)` and `(i, height[i])`. 1680 | 1681 | Find two lines that together with the x-axis form a container, such that the container contains the most water. 1682 | 1683 | Return the maximum amount of water a container can store. 1684 | 1685 | **Notice** that you may not slant the container. 1686 | 1687 | 1688 | **Example 1:**
1689 | 1690 | 1691 | 1692 | 1693 | > **Input:** `height = [1,8,6,2,5,4,8,3,7]`
1694 | > **Output:** `49`
1695 | > **Explanation:** The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section) the container can contain is 49. 1696 | 1697 | **Example 2:**
1698 | > **Input:** `height = [1,1]`
1699 | > **Output:** `1`
1700 | 1701 | ``` swift 1702 | 1703 | func maxArea(_ height: [Int]) -> Int { 1704 | guard !height.isEmpty else { return -1 } 1705 | 1706 | var maxArea = 0 1707 | var left = 0 1708 | var right = height.count - 1 1709 | 1710 | while left < right { 1711 | // Re-calc maxArea 1712 | let minHeight = min(height[left], height[right]) 1713 | let currentHeight = minHeight * (right - left) 1714 | maxArea = max(maxArea, currentHeight) 1715 | 1716 | // Move pointers 1717 | if height[left] < height[right] { 1718 | left += 1 1719 | } else { 1720 | right -= 1 1721 | } 1722 | } 1723 | return maxArea 1724 | } 1725 | 1726 | let input = [1, 8, 6, 2, 5, 4, 8, 3, 7] 1727 | let result = maxArea(input) 1728 | //print("Result: \(result)") 1729 | 1730 | ``` 1731 | 1732 | **[⬆ Back to Index](#index)** 1733 | 1734 | 1735 | 1736 | ### 🛡️ License 1737 | 1738 | This project is licensed under the MIT License - see the [`LICENSE`](https://github.com/lgreydev/Help/blob/master/License) file for details. 1739 | 1740 | ### 🙏 Support 1741 | 1742 | This project needs a ⭐️ from you. Don't forget to leave a star ⭐️ 1743 | 1744 | ### 😎 Contributing 1745 | Sergey Lukaschuk ✉️ s.lukaschuk@yahoo.com 1746 | -------------------------------------------------------------------------------- /Screenshots/ContainerWithMostWater.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lgreydev/Help/b847760f1e35b2eb69dc8f8af9122454f92503b2/Screenshots/ContainerWithMostWater.jpg -------------------------------------------------------------------------------- /Screenshots/PascalTriangleAnimated2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lgreydev/Help/b847760f1e35b2eb69dc8f8af9122454f92503b2/Screenshots/PascalTriangleAnimated2.gif -------------------------------------------------------------------------------- /Screenshots/home-screen-quick-actions.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lgreydev/Help/b847760f1e35b2eb69dc8f8af9122454f92503b2/Screenshots/home-screen-quick-actions.jpeg -------------------------------------------------------------------------------- /Screenshots/segue-001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lgreydev/Help/b847760f1e35b2eb69dc8f8af9122454f92503b2/Screenshots/segue-001.png -------------------------------------------------------------------------------- /Screenshots/segue-002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lgreydev/Help/b847760f1e35b2eb69dc8f8af9122454f92503b2/Screenshots/segue-002.png -------------------------------------------------------------------------------- /Screenshots/spiral.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lgreydev/Help/b847760f1e35b2eb69dc8f8af9122454f92503b2/Screenshots/spiral.jpg -------------------------------------------------------------------------------- /Screenshots/spiral1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lgreydev/Help/b847760f1e35b2eb69dc8f8af9122454f92503b2/Screenshots/spiral1.jpg --------------------------------------------------------------------------------