From 504d9b5dc2d2928e692d8bf325fbb09ebd213554 Mon Sep 17 00:00:00 2001 From: Lucas Abijmil Date: Wed, 25 Aug 2021 14:32:59 +0200 Subject: [PATCH] First commit --- .gitignore | 90 +++ .../project.pbxproj | 601 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 +++ .../Assets.xcassets/Contents.json | 6 + .../@(non)escaping closures/ContentView.swift | 119 ++++ .../@(non)escaping closures/Info.plist | 50 ++ .../Preview Assets.xcassets/Contents.json | 6 + .../__non_escaping_closuresApp.swift | 17 + .../@(non)escaping closuresTests/Info.plist | 22 + .../__non_escaping_closuresTests.swift | 33 + .../@(non)escaping closuresUITests/Info.plist | 22 + .../__non_escaping_closuresUITests.swift | 42 ++ ARC & Memory Leaks.playground/Contents.swift | 175 +++++ .../contents.xcplayground | 4 + .../Contents.swift | 311 +++++++++ .../contents.xcplayground | 4 + AccessControlLevel.playground/Contents.swift | 121 ++++ .../contents.xcplayground | 4 + .../Contents.swift | 40 ++ .../Contents.swift | 8 + .../Contents.swift | 26 + .../Pages/Zip.xcplaygroundpage/Contents.swift | 43 ++ .../contents.xcplayground | 9 + Async & Await/Await.xcodeproj/project.pbxproj | 339 ++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 +++ .../Await/Assets.xcassets/Contents.json | 6 + Async & Await/Await/Async & Await.swift | 145 +++++ Async & Await/Await/AwaitApp.swift | 17 + Async & Await/Await/ContentView.swift | 21 + Async & Await/Await/Info.plist | 50 ++ .../Preview Assets.xcassets/Contents.json | 6 + .../Contents.swift | 41 ++ .../Contents.swift | 42 ++ .../Contents.swift | 23 + .../Contents.swift | 21 + .../Contents.swift | 30 + .../Contents.swift | 16 + .../contents.xcplayground | 11 + .../Contents.swift" | 52 ++ .../contents.xcplayground" | 4 + Defer.playground/Contents.swift | 20 + Defer.playground/contents.xcplayground | 4 + .../Contents.swift | 85 +++ .../contents.xcplayground | 2 + .../project.pbxproj | 311 +++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../DiscardableResult/DiscardableResult.swift | 43 ++ .../Contents.swift | 137 ++++ .../Contents.swift | 20 + .../Contents.swift | 22 + .../Contents.swift | 48 ++ .../Contents.swift | 47 ++ .../Contents.swift | 44 ++ .../Contents.swift | 32 + .../Contents.swift | 12 + .../Contents.swift | 99 +++ .../contents.xcplayground | 14 + .../Contents.swift | 295 +++++++++ .../contents.xcplayground | 4 + .../Contents.swift | 14 + .../Contents.swift | 53 ++ .../contains.xcplaygroundpage/Contents.swift | 41 ++ .../filter.xcplaygroundpage/Contents.swift | 37 ++ .../flatMap.xcplaygroundpage/Contents.swift | 37 ++ .../Pages/map.xcplaygroundpage/Contents.swift | 34 + .../reduce.xcplaygroundpage/Contents.swift | 44 ++ .../contents.xcplayground | 12 + .../Contents.swift | 27 + .../Contents.swift | 6 + .../Contents.swift | 41 ++ Keypath.playground/contents.xcplayground | 8 + Lazy Property.playground/Contents.swift | 129 ++++ .../contents.xcplayground | 4 + .../Contents.swift | 35 + .../contents.xcplayground | 4 + Opaque Return Type.playground/Contents.swift | 67 ++ .../contents.xcplayground | 4 + Property Observers.playground/Contents.swift | 77 +++ .../contents.xcplayground | 4 + .../Contents.swift | 6 + .../Contents.swift | 192 ++++++ .../Contents.swift | 181 ++++++ .../Protocol.xcplaygroundpage/Contents.swift | 75 +++ Protocols.playground/contents.xcplayground | 9 + RawRepresentable.playground/Contents.swift | 51 ++ .../contents.xcplayground | 4 + .../Contents.swift | 29 + .../contents.xcplayground | 4 + .../Contents.swift | 29 + .../Contents.swift | 9 + .../Contents.swift | 31 + .../Contents.swift | 33 + .../Contents.swift | 25 + .../contents.xcplayground | 10 + Typealias.playground/Contents.swift | 19 + Typealias.playground/contents.xcplayground | 4 + .../URLRequest.xcodeproj/project.pbxproj | 585 +++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 +++ .../URLRequest/Assets.xcassets/Contents.json | 6 + .../URLRequest/ContentView.swift | 144 +++++ .../URLRequest/Info.plist | 55 ++ .../Preview Assets.xcassets/Contents.json | 6 + .../URLRequest/URLRequestApp.swift | 17 + .../URLRequestTests/Info.plist | 22 + .../URLRequestTests/URLRequestTests.swift | 33 + .../URLRequestUITests/Info.plist | 22 + .../URLRequestUITests/URLRequestUITests.swift | 42 ++ Unit Tests.playground/Contents.swift | 178 ++++++ Unit Tests.playground/contents.xcplayground | 4 + .../Class.xcplaygroundpage/Contents.swift | 7 + .../Struct.xcplaygroundpage/Contents.swift | 7 + .../Contents.swift | 345 ++++++++++ .../contents.xcplayground | 4 + private(set).playground/Contents.swift | 60 ++ private(set).playground/contents.xcplayground | 4 + .../Contents.swift | 7 + .../rethrows.xcplaygroundpage/Contents.swift | 89 +++ .../Contents.swift | 101 +++ .../contents.xcplayground | 8 + 129 files changed, 7338 insertions(+) create mode 100644 .gitignore create mode 100644 @(non)escaping closures/@(non)escaping closures.xcodeproj/project.pbxproj create mode 100644 @(non)escaping closures/@(non)escaping closures.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 @(non)escaping closures/@(non)escaping closures.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 @(non)escaping closures/@(non)escaping closures/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 @(non)escaping closures/@(non)escaping closures/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 @(non)escaping closures/@(non)escaping closures/Assets.xcassets/Contents.json create mode 100644 @(non)escaping closures/@(non)escaping closures/ContentView.swift create mode 100644 @(non)escaping closures/@(non)escaping closures/Info.plist create mode 100644 @(non)escaping closures/@(non)escaping closures/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 @(non)escaping closures/@(non)escaping closures/__non_escaping_closuresApp.swift create mode 100644 @(non)escaping closures/@(non)escaping closuresTests/Info.plist create mode 100644 @(non)escaping closures/@(non)escaping closuresTests/__non_escaping_closuresTests.swift create mode 100644 @(non)escaping closures/@(non)escaping closuresUITests/Info.plist create mode 100644 @(non)escaping closures/@(non)escaping closuresUITests/__non_escaping_closuresUITests.swift create mode 100644 ARC & Memory Leaks.playground/Contents.swift create mode 100644 ARC & Memory Leaks.playground/contents.xcplayground create mode 100644 ARC, Retain Cycle & Memory Leak.playground/Contents.swift create mode 100644 ARC, Retain Cycle & Memory Leak.playground/contents.xcplayground create mode 100644 AccessControlLevel.playground/Contents.swift create mode 100644 AccessControlLevel.playground/contents.xcplayground create mode 100644 Array functions, extension & tricks.playground/Pages/Enumerated.xcplaygroundpage/Contents.swift create mode 100644 Array functions, extension & tricks.playground/Pages/Introduction.xcplaygroundpage/Contents.swift create mode 100644 Array functions, extension & tricks.playground/Pages/SafeSubscript.xcplaygroundpage/Contents.swift create mode 100644 Array functions, extension & tricks.playground/Pages/Zip.xcplaygroundpage/Contents.swift create mode 100644 Array functions, extension & tricks.playground/contents.xcplayground create mode 100644 Async & Await/Await.xcodeproj/project.pbxproj create mode 100644 Async & Await/Await.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Async & Await/Await.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Async & Await/Await/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Async & Await/Await/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Async & Await/Await/Assets.xcassets/Contents.json create mode 100644 Async & Await/Await/Async & Await.swift create mode 100644 Async & Await/Await/AwaitApp.swift create mode 100644 Async & Await/Await/ContentView.swift create mode 100644 Async & Await/Await/Info.plist create mode 100644 Async & Await/Await/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Compound Assignment.xcplaygroundpage/Contents.swift create mode 100644 Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Emoji Operators.xcplaygroundpage/Contents.swift create mode 100644 Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Infix.xcplaygroundpage/Contents.swift create mode 100644 Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Postfix.xcplaygroundpage/Contents.swift create mode 100644 Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Prefix.xcplaygroundpage/Contents.swift create mode 100644 Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Introduction.xcplaygroundpage/Contents.swift create mode 100644 Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/contents.xcplayground create mode 100644 "Custom Prefix, Postfix, Infix Operators/Prefix, PostFix, Infix Operators \342\200\223 Lucas.playground/Contents.swift" create mode 100644 "Custom Prefix, Postfix, Infix Operators/Prefix, PostFix, Infix Operators \342\200\223 Lucas.playground/contents.xcplayground" create mode 100644 Defer.playground/Contents.swift create mode 100644 Defer.playground/contents.xcplayground create mode 100644 Dependecy Injection.playground/Pages/Untitled Page.xcplaygroundpage/Contents.swift create mode 100644 Dependecy Injection.playground/contents.xcplayground create mode 100644 DiscardableResult/DiscardableResult.xcodeproj/project.pbxproj create mode 100644 DiscardableResult/DiscardableResult.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 DiscardableResult/DiscardableResult.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 DiscardableResult/DiscardableResult/DiscardableResult.swift create mode 100644 Enum extensions & tricks.playground/Pages/Enum Codable.xcplaygroundpage/Contents.swift create mode 100644 Enum extensions & tricks.playground/Pages/Enum avec associated value.xcplaygroundpage/Contents.swift create mode 100644 Enum extensions & tricks.playground/Pages/Enum sans associated value.xcplaygroundpage/Contents.swift create mode 100644 Enum extensions & tricks.playground/Pages/For case pattern matching.xcplaygroundpage/Contents.swift create mode 100644 Enum extensions & tricks.playground/Pages/Guard case pattern matching.xcplaygroundpage/Contents.swift create mode 100644 Enum extensions & tricks.playground/Pages/If case pattern matching.xcplaygroundpage/Contents.swift create mode 100644 Enum extensions & tricks.playground/Pages/Indirect Enum.xcplaygroundpage/Contents.swift create mode 100644 Enum extensions & tricks.playground/Pages/Introduction.xcplaygroundpage/Contents.swift create mode 100644 Enum extensions & tricks.playground/Pages/Switch pattern matching.xcplaygroundpage/Contents.swift create mode 100644 Enum extensions & tricks.playground/contents.xcplayground create mode 100644 Grand Central Dispatch.playground/Contents.swift create mode 100644 Grand Central Dispatch.playground/contents.xcplayground create mode 100644 High Order Functions.playground/Pages/Introduction.xcplaygroundpage/Contents.swift create mode 100644 High Order Functions.playground/Pages/compactMap.xcplaygroundpage/Contents.swift create mode 100644 High Order Functions.playground/Pages/contains.xcplaygroundpage/Contents.swift create mode 100644 High Order Functions.playground/Pages/filter.xcplaygroundpage/Contents.swift create mode 100644 High Order Functions.playground/Pages/flatMap.xcplaygroundpage/Contents.swift create mode 100644 High Order Functions.playground/Pages/map.xcplaygroundpage/Contents.swift create mode 100644 High Order Functions.playground/Pages/reduce.xcplaygroundpage/Contents.swift create mode 100644 High Order Functions.playground/contents.xcplayground create mode 100644 Keypath.playground/Pages/Generalite.xcplaygroundpage/Contents.swift create mode 100644 Keypath.playground/Pages/Introduction.xcplaygroundpage/Contents.swift create mode 100644 Keypath.playground/Pages/Predicate with Keypath.xcplaygroundpage/Contents.swift create mode 100644 Keypath.playground/contents.xcplayground create mode 100644 Lazy Property.playground/Contents.swift create mode 100644 Lazy Property.playground/contents.xcplayground create mode 100644 Literal Expression & Log function.playground/Contents.swift create mode 100644 Literal Expression & Log function.playground/contents.xcplayground create mode 100644 Opaque Return Type.playground/Contents.swift create mode 100644 Opaque Return Type.playground/contents.xcplayground create mode 100644 Property Observers.playground/Contents.swift create mode 100644 Property Observers.playground/contents.xcplayground create mode 100644 Protocols.playground/Pages/Introduction.xcplaygroundpage/Contents.swift create mode 100644 Protocols.playground/Pages/Protocol Extension.xcplaygroundpage/Contents.swift create mode 100644 Protocols.playground/Pages/Protocol Inheritance.xcplaygroundpage/Contents.swift create mode 100644 Protocols.playground/Pages/Protocol.xcplaygroundpage/Contents.swift create mode 100644 Protocols.playground/contents.xcplayground create mode 100644 RawRepresentable.playground/Contents.swift create mode 100644 RawRepresentable.playground/contents.xcplayground create mode 100644 Static Properties & Methods.playground/Contents.swift create mode 100644 Static Properties & Methods.playground/contents.xcplayground create mode 100644 Strings extensions & tricks.playground/Pages/Custom String Interpolation.xcplaygroundpage/Contents.swift create mode 100644 Strings extensions & tricks.playground/Pages/Introduction.xcplaygroundpage/Contents.swift create mode 100644 Strings extensions & tricks.playground/Pages/Localized String with String Interpolation.xcplaygroundpage/Contents.swift create mode 100644 Strings extensions & tricks.playground/Pages/OptionalString.orEmpty.xcplaygroundpage/Contents.swift create mode 100644 Strings extensions & tricks.playground/Pages/StaticString & URL init custom.xcplaygroundpage/Contents.swift create mode 100644 Strings extensions & tricks.playground/contents.xcplayground create mode 100644 Typealias.playground/Contents.swift create mode 100644 Typealias.playground/contents.xcplayground create mode 100644 URLComponents + URLRequest/URLRequest.xcodeproj/project.pbxproj create mode 100644 URLComponents + URLRequest/URLRequest.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 URLComponents + URLRequest/URLRequest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 URLComponents + URLRequest/URLRequest/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 URLComponents + URLRequest/URLRequest/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 URLComponents + URLRequest/URLRequest/Assets.xcassets/Contents.json create mode 100644 URLComponents + URLRequest/URLRequest/ContentView.swift create mode 100644 URLComponents + URLRequest/URLRequest/Info.plist create mode 100644 URLComponents + URLRequest/URLRequest/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 URLComponents + URLRequest/URLRequest/URLRequestApp.swift create mode 100644 URLComponents + URLRequest/URLRequestTests/Info.plist create mode 100644 URLComponents + URLRequest/URLRequestTests/URLRequestTests.swift create mode 100644 URLComponents + URLRequest/URLRequestUITests/Info.plist create mode 100644 URLComponents + URLRequest/URLRequestUITests/URLRequestUITests.swift create mode 100644 Unit Tests.playground/Contents.swift create mode 100644 Unit Tests.playground/contents.xcplayground create mode 100644 init, init?, convenience, requiered.playground/Pages/Class.xcplaygroundpage/Contents.swift create mode 100644 init, init?, convenience, requiered.playground/Pages/Struct.xcplaygroundpage/Contents.swift create mode 100644 init, init?, convenience, requiered.playground/Pages/Untitled Page.xcplaygroundpage/Contents.swift create mode 100644 init, init?, convenience, requiered.playground/contents.xcplayground create mode 100644 private(set).playground/Contents.swift create mode 100644 private(set).playground/contents.xcplayground create mode 100644 try, throws & rethrows.playground/Pages/introduction.xcplaygroundpage/Contents.swift create mode 100644 try, throws & rethrows.playground/Pages/rethrows.xcplaygroundpage/Contents.swift create mode 100644 try, throws & rethrows.playground/Pages/try try? try!.xcplaygroundpage/Contents.swift create mode 100644 try, throws & rethrows.playground/contents.xcplayground diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..330d167 --- /dev/null +++ b/.gitignore @@ -0,0 +1,90 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +# .swiftpm + +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# Accio dependency management +Dependencies/ +.accio/ + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ diff --git a/@(non)escaping closures/@(non)escaping closures.xcodeproj/project.pbxproj b/@(non)escaping closures/@(non)escaping closures.xcodeproj/project.pbxproj new file mode 100644 index 0000000..5079cc2 --- /dev/null +++ b/@(non)escaping closures/@(non)escaping closures.xcodeproj/project.pbxproj @@ -0,0 +1,601 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 8D0545B924DD22720035ADE1 /* __non_escaping_closuresApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D0545B824DD22720035ADE1 /* __non_escaping_closuresApp.swift */; }; + 8D0545BB24DD22720035ADE1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D0545BA24DD22720035ADE1 /* ContentView.swift */; }; + 8D0545BD24DD22730035ADE1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D0545BC24DD22730035ADE1 /* Assets.xcassets */; }; + 8D0545C024DD22730035ADE1 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D0545BF24DD22730035ADE1 /* Preview Assets.xcassets */; }; + 8D0545CB24DD22730035ADE1 /* __non_escaping_closuresTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D0545CA24DD22730035ADE1 /* __non_escaping_closuresTests.swift */; }; + 8D0545D624DD22730035ADE1 /* __non_escaping_closuresUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D0545D524DD22730035ADE1 /* __non_escaping_closuresUITests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 8D0545C724DD22730035ADE1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8D0545AD24DD22720035ADE1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D0545B424DD22720035ADE1; + remoteInfo = "@(non)escaping closures"; + }; + 8D0545D224DD22730035ADE1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8D0545AD24DD22720035ADE1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D0545B424DD22720035ADE1; + remoteInfo = "@(non)escaping closures"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 8D0545B524DD22720035ADE1 /* @(non)escaping closures.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "@(non)escaping closures.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D0545B824DD22720035ADE1 /* __non_escaping_closuresApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = __non_escaping_closuresApp.swift; sourceTree = ""; }; + 8D0545BA24DD22720035ADE1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 8D0545BC24DD22730035ADE1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 8D0545BF24DD22730035ADE1 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 8D0545C124DD22730035ADE1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8D0545C624DD22730035ADE1 /* @(non)escaping closuresTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "@(non)escaping closuresTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D0545CA24DD22730035ADE1 /* __non_escaping_closuresTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = __non_escaping_closuresTests.swift; sourceTree = ""; }; + 8D0545CC24DD22730035ADE1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8D0545D124DD22730035ADE1 /* @(non)escaping closuresUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "@(non)escaping closuresUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D0545D524DD22730035ADE1 /* __non_escaping_closuresUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = __non_escaping_closuresUITests.swift; sourceTree = ""; }; + 8D0545D724DD22730035ADE1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D0545B224DD22720035ADE1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D0545C324DD22730035ADE1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D0545CE24DD22730035ADE1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 8D0545AC24DD22720035ADE1 = { + isa = PBXGroup; + children = ( + 8D0545B724DD22720035ADE1 /* @(non)escaping closures */, + 8D0545C924DD22730035ADE1 /* @(non)escaping closuresTests */, + 8D0545D424DD22730035ADE1 /* @(non)escaping closuresUITests */, + 8D0545B624DD22720035ADE1 /* Products */, + ); + sourceTree = ""; + }; + 8D0545B624DD22720035ADE1 /* Products */ = { + isa = PBXGroup; + children = ( + 8D0545B524DD22720035ADE1 /* @(non)escaping closures.app */, + 8D0545C624DD22730035ADE1 /* @(non)escaping closuresTests.xctest */, + 8D0545D124DD22730035ADE1 /* @(non)escaping closuresUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 8D0545B724DD22720035ADE1 /* @(non)escaping closures */ = { + isa = PBXGroup; + children = ( + 8D0545B824DD22720035ADE1 /* __non_escaping_closuresApp.swift */, + 8D0545BA24DD22720035ADE1 /* ContentView.swift */, + 8D40C6BF24E85D9D00E18FAB /* New Group */, + 8D0545BC24DD22730035ADE1 /* Assets.xcassets */, + 8D0545C124DD22730035ADE1 /* Info.plist */, + 8D0545BE24DD22730035ADE1 /* Preview Content */, + ); + path = "@(non)escaping closures"; + sourceTree = ""; + }; + 8D0545BE24DD22730035ADE1 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 8D0545BF24DD22730035ADE1 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 8D0545C924DD22730035ADE1 /* @(non)escaping closuresTests */ = { + isa = PBXGroup; + children = ( + 8D0545CA24DD22730035ADE1 /* __non_escaping_closuresTests.swift */, + 8D0545CC24DD22730035ADE1 /* Info.plist */, + ); + path = "@(non)escaping closuresTests"; + sourceTree = ""; + }; + 8D0545D424DD22730035ADE1 /* @(non)escaping closuresUITests */ = { + isa = PBXGroup; + children = ( + 8D0545D524DD22730035ADE1 /* __non_escaping_closuresUITests.swift */, + 8D0545D724DD22730035ADE1 /* Info.plist */, + ); + path = "@(non)escaping closuresUITests"; + sourceTree = ""; + }; + 8D40C6BF24E85D9D00E18FAB /* New Group */ = { + isa = PBXGroup; + children = ( + 8D40C6C024E85DA100E18FAB /* New Group */, + ); + path = "New Group"; + sourceTree = ""; + }; + 8D40C6C024E85DA100E18FAB /* New Group */ = { + isa = PBXGroup; + children = ( + ); + path = "New Group"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D0545B424DD22720035ADE1 /* @(non)escaping closures */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D0545DA24DD22730035ADE1 /* Build configuration list for PBXNativeTarget "@(non)escaping closures" */; + buildPhases = ( + 8D0545B124DD22720035ADE1 /* Sources */, + 8D0545B224DD22720035ADE1 /* Frameworks */, + 8D0545B324DD22720035ADE1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "@(non)escaping closures"; + productName = "@(non)escaping closures"; + productReference = 8D0545B524DD22720035ADE1 /* @(non)escaping closures.app */; + productType = "com.apple.product-type.application"; + }; + 8D0545C524DD22730035ADE1 /* @(non)escaping closuresTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D0545DD24DD22730035ADE1 /* Build configuration list for PBXNativeTarget "@(non)escaping closuresTests" */; + buildPhases = ( + 8D0545C224DD22730035ADE1 /* Sources */, + 8D0545C324DD22730035ADE1 /* Frameworks */, + 8D0545C424DD22730035ADE1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 8D0545C824DD22730035ADE1 /* PBXTargetDependency */, + ); + name = "@(non)escaping closuresTests"; + productName = "@(non)escaping closuresTests"; + productReference = 8D0545C624DD22730035ADE1 /* @(non)escaping closuresTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 8D0545D024DD22730035ADE1 /* @(non)escaping closuresUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D0545E024DD22730035ADE1 /* Build configuration list for PBXNativeTarget "@(non)escaping closuresUITests" */; + buildPhases = ( + 8D0545CD24DD22730035ADE1 /* Sources */, + 8D0545CE24DD22730035ADE1 /* Frameworks */, + 8D0545CF24DD22730035ADE1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 8D0545D324DD22730035ADE1 /* PBXTargetDependency */, + ); + name = "@(non)escaping closuresUITests"; + productName = "@(non)escaping closuresUITests"; + productReference = 8D0545D124DD22730035ADE1 /* @(non)escaping closuresUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8D0545AD24DD22720035ADE1 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1200; + LastUpgradeCheck = 1200; + TargetAttributes = { + 8D0545B424DD22720035ADE1 = { + CreatedOnToolsVersion = 12.0; + }; + 8D0545C524DD22730035ADE1 = { + CreatedOnToolsVersion = 12.0; + TestTargetID = 8D0545B424DD22720035ADE1; + }; + 8D0545D024DD22730035ADE1 = { + CreatedOnToolsVersion = 12.0; + TestTargetID = 8D0545B424DD22720035ADE1; + }; + }; + }; + buildConfigurationList = 8D0545B024DD22720035ADE1 /* Build configuration list for PBXProject "@(non)escaping closures" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 8D0545AC24DD22720035ADE1; + productRefGroup = 8D0545B624DD22720035ADE1 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D0545B424DD22720035ADE1 /* @(non)escaping closures */, + 8D0545C524DD22730035ADE1 /* @(non)escaping closuresTests */, + 8D0545D024DD22730035ADE1 /* @(non)escaping closuresUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D0545B324DD22720035ADE1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D0545C024DD22730035ADE1 /* Preview Assets.xcassets in Resources */, + 8D0545BD24DD22730035ADE1 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D0545C424DD22730035ADE1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D0545CF24DD22730035ADE1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D0545B124DD22720035ADE1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D0545BB24DD22720035ADE1 /* ContentView.swift in Sources */, + 8D0545B924DD22720035ADE1 /* __non_escaping_closuresApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D0545C224DD22730035ADE1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D0545CB24DD22730035ADE1 /* __non_escaping_closuresTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D0545CD24DD22730035ADE1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D0545D624DD22730035ADE1 /* __non_escaping_closuresUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 8D0545C824DD22730035ADE1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D0545B424DD22720035ADE1 /* @(non)escaping closures */; + targetProxy = 8D0545C724DD22730035ADE1 /* PBXContainerItemProxy */; + }; + 8D0545D324DD22730035ADE1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D0545B424DD22720035ADE1 /* @(non)escaping closures */; + targetProxy = 8D0545D224DD22730035ADE1 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 8D0545D824DD22730035ADE1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 8D0545D924DD22730035ADE1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 8D0545DB24DD22730035ADE1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"@(non)escaping closures/Preview Content\""; + DEVELOPMENT_TEAM = S7HY56XG6J; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = "@(non)escaping closures/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.--non-escaping-closures"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 8D0545DC24DD22730035ADE1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"@(non)escaping closures/Preview Content\""; + DEVELOPMENT_TEAM = S7HY56XG6J; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = "@(non)escaping closures/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.--non-escaping-closures"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 8D0545DE24DD22730035ADE1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = S7HY56XG6J; + INFOPLIST_FILE = "@(non)escaping closuresTests/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.--non-escaping-closuresTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/@(non)escaping closures.app/@(non)escaping closures"; + }; + name = Debug; + }; + 8D0545DF24DD22730035ADE1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = S7HY56XG6J; + INFOPLIST_FILE = "@(non)escaping closuresTests/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.--non-escaping-closuresTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/@(non)escaping closures.app/@(non)escaping closures"; + }; + name = Release; + }; + 8D0545E124DD22730035ADE1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = S7HY56XG6J; + INFOPLIST_FILE = "@(non)escaping closuresUITests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.--non-escaping-closuresUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "@(non)escaping closures"; + }; + name = Debug; + }; + 8D0545E224DD22730035ADE1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = S7HY56XG6J; + INFOPLIST_FILE = "@(non)escaping closuresUITests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.--non-escaping-closuresUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "@(non)escaping closures"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8D0545B024DD22720035ADE1 /* Build configuration list for PBXProject "@(non)escaping closures" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D0545D824DD22730035ADE1 /* Debug */, + 8D0545D924DD22730035ADE1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D0545DA24DD22730035ADE1 /* Build configuration list for PBXNativeTarget "@(non)escaping closures" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D0545DB24DD22730035ADE1 /* Debug */, + 8D0545DC24DD22730035ADE1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D0545DD24DD22730035ADE1 /* Build configuration list for PBXNativeTarget "@(non)escaping closuresTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D0545DE24DD22730035ADE1 /* Debug */, + 8D0545DF24DD22730035ADE1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D0545E024DD22730035ADE1 /* Build configuration list for PBXNativeTarget "@(non)escaping closuresUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D0545E124DD22730035ADE1 /* Debug */, + 8D0545E224DD22730035ADE1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 8D0545AD24DD22720035ADE1 /* Project object */; +} diff --git a/@(non)escaping closures/@(non)escaping closures.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/@(non)escaping closures/@(non)escaping closures.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/@(non)escaping closures/@(non)escaping closures.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/@(non)escaping closures/@(non)escaping closures.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/@(non)escaping closures/@(non)escaping closures.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/@(non)escaping closures/@(non)escaping closures.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/@(non)escaping closures/@(non)escaping closures/Assets.xcassets/AccentColor.colorset/Contents.json b/@(non)escaping closures/@(non)escaping closures/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/@(non)escaping closures/@(non)escaping closures/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/@(non)escaping closures/@(non)escaping closures/Assets.xcassets/AppIcon.appiconset/Contents.json b/@(non)escaping closures/@(non)escaping closures/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/@(non)escaping closures/@(non)escaping closures/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/@(non)escaping closures/@(non)escaping closures/Assets.xcassets/Contents.json b/@(non)escaping closures/@(non)escaping closures/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/@(non)escaping closures/@(non)escaping closures/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/@(non)escaping closures/@(non)escaping closures/ContentView.swift b/@(non)escaping closures/@(non)escaping closures/ContentView.swift new file mode 100644 index 0000000..c91e0cd --- /dev/null +++ b/@(non)escaping closures/@(non)escaping closures/ContentView.swift @@ -0,0 +1,119 @@ +// +// ContentView.swift +// @(non)escaping closures +// +// Created by Lucas Abijmil on 07/08/2020. +// + +import SwiftUI + +// Il existe deux types de closures lorsqu'elles sont utilisées en tant que paramètres d'une fonction / closure +// • @nonescaping : par défaut depuis Swift 3 +// - La fonction exécute la closure puis fini sa propre exécution +// - De ce fait, la closure est synchrone avec la fonction, c'est à dire qu'on ne pas peut retourner dans la closure une fois que l'exécution de la fonction est finie +// Conclusion : la closure n'est pas retenue en mémoire + +// • @escaping : lorsque la closure sort du scope de la fonction ––> appellé au sein d'une autre fonction / closure +// - La fonction s'exécute, le return de la closure peut (très) potentiellement sortir de la portée / scope de la fonction +// ––> à 99% du temps: appel de la closure (paramètre) dans une autre closure +// - De ce fait la closure est asynchrone, on pourra y retourner plusieurs fois même après la fin d'exécution de la fonction +// - Utilisée pour faire des fetchs / Dispatch sur les différents thread etc (tout ce qui est asynchrone dans sa globalité) +// Conclusion : la closure est retenue en mémoire permettant de faire de l'asynchrone (aka la chose la plus utilisé en 2020) + +// @escaping closure, ARC & Retain Cycle : +// - Petit rappel : une closure est une référence type elle peut donc créer une Reference Cycle et possède son propre count pour l'ARC +// - Toutes références à self dans une @escaping closure doit être précédé d'un [weak / unowned self] + +// Mon tips: +// À chaque fonction mettre la closure sans le @escaping +// Si erreur suivante : "Escaping closure captures non-escaping parameter 'escaping'" ––> rajouter le @escaping pour la closure dans les paramètres +// Et le tour est joué :) + +struct Closure { + var name: String + + func getSome(with nonEscaping: (Int) -> Void) { + var sum = 0 + for number in 1...10 { sum += number } + nonEscaping(sum) // Non escaping car la closure est exécutée de manière synchrone + } + + func getSome2(with escaping: @escaping (Int) -> Void) { + var sum = 0 + for number in 1...10 { sum += number } + DispatchQueue.main.async { + escaping(sum) // escaping car la closure est exécutée de manière asynchrone (ici dans une closure de DispatchQueue) + } + } + + func getSome3(for sum: Int, with nonEscaping: (Int) -> Void) { + var numberReturned = 0 + // Les if ... else (synchrone) ne sont pas considérés comme des closures, ainsi pas besoin de mettre la closure de paramètre en tant que @escaping + if sum == 0 { + for number in 1...10 { numberReturned += number } + nonEscaping(numberReturned) + } else { + for number in 1...10 { numberReturned += number } + nonEscaping(numberReturned + ) + } + } + + func getSome4(for sum: Int, with nonEscaping: (Int) -> Void) { + // Comme pour les if ... else, les switch ne sont pas des closures (car synchrone) + // Ainsi pas besoin de mettre la closure de paramètre en tant que @escaping + switch sum { + case 1: + var numberReturned = 0 + for number in 1...10 { numberReturned += number } + nonEscaping(numberReturned) + default: + var numberReturned = 0 + for number in 1...10 { numberReturned += number } + nonEscaping(numberReturned) + } + } + + func getSome5(with escaping: @escaping (Int) -> Void) { + var sum = 0 + for number in 1...10 { sum += number } + // escaping car dans une closure + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + escaping(sum) + } + + } +} + +class GetClosure { + var name: String + var res: Int + var closure: Closure + + init(name: String, res: Int, closure: Closure) { + self.name = name + self.res = res + self.closure = closure + } + + func closure1WithRetainCycle() { + // Appel d'une @escaping closure il faut donc éviter le Retain Cycle + closure.getSome2 { [weak self] number in // pour cela utiliser le [weak / unowned self], ici j'ai fais le choix du weak + guard let self = self else { return } // Checker le self (bonne pratique) avec le weak car peut être optionnel (et pas le unowned) + self.res = number + } + } + + func closure1BisWithRetainCycle() { + closure.getSome2 { [unowned self] number in + self.res = res + } + } + + func closure2WithRetainCycle() { + // Appel d'une @nonescaping closure, pas de retain cycle ––> exécution normale + closure.getSome { number in + self.res = number + } + } +} diff --git a/@(non)escaping closures/@(non)escaping closures/Info.plist b/@(non)escaping closures/@(non)escaping closures/Info.plist new file mode 100644 index 0000000..efc211a --- /dev/null +++ b/@(non)escaping closures/@(non)escaping closures/Info.plist @@ -0,0 +1,50 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UIApplicationSupportsIndirectInputEvents + + UILaunchScreen + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/@(non)escaping closures/@(non)escaping closures/Preview Content/Preview Assets.xcassets/Contents.json b/@(non)escaping closures/@(non)escaping closures/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/@(non)escaping closures/@(non)escaping closures/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/@(non)escaping closures/@(non)escaping closures/__non_escaping_closuresApp.swift b/@(non)escaping closures/@(non)escaping closures/__non_escaping_closuresApp.swift new file mode 100644 index 0000000..d48e3af --- /dev/null +++ b/@(non)escaping closures/@(non)escaping closures/__non_escaping_closuresApp.swift @@ -0,0 +1,17 @@ +// +// __non_escaping_closuresApp.swift +// @(non)escaping closures +// +// Created by Lucas Abijmil on 07/08/2020. +// + +import SwiftUI + +@main +struct __non_escaping_closuresApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/@(non)escaping closures/@(non)escaping closuresTests/Info.plist b/@(non)escaping closures/@(non)escaping closuresTests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/@(non)escaping closures/@(non)escaping closuresTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/@(non)escaping closures/@(non)escaping closuresTests/__non_escaping_closuresTests.swift b/@(non)escaping closures/@(non)escaping closuresTests/__non_escaping_closuresTests.swift new file mode 100644 index 0000000..2d20796 --- /dev/null +++ b/@(non)escaping closures/@(non)escaping closuresTests/__non_escaping_closuresTests.swift @@ -0,0 +1,33 @@ +// +// __non_escaping_closuresTests.swift +// @(non)escaping closuresTests +// +// Created by Lucas Abijmil on 07/08/2020. +// + +import XCTest +@testable import __non_escaping_closures + +class __non_escaping_closuresTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/@(non)escaping closures/@(non)escaping closuresUITests/Info.plist b/@(non)escaping closures/@(non)escaping closuresUITests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/@(non)escaping closures/@(non)escaping closuresUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/@(non)escaping closures/@(non)escaping closuresUITests/__non_escaping_closuresUITests.swift b/@(non)escaping closures/@(non)escaping closuresUITests/__non_escaping_closuresUITests.swift new file mode 100644 index 0000000..718c954 --- /dev/null +++ b/@(non)escaping closures/@(non)escaping closuresUITests/__non_escaping_closuresUITests.swift @@ -0,0 +1,42 @@ +// +// __non_escaping_closuresUITests.swift +// @(non)escaping closuresUITests +// +// Created by Lucas Abijmil on 07/08/2020. +// + +import XCTest + +class __non_escaping_closuresUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/ARC & Memory Leaks.playground/Contents.swift b/ARC & Memory Leaks.playground/Contents.swift new file mode 100644 index 0000000..770082c --- /dev/null +++ b/ARC & Memory Leaks.playground/Contents.swift @@ -0,0 +1,175 @@ +import UIKit + +// Pour gérer la mémoire : le compilateur utilise l'Automatic Reference Counting, raccourci en ARC +// ARC fonctionne de la manière suivante : +// - chaque référence incrémente le compte de l'objet en mémoire +// - chaque déférencement décrémente le compte de l'objet en mémoire +// - lorsque le compte de l'objet tombe à 0 il est supprimé de la mémoire voici un petit exemple + +class Developer { + var name: String + + init(name: String) { self.name = name ; print("Developer initialisé") } + + deinit { print("Developer déinitialisé") } +} + +// afin de pouvoir plus facilement la déinitialisé l'objet je le mets en optionnel (= nil) +var developer: Developer? +// Je crée la première référence donc le compte de l'objet est +1 +developer = Developer(name: "Lucas") + +// créons d'autre variable afin d'incrémenter le compte +var developer1 = developer // +1 ––> 2 +var developer2 = developer // +1 ––> 3 +var developer3 = developer // +1 ––> 4 + +// déinitialisons maintenant les variables +developer = nil // -1 ––> 3 +developer2 = nil // -1 ––> 2 +developer1 = nil // -1 ––> 1 + +// Ainsi je peux toujours accéder à la propriété name de developer3 +developer3?.name +developer3 = nil // -1 ––> 0 ––> l'objet est supprimé de la mémoire, exécution du déinit + +print("–––––––––") + +// Après cet exemple assez basique, je dois vous parler du type de référence à un objet +// Dans l'exemple d'avant, les références sont de types STRONG +// Toute référence à un object est par défaut de type STRONG +// Sachez qu'il existe en totu 3 types de références les voici: +// - strong (default): incrément et décremente à l'initialisation et déinitialisation +// - weak: n'incrémente et ne décrémente pas +// - unwoned: n'incrémente et ne décrémente pas + +// En reprenant l'exemple d'avant en passant une variable en weak +var dev: Developer? = Developer(name: "Lucas bis") // +1 ––> 1 +var dev1 = dev // +1 ––> 2 +var dev2 = dev // +1 ––> 3 +weak var dev3 = dev // +0 ––> 3 + +dev = nil // -1 ––> 2 +dev1 = nil // -1 ––> 1 +dev2 = nil // -1 ––> 0 + +// Ainsi je n'ai même pas passer dev3 en nil que l'objet a été déinitialisé + + + + +// Le fonctionnement de l'ARC peut souvent poser des soucis, surtout lorsque deux classes se font mutuellement référence +// voici un exemple +print("––––––––––––") +print("–––––––––––––") + +class Job { + var person: Person? + + deinit { print("Job deoalocated") } +} + +class Person { + var job: Job? + + deinit { print("Person dealocated") } +} + +var joe: Person? = Person() // Person: +1 ––> 1 +var deve: Job? = Job() // Job: +1 ––> 1 + + + +joe?.job = deve // Job: +1 ––> 2 +deve?.person = joe // Person: +1 ––> 2 + +// Maintenant déinitialisons les objets & on devrait voir apparaître les déinit +joe = nil // Person: -1 ––> 1 +deve = nil // Job: -1 ––> 1 + +// Les deux objets sont toujours en mémoire car le count est de 1 (et du coup pas de lancement de déinit) +// Pourtant, les variables sont nils ce qui fait que les propriétés des objets ne sont plus accesible +joe?.job +deve?.person + +// Ce phénomène est ce que l'on appelle le retain cycle +// Il garde les objets en mémoire alors qu'on ne peut plus y accéder par une quelconque manière +// C'est un cercle sans fin, vient du fait que les deux classes se font des STRONGS références l'une à l'autre + + +print("––––––––––––––––") +// MARK: - Comment résoudre le problème du retain cycle ? +// Rappel: le retain cycle vient du fait que chaque objet se font mutuellement référence de type STRONG + +// Il suffit de passer une des références en autre que strong afin de casser ce cercle sans fin +// C'est à dire soit: weak or unowned j'y reviens dans un peu plus loin + +// Le processus se fait en plusieurs étapes: +// - 1) Identifier qui est la class “parent" AKA class "indépendante" +// - 2) Dans la class enfant AKA class "dépendante" passer la propriété (qui pointe vers la class parent) en weak / unowned +// Ainsi on casse ce fameux cercle vicieux en passant la référence en weak ou unowned + +// Voici les modifications (la class Person est la class "Parent"). +class PersonBis { + var job: JobBis? + + deinit { print("PersonBis dealocated") } +} + +class JobBis { + weak var person: PersonBis? + + deinit { print("JobBis dealocated") } +} + +var joeBis: PersonBis? = PersonBis() // PersonBis: +1 ––> 1 +var deveBis: JobBis? = JobBis() // JobBis: +1 ––> 1 + +joeBis?.job = deveBis // JobBis: +1 ––> 2 +deveBis?.person = joeBis // PersonBis: +0 ––> 1 car la référence est déclaré en weak + +joeBis = nil // PersonBis: -1 ––> 0, l'objet est supprimé de la mémoire (lancement du déinit) + // JobBis: -1 ––> 1 +deveBis = nil // JobBis: -1 ––> 0, l'objet est supprimé de la mémoire (lancement du déinit) + +// Bien évidement on peut pour se faciliter la décision de "qui est la class enfant et qui est class parent" en mettant les deux références de type weak. Cependant on perd en logique sémantique. + +// MARK: - Que choisir entre WEAK & UNOWNED pour une référence vers un autre objet : +// - (strong, par défaut: la class "enfant" existe tant que la class "parent" existe) + +// - weak: l'objet "enfant" peut exister ou non , ainsi toutes les références de type weak sont donc optionnel (?) +// L'objet "enfant" n'existera plus si la class "parent" est supprimé de la mémoire (nil) + +// - unowned: l'objet enfant existe tout le temps (et n'est donc pas optionnel) du moment que la class parent est alloué +// L'objet "enfant" est supprimé lorsque l'objet parent est supprimé (étroite dépedance les objets enfant / parent) + +// Conclusion : +// - Si la valeur de l'objet "enfant" peut être nil à un moment ––> weak +// - si la valeur de l'objet "enfant" a toujours une value ––> unowned + +// Voici un exemple avec une unowned référence + +print("–––––––––––––––") +class PersonTer { + var job: JobTer? + + deinit { print("PersonTer dealocated") } +} + +class JobTer { + unowned var person = PersonTer() + + deinit { print("JobTer dealocated") } +} + +var joeTer: PersonTer? = PersonTer() +var deveTer: JobTer? = JobTer() + +joeTer?.job = deveTer +deveTer?.person = joeTer! + +joeTer = nil +deveTer = nil + + + diff --git a/ARC & Memory Leaks.playground/contents.xcplayground b/ARC & Memory Leaks.playground/contents.xcplayground new file mode 100644 index 0000000..5da2641 --- /dev/null +++ b/ARC & Memory Leaks.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/ARC, Retain Cycle & Memory Leak.playground/Contents.swift b/ARC, Retain Cycle & Memory Leak.playground/Contents.swift new file mode 100644 index 0000000..8473ce0 --- /dev/null +++ b/ARC, Retain Cycle & Memory Leak.playground/Contents.swift @@ -0,0 +1,311 @@ +import UIKit + +// ARC --> Automatique Reference Count +// Décide quand seront référencés et déférencés les valeurs stockées en mémoire +// Dès qu'on créer un objet (faire référence) l'ARC (qui fait partie du compilateur) alloue une partie de la mémoire pour cette donnée +// Dès qu'on détruit un objet (faire déréférence) l'ARC désalloue la mémoire afin d'être utilisé par d'autres *choses* +// Attention: si un objet à été désalloué par l'ARC, on ne pourra plus acéder à son instance sinon l'app pourrait crash +// Cependant l'ARC traque toutes les propriétés strong, constante et variable qui font référence à une instance, et cet objet ne sera pas déférencé (supprimé) tant qu'il aura au moins encore une strong référence + +// MARK: - Exemple +class Personn { + var name: String + + init(name: String) { + self.name = name + print("\(name) is being initialized") + } + + deinit { + print("\(name) de l'exemple de l'ARC dealocaled, kill by the ARC. 😢") + } +} + +// MARK: - Explication +// Trois type pour l'ARC: +// - strong: par défaut --> +1, protège l'objet d'être désalloué par l'ARC, par défaut +// - weak: le type doit être optionnel --> +0, compteur non incrémenté peut être donc possiblement nil (et ne fais pas -1 lorsqu'il est déférencé) + // --> À noter qu'une weak var ne pourra jamais être une constant (let) sinon l'erreur suivante apparaîtra: + // "weak" must be a mutable variable, because it change at runtime +// - unowned: pareil que weak sauf que la variable ne doit pas être optionnels --> + 0 (pas très utilisé à ma connaissance) + // --> CÀD que la variable existera tant que la class parent sera alloué +// MARK: La différence entre weak & unowned est un assez subtile (surtout pour les débutants) entrenez-vous c'est la meilleure des choses ;-) + // weak: la propriété peut être nil ou non --> explique le fait qu'elle doit obligatoirement être une variable et pas une constante. La propriété peut exister ou non + // unowned: la propriété doit obligatoirement avoir une valeur et cette propriété existera tant que la propriété parent existe + // elle est relié indirectement à l'état de la propriété parent + // Si la propriété parent est supprimé de la mémoire alors la propriété unowned sera également supprimé de la mémoire + + + +// Pour montrer la puissance de l'arc je vais faire uniquement des variables optionnels (car on peut les déinitialiser facilement en faisant var = nil) +// Ça va également vous démontrer la puissance des weak est sont utilisé très régulièrement avec les types optionnels (car il peuvent déinitialiser) +// Je ne vais pas utiliser de unowned reference dans cet exemple +// Lorsqu'une référence strong devient nil (déréférence) --> -1 +// Pour les références weak (et unowned) --> -0 +var ref1: Personn? +var ref2: Personn? +var ref3: Personn? +weak var ref4: Personn? // Mark: weak var ici + +// On initialise ref1 donc +1 pour Person (car de type strong) +ref1 = Personn(name: "Lucas") // count = 1 (une strong référence) +// Deux nouvel référence de type strong à la même réfenrence --> +2 donc au total +3 +ref2 = ref1 // count = 2 (deux strong référence) +ref3 = ref1 // count = 3 (trois strong référence) +ref4 = ref1 // count = toujours 3 (trois références strong + une weak (0)) +// On supprime une référence strong --> -1 donc +2 au count de l'objet, il est donc toujours maintenu dans la mémoire par l'arc +ref1 = nil // count = 2 (deux strong référence) --> on notera que même en mettant nil l'objet premier, on peut toujours accéder à cet objet car comme il y a encore des réf à cet objet il n'est donc pas supprimé de la mémoire. + +// la preuve ici +print(ref2?.name) +print(ref3?.name) +print(ref4?.name) + +ref2 = nil // count = 1 (une strong référence) +ref4 = nil // count = toujours 1 (car weak var et il reste une strong référence) + +// de ce fait je peux toujours accéder à ref3, la preuve: +print(ref3?.name) + +// déférencement du dernier objet strong +ref3 = nil // count = 0, l'objet est désalloué par l'ARC et le deinit se lance --> l'objet est supprimé de la mémoire + + + +// MARK: - Retain cycle & Memory Leak +// Le retain cycle arrive lorsque deux objets se font des références strong entre eux. Il faut visualiser ça un peu comme un cercle vicieux. +// Si les deux références de l'un à l'autre est de type strong alors il risque d'y avoir un problème, pour l'ARC + + +class Person { + var name: String + var macbook: MacBookPro? + + init(name: String, macbook: MacBookPro?) { + self.name = name + self.macbook = macbook + } + + deinit { + print("\(name) weak / unowned exemple was killed by the ARC") + } +} + +class MacBookPro { + var name: String + var person: Person? + + init(name: String, person: Person?) { + self.name = name + self.person = person + } + + deinit { + print("\(name) de weak / unowned exemple was killed by the ARC") + } +} + +// Déclaration de nos variables optionnels ici afin de pouvoir les déférencé plus facilement en faisant var = nil +var lucas: Person? +var MBP: MacBookPro? + +// Initialisation de nos variables +lucas = Person(name: "Lucas", macbook: nil) // on peut pas initialiser nos macbook étant donné qu'il ne l'est pas également +MBP = MacBookPro(name: "MacBook Pro 13\" ", person: nil) // on n'itialise pas non plus la valeur de person + +// MARK: Au niveau de l'arc on est au schéma suivant: +// lucas fait une référence de type strong a Person +// MBP fait également une référence de type strong à MacBookPro +// MARK: Ici on a donc bien les deinit qui se lancent étant donné qu'on a qu'une seule référence pour chaque objet. + +#warning("À activer désactiver") +//lucas = nil +//MBP = nil + +// Maintenant initialisons les variables qui se font mutuellement référence +lucas?.macbook = MBP +MBP?.person = lucas + +// MARK: Au niveau de l'ARC on est à la chose suivante: +// Lucas fait une strong référence à Person // Person = 1 +// Lucas fait une strong référence à MacBookPro via MBP // MacBookPro = 1 +// MBP fait une strong référence à Person via Lucas // Person = 2 +// MBP fait une strong référence à MacBookPro // MacBookPro = 2 + + // MARK: Maintenant dénitialistion lucas par exemple. Logiquement on devrait avoir donc le deinit qui s'exécute + +lucas = nil // lucas = nil, ainsi count Person = 1 + + +// Étant donné qu'il y a encore une référence à Person via MBP, l'objet n'est pas supprimé de la mémoire (puisque = 1) +// La preuve en printant le name de la person de mbp +print(MBP?.person?.name ?? "détruit") +// la preuve que lucas = nil +print(lucas?.name ?? "Détruit") +print(lucas?.macbook?.name ?? "Détruit") +// D'où le fait que le deinit ne s'est pas exécuté + +// MARK: - A noter +MBP = nil // ne lance pas non plus le deinit de mbp + +// Aucun deinit se lance car il reste toujours une référence strong à chaque objet donc aucun objet n'est supprimé de la mémoire. Cela est du au fait qu'on a encore une référence à chauqe objet: +// lucas.macbook // count de macbook = 1 +// MBP.person // count de person = 1 +// On ne peut plus accéder à aucune propriété , pourtant plus aucunes var n'est plus initialisé --> c'est ce qu'on appel MEMORY LEAK / Retain Cycle + + +// MARK: Comment résoudre ce problème ? +// La solution la plus simple et logique est de mettre une des deux propriété à l'autre objet en weak ou unowned, bien qu'on puisse déclarer les deux propriété en tant que weak / unowned +// Ainsi il n'y aura pas de strong référence entre eux et ainsi, on casse le cercle vicieux de début. +// MARK: Mais quel objet choisir dans ce cas ? Bonne question +// La réponse est ça dépend car c'est pas si simple, ça vient avec l'expérience. Au début penser au fait suivant "Quel objet est le moins importants entre les deux ? Qui est la class parent et qui est la class enfant ?" (sémantiquement) +// Et cette propriété sera celle weak / unowned +// Dans ce cas je décide que MacBookPro est un peu moins important que Person dans ce cas c'est la weak variable +// => Car sémantiquement un MacBookPro appartient obligatoirement à une Person +// ainsi j'aurai la class suivant + +#warning("Exemple avec weak var") +class MacBookPro2 { + var name: String + weak var person: Person2? // type optionnel car obligatoire avec weak + + init(name: String, person: Person2?) { + self.name = name + self.person = person + } + deinit { + print("\(name) weak exemple was killed by the ARC") + } +} + +class Person2 { + var name: String + var macbook: MacBookPro2? + + init(name: String, macbook: MacBookPro2?) { + self.name = name + self.macbook = macbook + } + + deinit { + print("\(name) of weak exemple was killed by the ARC") + } +} + +// MARK: De ce fait en faisant la même chose qu'avant +// Déclaration de nos variables optionnels ici +var lucas2: Person2? +var MBP2: MacBookPro2? + +// Initialisation de nos variables +lucas2 = Person2(name: "Lucas", macbook: nil) // on peut pas initialiser nos macbook étant donné qu'il ne l'est pas également +MBP2 = MacBookPro2(name: "MacBook Pro 13\" ", person: nil) // on n'itialise pas non plus la valeur de person + + +lucas2?.macbook = MBP2 +MBP2?.person = lucas2 +// MARK: Au niveau de l'arc on est au schéma suivant: +// lucas2 fait une référence de type strong a Person // person = 1 +// Lucas2 fait une strong référence à MacBookPro2 via MBP2 // MacBookPro2 = 1 +// MBP2 fait une weak référence à Person via Lucas2 // Person = toujours 1 +// MBP2 fait une strong référence à MacBookPro2 // MacBookPro2 = 2 + +// MARK: Ici on a donc bien les deinit qui se lancent étant donné qu'on a qu'une seule référence pour chaque objet. +lucas2 = nil // Ici on a bien le deinit qui s'exécute étant donné que le count de lucas2 est égale à 0 + +// Au niveau de l'arc ça donne le schéma suivant +// count de lucas2 = 0 car le déinit s'est exécuter --> l'objet a été supprimé de la mémoire +// count de MacbooPro2 = 1 car lucas2.macbook était une strong référence et donc sont compeut s'est décrémenté --> 2 - 1 = 1 +MBP2 = nil + + +// Si vous savez que votre propriété "enfant" ne sera jamais nil alors pour vous pouvez la mette de type unowned + +print("-----------------------------") + +final class Person79 { + var name: String + var computer: Macbook79? + + init(name: String) { + self.name = name + } + + deinit { + print("Person class déinitialisé") + } +} + +final class Macbook79 { + var name: String + unowned var owner: Person79 + + init(name: String, owner: Person79) { + self.name = name + self.owner = owner + } + + deinit { + print("Macbook79 class déinitialisé") + } +} + + +var lucas79: Person79? + +lucas79 = Person79(name: "Lucas 79") +lucas79?.computer = Macbook79(name: "mbp 79", owner: lucas79!) + +lucas79?.computer?.name = "jd" + +lucas79?.name +lucas79?.computer?.name + +lucas79 = nil + + + +final class Club { + var name: String + var joueur: Joueur? + + init(name: String, joueur: Joueur?) { + self.name = name + self.joueur = joueur + } + + deinit { + print("Club déinitialisé") + } +} + +final class Joueur { + var name: String + unowned var club: Club + + init(name: String, club: Club) { + self.name = name + self.club = club + } + + deinit { + print("Class \(name) joueur déinitialisé") + } +} + +var psg: Club? +psg = Club(name: "PSG", joueur: nil) +psg?.joueur = Joueur(name: "Mbappe", club: psg!) // Créer un objet d'@: 0x00003 + +//var mbappe: Joueur? +//mbappe = Joueur(name: "MBAPPE", club: psg!) // créer un objet d'@: 0x003420 +// +// +//mbappe?.club.joueur?.name +//mbappe?.name +// +//mbappe = nil + +psg = nil + +//mbappe = nil diff --git a/ARC, Retain Cycle & Memory Leak.playground/contents.xcplayground b/ARC, Retain Cycle & Memory Leak.playground/contents.xcplayground new file mode 100644 index 0000000..10d79fc --- /dev/null +++ b/ARC, Retain Cycle & Memory Leak.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/AccessControlLevel.playground/Contents.swift b/AccessControlLevel.playground/Contents.swift new file mode 100644 index 0000000..ee44436 --- /dev/null +++ b/AccessControlLevel.playground/Contents.swift @@ -0,0 +1,121 @@ +/*: + # Access Control Level : + * Permet de restreindre (ou non) l'accès à certaines parties du code, d'un fichier à l'autre, d'un module à l'autre. + * On peut attribuer des niveaux d'accès pour différents types (`class`, `struct`, `enum`) ainsi qu'aux propriétés, initializers et méthodes. + * Le niveau d'accès s'applique également aux extensions + * Il existe 5 types d'access control (du plus fermé au plus ouvert) : + * `private` + * `fileprivate` + * `internal` + * `public` + * `open` + */ + +/*: + ## Private : un des plus utilisé + * La propriété est accessible uniquement dans la déclaration du type + * Une extension dans le même fichier que la déclaration du type aura tout de même accès à cette propriété + * Conclusion : *Je veux que cette donnée ne soit accessible par personne* + */ +final class Person { + + var name: String + private var age: Int + + init(name: String, age: Int) { + self.name = name + self.age = age + } + + private func printName() { + print(name) + } +} + +let lucas = Person(name: "Lucas", age: 22) +// lucas.age // KO +// lucas.printName() // KO +/*: + ## Fileprivate : pas très utilisé + * Très similaire à `private`, mais la propriété reste accessible dans le même fichier (un peu moins fermé que `private`) + * Conclusion : *Je veux que cette donnée soit accessible uniquement dans le fichier de sa déclaration* + */ + +final class Personn { + var name: String + fileprivate var age: Int + + init(name: String, age: Int) { + self.name = name + self.age = age + } + + fileprivate func printName() { + print(name) + } +} + +let lucas2 = Personn(name: "Lucas", age: 22) +lucas2.age // Ok car dans le même fichier +lucas2.printName() // Ok car dans le même fichier +/*: + ## internal : par défaut (pas besoin de le déclarer) + * La propriété est accessible dans tout le module / projet (SwiftUI, UIKit par exemple) + * Conclusion : *Cette donnée est accessible à travers toute ma code base, mais pas pour les autres modules* + */ +final class Personnn { + internal var name: String + internal var age: Int + + init(name: String, age: Int) { + self.name = name + self.age = age + } + + func printName() { // internal par défaut + print(name) + } +} + +let lucas3 = Personnn(name: "Lucas", age: 22) +lucas3.name +lucas3.age +lucas3.printName() +/*: + ## public : accessible par tout le monde (principalement dans les frameworks) + * La propriété est accessible partout, d'un module à l'autre + * On peut subclasser une `class public` que dans le même module, impossible à l'extérieur de celui-ci + * Conclusion : *Cette donnée est accessible à n'importe qui, mais ne peut pas être subclassé à l'extérieur du module* + */ +public class Personnnn { + public var name: String + public var age: Int + + init(name: String, age: Int) { + self.name = name + self.age = age + } + + public func printName() { // internal par défaut + print(name) + } +} +/*: + ## open : plus ouvert que public (principalement utilisé dans les frameworks) + * La propriété est accessible partout, d'un module à l'autre et peut être subclassé à l'extérieur de son module + * Conclusion : *Cette donnée est accessible à n'importe qui, et peut être subclassé par n'importe qui* + */ + +open class Personnnnn { + open var name: String + open var age: Int + + init(name: String, age: Int) { + self.name = name + self.age = age + } + + open func printName() { // internal par défaut + print(name) + } +} diff --git a/AccessControlLevel.playground/contents.xcplayground b/AccessControlLevel.playground/contents.xcplayground new file mode 100644 index 0000000..515976f --- /dev/null +++ b/AccessControlLevel.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Array functions, extension & tricks.playground/Pages/Enumerated.xcplaygroundpage/Contents.swift b/Array functions, extension & tricks.playground/Pages/Enumerated.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..c57d591 --- /dev/null +++ b/Array functions, extension & tricks.playground/Pages/Enumerated.xcplaygroundpage/Contents.swift @@ -0,0 +1,40 @@ + import Foundation +//: ### **enumerated** : **accès à l'index et la value d'un array sans avoir à gérer l'index et subscript à l'array** +let people = ["Pierre", "Paul", "Jacques", "Michel", "Patrick"] +/*: +**Façon naïves d'accéder à l'index et la value d'un array** : +* Pas très Swifty : car on doit gérer les index nous mêmes et subscript à l'array avec l'index donné (assez bas level) +* Possibilité une fatal error : si on subscript à l'array avec un index hors de sa range +*/ + +for currentIndex in people.indices { + let currentValue = people[currentIndex] + print("Current value : \(currentValue), current index : \(currentIndex)") +} +print("–––––") + +/*: + **Façon plus Swifty, utiliser la fonction `enumerated()`** : + * Fonction qui renvoie un tuple, dans l'ordre : index, value + * Avantage : pas de gestion des index (sûr que le code ne va jamais crasher) +*/ + +for (index, value) in people.enumerated() { + print("Current value : \(value), current index : \(index)") +} +print("–––––") + +/*: +**Exemple pour éviter de faire une boucle `for` : on "déplace" le tuple dans les paramètres de la closure des fonctions `map` / `forEach`** + */ + +people.enumerated().map { (index, value) in + print("Current value : \(value), current index : \(index)") +} + +print("–––––") + +people.enumerated().forEach { (index, value) in + print("Current value : \(value), current index : \(index)") +} +//: [Home](Introduction)           [Next : `zip()` >](@next) diff --git a/Array functions, extension & tricks.playground/Pages/Introduction.xcplaygroundpage/Contents.swift b/Array functions, extension & tricks.playground/Pages/Introduction.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..5b6480b --- /dev/null +++ b/Array functions, extension & tricks.playground/Pages/Introduction.xcplaygroundpage/Contents.swift @@ -0,0 +1,8 @@ +/*: + # Fonctions sur les Arrays : + + * [`enumerated()`](Enumerated) : accès à l'index et la value d'un array sans avoir à gérer l'index et subscript à l'array + * [`zip(array1, array2)`](Zip) : boucler simultanément sur deux collections d'indice différents + * [`Safe Subscript`](SafeSubscript) : permet de renvoyer `nil` au lieu d'une `fatalError` si on subscript à un indice non contenu dans une `Collection` + + */ diff --git a/Array functions, extension & tricks.playground/Pages/SafeSubscript.xcplaygroundpage/Contents.swift b/Array functions, extension & tricks.playground/Pages/SafeSubscript.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..1174cdb --- /dev/null +++ b/Array functions, extension & tricks.playground/Pages/SafeSubscript.xcplaygroundpage/Contents.swift @@ -0,0 +1,26 @@ +import Foundation +//: ### **Safe Subscript** : **permet de renvoyer nil au lieu d'une `fatalError` si on subscript à un indice non contenu dans une `Collection`** +let array = [1, 2, 3, 4, 5] +/*: + Accès aux values d'une `Collection` + * L'index doit être contenu dans la collection, sinon génération d'une `fatalError` + */ +//: Accès à un index contenu dans la range de la collection +array[2] +//: Accès à un index non contenu dans la range de la collection, génération d'une fatalError +// array[22] +/*: + Création d'un subscript custom (extension sur le type `Collection`) : + * Renvoie la value optionnelle, si l'index est contenu dans la collection + * `nil` si l'index n'est pas contenu dans la collection + * *Use case* : lorsqu'on est pas sûr que l'index est contenu dans une `Collection` donnée + */ +extension Collection { + subscript(safe index: Index) -> Element? { + indices.contains(index) ? self[index] : nil + } +} + +array[safe: 2] // Optional(3) +array[safe: 22] +//: [< Previous : `zip()`](@previous)           [Home](Introduction)           [Next >](@next) diff --git a/Array functions, extension & tricks.playground/Pages/Zip.xcplaygroundpage/Contents.swift b/Array functions, extension & tricks.playground/Pages/Zip.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..683f123 --- /dev/null +++ b/Array functions, extension & tricks.playground/Pages/Zip.xcplaygroundpage/Contents.swift @@ -0,0 +1,43 @@ +import Foundation + +//: ### **zip**(array1, array2) : **boucler simultanément sur deux collections d'indice différents** + +let ints = [1, 2, 3, 4, 5] +let strings = ["Hello", "world", "welcome"] + +/*: +**Façon naïves de boucler sur deux collections** : +* Connaître l'index le plus petit via la fonction min +* Boucler jusqu'à cet index pour ne pas subscript sur le plus petit tableau (pour éviter un crash / fatalError) + */ + +for indice in 0..](@next) diff --git a/Array functions, extension & tricks.playground/contents.xcplayground b/Array functions, extension & tricks.playground/contents.xcplayground new file mode 100644 index 0000000..d5e12d9 --- /dev/null +++ b/Array functions, extension & tricks.playground/contents.xcplayground @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/Async & Await/Await.xcodeproj/project.pbxproj b/Async & Await/Await.xcodeproj/project.pbxproj new file mode 100644 index 0000000..089d1aa --- /dev/null +++ b/Async & Await/Await.xcodeproj/project.pbxproj @@ -0,0 +1,339 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 8D9CB2A525F61ACF00D23BD3 /* AwaitApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D9CB2A425F61ACF00D23BD3 /* AwaitApp.swift */; }; + 8D9CB2A725F61ACF00D23BD3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D9CB2A625F61ACF00D23BD3 /* ContentView.swift */; }; + 8D9CB2A925F61AD100D23BD3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D9CB2A825F61AD100D23BD3 /* Assets.xcassets */; }; + 8D9CB2AC25F61AD100D23BD3 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D9CB2AB25F61AD100D23BD3 /* Preview Assets.xcassets */; }; + 8D9CB2B525F61ADC00D23BD3 /* Async & Await.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D9CB2B425F61ADC00D23BD3 /* Async & Await.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 8D9CB2A125F61ACF00D23BD3 /* Await.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Await.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D9CB2A425F61ACF00D23BD3 /* AwaitApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AwaitApp.swift; sourceTree = ""; }; + 8D9CB2A625F61ACF00D23BD3 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 8D9CB2A825F61AD100D23BD3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 8D9CB2AB25F61AD100D23BD3 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 8D9CB2AD25F61AD100D23BD3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8D9CB2B425F61ADC00D23BD3 /* Async & Await.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Async & Await.swift"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D9CB29E25F61ACF00D23BD3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 8D9CB29825F61ACF00D23BD3 = { + isa = PBXGroup; + children = ( + 8D9CB2A325F61ACF00D23BD3 /* Await */, + 8D9CB2A225F61ACF00D23BD3 /* Products */, + ); + sourceTree = ""; + }; + 8D9CB2A225F61ACF00D23BD3 /* Products */ = { + isa = PBXGroup; + children = ( + 8D9CB2A125F61ACF00D23BD3 /* Await.app */, + ); + name = Products; + sourceTree = ""; + }; + 8D9CB2A325F61ACF00D23BD3 /* Await */ = { + isa = PBXGroup; + children = ( + 8D9CB2A425F61ACF00D23BD3 /* AwaitApp.swift */, + 8D9CB2A625F61ACF00D23BD3 /* ContentView.swift */, + 8D9CB2B425F61ADC00D23BD3 /* Async & Await.swift */, + 8D9CB2A825F61AD100D23BD3 /* Assets.xcassets */, + 8D9CB2AD25F61AD100D23BD3 /* Info.plist */, + 8D9CB2AA25F61AD100D23BD3 /* Preview Content */, + ); + path = Await; + sourceTree = ""; + }; + 8D9CB2AA25F61AD100D23BD3 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 8D9CB2AB25F61AD100D23BD3 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D9CB2A025F61ACF00D23BD3 /* Await */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D9CB2B025F61AD100D23BD3 /* Build configuration list for PBXNativeTarget "Await" */; + buildPhases = ( + 8D9CB29D25F61ACF00D23BD3 /* Sources */, + 8D9CB29E25F61ACF00D23BD3 /* Frameworks */, + 8D9CB29F25F61ACF00D23BD3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Await; + productName = Await; + productReference = 8D9CB2A125F61ACF00D23BD3 /* Await.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8D9CB29925F61ACF00D23BD3 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1240; + LastUpgradeCheck = 1240; + TargetAttributes = { + 8D9CB2A025F61ACF00D23BD3 = { + CreatedOnToolsVersion = 12.4; + }; + }; + }; + buildConfigurationList = 8D9CB29C25F61ACF00D23BD3 /* Build configuration list for PBXProject "Await" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 8D9CB29825F61ACF00D23BD3; + productRefGroup = 8D9CB2A225F61ACF00D23BD3 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D9CB2A025F61ACF00D23BD3 /* Await */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D9CB29F25F61ACF00D23BD3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D9CB2AC25F61AD100D23BD3 /* Preview Assets.xcassets in Resources */, + 8D9CB2A925F61AD100D23BD3 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D9CB29D25F61ACF00D23BD3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D9CB2B525F61ADC00D23BD3 /* Async & Await.swift in Sources */, + 8D9CB2A725F61ACF00D23BD3 /* ContentView.swift in Sources */, + 8D9CB2A525F61ACF00D23BD3 /* AwaitApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 8D9CB2AE25F61AD100D23BD3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 8D9CB2AF25F61AD100D23BD3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 8D9CB2B125F61AD100D23BD3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"Await/Preview Content\""; + DEVELOPMENT_TEAM = RY2PUYVX4Z; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = Await/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_SWIFT_FLAGS = "-Xfrontend -enable-experimental-concurrency"; + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.Await"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 8D9CB2B225F61AD100D23BD3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"Await/Preview Content\""; + DEVELOPMENT_TEAM = RY2PUYVX4Z; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = Await/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_SWIFT_FLAGS = "-Xfrontend -enable-experimental-concurrency"; + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.Await"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8D9CB29C25F61ACF00D23BD3 /* Build configuration list for PBXProject "Await" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D9CB2AE25F61AD100D23BD3 /* Debug */, + 8D9CB2AF25F61AD100D23BD3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D9CB2B025F61AD100D23BD3 /* Build configuration list for PBXNativeTarget "Await" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D9CB2B125F61AD100D23BD3 /* Debug */, + 8D9CB2B225F61AD100D23BD3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 8D9CB29925F61ACF00D23BD3 /* Project object */; +} diff --git a/Async & Await/Await.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Async & Await/Await.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Async & Await/Await.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Async & Await/Await.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Async & Await/Await.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Async & Await/Await.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Async & Await/Await/Assets.xcassets/AccentColor.colorset/Contents.json b/Async & Await/Await/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Async & Await/Await/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Async & Await/Await/Assets.xcassets/AppIcon.appiconset/Contents.json b/Async & Await/Await/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/Async & Await/Await/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Async & Await/Await/Assets.xcassets/Contents.json b/Async & Await/Await/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Async & Await/Await/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Async & Await/Await/Async & Await.swift b/Async & Await/Await/Async & Await.swift new file mode 100644 index 0000000..0238b95 --- /dev/null +++ b/Async & Await/Await/Async & Await.swift @@ -0,0 +1,145 @@ +// +// Async & Await.swift +// Await +// +// Created by Lucas Abijmil on 08/03/2021. +// + +import Foundation +import _Concurrency + + // MARK: - Exemple de fonction asynchrones classiques en iOS (mocks) +func getUserId(_ completion: @escaping (Int) -> Void) { + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + completion(42) + } +} + +func getUserFirtName(userID: Int, _ completion: @escaping (String) -> Void) { + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + completion("Lucas") + } +} + +func getUserLastName(userID: Int, _ completion: @escaping (String) -> Void) { + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + completion("Abijmil") + } +} + +func greetUser() { + getUserId { id in + getUserFirtName(userID: id) { firstName in + getUserLastName(userID: id) { lastName in + print(firstName, lastName) + } + } + } +} + +// MARK: Le problème des completions et qu'à chaque fois qu'on va fouloir faire des appels on est obligé de nester à cause de ces completions ––> devient vite le bordel (pyramid of Doom) + +// MARK: Nouveau : utilisation des fonctions async / await +// - async mot clé à ajouter aux fonctions asynchrones, de la même manière que throws +// - Ces fonctions async sont autorisées à bloquer leur exécution et donc bloqué le thread qui les appelle. En appellant une fonction async on garantie qu'on ne bloque pas notre application par erreur + +// MARK: "Traduction" des fonctions en version "async" +// - Utilisation d'une fonction de bas niveau, en attendant que les DispatchQueues aient leurs versions async +// - withUnsafeContinuation exécute une closure avec un objet "continuation" comme paramètre permettant d'appeler la fonction resume(returning:). En appelant with withUnsafeContinuation le thread de la fonction est bloqué et sera débloqué une fois l'appel à resume(returning:) +// - await : indique que la fonction bloque le thread juqu'à ce que la fonction appellé par wait retourne une value, similair à try mais pour une async fonction + +func getUserId() async -> Int { + return await withUnsafeContinuation { continuation in + getUserId { userID in + continuation.resume(returning: userID) + } + } +} + +func getUserFirstName(userID: Int) async -> String { + return await withUnsafeContinuation { continuation in + getUserFirtName(userID: userID) { firstName in + continuation.resume(returning: firstName) + } + } +} + +func getUserLastName(userID: Int) async -> String { + return await withUnsafeContinuation { continuation in + getUserLastName(userID: userID) { lastName in + continuation.resume(returning: lastName) + } + } +} + +// MARK: Plusieurs steps : +// - on appelle des fonctions async donc obligé de rajouter le async dans le signature de la fonction (comme une fonction qui throws) +// - cependant les fonctions marquées async ne peuvent pas être appelées par des fonctions "normales" puisques ces fonctions async peuvent bloquer le thread qui les exécute +// - geetUserAsync() est fonction async particulière : elle ne retourne aucune valeur, utilisation de @asyncHandler au lieu de async +// - @asyncHandler permet d'appeler des fonctions async (qui ne retourne pas de valeur) et d'être appellé par des fonctions "normales" +@asyncHandler func geetUserAsync() /*async*/ { + let userID = await getUserId() + let firstName = await getUserFirstName(userID: userID) + let lastName = await getUserLastName(userID: userID) + print(firstName, lastName) +} + +// MARK: Remarques & Optimisations (Async en parallèle) +// - Les appels sont fait les uns après les autres (getUserId ––> getUserFirstName ––> getUserLastName). Mais on pourrait exécuter getUserFirstName & getUserLastName en parallèle +// - Analysons : let firstName = await getUserFirstName(userID: userID) (même logique pour getUserFirstName(userID: userID) +// 1) On créer une background task : getUserFirstName(userID: userID) +// 2) On exécute cette task (en bloquant le thread) grâce à l'utilisation du mot clé await et on débloque le thread une fois qu'une valeur est returned (storé dans firstName) +// - Ainsi pour exécuter des fonctions en parallèle on doit découpler ces deux étapes (création, exécution) : +// 1) On créer les tasks que nous avons besoins, grâce à la nouvelle expression : async let firstName = getUserFirstName(userID: userID) +// 2) On exécutes ces tasks d'une seule traite afin de les exécuter en parallèles +// - async let firstName = getUserFirstName(userID: userID) : on store une task qui une fois appellée sera capable de nous retourner le firstName + +@asyncHandler func getUserAsyncInParall() { + let userID = await getUserId() // ici on garde le await (bloquant le thread) car on est obligé d'attendre d'avoir le userID pour la suite + async let firstName = getUserFirstName(userID: userID) // création de la task + async let lastName = getUserLastName(userID: userID) // création de la task + await print(firstName, lastName) // await pour exécuter ces tasks en parallèles et une fois leurs exécution fini, le compilateur pourra exécuter ce qui suit (ici le print) +} + + +// MARK: - Transformer une fonction "classique" en async fonction +// Exemple avec la fonction URLSession.shared.dataTask +func regularPrintNetworkData() { + + let serviceURL = URL(string: "https://samples.openweathermap.org/data/2.5/weather?id=2172797&appid=b6907d289e10d714a6e88b30761fae22")! + + URLSession.shared.dataTask(with: serviceURL) { data, response, error in + guard let data = data, + let dataValue = String(data: data, encoding: .utf8) + else { return } + + print(dataValue) + } + .resume() +} + +// MARK: Extension sur URLSession +extension URLSession { + + func dataTask(with url: URL) async -> (Data?, URLResponse?, Error?) { + return await withUnsafeContinuation { continuation in + self.dataTask(with: url) { data, response, error in + continuation.resume(returning: (data, response, error)) + } + .resume() + } + } +} + +@asyncHandler func asyncPrintNetworkData() { + + let serviceURL = URL(string: "https://samples.openweathermap.org/data/2.5/weather?id=2172797&appid=b6907d289e10d714a6e88b30761fae22")! + + let (data, _, _) = await URLSession.shared.dataTask(with: serviceURL) + + guard let data = data, + let dataValue = String(data: data, encoding: .utf8) + else { return } + + print(dataValue) +} diff --git a/Async & Await/Await/AwaitApp.swift b/Async & Await/Await/AwaitApp.swift new file mode 100644 index 0000000..b69ad3f --- /dev/null +++ b/Async & Await/Await/AwaitApp.swift @@ -0,0 +1,17 @@ +// +// AwaitApp.swift +// Await +// +// Created by Lucas Abijmil on 08/03/2021. +// + +import SwiftUI + +@main +struct AwaitApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/Async & Await/Await/ContentView.swift b/Async & Await/Await/ContentView.swift new file mode 100644 index 0000000..1d756f0 --- /dev/null +++ b/Async & Await/Await/ContentView.swift @@ -0,0 +1,21 @@ +// +// ContentView.swift +// Await +// +// Created by Lucas Abijmil on 08/03/2021. +// + +import SwiftUI + +struct ContentView: View { + var body: some View { + Text("Hello, world!") + .padding() + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/Async & Await/Await/Info.plist b/Async & Await/Await/Info.plist new file mode 100644 index 0000000..efc211a --- /dev/null +++ b/Async & Await/Await/Info.plist @@ -0,0 +1,50 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UIApplicationSupportsIndirectInputEvents + + UILaunchScreen + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Async & Await/Await/Preview Content/Preview Assets.xcassets/Contents.json b/Async & Await/Await/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Async & Await/Await/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Compound Assignment.xcplaygroundpage/Contents.swift b/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Compound Assignment.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..add9a78 --- /dev/null +++ b/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Compound Assignment.xcplaygroundpage/Contents.swift @@ -0,0 +1,41 @@ +//: [Previous](@previous) +/*: + # Custom Compound Assignment Operator + ### Setup the structures + */ +struct Team { + let title: String + private(set) var members: [Member] + + mutating func add(_ member: Member) { + members.append(member) + } +} + +struct Member: CustomDebugStringConvertible { + let name: String + + var debugDescription: String { name } +} +/*: + ### Adding a member without a custom operator + */ +var team = Team(title: "Developers 🔵🔴⚪️", members: [Member(name: "Antoine 🇳🇱")]) + +let newMember = Member(name: "Donny 🇳🇱") +team.add(newMember) +/*: + ### Setup the custom Compound Assignment Operator += + */ +extension Team { + static func += (lhs: inout Team, rhs: Member) { + lhs.add(rhs) + } +} +/*: + ### Use the custom operator in code + */ +let anotherMember = Member(name: "Vincent 🇫🇷") +team += anotherMember +print(team.members) +//: [Next](@next) diff --git a/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Emoji Operators.xcplaygroundpage/Contents.swift b/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Emoji Operators.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..4b271e1 --- /dev/null +++ b/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Emoji Operators.xcplaygroundpage/Contents.swift @@ -0,0 +1,42 @@ +//: [Previous](@previous) +/*: + # Custom Emoji Operator + ### Setup the custom operator + */ +extension String { + static func + (lhs: String, rhs: String) -> String { + switch (lhs, rhs) { + case ("🛹", "❄️"): + return "🏂" + case ("😬", "❄️"): + return "🥶" + case ("😢", "🔥"): + return "🥵" + case ("🥕", "🥬"): + return "🥬" + case ("🥬", "🥒"): + return "🥒" + case ("🥒", "🍅"): + return "🥗" + case ("🧔", "💈"): + return "👶🏻" + case ("🦏", "🌈"): + return "🦄" + case ("🔨", "🔧"): + return "🛠" + default: + print("\(lhs) and \(rhs) not matched") + return "⁉️" + } + } +} +/*: + ### Making use of the custom operator + */ +print("🛹" + "❄️") +print("😬" + "❄️") +print("😢" + "🔥") +print("🥕" + "🥬" + "🥒" + "🍅") +print("🧔" + "💈") +print("🦏" + "🌈") +print("🔨" + "🔧") diff --git a/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Infix.xcplaygroundpage/Contents.swift b/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Infix.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..0a206ac --- /dev/null +++ b/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Infix.xcplaygroundpage/Contents.swift @@ -0,0 +1,23 @@ +//: [Previous](@previous) +/*: + # Custom Infix Operator + ### Setup the progress view + */ +let firstNumbers: Set = [1, 4, 5] +let secondNumbers: Set = [1, 4, 6] +/*: + ### Setup the Custom Infix Operator ~ + See [Operator Declarations](https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations) for all precedence options. + */ +infix operator +-: AdditionPrecedence +extension Set { + static func +- (lhs: Set, rhs: Set) -> Set { + return lhs.union(rhs) + } +} +/*: + ### Use the custom operator in code + */ +let uniqueNumbers = firstNumbers +- secondNumbers +print(uniqueNumbers) +//: [Next](@next) diff --git a/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Postfix.xcplaygroundpage/Contents.swift b/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Postfix.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..0528a0f --- /dev/null +++ b/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Postfix.xcplaygroundpage/Contents.swift @@ -0,0 +1,21 @@ +//: [Previous](@previous) +/*: + # Custom Postfix Operator + ### Setup the progress view + */ +let progressView = UIProgressView(progressViewStyle: .bar) +progressView.trackTintColor = .black +progressView.progressTintColor = .orange +/*: + ### Setup the custom prefix operator ~ + */ +import UIKit +postfix operator % +postfix func % (percentage: Int) -> Float { + return (Float(percentage) / 100) +} +/*: + ### Use the custom operator in code + */ +progressView.progress = 20% +//: [Next](@next) diff --git a/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Prefix.xcplaygroundpage/Contents.swift b/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Prefix.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..d1084b6 --- /dev/null +++ b/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Custom Prefix.xcplaygroundpage/Contents.swift @@ -0,0 +1,30 @@ +/*: + # Custom Prefix Operator + ### Setup the formatting + */ +import Foundation +let currencyFormatter = NumberFormatter() +currencyFormatter.numberStyle = .currency +currencyFormatter.locale = Locale.current + +let priceString = currencyFormatter.string(from: 9999.99)! +print(priceString) // Displays $9,999.99 in the current locale, for example $ dollars. +/*: + ### Setup the custom prefix operator ~ + */ +prefix func ~ (value: NSNumber) -> String { + let currencyFormatter = NumberFormatter() + currencyFormatter.numberStyle = .currency + currencyFormatter.locale = Locale.current + + return currencyFormatter.string(from: value)! +} +/*: + ### Use the custom operator in code + */ +let decimalInputPrice: String = ~843.32 +print(decimalInputPrice) // Prints: $843.32 + +let intInputPrice: String = ~300 +print(intInputPrice) // Prints: $300.00 +//: [Next](@next) diff --git a/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Introduction.xcplaygroundpage/Contents.swift b/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Introduction.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..61e2ead --- /dev/null +++ b/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/Pages/Introduction.xcplaygroundpage/Contents.swift @@ -0,0 +1,16 @@ +/*: + # Custom Operators Playground + This playground demonstrates custom operators in Swift. + + - Related blogpost: [Custom Operators in Swift with practical code examples](https://www.avanderlee.com/swift/custom-operators-swift/) + + Use the footer navigation or directly jump to any of the operators: + + - [Custom Prefix](Custom%20Prefix) + - [Custom Postfix](Custom%20Postfix) + - [Custom Infix](Custom%20Infix) + - [Custom Compound Assignment](Custom%20Compound%20Assignment) + - [Custom Emoji Operators](Custom%20Emoji%20Operators) + + [Next](@next) + */ diff --git a/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/contents.xcplayground b/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/contents.xcplayground new file mode 100644 index 0000000..cc9f5e2 --- /dev/null +++ b/Custom Prefix, Postfix, Infix Operators/Custom Operators.playground/contents.xcplayground @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git "a/Custom Prefix, Postfix, Infix Operators/Prefix, PostFix, Infix Operators \342\200\223 Lucas.playground/Contents.swift" "b/Custom Prefix, Postfix, Infix Operators/Prefix, PostFix, Infix Operators \342\200\223 Lucas.playground/Contents.swift" new file mode 100644 index 0000000..cca0ff8 --- /dev/null +++ "b/Custom Prefix, Postfix, Infix Operators/Prefix, PostFix, Infix Operators \342\200\223 Lucas.playground/Contents.swift" @@ -0,0 +1,52 @@ +import Foundation + +extension Optional where Wrapped == String { + + var orEmpty: String { + switch self { + case .some(let value): + return value + case .none: + return "" + } + } +} + +// MARK: - On peut créer des custom opérators de la manière suivante +prefix operator ~!! +postfix operator ++/ + +// MARK: - Prefix operator assez courant (ex : pour les prix de manière locale) +prefix func ~(value: NSNumber) -> String { + let currencyFormatter = NumberFormatter() + currencyFormatter.numberStyle = .currency + currencyFormatter.locale = .current + + return currencyFormatter.string(from: value).orEmpty +} + +let price1 = ~99 +print(~99) +let price2 = ~105.89 +print(price2) + +// MARK: - Postfix operator, assez courant (ex : avec les pourcentages) +postfix operator % +postfix func %(percentage: Double) -> Double { + return percentage / 100 +} +print(80%) +print(30%) + + +// MARK: - Infix operator, pas très courant (entre deux variables) +infix operator +-: AdditionPrecedence +extension Set { + static func +- (lhs: Set, rhs: Set) -> Set { + return lhs.union(rhs) + } +} +let firstNumbers: Set = [1, 4, 5] +let secondNumbers: Set = [1, 4, 6] +let uniqueNumbers = firstNumbers +- secondNumbers +print(uniqueNumbers) diff --git "a/Custom Prefix, Postfix, Infix Operators/Prefix, PostFix, Infix Operators \342\200\223 Lucas.playground/contents.xcplayground" "b/Custom Prefix, Postfix, Infix Operators/Prefix, PostFix, Infix Operators \342\200\223 Lucas.playground/contents.xcplayground" new file mode 100644 index 0000000..0934b17 --- /dev/null +++ "b/Custom Prefix, Postfix, Infix Operators/Prefix, PostFix, Infix Operators \342\200\223 Lucas.playground/contents.xcplayground" @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Defer.playground/Contents.swift b/Defer.playground/Contents.swift new file mode 100644 index 0000000..852781c --- /dev/null +++ b/Defer.playground/Contents.swift @@ -0,0 +1,20 @@ +import Foundation +/*: +# **`Defer`** : + * Permet d'éxuter une closure avant de finir l'exécution d'une fonction (avant de quitter son scope) + */ +func testDefer() { + defer { + print("Work is done") + } + print("Work is starting") +} +testDefer() +//: L'odre d'éxution des `defer` est inversé par rapport à leur déclaration (on remonte du "bas" de la function vers le "haut") +func multipleDefer() { + defer { print("1") } + defer { print("2") } + defer { print("3") } + print("4") +} +multipleDefer() diff --git a/Defer.playground/contents.xcplayground b/Defer.playground/contents.xcplayground new file mode 100644 index 0000000..36fbf39 --- /dev/null +++ b/Defer.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Dependecy Injection.playground/Pages/Untitled Page.xcplaygroundpage/Contents.swift b/Dependecy Injection.playground/Pages/Untitled Page.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..7c7f55e --- /dev/null +++ b/Dependecy Injection.playground/Pages/Untitled Page.xcplaygroundpage/Contents.swift @@ -0,0 +1,85 @@ +import Foundation +/*: + # **Injection de dépendances** + * L'injection de dépendances consiste à fournir des instances (objets déjà initialiasés) pour l'initialisation d'un autre objet + */ +final class RequestManager { + + var name = "RequestManager" +} +let requestManager = RequestManager() +/*: + ### Exemple + * **Sans injection de dépendance**, puisque la class *ViewModel* créer l'instance de *RequestManager* à l'intérieur de sa définition + */ +final class ViewModel { + var requestManager = RequestManager() +} +let viewModel = ViewModel() +//: * **Avec injection de dépendance**, puisqu'on fournit une instance de *RequestManager* pour l'initialisation de la class *ViewModelBis* +final class ViewModelBis { + var requestManager: RequestManager + + init(requestManager: RequestManager) { + self.requestManager = requestManager + } +} +let viewModelBis = ViewModelBis(requestManager: requestManager) +/*: + ### Avantage de l'injection de dépendances : + * Les properties et la class (en elle-même) sont totalement découplés, la class ne créer aucun objet à part elle même ––> SOLID + * Les responsabilités et les requierements de la class sont clairement identifiables et identifiés + * Unit Test plus facile à mettre en place car tous les objets sont isolés / découplés les un des autres +*/ + +/*: + ### Injection de dépendances via la POP + * Avec Swift, les injections de dépendances sont encore plus puissantes via la POP + * Le viewModel n'est plus contraint à un unique objet du type, ainsi, il peut donc être initialisé par n'importe quel objet du type du protocol + * Le viewModel est donc ainsi découplé de tout objet du type du protocol et de l'implémentation des fonctionnalités + */ +protocol Serializer { + func serialize(data: Any) -> Data? +} + +class RequestSerializer: Serializer { + func serialize(data: Any) -> Data? { return nil } +} + +class ViewModelSerializer { + var serializer: Serializer + + init(serializer: Serializer) { + self.serializer = serializer + } +} +let serializer = RequestSerializer() +let viewModelSerializer = ViewModelSerializer(serializer: serializer) +/*: + ### Les 3 types d'injection de dépendances : + * **Initializer Injection** (le plus courant) + */ +class InitializerInjection { + var requestManager: RequestManager + + init(requestManager: RequestManager) { + self.requestManager = requestManager + } +} +InitializerInjection(requestManager: requestManager) +//: * **Property Injection** +class PropertyInjection { + var requestManager: RequestManager? +} +let propertyInjection = PropertyInjection() +propertyInjection.requestManager = requestManager +//: * **Method Injection** +class MethodInjection { + var requestManager: RequestManager? + + func initializeRequestManager(requestManager: RequestManager) { + self.requestManager = requestManager + } +} +let methodInjection = MethodInjection() +methodInjection.initializeRequestManager(requestManager: requestManager) diff --git a/Dependecy Injection.playground/contents.xcplayground b/Dependecy Injection.playground/contents.xcplayground new file mode 100644 index 0000000..440e4b7 --- /dev/null +++ b/Dependecy Injection.playground/contents.xcplayground @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/DiscardableResult/DiscardableResult.xcodeproj/project.pbxproj b/DiscardableResult/DiscardableResult.xcodeproj/project.pbxproj new file mode 100644 index 0000000..5188bdb --- /dev/null +++ b/DiscardableResult/DiscardableResult.xcodeproj/project.pbxproj @@ -0,0 +1,311 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 8DB27DAC25337AB1008D4628 /* DiscardableResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DB27DAB25337AB1008D4628 /* DiscardableResult.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 8DB27D9825337AA0008D4628 /* DiscardableResult.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DiscardableResult.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8DB27DAB25337AB1008D4628 /* DiscardableResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscardableResult.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DB27D9525337AA0008D4628 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 8DB27D8F25337AA0008D4628 = { + isa = PBXGroup; + children = ( + 8DB27D9A25337AA0008D4628 /* DiscardableResult */, + 8DB27D9925337AA0008D4628 /* Products */, + ); + sourceTree = ""; + }; + 8DB27D9925337AA0008D4628 /* Products */ = { + isa = PBXGroup; + children = ( + 8DB27D9825337AA0008D4628 /* DiscardableResult.app */, + ); + name = Products; + sourceTree = ""; + }; + 8DB27D9A25337AA0008D4628 /* DiscardableResult */ = { + isa = PBXGroup; + children = ( + 8DB27DAB25337AB1008D4628 /* DiscardableResult.swift */, + ); + path = DiscardableResult; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8DB27D9725337AA0008D4628 /* DiscardableResult */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8DB27DA725337AA3008D4628 /* Build configuration list for PBXNativeTarget "DiscardableResult" */; + buildPhases = ( + 8DB27D9425337AA0008D4628 /* Sources */, + 8DB27D9525337AA0008D4628 /* Frameworks */, + 8DB27D9625337AA0008D4628 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = DiscardableResult; + productName = DiscardableResult; + productReference = 8DB27D9825337AA0008D4628 /* DiscardableResult.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8DB27D9025337AA0008D4628 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1200; + LastUpgradeCheck = 1200; + TargetAttributes = { + 8DB27D9725337AA0008D4628 = { + CreatedOnToolsVersion = 12.0.1; + }; + }; + }; + buildConfigurationList = 8DB27D9325337AA0008D4628 /* Build configuration list for PBXProject "DiscardableResult" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 8DB27D8F25337AA0008D4628; + productRefGroup = 8DB27D9925337AA0008D4628 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8DB27D9725337AA0008D4628 /* DiscardableResult */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8DB27D9625337AA0008D4628 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DB27D9425337AA0008D4628 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DB27DAC25337AB1008D4628 /* DiscardableResult.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 8DB27DA525337AA3008D4628 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 8DB27DA625337AA3008D4628 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 8DB27DA825337AA3008D4628 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"DiscardableResult/Preview Content\""; + DEVELOPMENT_TEAM = P2HRGX54G3; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = DiscardableResult/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.DiscardableResult"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 8DB27DA925337AA3008D4628 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"DiscardableResult/Preview Content\""; + DEVELOPMENT_TEAM = P2HRGX54G3; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = DiscardableResult/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.DiscardableResult"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8DB27D9325337AA0008D4628 /* Build configuration list for PBXProject "DiscardableResult" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8DB27DA525337AA3008D4628 /* Debug */, + 8DB27DA625337AA3008D4628 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8DB27DA725337AA3008D4628 /* Build configuration list for PBXNativeTarget "DiscardableResult" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8DB27DA825337AA3008D4628 /* Debug */, + 8DB27DA925337AA3008D4628 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 8DB27D9025337AA0008D4628 /* Project object */; +} diff --git a/DiscardableResult/DiscardableResult.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/DiscardableResult/DiscardableResult.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/DiscardableResult/DiscardableResult.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/DiscardableResult/DiscardableResult.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/DiscardableResult/DiscardableResult.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/DiscardableResult/DiscardableResult.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/DiscardableResult/DiscardableResult/DiscardableResult.swift b/DiscardableResult/DiscardableResult/DiscardableResult.swift new file mode 100644 index 0000000..3d74dc4 --- /dev/null +++ b/DiscardableResult/DiscardableResult/DiscardableResult.swift @@ -0,0 +1,43 @@ +// +// DiscardableResult.swift +// DiscardableResult +// +// Created by Lucas Abijmil on 11/10/2020. +// + +import Foundation + +final class Discardable { + + // Il peut arriver que dans certains cas on veuille ignorer le return d'un résultat car le code à l'intérieur de cette fonction est intéressant mais pas le return + func presentationNotDiscardable() -> String { + let string = "Lucas Abijmil" + print("Bonjour je suis \(string)") + + return string + } + + // Non-utilisation du return de la fonction d'exemple + // Comme le return de la fonction n'est pas utilisé, un warning apparait + // Autrement on peut obtenir le résultat de la fonction dans une variable qui ne nous servira à rien "_" + func notDiscardable() { + presentationNotDiscardable() + _ = presentationNotDiscardable() + } + + // Pour qu'on puisse ignorer le return d'une fonction + // Il suffit de rajouter l'attribut @discarbleResult avant la déclaration de fonction retournant une valeur + // Ainsi on peut, mais on est pas obligé d'utiliser le return de cette fonction + @discardableResult + func presentationDiscardable() -> String { + let string = "Lucas Abijmil" + print("Bonjour je suis \(string)") + + return string + } + + // En ayant rajouté cet attribut, il n'y a plus de warning :-) + func discardable() { + presentationDiscardable() + } +} diff --git a/Enum extensions & tricks.playground/Pages/Enum Codable.xcplaygroundpage/Contents.swift b/Enum extensions & tricks.playground/Pages/Enum Codable.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..ef7a7aa --- /dev/null +++ b/Enum extensions & tricks.playground/Pages/Enum Codable.xcplaygroundpage/Contents.swift @@ -0,0 +1,137 @@ +import Foundation +/*: + # Rendre une `enum` conforme au protocol `Codable` + * Rappel : `Codable` = `Decodable & Encodable` + */ + +/*: + ## `Enum` avec `RawValue` : + * Depuis Swift 5 les `enum` avec `RawValue` sont par défaut conforme au protocol `Codable` + */ +enum Direction: String, Codable { + case north + case south + case east + case west +} +//: ## `Enum` sans `RawValue` et `associated value` +//: * *Step 1* : Déclaration de l'`enum` +enum Action { + case run + case walk + case gym + case talk +} +//: * *Step 2* : Rendre l'`enum` conforme au protocol `Codable` +extension Action: Codable { +/*: + Déclaration d'une `enum` de type `CodingKey`, stockant les clés d'encodage et de décodage + * Déclaration d'un unique cas, qui est ni plus ni moins qu'une `RawValue` mais non exposée + * Ici, `rawValue` correspond à la n-ième position du cas + */ + enum CodingKeys: CodingKey { + case rawValue + } +//: Déclaration d'une `enum` pour la gestion des erreurs (un unique cas d'erreur est suffisant) + enum CodingError: Error { + case uknown(String) + } +/*: + Rendre l'`enum` conforme au protocol `Decodable` + * `container` : permet d'accéder à l'unique *clé de décodage* (le cas de l'`enum` conforme à `CodingKey`) + * `switch` sur sa value et on assigne le bon cas en fonction + */ + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let rawValue = try container.decode(Int.self, forKey: .rawValue) + switch rawValue { + case 0: + self = .run + case 1: + self = .walk + case 2: + self = .gym + case 3: + self = .talk + default: + throw CodingError.uknown("Quelque chose s'est mal passé durant le décodage") + } + } +/*: + Rendre l'`enum` conforme au protocol `Encodable` + * `container` : permet d'accéder à l'unique *clé d'encodage* (le cas de l'`enum` conforme à `CodingKey`) + * On passe la position du cas pour la valeur d'encodage + */ + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + switch self { + case .run: + try container.encode(0, forKey: .rawValue) + case .walk: + try container.encode(1, forKey: .rawValue) + case .gym: + try container.encode(2, forKey: .rawValue) + case .talk: + try container.encode(3, forKey: .rawValue) + } + } +} +//: ## `Enum` avec `associated values` mais sans `RawValue` +//: * *Step 1* : Déclaration de l'`enum` +enum Footballeur { + case gardient(name: String) + case defenseur(km: Double) + case milieu(isRecup: Bool) + case attaquant(numero: Int) +} +//: * *Step 2* : Rendre l'`enum` conforme au protocol `Codable` +extension Footballeur: Codable { +/*: + Déclaration d'une `enum` de type `CodingKey`, stockant les clés d'encodage et de décodage + * Déclaration de chaque cas de l'`enum` mais sans le tuple des `associated types` + */ + enum CodingKeys: CodingKey { + case gardient + case defenseur + case milieu + case attaquant + } +//: Déclaration d'une `enum` pour la gestion des erreurs (un unique cas d'erreur est suffisant) + enum CodingError: Error { + case uknown(String) + } +/*: + Rendre l'`enum` conforme au protocol `Decodable` + * `container` : permet d'accéder aux *clé de décodage* (les cas de l'`enum` conforme à `CodingKey`) + * Principe : On essaye de decoder le tuple correspondant aux `associated values` + * Si réussis alors on assigne et `return`, autrement `throw` une erreur + */ + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + if let name = try? container.decode(String.self, forKey: .gardient) { self = .gardient(name: name); return } + else if let km = try? container.decode(Double.self, forKey: .defenseur) { self = .defenseur(km: km); return } + else if let isRecup = try? container.decode(Bool.self, forKey: .milieu) { self = .milieu(isRecup: isRecup); return } + else if let numero = try? container.decode(Int.self, forKey: .attaquant) { self = .attaquant(numero: numero); return } + throw CodingError.uknown("Quelque chose s'est mal passé durant le décodage") + } +/*: + Rendre l'`enum` conforme au protocol `Encodable` + * `container` : permet d'accéder aux *clé d'encodage* (les cas de l'`enum` conforme à `CodingKey`) + * On passe le tuple pour la valeur d'encodage et la clé correspondant au cas associé + */ + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + switch self { + case .gardient(let name): + try container.encode(name, forKey: .gardient) + case .defenseur(let km): + try container.encode(km, forKey: .defenseur) + case .milieu(let isRecup): + try container.encode(isRecup, forKey: .milieu) + case .attaquant(let numero): + try container.encode(numero, forKey: .attaquant) + } + } +} + +//: [< Previous: `for case` pattern matching](@previous)           [Home](Introduction)           [Next: `indirect enum` >](@next) diff --git a/Enum extensions & tricks.playground/Pages/Enum avec associated value.xcplaygroundpage/Contents.swift b/Enum extensions & tricks.playground/Pages/Enum avec associated value.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..b105879 --- /dev/null +++ b/Enum extensions & tricks.playground/Pages/Enum avec associated value.xcplaygroundpage/Contents.swift @@ -0,0 +1,20 @@ +import Foundation +/*: + ## Création d'une `enum` avec **associated values** : + * Les `associated values` sont considérées comme un tuple + * On peut donc les préfixer par un label (rajoute du context) + */ +//: `Associated values` non préfixées +enum Mediaa { + case book(String, String, Int) + case movie(String, String, Int) + case website(String, URL) +} +//: `Associated values` préfixées +enum Media { + case book(title: String, author: String, year: Int) + case movie(title: String, director: String, year: Int) + case website(title: String, url: URL) +} + +//: [< Previous: `enum` sans `associated value`](@previous)           [Home](Introduction)           [Next: `switch` pattern matching >](@next) diff --git a/Enum extensions & tricks.playground/Pages/Enum sans associated value.xcplaygroundpage/Contents.swift b/Enum extensions & tricks.playground/Pages/Enum sans associated value.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..4bb75e8 --- /dev/null +++ b/Enum extensions & tricks.playground/Pages/Enum sans associated value.xcplaygroundpage/Contents.swift @@ -0,0 +1,22 @@ +//: ## Création d'une `enum` sans **associated values** +enum Direction { + case north + case south + case east + case west + + var description: String { + switch self { + case .north: + return "North" + case .south: + return "South" + case .east: + return "East" + case .west: + return "West" + } + } +} + +//: [Home](Introduction)           [Next: `enum` avec `associated value` >](@next) diff --git a/Enum extensions & tricks.playground/Pages/For case pattern matching.xcplaygroundpage/Contents.swift b/Enum extensions & tricks.playground/Pages/For case pattern matching.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..6d159fc --- /dev/null +++ b/Enum extensions & tricks.playground/Pages/For case pattern matching.xcplaygroundpage/Contents.swift @@ -0,0 +1,48 @@ +import Foundation +/*: + ## for case pattern matching avec `associated values` + * Permet de boucler sur un cas spécifique d'une `enum` sans passer par un `switch` + */ + +enum Media { + case book(title: String, author: String, year: Int) + case movie(title: String, director: String, year: Int) + case website(title: String, url: URL) +} + +let mediaList: [Media] = [ + .book(title: "Harry Potter and the Philosopher's Stone", author: "J.K. Rowling", year: 1997), + .movie(title: "Harry Potter and the Philosopher's Stone", director: "Chris Columbus", year: 2001), + .book(title: "Harry Potter and the Chamber of Secrets", author: "J.K. Rowling", year: 1999), + .movie(title: "Harry Potter and the Chamber of Secrets", director: "Chris Columbus", year: 2002), + .book(title: "Harry Potter and the Prisoner of Azkaban", author: "J.K. Rowling", year: 1999), + .movie(title: "Harry Potter and the Prisoner of Azkaban", director: "Alfonso Cuarón", year: 2004), + .movie(title: "J.K. Rowling: A Year in the Life", director: "James Runcie", year: 2007), + .website(title: "Yo", url: URL(string:"https://en.wikipedia.org/wiki/List_of_Harry_Potter-related_topics")!) +] + +//: Utilisation d'aucune `associated values` +for case .book in mediaList { + print("This media is a book") +} +//: Utilisation de certaines `associated values`, celles non utilisées sont remplacées par `_` +for case let .book(title, _, _) in mediaList { + print("This media is a book called \(title)") +} +//: Utilisation de toutes les `associated values` +for case let .book(title, author, year) in mediaList { + print("This media is a book called \(title) wrote by \(author) in \(year)") +} + +//: ## for case pattern matching avec conditions sur les `associated values` + +//: Conditions **strictes** sur les `associated values` +for case .book(title: "Harry Potter and the Prisoner of Azkaban", author: "J.K. Rowling", year: 1999) in mediaList { + print("This media is a book called Harry Potter and the Prisoner of Azkaban wrote by J.K. Rowling in 1999.") +} +//: Conditions sur les `associated values` avec le mot clé `where` +for case let .book(title, author, year) in mediaList where !title.isEmpty && !author.isEmpty && year >= 1_950 { + print("This media is a book called \(title) wrote by \(author) in \(year)") +} + +//: [< Previous: `guard case` pattern matching](@previous)           [Home](Introduction)           [Next: rendre une `enum` conforme au protocol `Codable` >](@next) diff --git a/Enum extensions & tricks.playground/Pages/Guard case pattern matching.xcplaygroundpage/Contents.swift b/Enum extensions & tricks.playground/Pages/Guard case pattern matching.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..f53043c --- /dev/null +++ b/Enum extensions & tricks.playground/Pages/Guard case pattern matching.xcplaygroundpage/Contents.swift @@ -0,0 +1,47 @@ +import Foundation +/*: + ## guard case pattern matching avec `associated values` + * Permet de matcher un cas spécifique d'une `enum` sans passer par un `switch` + */ +enum Media { + case book(title: String, author: String, year: Int) + case movie(title: String, director: String, year: Int) + case website(title: String, url: URL) +} +//: Utilisation d'aucune `associated values` +func mediaIsABookWithoutAssociated(for media: Media) { + guard case .book = media else { return } + print("This media is a book") +} +//: Ou +func mediaIsABookWithoutAssociated2(for media: Media) { + guard case .book(_, _, _) = media else { return } + print("This media is a book") +} +//: Utilisation de certaines `associated values`, celles non utilisées sont remplacées par `_` +func mediaIsABook(for media: Media) { + guard case let .book(title, author, _) = media else { return } + print("This media is a book called \(title) wrote by \(author)") +} +//: Utilisation de toutes les `associated values` +func mediaIsABook2(for media: Media) { + guard case let .book(title, author, year) = media else { return } + print("This media is a book called \(title) wrote by \(author) in \(year)") +} +//: ## guard case pattern matching avec conditions sur les `associated values` + +//: Conditions **strictes** sur les `associated values` +func mediaIsABook3(for media: Media) { + guard case .book("Formation Swift", "Lucas Abijmil", 2020) = media else { return } + print("This media is a book called Formation Swift wrote by Lucas Abijmil in 2020") +} +/*: + Conditions sur les `associated values` + * On remplace les `&&` par des `,` (même synaxe que pour un `guard let`) + */ +func mediaIsABook4(for media: Media) { + guard case let .book(title, author, year) = media, !title.isEmpty, !author.isEmpty, year >= 2_000 else { return } + print("This media is a book called \(title) wrote by \(author) in \(year)") +} + +//: [< Previous: `if case` pattern matching](@previous)           [Home](Introduction)           [Next: `for case` pattern matching >](@next) diff --git a/Enum extensions & tricks.playground/Pages/If case pattern matching.xcplaygroundpage/Contents.swift b/Enum extensions & tricks.playground/Pages/If case pattern matching.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..63199d2 --- /dev/null +++ b/Enum extensions & tricks.playground/Pages/If case pattern matching.xcplaygroundpage/Contents.swift @@ -0,0 +1,44 @@ +import Foundation +/*: + ## if case pattern matching avec `associated values` + * Permet de matcher un cas spécifique d'une `enum` sans passer par un `switch` + */ + +enum Media { + case book(title: String, author: String, year: Int) + case movie(title: String, director: String, year: Int) + case website(title: String, url: URL) +} + +let mediaTest: Media = .book(title: "Me", author: "Lucas Abijmil", year: 2020) +//: Utilisation d'aucune `associated values` +if case .book = mediaTest { + print("This media is a book") +} +//: Ou +if case .book(_, _, _) = mediaTest { + print("This media is a book") +} +//: Utilisation de certaines `associated values`, celles non utilisées sont remplacées par `_` +if case let .book(title, author, _) = mediaTest { + print("This media is a book called \(title) wrote by \(author)") +} +//: Utilisation de toutes les `associated values` +if case let .book(title, author, year) = mediaTest { + print("This media is a book called \(title) wrote by \(author) in \(year)") +} +//: ## if case pattern matching avec conditions sur les `associated values` + +//: Conditions **strictes** sur les `associated values` +if case .book(title: "Me", author: "Lucas", year: 2020) = mediaTest { + print("This media is a book") +} +/*: + Conditions sur les `associated values` + * On remplace les `&&` par des `,` (même synaxe que pour un `if let`) + */ +if case let .book(title, author, year) = mediaTest, !title.isEmpty, !author.isEmpty, year >= 2_000 { + print("This media is book called \(title) wrote by \(author) in \(year)") +} + +//: [< Previous: `switch` pattern matching](@previous)           [Home](Introduction)           [Next: `guard case` pattern matching >](@next) diff --git a/Enum extensions & tricks.playground/Pages/Indirect Enum.xcplaygroundpage/Contents.swift b/Enum extensions & tricks.playground/Pages/Indirect Enum.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..2b9ec30 --- /dev/null +++ b/Enum extensions & tricks.playground/Pages/Indirect Enum.xcplaygroundpage/Contents.swift @@ -0,0 +1,32 @@ +/*: + # `enum` récursive avec `indirect` + * Pour qu'une `enum` soit récursive, un `case` doit avoir une `associated value` du type de cette `enum` + * Rappel : une `enum` est une *value type* et une *value type* ne peux pas se contenir elle-même (càd se faire référence dans un `case`) + * Prenons l'exemple d'une **liste chaînée** + */ + +/*: + * Déclaration d'une `enum` récursive sans l'utilisatiopn `indirect` + * Compiler error : *Recursive enum `LinkedList` is not marked `indirect`* + */ + +enum LinkedListe { + case endpoint(T) + case node(T, LinkedListe) +} + +/*: + * Ainsi pour rendre une `enum` récursive, il faut utiliser le mot clé `indirect` + * Deux possibilités : + * L'appliqué **uniquement** aux cas qui font référence à l'enum + * L'appliqué à l'`enum` globalement + * `indirect` indique au compilo qu'on veut une autre référence du même type dans notre *value type*, ici notre `enum` + */ +indirect enum LinkedList { + case endpoint(T) + case node(T, LinkedList) +} + +let linkedList: LinkedList = .node(0, .node(1, .node(2, .endpoint(3)))) + +//: [< Previous: `enum` conforme à `Codable`](@previous)           [Home](Introduction)           [Next: NEXT >](@next) diff --git a/Enum extensions & tricks.playground/Pages/Introduction.xcplaygroundpage/Contents.swift b/Enum extensions & tricks.playground/Pages/Introduction.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..ef36e67 --- /dev/null +++ b/Enum extensions & tricks.playground/Pages/Introduction.xcplaygroundpage/Contents.swift @@ -0,0 +1,12 @@ +/*: + + # Enum Extensions & tricks + * [enum sans associated value](Enum%20sans%20associated%20value) : création d'une `enum` sans `associated value` + * [enum avec associated value](Enum%20avec%20associated%20value) : création d'une `enum` avec `associated values` + * [switch pattern matching](Switch%20pattern%20matching) : pattern matching dans un `switch` + * [if case pattern matching](If%20case%20pattern%20matching) : `if case` pattern matching + * [guard case pattern matching](Guard%20case%20pattern%20matching) : `guard case` pattern matching + * [for case pattern matching](For%20case%20pattern%20matching) : `for case` pattern matching + * [enum conforme au protocol Codable](Enum%20Codable) : rendre une `enum` conforme au protocol `Codable` + * [indirect enum](Indirect%20Enum) : rendre une `enum` récursive avec `indirect` + */ diff --git a/Enum extensions & tricks.playground/Pages/Switch pattern matching.xcplaygroundpage/Contents.swift b/Enum extensions & tricks.playground/Pages/Switch pattern matching.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..ea133f1 --- /dev/null +++ b/Enum extensions & tricks.playground/Pages/Switch pattern matching.xcplaygroundpage/Contents.swift @@ -0,0 +1,99 @@ +import Foundation +//: ## Pattern matching dans un `switch` avec `associated values` +enum Media { + case book(title: String, author: String, year: Int) + case movie(title: String, director: String, year: Int) + case website(title: String, url: URL) +} +/*: + Deux solutions possibles pour **ne pas utiliser** les `associated values` : + * Ne déclarer aucunes `associated values` ➡️ utilisation d'un `case` *brut* (à favoriser) + * Remplacer la déclaration des `associated values` par des `_` + */ +extension Media { + + var type: String { + switch self { + case .book: + return "Book" + case .movie(_, _, _): + return "Movie" + case .website: + return "Website" + } + } +} +/*: + Utilisation des `associated values` dans un switch + * Ajout du mot clé `let` pour la déclaration des `associated values` + * Remplacement des `associated values` non utilisées par `_` + */ +extension Media { + + var mediaTitle: String { + switch self { + case let .book(title, _, _): + return title + case let .movie(title, director, _): + return title + director + case let .website(title, _): + return title + } + } +} +/*: + Si plusieurs `case` retourne la même valeur (avec ou sans `associated values`), on peut les déclarer sur une seule ligne + * Si `associated values` : il faut que les labels aient les mêmes noms + */ +extension Media { + + var mediaTitle2: String { + switch self { + case let .book(title, author, _), let .movie(title, author, _): + return title + author + case let .website(title, _): + return title + } + } +} +/*: + Conditions **strictes** sur les `associated values` + * Doit obligatoirement avoir un `default` + */ +extension Media { + + var isFromMe: Bool { + switch self { + case .book(title: "Lucas", author: "Me", _): + return true + case .movie(title: "", director: "Me", year: _): + return true + case .website(title: "Me", url: _): + return true + default: + return false + } + } +} +/*: + Conditions sur les `assiociated values` + * Utilisation du mot clé `where` + * Doit obligatoirement avec un `default` + */ +extension Media { + + var published: Bool { + switch self { + case let .book(title, author, year) where !title.isEmpty && !author.isEmpty && year >= 2_000: + return true + case let .movie(_, director, year) where !director.isEmpty && year >= 1_950: + return true + case let .website(_, url) where url.isFileURL && url.relativeString != "" : + return true + default: + return false + } + } +} + +//: [< Previous: `enum` avec `associated value`](@previous)           [Home](Introduction)           [Next: `if case` pattern matching >](@next) diff --git a/Enum extensions & tricks.playground/contents.xcplayground b/Enum extensions & tricks.playground/contents.xcplayground new file mode 100644 index 0000000..6de0e39 --- /dev/null +++ b/Enum extensions & tricks.playground/contents.xcplayground @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Grand Central Dispatch.playground/Contents.swift b/Grand Central Dispatch.playground/Contents.swift new file mode 100644 index 0000000..3bdb61f --- /dev/null +++ b/Grand Central Dispatch.playground/Contents.swift @@ -0,0 +1,295 @@ +import Foundation + +// MARK: - Grand Central Dispatch : +// Le Grand Central Dispatch (GCD) permet de gérer une grande partie des performances d'une application. +// Il permet de faire de la programmation concurrente, du multi-threading etc... + + +// MARK: - I : Exécution Synchrone et Asynchrone + +// Synchrone : Le programme attend que l'exécution d'une tâche se finisse avant de faire d'autres opérations. Cette méthode bloque le processeur. +// Code qui s'exécute de "haut en bas" comme par exemple les fonctions avec des returns + +func presentationSynchrone() -> String { + return "Lucas Abijmil" +} + + +// ––––––––––– + + +// Asynchrone : Le programme n'attends pas la fin de l'exécution d'une tâche pour faire d'autres opérations. Cette méthode ne bloque pas le processeur. +// Le programme revient directemment sans attendre que la tâche soit finie, comme par exemple les fonctions sans return mais avec des closures (@non•escaping) +// La méthode asynchrone est utilisé dans de très nombreux cas en informatique : fetch d'API, accès à des DataBase, lecture d'un Fichier etc... +// Souvent utilisé pour des tâches potentiellement longues (en fonctions de nombreux paramètres) + +func presentationAsynchrone(then completion: (String) -> Void) { + completion("Lucas Abijmil") +} + + + +// MARK: - II : DispatchQueue, "Custom DispatchQueue", DispatchQueue + delay + +// Les tâches du GCD s'organises en Queue (file d'attente), représentable par une file FIFO First In, First Out +// Le GCD assure de ne lancer qu'une seule tâche à la fois + +// Il existe deux types de Queues : +// - Serial: DispatchQueue.main +// • Lance une seule tâche à la fois (selon l'ordre de la file) et attend que cette tâche soit finit pour commencer l'exécution de la tâche suivante +// • N'exécute qu'UNE SEULE tâche à la fois +// • [n, n+1, n+2] ––> lancement de la tâche n ––> fin de la tâche n ––> lancement de la tâche n+1 ––> fin de la tâche n+1 ––> lancement de la tâche n+2 ––> fin de la tâche n+2 +// => à l'arrivée : [n, n+1, n+2] + +// - Concurrent : DispatchQueue.global +// • Lance une tâche à la fois (selon l'ordre de la file) MAIS n'attend PAS la fin de cette tâche pour lancer l'exécution de la tâche suivante +// • Exécute PLUSIEURS tâches à la fois et l'ordre de fin d'exécution de chaque tâche n'est pas prévisible +// • [n, n+1, n+2] ––> lancement de la tâche n ––> lancement de la tâche n+1 ––> lancement de la tâche n+2 +// => à l'arrivée : [????] on ne peut pas savoir + + +// ––––––––––– + + +// Voici les DispatchQueue selon leur ordre d'importance (du + important au - important) +DispatchQueue.main // MARK: À utiliser pour update la UI +DispatchQueue.global(qos: .userInteractive) // Très forte priorité : UI update (main thread) & animations +DispatchQueue.global(qos: .userInitiated) // Forte priorité : résultats immédiats (chargement de données depuis la UI genre un Button ) +DispatchQueue.global(qos: .default) // Defaut, tâche que l'application initie +DispatchQueue.global() // Si pas de précision pour la qos ––> default par défaut (LMAO le jeu de mot) +DispatchQueue.global(qos: .utility) // Faible priorité : tâches longues (mise en réseau, recherche continue de donnée) +DispatchQueue.global(qos: .background) // Très faible priorité : tâches cachés à l'utilisateur (prefetch / tâches en background) +DispatchQueue.global(qos: .unspecified) // ?? / Plus faible des priorités ––> rien trouvé à propos de cette queue + + +// ––––––––––– + + +// MARK: Création de nos propre DispatchQueue + +// - Création d'une Serial Queue +DispatchQueue(label: "serialCustomQueue") + +// - Création d'une Concurrent Queue +// Plein d'init possible, nottament pour définir la QOS (par défaut : en fonction de ce qui est disponible comme ressource) +DispatchQueue(label: "ConcurrentCustomQueue", attributes: .concurrent) + + +// ––––––––––– + + +// MARK: Exemple – exécution d'une tâche dans le background qui va par la suite mettre à jour la UI tout ça en méthode asynchrone +DispatchQueue.global(qos: .background).async { + // some code here + DispatchQueue.main.async { + // update UI here + } +} + + +// ––––––––––– + +// MARK: De temps à autre, il est possible de vouloir faire quelque chose après un delay (uniquement possible en asynchrone) +// Disponible pour les 2 types de Queue (serial & concurrent) + +DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) { + // code executed after 2 seconds +} + +DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + .seconds(5)) { + // code executed after 5 secod +} + + +// ––––––––––– + + +// MARK: Exécution de tâches concurrentes, l'ordre de fin d'exécution est donc totalement alétoire +DispatchQueue.concurrentPerform(iterations: 5) { task in + print(task) +} + + +// MARK: - III : DispatchWorkItem + +// Le GCD n'est rien d'autre qu'une liste de tâches exécutée +// une tâche est donc un DispatchWorkItem de type : () -> Void +// Cette tâche peut être inséré dans une DispatchQueue ou un DispatchGroup + +// Méthodes disponibles : +// - perform : exécution de la tâche +// - wait : applique un DispatchTime avant toute prochaine action exécuté par le DispatchWorkItem +// - notify : permet d'informer à une DispatchQueue que l'exécution du DispatchWorkItem est finie +// - cancel : annule l'exécution d'un DispatchWorkItem +// workItem.isCancelled : bool qui indique si la tâche a été annulée + + +// ––––––––––– + + +let workItem = DispatchWorkItem { + print("workItem is computing a task....") +} + +// Tâche sera exécuté sur la default queue et de manière asynchrone +DispatchQueue.global(qos: .default).async { + workItem.perform() // exécution de la tâche +} + +// Lorsque l'exécution de la tâche est finie, on notify la main queue pour run du code +workItem.notify(queue: .main) { + print("workItem has finished its work") +} + + +// ––––––––––– + + +// MARK: Exemple un peu plus complexe + +var workItemBis: DispatchWorkItem? + +workItemBis = DispatchWorkItem { + // La tâche est composée de "5 sous tâches" + for task in 0...5 { + + // unwrap de l'optionnel + vérification que la tâche n'est pas cancelled + guard let workItem = workItemBis, !workItem.isCancelled else { print("workItemBis has been canceled"); break } + + // exécution de la tâche après un delay de 1 secondes + workItem.wait(timeout: .now() + .seconds(1)) + print("workItemBis is computing task n°\(task)") + } +} + +// Exécution de la tâche +DispatchQueue.global(qos: .default).async(execute: workItemBis!) // pareil que .async { workItemBis!.perform() } + +// Annulation de la tâches après un delay de 2 secondes +DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + .seconds(2)) { workItemBis?.cancel() } + + + +// MARK: - IV : DispatchGroup + + +// DispatchGroup permet d'attendre que toutes les tâches d'un group soit fini avant de continuer l'exécution +// "Rend un ensemble de tâches asynchrones synchronisé, tout en laissant chaque sous-tâche asynchrone" +// DispatchGroup est très utile pour fetch des datas depuis plusieurs APIs qui ont besoin d'être "rassemblées" + + +// Méthodes disponibles : +// - enter : indique le début d'exécution d'une tâche du DispatchGroup +// - leave : indique la fin d'exécution d'une tâche du DispatchGroup +// - notify : exécution d'une closure () -> Void lorsque le count du DispatchGroup est de 0 | indique que toutes les tâches d'un DispatchGroup sont finies +// - wait : permet d'appliquer un DispatchTime sur un DispatchGroup + + +// MARK: Obligation - Le "count" d'un DispatchGroup doit toujours être de 0 à la fin d'exécution de toutes les tâches +// - group.enter() : +1 au count du group +// - group.leaver() : -1 au count du group + +let group = DispatchGroup() + +func fetchSomeData(completion: () -> Void) { completion() } + +func fetchAllData() { + group.enter() // group += 1 + fetchSomeData { + print("Fetching some data from API n°1 ...") + sleep(1) + print("Fetch n°1 completed") + group.leave() // group -= 1 + } + + group.enter() // group += 1 + fetchSomeData { + print("Fetching some data from API n°2 ...") + sleep(2) + print("Fetch n°2 completed") + group.leave() // group -= 1 + } + + group.enter() // group += 1 + fetchSomeData { + print("Fetching some data from API n°3 ...") + sleep(3) + print("Fetch n°3 completed") + group.leave() // group -= 1 + } +} + +fetchAllData() + +// Exécution de la closure uniquement si le count du DipatchGroup est 0 et si toutes les tâches ont finis leur travail +group.notify(queue: .main) { + print("All data from all APIs are fetched") +} + + +// MARK: - V : DispatchSemaphore + +// DispatchSemaphore permet de restreindre l'accès à une ressource partagée (DB, cache...) par plusieurs thread (système concurrent) +// Permet de rendre une tâche asynchrone en synchrone ––> les queues utilisent les sémaphores que de manière asynchrone (assez logique, relire début) + +// Méthodes disponibles : +// - wait : permet d'indiquer qu'on utilise la ressource partagée (si celle-ci es disponible, sinon on wait) +// - signal : permet d'indiquer la fin de l'utilisation de la ressource partagée + +// MARK: Fonctionnement : +// - À l'init du semaphore on indique le nombre de threads authorisé à utilisé simulatanement la ressource +// - wait : -1 à la value du semaphore +// - signal : +1 à la value du semaphore + +// MARK: Fonction - Value du semaphore : +// - value > 0 : d'autres threads peuvent utilisée la ressource partagée +// - value = 0 : aucun autre thread ne peut utilisé la ressource partagée + +// MARK: Tips: ne JAMAIS utilisé semaphore.wait() sur le main thread sinon crash de l'app / codes + + +let semaphore = DispatchSemaphore(value: 1) // value = 1 ––> 1 seul thread autorisé à accéder à la ressource partagé + +DispatchQueue.global(qos: .default).async { + print("Task 1 – wait") + semaphore.wait() // value -= 1 (value = 0 ––> ressource partagée plus accessible par les autres threads) + sleep(1) // comme un DispatchTime de 1 seconde + semaphore.signal() // value += 1 (value = 1 ––> ressource partagée à nouveau accessible par les autres threads) + print("Task 1 – done") +} + +DispatchQueue.global(qos: .default).async { + print("Task 2 – wait") + semaphore.wait() // value -= 1 (value = 0 ––> ressource partagée plus accessible par les autres threads) + sleep(1) // comme un DispatchTime de 1 seconde + semaphore.signal() // value += 1 (value = 1 ––> ressource partagée à nouveau accessible par les autres threads) + print("Task 2 – done") +} + +DispatchQueue.global(qos: .default).async { + print("Task 3 – wait") + semaphore.wait() // value -= 1 (value = 0 ––> ressource partagée plu saccessible par les autres threads) + sleep(1) + semaphore.signal() // value += 1 (value = 1 ––> ressource partagée à nouveau accessible par les autres threads) + print("Task 3 – done") +} + + + + +// MARK: - VI : DispatchSource + +// Pas très utilisé dans la pratique car objet de bas niveau +// DispatchSource permet de surveiller des fichiers, ports, signaux (avec des sources de répartitions) +// Pour plus d'info aller voir la doc d'Apple + +// Petit exemple avec un dispatchSource timer + +let timer = DispatchSource.makeTimerSource() +timer.schedule(deadline: .now(), repeating: .seconds(1)) +timer.setEventHandler { + print("Hello there") +} +timer.resume() + + diff --git a/Grand Central Dispatch.playground/contents.xcplayground b/Grand Central Dispatch.playground/contents.xcplayground new file mode 100644 index 0000000..a751024 --- /dev/null +++ b/Grand Central Dispatch.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/High Order Functions.playground/Pages/Introduction.xcplaygroundpage/Contents.swift b/High Order Functions.playground/Pages/Introduction.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..ee3c120 --- /dev/null +++ b/High Order Functions.playground/Pages/Introduction.xcplaygroundpage/Contents.swift @@ -0,0 +1,14 @@ +/*: + # High Order Functions + * [filter](filter) : Filtre les éléments d'une `Collection` en fonction d'une ou plusieurs condition(s) + * [map](map) : Applique une transformation à chaque élément d'une `Collection` + * [reduce](reduce) : Combine tous les éléments d'une `Collection` dans une valeur + * [compactMap](compactMap) : Unwrap les optionnels et supprime les éléments `nil` d'une `Collection` + * [flatMap](flatMap) : Réduit d'une dimension la dimension d'une `Collection` en concaténant ses éléments + * [contains](contains) : Indique si la `Collection` contient au moins une fois l'élément passé en paramètre ou contient au moins un élément satisfaisant la ou les conditions dans la closure + + + + ## Avantage commun à toutes les High Order Functions + * Le résultat d'une HOF peut être stocké dans une constante, rendant le résultat immutable par la suite + */ diff --git a/High Order Functions.playground/Pages/compactMap.xcplaygroundpage/Contents.swift b/High Order Functions.playground/Pages/compactMap.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..d8ed133 --- /dev/null +++ b/High Order Functions.playground/Pages/compactMap.xcplaygroundpage/Contents.swift @@ -0,0 +1,53 @@ +/*: + # CompactMap + * Permet d'unwrap les optionnels, supprimant les éléments `nil` d'une `Sequence` + * Peut permettre de faire des transformations de type + * Très puissant pour matcher un `case` d'une `Enum` et retourner `nil` pour les cas qui ne nous intéresse pas + * Return une `Sequence` du **même type** que celui de la transformation (ou de la `Sequence` de base si pas de transformation) + */ +//: Avec des données *primitives* +let array = [1, 2, nil, nil, nil, 3, nil, 4, 5, nil, nil, 6, 7, 8, nil, 9, nil, 10] +//: * `compactMap` pour supprimer les éléments `nil` de l'`Array [Int?]`, `return : [Int]` +let elementNotNil = array.compactMap { $0 } +/*: + * `compactMap` pour transformer `[String]` en `[Int]`, `return [Int]` + * Pour chaque élément de la `Sequence` on a la chose suivante : + * `String` ––> `Int?` (cast) ––> retourne `Int` ou `nil` et est donc supprimé + */ +let strings = ["1", "deux", "trois", "4", "5", "6", "sept", "huit", "9", "10"] +let intElements = strings.compactMap { Int($0) } +//: Avec des données *non primitives* +struct Store { + let name: String + var hardware: String? +} +let stores = [Store(name: "store 1", hardware: "Apple TV"), + Store(name: "store 2", hardware: "MacBook"), + Store(name: "store 3", hardware: "iPhone"), + Store(name: "store 4", hardware: nil)] +//: * `compactMap` pour obtenir les hardwares sans optionnel, `return [String]` +let hardwares = stores.compactMap { $0.hardware } +//: * Exemple pour matcher un unique `case` d'une `Enum` +enum Footballeur { + case gardient(String) + case defenseur(Double) + case milieu(Bool) + case attaquant(Int) +} +let footballeurs: [Footballeur?] = [.gardient("Navas"), .gardient("Lloris"), nil, + .defenseur(10.3), .defenseur(12.9), nil, + .milieu(false), .milieu(true), nil, + .attaquant(9), .attaquant(27), nil] +/*: + * `compactMap` pour obtenir l'`associated value` pour les attaquants, les autres postes sont ignorés, `return [Int]` + * Pour chaque élément de la `Sequence` on a la chose suivante : + * On match le cas `attaquant` et on retourne son numéro, autrement on retourne `nil` et est donc supprimé + */ +let numeroAttaquants: [Int] = footballeurs.compactMap { + if case .attaquant(let numero) = $0 { + return numero + } else { + return nil + } +} +//: [< Previous: `reduce`](@previous)           [Home](Introduction)           [Next: `flatMap` >](@next) diff --git a/High Order Functions.playground/Pages/contains.xcplaygroundpage/Contents.swift b/High Order Functions.playground/Pages/contains.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..bfbcf33 --- /dev/null +++ b/High Order Functions.playground/Pages/contains.xcplaygroundpage/Contents.swift @@ -0,0 +1,41 @@ +/*: + # Contains(_:) + * Renvoie un `Bool` indiquant si la `Sequence` contient au moins une fois l'élément passé en paramètre + * Le type de la `Sequence` doit être conforme à `Equatable` autrement il faut utiliser `contains(where:)` + * Principalement utilisé avec des types *primitifs* + */ +//: Avec des données *primitives* +let array = [1, 2, 3, 4, 5] +let three = array.contains(3) +let six = array.contains(6) +//: Avec des données *non primitives* +struct Person: Equatable { + let name: String + let age: Int +} + +let persons = [Person(name: "Lucas", age: 22), + Person(name: "Pierre", age: 30), + Person(name: "Paul", age: 40), + Person(name: "Jacques", age: 50)] + +let lucas = persons.contains(Person(name: "Lucas", age: 22)) +let lucas2 = persons.contains(Person(name: "Luka", age: 22)) +/*: + # Contains(where:) + * Renvoie un `Bool` indiquant si la `Sequence` contient au moins un élément satisfaisant la ou les conditions passée(s) dans la closure + * Contrairement à `contains(_:)` le type de la `Sequence` ne doit pas forcément être conforme à `Equatable` + * Pratique pour tester des propriétés de types *non primitifs* + */ +struct Person2 { + let name: String + let age: Int +} +let persons2 = [Person2(name: "Lucas", age: 22), + Person2(name: "Pierre", age: 30), + Person2(name: "Paul", age: 40), + Person2(name: "Jacques", age: 50)] + +let lucas3 = persons2.contains(where: { $0.name == "Lucas" }) +let lucas4 = persons2.contains(where: { $0.name == "Luka" }) +//: [< Previous: `flatMap`](@previous)           [Home](Introduction)           [Next: >](@next) diff --git a/High Order Functions.playground/Pages/filter.xcplaygroundpage/Contents.swift b/High Order Functions.playground/Pages/filter.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..a020b26 --- /dev/null +++ b/High Order Functions.playground/Pages/filter.xcplaygroundpage/Contents.swift @@ -0,0 +1,37 @@ +/*: + ## Filter + * Filtre et retourne les éléments d'une `Sequence` satisfaisant la ou les conditions passée(s) dans la closure + * Return une `Sequence` du **même type** que celle qui est filtrée + */ +//: Avec des données *primitives* +let array = [1, 2, 3, 4, 3, 3] +//: * `for` loop pour obtenir [3, 3, 3] (pas optimisé) +var filteredLoop = [Int]() +for number in array where number == 3 { + filteredLoop.append(number) +} +//: * `filter` pour obtenir [3, 3, 3] (optimisé), `return : [Int]` +let threes = array.filter { $0 == 3 } +//: * `filter` pour obtenir [2, 4], `return : [Int]` +let multipleOf2 = array.filter { $0.isMultiple(of: 2) } +//: * `filter` avec plusieurs conditions, `return : [Int] ` +let result = array.filter { $0 == 3 && !$0.isMultiple(of: 2) } +let result2 = array.filter { $0 == 3 || $0 == 2 } +//: Avec des données *non primitives* +struct Device { + var type: String + var price: Float + var color: String +} + +var myDevices = [ + Device(type: "iMac Pro", price: 4_999, color: "Space Grey"), + Device(type: "iPhone", price: 799, color: "White"), + Device(type: "iPhone", price: 699, color: "Black"), + Device(type: "iPad", price: 599, color: "Space Grey"), + Device(type: "Apple Watch", price: 349, color: "Space Grey"), + Device(type: "Apple TV", price: 199, color: "Black") +] +//: * `filter` pour obtenir un tableau de `Device` avec uniquement des "iPhone" en `type`, `return : [Device]` +let iphones = myDevices.filter { $0.type == "iPhone" } +//: [Home](Introduction)           [Next: `map` >](@next) diff --git a/High Order Functions.playground/Pages/flatMap.xcplaygroundpage/Contents.swift b/High Order Functions.playground/Pages/flatMap.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..19b1ab6 --- /dev/null +++ b/High Order Functions.playground/Pages/flatMap.xcplaygroundpage/Contents.swift @@ -0,0 +1,37 @@ +/*: + # FlatMap + * Permet de concaténer plusieurs `Sequence` entre elles + * Réduit d'une dimension la `Sequence` *"mère"* + * Retourne le **même type** que celui de la `Sequence` avec une dimension en moins + */ +//: Avec des données *primitives* +//: * `flatMap` pour obtenir un `Array` à une seule dimension, `return [Int]` +let deuxDimensions: [[Int]] = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] +] +let uneDimension = deuxDimensions.flatMap { $0 } +//: * `flatMap` pour obtenir un `Array` à une seule dimension, `return [Int]` +let troisDimensions: [[[Int]]] = [ +[ [1, 2, 3], + [4, 5, 6] ], +[ [1, 2, 3], + [4, 5, 6] ] +] +let oneDimension = troisDimensions.flatMap { $0.flatMap { $0 } } +let oneDimensionBis = troisDimensions.flatMap { $0 }.flatMap { $0 } +//: Avec des données *non primitives* +struct Store { + let name: String + let hardware: [String] +} +let stores = [Store(name: "store 1", hardware: ["iPhone", "iPad", "Apple TV"]), + Store(name: "store 2", hardware: ["MacBook", "MacBook Pro", "Mac Pro"]), + Store(name: "store 3", hardware: [])] +//: * `flatMap` pour les `hardware` dans un tableau unique, `return [String]` +let hardwares = stores.flatMap { $0.hardware } + + +let somthing = [1, 2, 3, 4, 5] +//: [< Previous: `compactMap`](@previous)           [Home](Introduction)           [Next: `contains` >](@next) diff --git a/High Order Functions.playground/Pages/map.xcplaygroundpage/Contents.swift b/High Order Functions.playground/Pages/map.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..ec7c813 --- /dev/null +++ b/High Order Functions.playground/Pages/map.xcplaygroundpage/Contents.swift @@ -0,0 +1,34 @@ +/*: + ## Map : + * Applique la transformation passé dans la closure (nouveau type, opération arithmétique) à chaque élément de la `Sequence` et le retourne + * Return le **même type** que celui de la transformation + */ +//: Avec des données *primitives* +let array = [1, 2, 3, 4, 5] +//: * `for` loop pour obtenir [2, 4, 6, 8, 10] (pas optimisé) +var mapLoop = [Int]() +for number in array { + mapLoop.append(number * 2) +} +//: * `map` pour obtenir [2, 4, 6, 8, 10] (optimisé), `return : [Int]` +let doubleElement = array.map { $0 * 2 } +//: * `map` pour obtenir ["1", "2", "3", "4", "5"], `return : [String]` +let stringElement = array.map { String($0) } +//: Avec des données *non primitives* +struct Device { + var type: String + var price: Float + var color: String +} + +var myDevices = [ + Device(type: "iMac Pro", price: 4_999, color: "Space Grey"), + Device(type: "iPhone", price: 799, color: "White"), + Device(type: "iPhone", price: 699, color: "Black"), + Device(type: "iPad", price: 599, color: "Space Grey"), + Device(type: "Apple Watch", price: 349, color: "Space Grey"), + Device(type: "Apple TV", price: 199, color: "Black") +] +//: * `map` pour doubler chaque prix, `return : [Float]` +let doublePrice = myDevices.map { $0.price * 2 } +//: [< Previous: `filter`](@previous)           [Home](Introduction)           [Next: `reduce` >](@next) diff --git a/High Order Functions.playground/Pages/reduce.xcplaygroundpage/Contents.swift b/High Order Functions.playground/Pages/reduce.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..f7b1dcc --- /dev/null +++ b/High Order Functions.playground/Pages/reduce.xcplaygroundpage/Contents.swift @@ -0,0 +1,44 @@ +/*: + ## Reduce : + * Combine tous les éléments d'une `Sequence` dans une valeur et la retourne, qui peut être de n'importe quel type + * Fonction qui prend deux paramètres : + * `initialResult` : la valeur de départ, premier paramètre de la fonction + * `nextPartialResult` : closure qui applique une opération en utilisant l'`initialResult` ($0) et un élément de la `Sequence` ($1) + * Principalement utilisé pour des opérations arithmétiques + */ +//: Avec des données *primitives* +let array = [1, 2, 3, 4] +//: * `for` loop pour obtenir 10 +var reduceLoop = 0 +for number in array { + reduceLoop += number +} +/*: + * `reduce` pour obtenir 10. + * `initialResult` : 0 + * `nextPartialResult` : closure utilisant l'`initialResult` et un élément de la `Sequence`. Ici l'opération est l'addition + */ +let reduce = array.reduce(0, { $0 + $1 }) +//: * Si l'élément de la `Sequence` est du même type que l'`initialResult`, l'expression de la closure peut être simplifié en indiquant uniquement l'opération de celle-ci +let reduce2 = array.reduce(0, +) +//: Avec des données *non primitives* +struct Device { + var type: String + var price: Float + var color: String +} + +var myDevices = [ + Device(type: "iMac Pro", price: 4_999, color: "Space Grey"), + Device(type: "iPhone", price: 799, color: "White"), + Device(type: "iPhone", price: 699, color: "Black"), + Device(type: "iPad", price: 599, color: "Space Grey"), + Device(type: "Apple Watch", price: 349, color: "Space Grey"), + Device(type: "Apple TV", price: 199, color: "Black") +] +/*: + * `reduce` pour obtenir la somme du prix de tous les produits + * Ici l'expression n'est pas réductible, car l'élément de la `Sequence` est d'un type différent de celui de l'`initialResult` + */ +let totalPrice = myDevices.reduce(0, { $0 + $1.price }) +//: [< Previous: `map`](@previous)           [Home](Introduction)           [Next: `compactMap` >](@next) diff --git a/High Order Functions.playground/contents.xcplayground b/High Order Functions.playground/contents.xcplayground new file mode 100644 index 0000000..1475e6a --- /dev/null +++ b/High Order Functions.playground/contents.xcplayground @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Keypath.playground/Pages/Generalite.xcplaygroundpage/Contents.swift b/Keypath.playground/Pages/Generalite.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..5be7eef --- /dev/null +++ b/Keypath.playground/Pages/Generalite.xcplaygroundpage/Contents.swift @@ -0,0 +1,27 @@ +/*: + ## Généralité sur le `KeyPath` + * Ici on prend l'exemple avec des HOF car c'est une des situation où l'utilisation des `KeyPath` est la plus puissante + */ +let numbers = [1, 2, 3, 4, 5] +//: * Transformons ce `[Int]` en `[String]` +let numbersString = numbers.map { $0.description } +/*: + * Quelques remarques sur ce que nous venons de faire : + * Assez low-level : utilisation des `{ }` et `$0` + * La closure sert de `getter` pour une property de leur argument et le return (ici description) + */ +/*: + * Qu'est ce un qu'un `KeyPath` ? + * Le `KeyPath` permet d'avoir une référence d'une propriété qu'on peut invoquer sur une instance afin de retourner cette propriété + * Le compilateur est capable de prendre le `KeyPath` pour le transformer en `getter` function + * Le `KeyPath` est une literal syntax qui permet de référencer le `getter` ou le `setter` d'une propriété d'un type + * Avantage : plus redeable et plus safe à l'utilisation + */ +let numbersStrings = numbers.map(\.description) +/*: + * Behind the scene, le `KeyPath` est un type générique avec 2 arguments génériques : + * `Root` : type de l'instance sur lequel on va invoqué le `KeyPath` (ici `Int`) + * `Value` : type de la propriété dont le `KeyPath` fait référence (ici `String`) + * `KeyPath` transformé par le compilatuer en fonction avec la signature suivante : `(Root) -> Value` + */ +//: [Home](Introduction)           [Next : Prédicat avec un `KeyPath` >](@next) diff --git a/Keypath.playground/Pages/Introduction.xcplaygroundpage/Contents.swift b/Keypath.playground/Pages/Introduction.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..7d3585d --- /dev/null +++ b/Keypath.playground/Pages/Introduction.xcplaygroundpage/Contents.swift @@ -0,0 +1,6 @@ +/*: + # KeyPath : + + * [Généralité du `KeyPath`](Generalite) : Généralité du `KeyPath` + * [Predicate avec `Keypath`](Predicate%20with%20Keypath) : créer un predicate (test booléan) avec un `KeyPath` + */ diff --git a/Keypath.playground/Pages/Predicate with Keypath.xcplaygroundpage/Contents.swift b/Keypath.playground/Pages/Predicate with Keypath.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..a6fcd8d --- /dev/null +++ b/Keypath.playground/Pages/Predicate with Keypath.xcplaygroundpage/Contents.swift @@ -0,0 +1,41 @@ +/*: + ## Création d'un prédicat avec l'utilsation du `KeyPath` + * Ici on prend l'exemple avec des HOF car c'est une des situation où l'utilisation des `KeyPath` est la plus puissante + */ +struct Person { + let age: Int +} + +let people = [Person(age: 45), Person(age: 12), Person(age: 39), Person(age: 41), Person(age: 20), Person(age: 8)] +//: Exemple d'un prédicat sans l'utilisation de `KeyPath` (assez classique) : +let majeurs = people.filter { $0.age > 18 } +/*: + Création du prédicat mais avec l'utilisation du `KeyPath` + * Définition d'un prédicat : test d'un élément qui renvoie un `Bool`, on peut donc définir le type suivant : + * `Typealias` qui prend un argument générique, appellé `Element`, pour rester raccord avec la définition d'un `Element` d'une `Sequence` + */ +typealias Predicate = (Element) -> Bool +/*: + Création de la fonction pour tester le prédicat : + * Deux arguments génériques : + * `Element` : l'élément d'une `Sequence` + * `Property` : doit être conforme à `Comparable` puisque c'est la valeur comparée + * `lhs` : `KeyPath` [voir ici](@previous) + * `rhs` : la `Property` (constante) comparée + */ +func > (_ lhs: KeyPath, _ rhs: Property) -> Predicate { + return { element in + element[keyPath: lhs] > rhs + } +} + +let majeursKeypath = people.filter(\.age > 18) +//: Création de la fonction pour un prédicat avec `<` (l'inverse de ci-dessus) +func < (_ lhs: KeyPath, _ rhs: Property) -> Predicate { + return { element in + element[keyPath: lhs] < rhs + } +} + +let mineursKeypath = people.filter(\.age < 18) +//: [< Previous : Généralité](@previous)           [Home](Introduction)           [Next >](@next) diff --git a/Keypath.playground/contents.xcplayground b/Keypath.playground/contents.xcplayground new file mode 100644 index 0000000..da6c5a9 --- /dev/null +++ b/Keypath.playground/contents.xcplayground @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Lazy Property.playground/Contents.swift b/Lazy Property.playground/Contents.swift new file mode 100644 index 0000000..4b1fe6b --- /dev/null +++ b/Lazy Property.playground/Contents.swift @@ -0,0 +1,129 @@ +import Foundation +/*: + # **Lazy Property** + * `Lazy` : "just in time", la property repousse le travail jusqu'à ce qu'on en est besoin + * Une property `lazy` n'est calculée qu'une **seule fois** (ou zéro si jamais accédée), à l'inverse d'une `computed` property qui est, elle, recalculée à chaque accès + * À utiliser lorsque : + * La property a besoin de connaître la valeur de d'autres properties + * Le calcul de la property est important / complexe (= long) + */ +//: **Cas n°1** : Property qui a besoin de connaître la valeur de d'autres properties +struct Player { + var name: String + var team: String + var position: String + + // Doit être lazy car à l'initialisation on ne connait pas les valeurs pour : name, position & team + lazy var introduction: String = { + return "Now entering the game: \(name), \(position) for the \(team)" + }() +} +var jordan = Player(name: "Jordan", team: "Bulls", position: "Shooting guard") +print(jordan.introduction) +//: **Cas n°2** : Property donc le calcul est important / complexe (= long) +struct Calculator { + static func calculateGamesPlayed() -> Int { + var games = [Int]() + for i in 1...4_000 { + games.append(i) + print("Append games n°\(i)") + } + return games.last! + } +} +//: * Property "chère" a calculer à l'init +struct Players { + var name: String + var team: String + var position: String + var gamesPlayed = Calculator.calculateGamesPlayed() +} +/*: + Création d'une instance du type `Players` avec `gamesPlayed` calculé lors de l'init, car valeur par défaut. + * Computation de `gamesPlayed` est important (calcul des 4 000 parties) 👉 l'init est donc assez long + */ +var jordans = Players(name: "Jordan", team: "Bulls", position: "Shooting Guard") +//: * Property "chère" marquée `lazy`, donc non calculée à l'init +struct Players2 { + var name: String + var team: String + var position: String + lazy var gamesPlayed = { + return Calculator.calculateGamesPlayed() + }() +} +/*: + Création d'une instance du type `Players2` avec `gamesPlayed` marqué `lazy`, donc non calculé à l'init. + * Computation de `gamesPlayed` fait uniquement lors du **premier accès** à cette property 👇 + * Init plus rapide + * Peut ne jamais être computé si on n'accède jamais à la property + */ +var jordans2 = Players2(name: "Jordan", team: "Bulls", position: "Shooting Guard") +jordans2.gamesPlayed +//: ## Remarques +/*: + 1. `lazy` vs `computed` property + * `lazy` est computé qu'une **seule fois** (ou zéro si jamais accédée) 👉 à utiliser lorsque la computation est importante ou que la property est dépendante de d'autres properties + * `lazy` **store** la valeur de la property lors du premier accès + * `computed` est computé à chaque accès 👉 à utiliser si la property est dépendantes de d'autres property mutables, mais peut coûter cher en calcul + * `computed` **ne store jamais** la valeur de la property + */ +struct LazyVSComputed { + var computedGamesPlayed: Int { + return Calculator.calculateGamesPlayed() + } + + lazy var lazyGamesPlayed: Int = { + return Calculator.calculateGamesPlayed() + }() +} +var lazyVSComputed = LazyVSComputed() +// 👇 LAZY +print(lazyVSComputed.lazyGamesPlayed) // 👈 Calculé ici +print(lazyVSComputed.lazyGamesPlayed) // 👈 Non calculé ici +// 👇 Computed +print(lazyVSComputed.computedGamesPlayed) // 👈 Calculé ici +print(lazyVSComputed.computedGamesPlayed) // 👈 Calculé ici +/*: + 2. Une `lazy` property ne pas être `let` (constante) + * Compiler error: *'lazy' cannot be used on a let* 👇 + * Une `lazy` property **ne pas être constante** (let) car son getter est mutable. Elle n'est pas computé à l'init d'une instance et on ne sait pas si elle sera computé à un moment ou non. + */ +struct LazyConstante { + // 👇 K.O + // lazy let expensiveToCompute: Int = { + // return (0...1000).reduce(0, +) + // }() +} +/*: + 3. Une `lazy` property ne peut pas être accéder via une constante d'instance de type `value type` (immutable) + * Compiler error: *Cannot use mutating getter on immutable value: `lazyness` is a `let` constant* 👇 + * Une `lazy` property à son getter mutable ainsi lors du premier accès à la property, l'état de l'objet change car la valeur sera storée dans celle-ci. Il est donc mutable à l'inverse d'une `value type` (struct) + * Cependant, une `lazy` property peut être accéder via une constante d'instance de type `reference type` + */ +struct LazynessStruct { + lazy var expensiveToCompute: Int = { + return (0...1000).reduce(0, +) + }() +} +final class LazynessClass { + lazy var expensiveToCompute: Int = { + return (0...1000).reduce(0, +) + }() +} +let lazynessStruct = LazynessStruct() +//lazynessStruct.expensiveToCompute // 👈 K.O. +let lazynessClass = LazynessClass() +lazynessClass.expensiveToCompute // 👈 O.K. +/*: + 4. Les `static` properties sont `lazy` par défaut + * Compiler error: *Compilator error : 'lazy' must not be used on an already-lazy global* + * Les `static` properties sont très utilisées, ainsi au démarrage d'un programme, si cela n'était pas le cas, cela couterai très cher en ressource (et prendrait beaucoup de temps !) + */ +extension Float { + /*lazy*/ static let seven = 7.0 +} +/*: + 5. Une `lazy` property n'est pas thread safe, car peut être accédée par plusieurs threads en même temps + * cf : https://docs.swift.org/swift-book/LanguageGuide/Properties.html + */ diff --git a/Lazy Property.playground/contents.xcplayground b/Lazy Property.playground/contents.xcplayground new file mode 100644 index 0000000..515976f --- /dev/null +++ b/Lazy Property.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Literal Expression & Log function.playground/Contents.swift b/Literal Expression & Log function.playground/Contents.swift new file mode 100644 index 0000000..bd75881 --- /dev/null +++ b/Literal Expression & Log function.playground/Contents.swift @@ -0,0 +1,35 @@ +import Foundation +/*: + # **Debug** – Literal Expresssion & log function + * Les `Literal Expression` par défaut fournit par Swift nous permettent d'obtenir des informations utiles pour *debug* + * Grâce à cela on peut créer une fonction `log` qui va nous permettre de plus facilement debugger notre code + */ + +/*: + Voici les `Literal Expression` fournit par Swift : + * `#file` / `#filePath` : `String` ––> Le chemin d'accès au fichier. + * `#fileID` : `String` ––> Le nom du fichier et du module. + * `#line` : `Int` ––> Le numéro de ligne. + * `#column` : `Int` ––> Le numéro de la colonne dans laquelle il commence. + * `#function`: `String` ––> Le nom de la fonction. + * `#dsohandle` : `UnsafeRawPointer` ––> Token qui identifie l'exécutable ou dylib image. +*/ +print(#file) +print(#filePath) +print(#fileID) +print(#line) +print(#column) +print(#function) +//print(#dsohandle) // KO dans un playground +/*: + Grâce à ces `Literal Expression` on peut créer une fonction `log` qui va nous permettre de debugger plus facilement + */ +func log(_ message: String = "No message", _ file: String = #file, _ function: String = #function, _ line: Int = #line) { + print("[FILE : \(file.uppercased()) – FUNCTION : \(function.uppercased()) – LINE : \(line)] – MESSAGE : \(message)") +} +//: Fonction qui appele la fonction log +func testLogFunction() { + log() + log("Hello custom log function !") +} +testLogFunction() diff --git a/Literal Expression & Log function.playground/contents.xcplayground b/Literal Expression & Log function.playground/contents.xcplayground new file mode 100644 index 0000000..7388470 --- /dev/null +++ b/Literal Expression & Log function.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Opaque Return Type.playground/Contents.swift b/Opaque Return Type.playground/Contents.swift new file mode 100644 index 0000000..22fc354 --- /dev/null +++ b/Opaque Return Type.playground/Contents.swift @@ -0,0 +1,67 @@ +import Foundation +/*: + # Opaque Return Type & `Some` + * Uniquement pour des return dont le type est un protocol + * Présent notamment dans SwiftUI 👉 `var body: some View` + * Le body s'attend à être de type `View` mais ne sait pas que quels `View` il sera composé + */ +//: Disons que je veux implémenter une fonction `getData` dont le `return` est un type conforme au protocol `Collection` (Array, Dictionnary etc...) +func getData() -> Collection { + return [1, 2, 3, 4, 5] +} +/*: + Raison de l'erreur du compilateur : + * Le protocol `Collection` est un protocol avec un `associatedtype` (appellé `Element`) 👇 + * On ne peut pas utiliser ce protocol comme un `return type` ou type de variable + * On peut uniquement utiliser ce protocol comme une *generic constraint* + */ +/*: + L'utilisation du mot clé `some` résout ce type de problème 👇 + * Signification 👉 cette function return *some type* qui conforme au protocol retourné (ici `Collection`) + * Grâce à `some` le compilo connaît le type qui va être retourné par la fonction (ici Array) + */ +func getDatas() -> some Collection { + return [1, 2, 3, 4, 5] +} +/*: + **Limitation du `some`** + * On ne peut retourner qu'un **unique** type qui est conforme au protocol retourné pour un `opaque return type` 👇 + * On peut donc **soit retourné un Array soit un Dictionnary** + */ +func getArrayOrDictionary() -> some Collection { + return Bool.random() ? [1, 2, 3, 4, 5] : [1: "one", 2: "two", 3: "three", 4: "four", 5: "five"] +} +let data = getDatas() +data.count +//: * `element` n'est pas de type `Int` (même si le compilo le sait), mais de type `((some Collection).Element)?` 👉 due à l'`opaque return type` +let element = data.randomElement() +//: * Si on veut utiliser `element` comme un `Int`, on fait un `cast` classique +if let element = element as? Int { + print("I'm a Integer, here's my value : \(element)") +} else { + print("I'm not a Integer") +} + +func getOneTypeOfValue() -> some Equatable { + return 0 +} +let firstValue = getOneTypeOfValue() +let secondValue = getOneTypeOfValue() +/*: + * Vérifions l'égalité de deux values venant d'un même `opaque return type` + * Le code compile 👉 ces deux valeurs ont été obtenu grâce à la même fonction avec un `opaque return type` + * Le type du `some Equatable` de `firstValue` & `secondValue` est donc le *même* + */ +firstValue == secondValue + +func getAnotherTypeOfValue() -> some Equatable { + return 1 +} + +let thirdValue = getAnotherTypeOfValue() +/*: + * Vérifions l'égalité de deux values ne venant pas du même `opaque return type` + * Le code ne compile pas 👉 ces deux valeurs ont été obtenu par deux fonctions différente avec un `opaque return type` potentiellement différent 👇 + * Même si c'est deux fonctions retournent `some Equatable`, ce type peut être différent d'une fonction à l'autre + */ +firstValue == thirdValue diff --git a/Opaque Return Type.playground/contents.xcplayground b/Opaque Return Type.playground/contents.xcplayground new file mode 100644 index 0000000..6156abb --- /dev/null +++ b/Opaque Return Type.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Property Observers.playground/Contents.swift b/Property Observers.playground/Contents.swift new file mode 100644 index 0000000..e1cc227 --- /dev/null +++ b/Property Observers.playground/Contents.swift @@ -0,0 +1,77 @@ +/*: + # Property Observers + * Observe les changements de la valeur d'une propriété + * Appellé à chaque fois que la valeur est modifiée, même si pour une même valeur + * Permet d'exécuter du code à chaque modification + */ + +/*: + * Une property observers peut être composée de deux blocks + * `willSet` : exécute du code juste avant que la propriété change de valeur (rarement utilisé). Paramètre par défaut : `newValue` + * `myProperty` : valeur avant modification. + * `newValue` : valeur de la propriété après modification. **Immutable** + * `didSet` : exécute du code juste après que la propriété ait changé de valeur. Paramètre par défaut : `oldValue` + * `myProperty` : valeur après la modification. **Mutable** + * `oldValue` : valeur de la propriété avant la modification. **Immutable** + */ +struct Username { + + var name: String { + willSet { +// newValue = "Something new" // KO + print("Current Value : \(name) ||", "Next Value : \(newValue)") + } didSet { +// oldValue = "Something new" // KO + print("Current Value : \(name) ||", "Old Value : \(oldValue)") + } + } +} +var username = Username(name: "Lucas") +username.name = "Marc" +/*: + * Même s'il est préférable d'utiliser les conventions de Swift, il est possible de renommer les paramètres de `willSet` et `didSet`. Il suffit de déclarer le nom entre parenthèses. + */ +struct UserNameBis { + + var name: String { + willSet(newName) { + print("Current Value : \(name) ||", "Next Value : \(newName)") + } didSet(oldName) { + print("Current Value : \(name) ||", "Old Value : \(oldName)") + } + } +} +/*: + * Exemple plus proche d'une business logic "réelle" + */ +struct Person { + var firstName: String + var lastName: String + var age: Int { + didSet { + age = max(0, age) + } + } +} +/*: + * **Remarque** : `didSet` n'est pas appellé lors de la première initialisation de la propriété, qui fait sens puisque `self` n'est pas encore totalement initialisé + */ +final class PersonObservers { + + var person: Person { + didSet { + print("Person has received new value(s)") + print("OLD VALUES :" , oldValue.firstName, oldValue.lastName, oldValue.age) + print("NEW VALUES :" , person.firstName, person.lastName, person.age) + } + } + + init(person: Person) { + self.person = person + } +} + +let lucas = Person(firstName: "Lucas", lastName: "Abijmil", age: 22) +let lucasObserver = PersonObservers(person: lucas) +lucasObserver.person.age = -4 +lucasObserver.person.age // 0 grâce à la logique du didSet du model diff --git a/Property Observers.playground/contents.xcplayground b/Property Observers.playground/contents.xcplayground new file mode 100644 index 0000000..515976f --- /dev/null +++ b/Property Observers.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Protocols.playground/Pages/Introduction.xcplaygroundpage/Contents.swift b/Protocols.playground/Pages/Introduction.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..e0d9260 --- /dev/null +++ b/Protocols.playground/Pages/Introduction.xcplaygroundpage/Contents.swift @@ -0,0 +1,6 @@ +/*: + # Protocol & Protocol Oriented Programming (POP) + * [Déclaration & conformance à un protocol](Protocol) + * [Protocol & implémentation par défaut](Protocol%20Extension) + * [Protocol inheritance & POP](Protocol%20Inheritance) + */ diff --git a/Protocols.playground/Pages/Protocol Extension.xcplaygroundpage/Contents.swift b/Protocols.playground/Pages/Protocol Extension.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..b009205 --- /dev/null +++ b/Protocols.playground/Pages/Protocol Extension.xcplaygroundpage/Contents.swift @@ -0,0 +1,192 @@ +/*: + # Protocol & Implémentation par défaut + * Les `protocols` sont très puissant en Swift, selon Apple, Swift est un POP + * La puissance des implémentations par défaut est que cela permet d'avoir le concept d'héritage mais avec tout type d'objet : `value` ou `reference` type + * Une implémentation par défaut ce fait dans une `extension` du `protocol` + */ +/*: + Création d'un `protocol` `ColorChangable` et implémentation par 3 objets + */ +protocol ColorChangable { + func changeColor() +} + +final class MyButton: ColorChangable { + + func changeColor() { + print("Changing to white...") + } +} + +struct MyLabel: ColorChangable { + + func changeColor() { + print("Changing to white...") + } +} + +final class MyView: ColorChangable { + + func changeColor() { + print("Changing to white...") + } +} +/*: + ⚠️ **Code smell** : on fait trois fois la même implémentation + * Pour éviter cela, on va faire une implémentation par défaut pour la fonction `changeColor` dans une `extension` du `protocol` + * Les objets conformes à ce `protocol` n'ont plus nécessairement besoin d'implémenter `changeColor` + * Si les objets implémentent `changeColor` alors on utilise cette implémentation, autrement on prend l'implémentation par défaut (faite dans l'`extension` du `protocol`) + */ +protocol ColorChangable1 { + func changeColor() +} + +extension ColorChangable1 { + + func changeColor() { + print("Changing to white...") + } +} + +final class MyButton1: ColorChangable1 { + + // implémentation propre à MyButton1 + func changeColor() { + print("Changing to black...") + } +} + +struct MyLabel1: ColorChangable1 { + + // implémentation propre à MyLabel1 + func changeColor() { + print("Changing to red...") + } +} + +final class MyView1: ColorChangable1 { } + +let myButton1 = MyButton1() +myButton1.changeColor() // utilisation de l'implémentation de l'objet +let myLabel1 = MyLabel1() +myLabel1.changeColor() // utilisation de l'implémentation de l'objet +let myView1 = MyView1() +myView1.changeColor() // utilisation de l'implémentation par défaut +//: Exemple avec deux `protocols` qui font chacun une implémentation par défaut +protocol ColorChangable2 { + func changeColor() +} + +extension ColorChangable2 { + + func changeColor() { + print("Changing to white...") + } +} + +protocol TextClearable2 { + func clearText() +} + +extension TextClearable2 { + + func clearText() { + print("Clearing text...") + } +} + +final class MyButton2: ColorChangable2, TextClearable2 { + + // implémentation propre à MyButton2 + func changeColor() { + print("Changing to black...") + } +} + +struct MyLabel2: ColorChangable2, TextClearable2 { + + // implémentation propre à MyLabel2 + func changeColor() { + print("Changing to red...") + } + + // Implémentation propre à MyLabel2 + func clearText() { + print("Clearing last character...") + } +} + +final class MyView2: ColorChangable2, TextClearable2 { } + +let myButton2 = MyButton2() +myButton2.changeColor() // utilisation de l'implémentation de l'objet +myButton2.clearText() // utilisation de l'implémentation par défaut +let myLabel2 = MyLabel2() +myLabel2.changeColor() // utilisation de l'implémentation de l'objet +myLabel2.clearText() // utilisation de l'implémentation de l'objet +let myView2 = MyView2() +myView2.changeColor() // utilisation de l'implémentation par défaut +myView2.clearText() // utilisation de l'implémentation par défaut +/*: + Combiner des `protocols` grâce au `protocol inheritance` + * Création d'un `protocol` qui hérite de `ColorChangable3` & `TextClearable3` + * `ColorAndTextUpdatable3` hérite de tous les requirements et implémentation par défaut de `ColorChangable3` & `TextClearable3` + * On décide de ne pas rajouter de requierements *propre* à `ColorAndTextUpdatable3` + */ +protocol ColorChangable3 { + func changeColor() +} + +extension ColorChangable3 { + + func changeColor() { + print("Changing to white...") + } +} + +protocol TextClearable3 { + func clearText() +} + +extension TextClearable3 { + + func clearText() { + print("Clearing text...") + } +} + +protocol ColorAndTextUpdatable3: ColorChangable3, TextClearable3 { } + +final class MyButton3: ColorAndTextUpdatable3 /* = ColorChangable3, TextClearable3 */ { + + // implémentation propre à MyButton2 + func changeColor() { + print("Changing to black...") + } +} + +struct MyLabel3: ColorAndTextUpdatable3 /* = ColorChangable3, TextClearable3*/ { + + // implémentation propre à MyLabel2 + func changeColor() { + print("Changing to red...") + } + + // Implémentation propre à MyLabel2 + func clearText() { + print("Clearing last character...") + } +} + +final class MyView3: ColorAndTextUpdatable3 { } + +let myButton3 = MyButton3() +myButton3.changeColor() // utilisation de l'implémentation de l'objet +myButton3.clearText() // utilisation de l'implémentation par défaut +let myLabel3 = MyLabel3() +myLabel3.changeColor() // utilisation de l'implémentation de l'objet +myLabel3.clearText() // utilisation de l'implémentation de l'objet +let myView3 = MyView3() +myView3.changeColor() // utilisation de l'implémentation par défaut +myView3.clearText() // utilisation de l'implémentation par défaut +//: [< Previous: Déclaration & conformance à un protocol](@previous)           [Home](Introduction)           [Next: Protocol inheritance & POP >](@next) diff --git a/Protocols.playground/Pages/Protocol Inheritance.xcplaygroundpage/Contents.swift b/Protocols.playground/Pages/Protocol Inheritance.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..560e519 --- /dev/null +++ b/Protocols.playground/Pages/Protocol Inheritance.xcplaygroundpage/Contents.swift @@ -0,0 +1,181 @@ +/*: + # Protocol inheritance + * Un des pilliers de la POP 👉 évite les *massiv `protocol`* + * Permet aux `protocols` d'hériter d'autres `protocols` et d'ajouter de nouveaux requierements propres à ce `protocol` si besoin + */ +/*: + Exemple d'un *massiv protocol* `GameUnit` représentant tout type d'unité dans un jeu-vidéo + */ +protocol GameUnit { + var name: String { get } + var position: (x: Float, y: Float) { get set } + var health: UInt { get set } + var hitPower: UInt { get set } + + mutating func moveBy(_ x: Float, _ y: Float) + func fireAt(unit: inout GameUnit) +} +//: Exemple d'une class `Trooper` qui se conforme à ce `protocol` +final class Trooper: GameUnit { + + var name: String + var position: (x: Float, y: Float) + var health: UInt + var hitPower: UInt + + init(name: String, position: (Float, Float), health: UInt, hitPower: UInt) { + self.name = name + self.position = position + self.health = health + self.hitPower = hitPower + } + + func moveBy(_ x: Float, _ y: Float) { + self.position.x += x + self.position.y += y + } + + func fireAt(unit: inout GameUnit) { + unit.health -= 1 + } +} +/*: + Exemple d'une class `Building` qui se conforme à ce `protocol` + * On remarque que `moveBy` et `fireAt` ne sont pas nécessaires pour cet objet + * Conclusion 👉 `GameUnit` est *massiv* puisque certaines unités ne font pas d'implémentation propre pour certains requierements + */ +final class Building: GameUnit { + + var name: String + var position: (x: Float, y: Float) + var health: UInt + var hitPower: UInt + + init(name: String, position: (Float, Float), health: UInt, hitPower: UInt) { + self.name = name + self.position = position + self.health = health + self.hitPower = hitPower + } + + func moveBy(_ x: Float, _ y: Float) { + // MARK: NOT NEEDED CAUSE BUILDING CAN'T MOVE + } + + func fireAt(unit: inout GameUnit) { + // MARK: NOT NEEDED + } +} +/*: + `GameUnit` a donc trop de responsabilité / requierements 👇 + * Création de plusieurs `protocols` qui pourront hérité les uns des autres pour un design plus conscencieux + */ +/*: + Chaque unité dans le jeu doit avoir comme properties un `name`, `position` & `health` 👇 + * Création d'un `protocol BaseGameUnit` qui a ces requierements + */ +protocol BaseGameUnit { + var name: String { get } + var position: (x: Float, y: Float) { get set } + var health: UInt { get set } +} +/*: + Une unité peut être mouvante, c'est donc un `BaseGameUnit` mais avec la function `moveBy` + * Création d'un `protocol MovingGameUnit` héritant de `BaseGameUnit` et qui a comme requierement en plus la function `moveBy` + */ +protocol MovingGameUnit: BaseGameUnit { + mutating func moveBy(_ x: Float, _ y: Float) +} +/*: + Une unité peut tirer, c'est donc un `BaseGameUnit` mais avec la propriété `hitPower` et la function `fireAt` + * Création d'un `protocol MilitaryGameUnit` héritant de `BaseGameUnit` et qui a comme requierement en plus la property `hitPower` et la function `fireAt` + */ +protocol MilitaryGameUnit: BaseGameUnit { + var hitPower: UInt { get set } + + func fireAt(unit: inout BaseGameUnit) +} +//: Un building est donc de type / se conforme à `BaseGameUnit` car ne tire pas et ne se déplace pas (≠ `MovingGameUnit` & `MilitaryGameUnit`) +final class Buildings: BaseGameUnit { + + var name: String + var position: (x: Float, y: Float) + var health: UInt + + init(name: String, position: (Float, Float), health: UInt) { + self.name = name + self.position = position + self.health = health + } +} +//: Créons un objet représentant une unité mouvante `Horse`. Il est donc du type / se conforme à `MovingGameUnit` +struct Horse: MovingGameUnit { + + var name: String + var position: (x: Float, y: Float) + var health: UInt + + mutating func moveBy(_ x: Float, _ y: Float) { + self.position.x += x + self.position.y += y + } +} +//: Créons un objet représentant une unité mouvante et qui peut tirer `Warrior`. Il est donc du type / se conforme à `MovingGameUnit` & `MilitaryGameUnit` +struct Warrior: MovingGameUnit, MilitaryGameUnit { + + var name: String + var position: (x: Float, y: Float) + var health: UInt + var hitPower: UInt + + mutating func moveBy(_ x: Float, _ y: Float) { + self.position.x += x + self.position.y += y + } + + func fireAt(unit: inout BaseGameUnit) { + unit.health -= 2 + } +} +/*: + Pour les unités qui tire et se déplace, celles-ci doivent se conformer à `MovingGameUnit` & `MilitaryGameUnit`. On peut donc merger de deux manières ces `protocols` : + * Création d'un `protocol` héritant de `MovingGameUnit` & `MilitaryGameUnit` et qui ne rajoute pas de nouveaux requierements + * Création d'un `typealias` + */ +//: Méthode 1 : création d'un `protocol MovingMilitaryUnit` héritant de ces deux `protocols` +protocol MovingMilitaryUnit: MovingGameUnit, MilitaryGameUnit { } + +struct Soldier: MovingMilitaryUnit { + var name: String + var position: (x: Float, y: Float) + var health: UInt + var hitPower: UInt + + mutating func moveBy(_ x: Float, _ y: Float) { + self.position.x += x + self.position.y += y + } + + func fireAt(unit: inout BaseGameUnit) { + unit.health -= 2 + } +} +//: Méthode 2 : création d'un `typealias` +typealias MovingMilitaryGameUnit = MovingGameUnit & MilitaryGameUnit + +struct Tank: MovingMilitaryGameUnit { + var name: String + var position: (x: Float, y: Float) + var health: UInt + var hitPower: UInt + + mutating func moveBy(_ x: Float, _ y: Float) { + self.position.x += x + self.position.y += y + } + + func fireAt(unit: inout BaseGameUnit) { + unit.health -= 2 + } +} +//: [< Previous: Protocol & implémentation par défaut](@previous)           [Home](Introduction)           [Next >](@next) diff --git a/Protocols.playground/Pages/Protocol.xcplaygroundpage/Contents.swift b/Protocols.playground/Pages/Protocol.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..e99a6cb --- /dev/null +++ b/Protocols.playground/Pages/Protocol.xcplaygroundpage/Contents.swift @@ -0,0 +1,75 @@ +import Foundation +/*: + # Déclaration & Conformance à un protocol + * Un `protocol` est une interface qui définit les `properties` et `functions` requises pour être du type / conforme à ce `protocol` + * Dans la déclaration d'un `protocol` on ne fait que déclarer les `properties` et `functions` (incluant l'`init`), **on ne fait pas l'implémentation** + * Pour les `value type`, il faudra préciser le `mutating` avant les fonctions si cela est nécessaire + */ +/*: + Les `properties` doivent être déclaré `get` ou `get set` : + * `get` : constante, variable, computed property (get only) ou private(set) 👉 non mutable en dehors de sa définition + * `get set` : variable ou computed property (get & set) 👉 mutable en dehors de sa définition + * Par défaut le compilateur met toutes les `properties` en variable car il les considère en tant que `computed properties` + */ +protocol CryptoCurrency { + var name: String { get } + var price: Int { get set } + + init(price: Int) + + func showHistory() + mutating func transfer() +} +//: Création d'une `struct` conforme au protocol `CryptoCurrency` +struct Bitcoin: CryptoCurrency { + + let name: String + var price: Int + + init(price: Int) { + self.name = "Btc" + self.price = price + } + + func showHistory() { + print(name, "history is so bad !") + } + + mutating func transfer() { + print("Transfering", name, "...") + price += 100 + print("Actual price", price) + } +} +//: Création d'une `final class` conforme au protocol `CryptoCurrency` +final class Ethereum: CryptoCurrency { + + let name: String + var price: Int + + init(price: Int) { + self.name = "Eth" + self.price = price + } + + func showHistory() { + print(name, "history is so cool !") + } + + func transfer() { + print("Transfering", name, "...") + price += 1000 + print("Actual price", price) + } +} +/*: + Avantage d'utiliser les `protocols` plutôt que l'héritage de class : + * Implémentation propre à chaque objet conforme / du type du `protocol` + * Une `value type` ou `reference type` peut être conforme à un même `protocol` + * On peut se conformer à plusieurs `protocols` tandis qu'on peut hériter que d'une **seule class** en Swift + * Combiner plusieurs objets qui sont conformes au même protocol 👇 + */ +let cryptoCurrencies: [CryptoCurrency] = [Bitcoin(price: 900), Ethereum(price: 500)] +cryptoCurrencies.forEach { print($0.name, $0.price) } +cryptoCurrencies.forEach { $0.showHistory() } +//: [Home](Introduction)           [Next: Protocol & implémentation par défaut >](@next) diff --git a/Protocols.playground/contents.xcplayground b/Protocols.playground/contents.xcplayground new file mode 100644 index 0000000..d84484c --- /dev/null +++ b/Protocols.playground/contents.xcplayground @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/RawRepresentable.playground/Contents.swift b/RawRepresentable.playground/Contents.swift new file mode 100644 index 0000000..380de5d --- /dev/null +++ b/RawRepresentable.playground/Contents.swift @@ -0,0 +1,51 @@ +import Foundation +/*: + # Protocol RawRepresentable + * Permet de storer une valeur sous-jacente au sein d'un type (plus bas niveau que le type lui-même) + * Cette propriété / valeur est nommé `rawValue` (de la même manière qu'une `enum` avec une `rawValue`) + * *Requierement du protocol* : implémentation d'une propriété nommé `rawValue` du **type que l'on souhaite** + */ +//: Création d'une `enum` dont la `rawValue` est de type `String` +enum Numbers: String { + case zero + case one +} +//: Accès à la `rawValue` de chaque cas +Numbers.zero.rawValue +Numbers.one.rawValue +/*: + Création d'une `struct` conforme au protocol `RawRepresentable` + * Conformance au protocol grâce à la propriété `rawValue`, ici de type `String` + */ +struct LocalizedKey: RawRepresentable { + var rawValue: String + + var localizedString: String { + return NSLocalizedString(rawValue, comment: "") + } + + // MARK: Utile pour faire de la data validation lors de l'init + /*init?(rawValue: String) { + // Bussiness logic here + }*/ +} +/*: + Avantage par rapport à une `enum` qui a une `rawValue` : + * Pour une `enum` tous les cas doivent être listés dans la déclaration de l'`enum`. Les `enum` ont besoin d'être exhaustive (type safety) + * Tandis qu'avec un type conforme à `RawRepresentable` n'a pas besoin d'être exhaustif, car il y a peu de chance qu'on ai besoin de switcher sur tous les cas de ce type (ici `LocalizedKey`) + * À l'inverse on veut garder le `type safety` du type et avoir accès à sa `rawValue` lorsqu'on en a besoin + */ +//: De plus on peut définir nos cas dans une extension, ce qui n'est pas possible avec une `enum` +extension LocalizedKey { + static let firstScreenTitle = LocalizedKey(rawValue: "first.screen.title") +} + +extension LocalizedKey { + static let secondScreenTitle = LocalizedKey(rawValue: "second.screen.title") +} + +LocalizedKey.firstScreenTitle.localizedString +LocalizedKey.secondScreenTitle.localizedString +//: Ressemble de très près (100 %) à la déclaration lorsqu'on accède à la `rawValue` d'une `enum` +LocalizedKey.firstScreenTitle.rawValue +LocalizedKey.secondScreenTitle.rawValue diff --git a/RawRepresentable.playground/contents.xcplayground b/RawRepresentable.playground/contents.xcplayground new file mode 100644 index 0000000..515976f --- /dev/null +++ b/RawRepresentable.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Static Properties & Methods.playground/Contents.swift b/Static Properties & Methods.playground/Contents.swift new file mode 100644 index 0000000..bcb6a72 --- /dev/null +++ b/Static Properties & Methods.playground/Contents.swift @@ -0,0 +1,29 @@ +import Foundation + +/*: + # **`static` properties & functions** + * Permet l'accès à des propriétés et fonctions d'un type sans créer une instance de celui-ci + * Ces propriétés et fonctions sont considérées comme des constantes "partagées" par le type + * Les `static` properties sont `lazy` par défaut + * Peut être utilisé dans des `struct`, `class` ou `enum` + */ + +//: Déclaration d'une `struct` qui déclare une `static` properties (*creator*) +struct Video { + + static let creator = "Lucas Abijmil" + var title: String + var views: Int +} +//: Accès à *creator* sans création d'instance du type `Video` : +Video.creator +//: Création d'une instance du type `Video` +var video = Video(title: "Formation Swift", views: 2_500) +//: Accès à une `static` properties ou function **uniquement depuis le type et non depuis une instance créée** +// video.creator ––> KO +/*: + ## Cas d'utilisation : + * UI d'une application (très courant) : `enum UI` / `struct UI` + * singleton : `static let shared = MyObject()` (pas très recommandé en vrai) + * Module + */ diff --git a/Static Properties & Methods.playground/contents.xcplayground b/Static Properties & Methods.playground/contents.xcplayground new file mode 100644 index 0000000..89da2d4 --- /dev/null +++ b/Static Properties & Methods.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Strings extensions & tricks.playground/Pages/Custom String Interpolation.xcplaygroundpage/Contents.swift b/Strings extensions & tricks.playground/Pages/Custom String Interpolation.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..cd8e8d8 --- /dev/null +++ b/Strings extensions & tricks.playground/Pages/Custom String Interpolation.xcplaygroundpage/Contents.swift @@ -0,0 +1,29 @@ +import Foundation +//: ### Création d'une custom `String Interpolation` +struct User { + let firstName: String + let lastName: String +} + +let user = User(firstName: "Lucas", lastName: "Abijmil") + +//: String Interpolation pour utiliser le prénom & nom du user (classique) +print("My name is \(user.firstName) \(user.lastName)") + +/*: Si la même string revient souvent avec les mêmes arguments d'interpolations : + * Implémentons une custom String Interpolation qui nous facilitera la vie + + **Extension sur le type `String.StringInterpolation` via la fonction `appendInterpolation` (convention)** + */ + +extension String.StringInterpolation { + mutating func appendInterpolation(userPresented: User) { + appendInterpolation("My name is \(userPresented.firstName) \(userPresented.lastName)") + } +} + +//: Exemple d'utilisation : +print("\(userPresented: user)") + + +//: [< Previous: `optionalString.orEmpty`](@previous)           [Home](Introduction)           [Next: `Localized String` with `String Interpolation` >](@next) diff --git a/Strings extensions & tricks.playground/Pages/Introduction.xcplaygroundpage/Contents.swift b/Strings extensions & tricks.playground/Pages/Introduction.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..a615ca6 --- /dev/null +++ b/Strings extensions & tricks.playground/Pages/Introduction.xcplaygroundpage/Contents.swift @@ -0,0 +1,9 @@ +/*: + +# Strings Extensions & tricks + + * [optionalString.orEmpty](OptionalString.orEmpty) : Encapsuler la logique d'unwrap pour les `Optional String` (*extension*) + * [Custom String Interpolation](Custom%20String%20Interpolation) : Création d'une custom `String Interpolation` (*extension*) + * [Localized String with String Interpolation](Localized%20String%20with%20String%20Interpolation) : Utilisation d'une `Localized String` via `String Interpolation` pouvant prendre des paramètres (*extension*) + * [StaticString & URL init custom](StaticString%20&%20URL%20init%20custom) : Définir des `URL` avec des `StaticString` au compile time, les rendant immutables pendant le runtime (*extension*) +*/ diff --git a/Strings extensions & tricks.playground/Pages/Localized String with String Interpolation.xcplaygroundpage/Contents.swift b/Strings extensions & tricks.playground/Pages/Localized String with String Interpolation.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..cf43aee --- /dev/null +++ b/Strings extensions & tricks.playground/Pages/Localized String with String Interpolation.xcplaygroundpage/Contents.swift @@ -0,0 +1,31 @@ +import Foundation +/*: + ### Utilisation d'une `Localized String` via `String Interpolation` pouvant prendre des paramètres + * En rapport avec les [Customs String Interpolation](@previous) + * **Extension sur le type `String.StringInterpolation` via la fonction `appendInterpolation` (convention)** + */ +extension String.StringInterpolation { + mutating func appendInterpolation(localized key: String, _ args: CVarArg...) { + let localized = String(format: NSLocalizedString(key, comment: ""), arguments: args) + appendLiteral(localized) + } +} + +/*: Exemple d'utilisation pour une `Localized String` sans paramètre : + * "welcome.screen" = "Bienvenue sur notre application !" + */ + +print("\(localized: "welcome.screen")") + +/*: Exemple d'utilisation pour une `Localized String` avc paramètre : + * "welcome.screen.greetings" = "Hello %@ !", où %@ = format pour une string + * Résultat : "Hello Lucas !" + */ + +struct User { + let firstName: String +} +let user = User(firstName: "Lucas") + +print("\(localized: "welcome.screen.greetings", user.firstName)") +//: [< Previous: Custom `String Interpolation`](@previous)           [Home](Introduction)           [Next: `StaticString` & `URL init` custom>](@next) diff --git a/Strings extensions & tricks.playground/Pages/OptionalString.orEmpty.xcplaygroundpage/Contents.swift b/Strings extensions & tricks.playground/Pages/OptionalString.orEmpty.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..87fb12a --- /dev/null +++ b/Strings extensions & tricks.playground/Pages/OptionalString.orEmpty.xcplaygroundpage/Contents.swift @@ -0,0 +1,33 @@ +import Foundation +/*: ### optionalString.orEmpty : Encapsuler la logique d'unwrap pour les `Optional String` + * Extension sur les `Optional` où l'optionnel est une `String` + * Gain en readability – évite des `nil coalscing` + * `Optional` : **enum avec deux case** + * `.some(let value)` + * `.none` + */ +extension Optional where Wrapped == String { + + var orEmpty: String { + switch self { + case .some(let value): + return value + case .none: + return "" + } + } +} + +//: Exemple d'utilisation +var name: String? = "Lucas" +func someBussinessLogic(for name: String) { + print(name) +} + +//: * Sans l'utilisation de l'extension – nil coalscing +someBussinessLogic(for: name ?? "") + +//: * Avec l'utilisation de l'extension +someBussinessLogic(for: name.orEmpty) + +//: [Home](Introduction)           [Next: Custom `String Interpolation` >](@next) diff --git a/Strings extensions & tricks.playground/Pages/StaticString & URL init custom.xcplaygroundpage/Contents.swift b/Strings extensions & tricks.playground/Pages/StaticString & URL init custom.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..cd4a37a --- /dev/null +++ b/Strings extensions & tricks.playground/Pages/StaticString & URL init custom.xcplaygroundpage/Contents.swift @@ -0,0 +1,25 @@ +import Foundation +/*: + ### Définir des `URL` avec des `StaticString` au compile time, les rendant immutables pendant le runtime + * permet d'éviter de force unwrap les `URL` connus et qu'on déclare à la mano + + L'`init(string: _)` oblige de force unwrap, car cette `URL` peut changer pendant le runtime. En la force unwrappant on indique implicitement qu'elle ne pourra changer pendant le runtime + */ +let url = URL(string: "https://www.google.fr")! +/*: `StaticString` : `String` connue au compile time qui ne pourra être modifiée pendant le runtime + * Grâce à ce type on va pouvoir définir des `URL` qu'on passe à la mano (et donc qui ne changeront pas au runtime) + * **Extension sur `URL` avec un force unwrap qui a du sens (vu que la `String` est immutable au runtime)** + */ +extension URL { + + init(staticString: StaticString) { + self.init(string: "\(staticString)")! + } +} + +let url2 = URL(staticString: "https://www.google.fr") + +//: Pour rajouter des paths à des `URL` créée à partir de cette extension : utiliser la fonction `appendPathComponent` +var url3 = URL(staticString: "https://www.google.fr") +url3.appendPathComponent("maps") +//: [< Previous: Custom `String Interpolation`](@previous)           [Home](Introduction)           [Next >](@next) diff --git a/Strings extensions & tricks.playground/contents.xcplayground b/Strings extensions & tricks.playground/contents.xcplayground new file mode 100644 index 0000000..6d3fc15 --- /dev/null +++ b/Strings extensions & tricks.playground/contents.xcplayground @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Typealias.playground/Contents.swift b/Typealias.playground/Contents.swift new file mode 100644 index 0000000..eab0a47 --- /dev/null +++ b/Typealias.playground/Contents.swift @@ -0,0 +1,19 @@ +import Foundation +/*: + # **`Typealias`** : + * Permet de renommer un type déjà existant + */ + +//: `typealias` sur le type `Int` +typealias ID = Int +func fetchUserName(userID: Int, completion: @escaping (Result) -> Void) { } +func fetchUserName1(userID: ID, completion: @escaping (Result) -> Void) { } +//: ## Création de `typealias` pour le `Result` de fonction asynchrones 👍 +//: * `typealias` non optimisé car le type du `Result` est hard-codé (`String`) +typealias CompletionString = (Result) -> Void +func fetchUserName2(userID: ID, completion: @escaping CompletionString) { } +//: * `typealias` générique, optimisé car on pourra déclarer le type du `Result` +typealias Completion = (Result) -> Void +func fetchUserName3(userID: ID, completion: @escaping Completion) { } +func fetchUserIsPremium(userID: ID, completion: @escaping Completion) { } +func fetchUserFriends(userID: ID, completion: @escaping Completion<[String]>) { } diff --git a/Typealias.playground/contents.xcplayground b/Typealias.playground/contents.xcplayground new file mode 100644 index 0000000..515976f --- /dev/null +++ b/Typealias.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/URLComponents + URLRequest/URLRequest.xcodeproj/project.pbxproj b/URLComponents + URLRequest/URLRequest.xcodeproj/project.pbxproj new file mode 100644 index 0000000..cbbd9c6 --- /dev/null +++ b/URLComponents + URLRequest/URLRequest.xcodeproj/project.pbxproj @@ -0,0 +1,585 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 8D6C0F0524FA84C600041F8C /* URLRequestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D6C0F0424FA84C600041F8C /* URLRequestApp.swift */; }; + 8D6C0F0724FA84C600041F8C /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D6C0F0624FA84C600041F8C /* ContentView.swift */; }; + 8D6C0F0924FA84C900041F8C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D6C0F0824FA84C900041F8C /* Assets.xcassets */; }; + 8D6C0F0C24FA84C900041F8C /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D6C0F0B24FA84C900041F8C /* Preview Assets.xcassets */; }; + 8D6C0F1724FA84C900041F8C /* URLRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D6C0F1624FA84C900041F8C /* URLRequestTests.swift */; }; + 8D6C0F2224FA84C900041F8C /* URLRequestUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D6C0F2124FA84C900041F8C /* URLRequestUITests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 8D6C0F1324FA84C900041F8C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8D6C0EF924FA84C600041F8C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D6C0F0024FA84C600041F8C; + remoteInfo = URLRequest; + }; + 8D6C0F1E24FA84C900041F8C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8D6C0EF924FA84C600041F8C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D6C0F0024FA84C600041F8C; + remoteInfo = URLRequest; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 8D6C0F0124FA84C600041F8C /* URLRequest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = URLRequest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D6C0F0424FA84C600041F8C /* URLRequestApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRequestApp.swift; sourceTree = ""; }; + 8D6C0F0624FA84C600041F8C /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 8D6C0F0824FA84C900041F8C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 8D6C0F0B24FA84C900041F8C /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 8D6C0F0D24FA84C900041F8C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8D6C0F1224FA84C900041F8C /* URLRequestTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = URLRequestTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D6C0F1624FA84C900041F8C /* URLRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRequestTests.swift; sourceTree = ""; }; + 8D6C0F1824FA84C900041F8C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8D6C0F1D24FA84C900041F8C /* URLRequestUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = URLRequestUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D6C0F2124FA84C900041F8C /* URLRequestUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRequestUITests.swift; sourceTree = ""; }; + 8D6C0F2324FA84C900041F8C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D6C0EFE24FA84C600041F8C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D6C0F0F24FA84C900041F8C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D6C0F1A24FA84C900041F8C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 8D6C0EF824FA84C600041F8C = { + isa = PBXGroup; + children = ( + 8D6C0F0324FA84C600041F8C /* URLRequest */, + 8D6C0F1524FA84C900041F8C /* URLRequestTests */, + 8D6C0F2024FA84C900041F8C /* URLRequestUITests */, + 8D6C0F0224FA84C600041F8C /* Products */, + ); + sourceTree = ""; + }; + 8D6C0F0224FA84C600041F8C /* Products */ = { + isa = PBXGroup; + children = ( + 8D6C0F0124FA84C600041F8C /* URLRequest.app */, + 8D6C0F1224FA84C900041F8C /* URLRequestTests.xctest */, + 8D6C0F1D24FA84C900041F8C /* URLRequestUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 8D6C0F0324FA84C600041F8C /* URLRequest */ = { + isa = PBXGroup; + children = ( + 8D6C0F0424FA84C600041F8C /* URLRequestApp.swift */, + 8D6C0F0624FA84C600041F8C /* ContentView.swift */, + 8D6C0F0824FA84C900041F8C /* Assets.xcassets */, + 8D6C0F0D24FA84C900041F8C /* Info.plist */, + 8D6C0F0A24FA84C900041F8C /* Preview Content */, + ); + path = URLRequest; + sourceTree = ""; + }; + 8D6C0F0A24FA84C900041F8C /* Preview Content */ = { + isa = PBXGroup; + children = ( + 8D6C0F0B24FA84C900041F8C /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 8D6C0F1524FA84C900041F8C /* URLRequestTests */ = { + isa = PBXGroup; + children = ( + 8D6C0F1624FA84C900041F8C /* URLRequestTests.swift */, + 8D6C0F1824FA84C900041F8C /* Info.plist */, + ); + path = URLRequestTests; + sourceTree = ""; + }; + 8D6C0F2024FA84C900041F8C /* URLRequestUITests */ = { + isa = PBXGroup; + children = ( + 8D6C0F2124FA84C900041F8C /* URLRequestUITests.swift */, + 8D6C0F2324FA84C900041F8C /* Info.plist */, + ); + path = URLRequestUITests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D6C0F0024FA84C600041F8C /* URLRequest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D6C0F2624FA84C900041F8C /* Build configuration list for PBXNativeTarget "URLRequest" */; + buildPhases = ( + 8D6C0EFD24FA84C600041F8C /* Sources */, + 8D6C0EFE24FA84C600041F8C /* Frameworks */, + 8D6C0EFF24FA84C600041F8C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = URLRequest; + productName = URLRequest; + productReference = 8D6C0F0124FA84C600041F8C /* URLRequest.app */; + productType = "com.apple.product-type.application"; + }; + 8D6C0F1124FA84C900041F8C /* URLRequestTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D6C0F2924FA84C900041F8C /* Build configuration list for PBXNativeTarget "URLRequestTests" */; + buildPhases = ( + 8D6C0F0E24FA84C900041F8C /* Sources */, + 8D6C0F0F24FA84C900041F8C /* Frameworks */, + 8D6C0F1024FA84C900041F8C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 8D6C0F1424FA84C900041F8C /* PBXTargetDependency */, + ); + name = URLRequestTests; + productName = URLRequestTests; + productReference = 8D6C0F1224FA84C900041F8C /* URLRequestTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 8D6C0F1C24FA84C900041F8C /* URLRequestUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D6C0F2C24FA84C900041F8C /* Build configuration list for PBXNativeTarget "URLRequestUITests" */; + buildPhases = ( + 8D6C0F1924FA84C900041F8C /* Sources */, + 8D6C0F1A24FA84C900041F8C /* Frameworks */, + 8D6C0F1B24FA84C900041F8C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 8D6C0F1F24FA84C900041F8C /* PBXTargetDependency */, + ); + name = URLRequestUITests; + productName = URLRequestUITests; + productReference = 8D6C0F1D24FA84C900041F8C /* URLRequestUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8D6C0EF924FA84C600041F8C /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1200; + LastUpgradeCheck = 1200; + TargetAttributes = { + 8D6C0F0024FA84C600041F8C = { + CreatedOnToolsVersion = 12.0; + }; + 8D6C0F1124FA84C900041F8C = { + CreatedOnToolsVersion = 12.0; + TestTargetID = 8D6C0F0024FA84C600041F8C; + }; + 8D6C0F1C24FA84C900041F8C = { + CreatedOnToolsVersion = 12.0; + TestTargetID = 8D6C0F0024FA84C600041F8C; + }; + }; + }; + buildConfigurationList = 8D6C0EFC24FA84C600041F8C /* Build configuration list for PBXProject "URLRequest" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 8D6C0EF824FA84C600041F8C; + productRefGroup = 8D6C0F0224FA84C600041F8C /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D6C0F0024FA84C600041F8C /* URLRequest */, + 8D6C0F1124FA84C900041F8C /* URLRequestTests */, + 8D6C0F1C24FA84C900041F8C /* URLRequestUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D6C0EFF24FA84C600041F8C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D6C0F0C24FA84C900041F8C /* Preview Assets.xcassets in Resources */, + 8D6C0F0924FA84C900041F8C /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D6C0F1024FA84C900041F8C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D6C0F1B24FA84C900041F8C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D6C0EFD24FA84C600041F8C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D6C0F0724FA84C600041F8C /* ContentView.swift in Sources */, + 8D6C0F0524FA84C600041F8C /* URLRequestApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D6C0F0E24FA84C900041F8C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D6C0F1724FA84C900041F8C /* URLRequestTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D6C0F1924FA84C900041F8C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D6C0F2224FA84C900041F8C /* URLRequestUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 8D6C0F1424FA84C900041F8C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D6C0F0024FA84C600041F8C /* URLRequest */; + targetProxy = 8D6C0F1324FA84C900041F8C /* PBXContainerItemProxy */; + }; + 8D6C0F1F24FA84C900041F8C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D6C0F0024FA84C600041F8C /* URLRequest */; + targetProxy = 8D6C0F1E24FA84C900041F8C /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 8D6C0F2424FA84C900041F8C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 8D6C0F2524FA84C900041F8C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 8D6C0F2724FA84C900041F8C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"URLRequest/Preview Content\""; + DEVELOPMENT_TEAM = S7HY56XG6J; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = URLRequest/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.URLRequest"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 8D6C0F2824FA84C900041F8C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"URLRequest/Preview Content\""; + DEVELOPMENT_TEAM = S7HY56XG6J; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = URLRequest/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.URLRequest"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 8D6C0F2A24FA84C900041F8C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = S7HY56XG6J; + INFOPLIST_FILE = URLRequestTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.URLRequestTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/URLRequest.app/URLRequest"; + }; + name = Debug; + }; + 8D6C0F2B24FA84C900041F8C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = S7HY56XG6J; + INFOPLIST_FILE = URLRequestTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.URLRequestTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/URLRequest.app/URLRequest"; + }; + name = Release; + }; + 8D6C0F2D24FA84C900041F8C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = S7HY56XG6J; + INFOPLIST_FILE = URLRequestUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.URLRequestUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = URLRequest; + }; + name = Debug; + }; + 8D6C0F2E24FA84C900041F8C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = S7HY56XG6J; + INFOPLIST_FILE = URLRequestUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "Lucas-Abijmil.URLRequestUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = URLRequest; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8D6C0EFC24FA84C600041F8C /* Build configuration list for PBXProject "URLRequest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D6C0F2424FA84C900041F8C /* Debug */, + 8D6C0F2524FA84C900041F8C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D6C0F2624FA84C900041F8C /* Build configuration list for PBXNativeTarget "URLRequest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D6C0F2724FA84C900041F8C /* Debug */, + 8D6C0F2824FA84C900041F8C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D6C0F2924FA84C900041F8C /* Build configuration list for PBXNativeTarget "URLRequestTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D6C0F2A24FA84C900041F8C /* Debug */, + 8D6C0F2B24FA84C900041F8C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D6C0F2C24FA84C900041F8C /* Build configuration list for PBXNativeTarget "URLRequestUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D6C0F2D24FA84C900041F8C /* Debug */, + 8D6C0F2E24FA84C900041F8C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 8D6C0EF924FA84C600041F8C /* Project object */; +} diff --git a/URLComponents + URLRequest/URLRequest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/URLComponents + URLRequest/URLRequest.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/URLComponents + URLRequest/URLRequest.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/URLComponents + URLRequest/URLRequest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/URLComponents + URLRequest/URLRequest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/URLComponents + URLRequest/URLRequest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/URLComponents + URLRequest/URLRequest/Assets.xcassets/AccentColor.colorset/Contents.json b/URLComponents + URLRequest/URLRequest/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/URLComponents + URLRequest/URLRequest/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/URLComponents + URLRequest/URLRequest/Assets.xcassets/AppIcon.appiconset/Contents.json b/URLComponents + URLRequest/URLRequest/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/URLComponents + URLRequest/URLRequest/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/URLComponents + URLRequest/URLRequest/Assets.xcassets/Contents.json b/URLComponents + URLRequest/URLRequest/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/URLComponents + URLRequest/URLRequest/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/URLComponents + URLRequest/URLRequest/ContentView.swift b/URLComponents + URLRequest/URLRequest/ContentView.swift new file mode 100644 index 0000000..5ab6a7d --- /dev/null +++ b/URLComponents + URLRequest/URLRequest/ContentView.swift @@ -0,0 +1,144 @@ +// +// ContentView.swift +// URLRequest +// +// Created by Lucas Abijmil on 29/08/2020. +// + +import SwiftUI + +struct ContentView: View { + var body: some View { + ZStack { + Text("Hello, world!") + .padding() + } + .onAppear { + let url = try? generateURL() + guard let bis = url else { return } + callApi(with: bis) + let postURl = try? postURL() + guard let purl = postURl else { return } + postApi(with: purl) + } + } + func generateURL() throws -> URL { + // Création d'une URL solid via URLComponents + var components = URLComponents() + // scheme + components.scheme = "https" + // "site" + components.host = "jsonplaceholder.typicode.com" + // "chemin" + components.path = "/users" + + // Check en cas d'erreur + guard let url = components.url else { + throw URLError.uknwown("") + } + + return url + } + + func postURL() throws -> URL { + // Création d'une URL solid via URLComponents + var components = URLComponents() + // scheme + components.scheme = "https" + // "site" + components.host = "jsonplaceholder.typicode.com" + // chemin + components.path = "/posts" + + guard let url = components.url else { + throw URLError.uknwown("") + } + + return url + } + + func callApi(with url: URL) { + // si méthode get pas nécessaire de faire une URLRequest + URLSession.shared.dataTask(with: url) { data, response, error in + print(url) + if let response = response { + print(response) + } + + if let error = error { + print(error) + } + + if let data = data { + print(data) + do { + // convertion : bytes --> JSON format pas de parsing en format interne app + let json = try JSONSerialization.jsonObject(with: data, options: []) + print(json) + } catch { + print(error.localizedDescription) + } + } + } + // ne pas oublie de resume la dataTask + .resume() + } + + func postApi(with url: URL) { + + // dictionnaire qui contient les informations nécessaires envoyé au serveur + let paramaters = ["username": "lucasAbijmil", "tweet" : "je suis le meilleur dév iOS de France"] + + // convertion : dictionnaire ––> JSONFormat + guard let httpBody = try? JSONSerialization.data(withJSONObject: paramaters, options: []) else { return } + + // création de l'URLRequest + var request = URLRequest(url: url) + // On pourrai faire une URLRequest pour la méthode GET mais pas obligatoire + request.httpMethod = "POST" + // on fournit les data (JSON) + request.httpBody = httpBody + // permet générer un JSON interprétable pour l'API + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + print(url) + print(request) + + + + + URLSession.shared.dataTask(with: request) { data, response, error in + + if let response = response { + print(response) + } + + if let data = data { + print(data) + do { + let json = try JSONSerialization.jsonObject(with: data, options: []) + print(json) + } catch { + print(error.localizedDescription) + } + } + + + if let error = error { + print(error) + } + } + // ne pas oublie de resume la dataTask + .resume() + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} + + +enum URLError: Error { + case uknwown(String) +} diff --git a/URLComponents + URLRequest/URLRequest/Info.plist b/URLComponents + URLRequest/URLRequest/Info.plist new file mode 100644 index 0000000..c02a6ac --- /dev/null +++ b/URLComponents + URLRequest/URLRequest/Info.plist @@ -0,0 +1,55 @@ + + + + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UIApplicationSupportsIndirectInputEvents + + UILaunchScreen + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/URLComponents + URLRequest/URLRequest/Preview Content/Preview Assets.xcassets/Contents.json b/URLComponents + URLRequest/URLRequest/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/URLComponents + URLRequest/URLRequest/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/URLComponents + URLRequest/URLRequest/URLRequestApp.swift b/URLComponents + URLRequest/URLRequest/URLRequestApp.swift new file mode 100644 index 0000000..75db3cd --- /dev/null +++ b/URLComponents + URLRequest/URLRequest/URLRequestApp.swift @@ -0,0 +1,17 @@ +// +// URLRequestApp.swift +// URLRequest +// +// Created by Lucas Abijmil on 29/08/2020. +// + +import SwiftUI + +@main +struct URLRequestApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/URLComponents + URLRequest/URLRequestTests/Info.plist b/URLComponents + URLRequest/URLRequestTests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/URLComponents + URLRequest/URLRequestTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/URLComponents + URLRequest/URLRequestTests/URLRequestTests.swift b/URLComponents + URLRequest/URLRequestTests/URLRequestTests.swift new file mode 100644 index 0000000..f8dca99 --- /dev/null +++ b/URLComponents + URLRequest/URLRequestTests/URLRequestTests.swift @@ -0,0 +1,33 @@ +// +// URLRequestTests.swift +// URLRequestTests +// +// Created by Lucas Abijmil on 29/08/2020. +// + +import XCTest +@testable import URLRequest + +class URLRequestTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/URLComponents + URLRequest/URLRequestUITests/Info.plist b/URLComponents + URLRequest/URLRequestUITests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/URLComponents + URLRequest/URLRequestUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/URLComponents + URLRequest/URLRequestUITests/URLRequestUITests.swift b/URLComponents + URLRequest/URLRequestUITests/URLRequestUITests.swift new file mode 100644 index 0000000..85c1cb7 --- /dev/null +++ b/URLComponents + URLRequest/URLRequestUITests/URLRequestUITests.swift @@ -0,0 +1,42 @@ +// +// URLRequestUITests.swift +// URLRequestUITests +// +// Created by Lucas Abijmil on 29/08/2020. +// + +import XCTest + +class URLRequestUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/Unit Tests.playground/Contents.swift b/Unit Tests.playground/Contents.swift new file mode 100644 index 0000000..5ca4b40 --- /dev/null +++ b/Unit Tests.playground/Contents.swift @@ -0,0 +1,178 @@ +import XCTest +/*: + # Tests Unitaires + * Permet de tester le fonctionnel d'une partie de code (généralement 1 test = 1 cas de fonctionnel) + * Doit être une class qui hérite de `XCTestCase` + * Chaque fonction de test doit être pré-fixée par `test` + */ +final class TestObject { + + let name: String + + init(name: String) { + self.name = name + } +} + +enum UknownError: Error { + case whateEver +} + +func throwingFunction() throws { + throw UknownError.whateEver +} + +func notThrowingFunction() throws { } + + + +final class MyTests: XCTestCase { +//: ## Boolean Assertions +//: * `XCTAssert(expression)` 👉 Vérifie qu'un expression est vraie + func testAssert() { + let value = 0 + + XCTAssert(value == 0) + } +//: * `XCTAssertTrue(expression)`👉 Vérifie qu'un expression est vraie + func testAssertTrue() { + let value = 0 + + XCTAssertTrue(value == 0) + } +//: * `XCTAssertFalse(expression)` 👉 Vérifie qu'un expression est fausse + func testAssertFalse() { + let value = 0 + + XCTAssertFalse(value != 0) + } +//: ## Nil & Non-Nil Assertions +//: * `XCTAssertNil(expression)` 👉 Vérifie que la valeur d'une expression est nil + func testAssertNil() { + let value: Int? = nil + + XCTAssertNil(value) + } +//: * `XCTAssertNotNil(expression)` 👉 Vérifie que la valeur d'une expression est non-nil + func testAssertNotNil() { + let value: Int? = 0 + + XCTAssertNotNil(value) + } +//: * `XCTUnwrap(expression)` 👉 Permet d'unwrap une expression (le test throws du coup) + func testXCTUnwrap() throws { + let value = [1, 2, 3] + + let firstValue = try XCTUnwrap(value.first) + + XCTAssertEqual(firstValue, value[0]) + } +//: ## Equality & Inequality Assertions +//: * `XCTAssertEqual(expression1, expression2)` 👉 Vérifie que deux valeurs sont égales + func testXCTAssertEqual() { + let currentValue = [1, 2, 3] + let expectedValue = [1, 2, 3] + + XCTAssertEqual(currentValue, expectedValue) + } +//: * `XCTAssertNotEqual(expression1, expression2)` 👉 Vérifie que deux valeurs ne sont pas égales + func testXCTAssertNotEqual() { + let currentValue = [0, 1, 2] + let expectedValue = [1, 2, 3] + + XCTAssertNotEqual(currentValue, expectedValue) + } +//: * `XCTAssertIdentical(expression1, expression2)` 👉 Vérifie que deux instances sont égales (référence) + func testXCTAssertIdentical() { + let currentValue = TestObject(name: "Lucas") + + XCTAssertIdentical(currentValue, currentValue) + } +//: * `XCTAssertNotIdentical(expression1, expression2)` 👉 Vérifie que deux instances ne sont pas égales (référence) + func testXCTAssertNotIdentical() { + let currentValue = TestObject(name: "Lucas") + let expectedValue = TestObject(name: "Lucas") + + XCTAssertNotIdentical(currentValue, expectedValue) + } +//: ## Comparable Value Assertions +//: * `XCTAssertGreaterThan(expression1, expression2)` 👉 Vérifie que la valeur de l'expression1 est strictement supérieur à celle de l'expression2 + func testXCTAssertGreaterThan() { + let currentValue = 5 + let testValue = 0 + + XCTAssertGreaterThan(currentValue, testValue) + } +//: * `XCTAssertGreaterThanOrEqual(expression1, expression2)` 👉 Vérifie que la valeur de l'expression1 est supérieur ou égale à celle de l'expression2 + func testXCTAssertGreaterThanOrEqual() { + let currentValue = 5 + let testValue = 5 + + XCTAssertGreaterThanOrEqual(currentValue, testValue) + } +//: * `XCTAssertLessThan(expression1, expression2)` 👉 Vérifie que la valeur de l'expression1 est strictement inférieur à celle de l'expression2 + func testXCTAssertLessThan() { + let currentValue = 0 + let testValue = 5 + + XCTAssertLessThan(currentValue, testValue) + } +//: * `XCTAssertLessThanOrEqual(expression1, expression2)` 👉 Vérifie que la valeur de l'expression1 est inférieur ou égale à celle de l'expression2 + func testXCTAssertLessThanOrEqual() { + let currentValue = 5 + let testValue = 5 + + XCTAssertLessThanOrEqual(currentValue, testValue) + } +//: ## Check whether a function call throws, or doesn’t throw, an error +//: * `XCTAssertThrowsError(expression)` 👉 Vérifie qu'une expression génère une erreur (99,99% du temps pour des function throws) + func testXCTAssertThrowsError() { + XCTAssertThrowsError(try throwingFunction()) + } +//: * `XCTAssertNoThrow(expression)` 👉 Vérifie qu'une expression ne génère pas d'erreur (99,99% du temps pour des function throws) + func testXCTAssertNoThrow() { + XCTAssertNoThrow(try notThrowingFunction()) + } +//: ## Generate a failure immediately and unconditionally +//: * `XCTFail(_ message: String = "")` 👉 Génère une erreur immédiatement & sans condition + func testXCTFail() { + XCTFail("Test fail") + } +//: ## Anticipate known test failures to prevent failing tests from affecting your workflows +//: * `XCTExpectFailure(_ failureReason: String? = nil)` 👉 Vérifie que le test échoue mais ne renvoie pas de failure pour les UTs + func testXCTExpectFailure() { + let shouldPass = false + XCTExpectFailure("I should fix this UT") + XCTAssertTrue(shouldPass) + } +//: ## Skip tests when meeting specified conditions. +//: * `XCTSkip(_ message: String = "")` 👉 Permet d'ignorer (de ne pas le tester) le test + func testwhatever() { + let shouldBeSkipped = true + if shouldBeSkipped { + XCTSkip("Skipping this UT : \(#function)") + } + } +//: ## Asynchronous Tests and Expectations + func testAsynchronousFunction() { +//: 1) Créer une `XCTestExpectation` 👉 uniquement pour tester du code asynchrone + let expectation = XCTestExpectation(description: "Download apple.com home page") +//: 2) Appel de la fonction asynchrone (ici un call réseau) + let url = URL(string: "https://apple.com")! + URLSession.shared.dataTask(with: url) { data, _, error in + if let error = error { +//: 3) S'il y a une erreur 👉 `XCTFail` afin d'arrêter le test et de renvoyer une erreur + XCTFail(error.localizedDescription) + } else { + XCTAssertNotNil(data) +//: 4) Sinon tout s'est bien passé 👉 appel de `fulfill()` sur le `XCTestExpectation` 👉 le code asynchrone est fini + expectation.fulfill() + } + } + .resume() +//: 5) `wait(for expectations: [XCTestExpectation], timeout seconds: TimeInterval)` 👉 attend jusqu'à ce le `XCTestExpectation` soit satisfait, avec un delai maximum de 10 secondes + wait(for: [expectation], timeout: 10.0) + } +} + +MyTests.defaultTestSuite.run() // Permet de run les test unitaires dans XCode diff --git a/Unit Tests.playground/contents.xcplayground b/Unit Tests.playground/contents.xcplayground new file mode 100644 index 0000000..6156abb --- /dev/null +++ b/Unit Tests.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/init, init?, convenience, requiered.playground/Pages/Class.xcplaygroundpage/Contents.swift b/init, init?, convenience, requiered.playground/Pages/Class.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..5275afb --- /dev/null +++ b/init, init?, convenience, requiered.playground/Pages/Class.xcplaygroundpage/Contents.swift @@ -0,0 +1,7 @@ +//: [Previous](@previous) + +import Foundation + +var greeting = "Hello, playground" + +//: [Next](@next) diff --git a/init, init?, convenience, requiered.playground/Pages/Struct.xcplaygroundpage/Contents.swift b/init, init?, convenience, requiered.playground/Pages/Struct.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..5275afb --- /dev/null +++ b/init, init?, convenience, requiered.playground/Pages/Struct.xcplaygroundpage/Contents.swift @@ -0,0 +1,7 @@ +//: [Previous](@previous) + +import Foundation + +var greeting = "Hello, playground" + +//: [Next](@next) diff --git a/init, init?, convenience, requiered.playground/Pages/Untitled Page.xcplaygroundpage/Contents.swift b/init, init?, convenience, requiered.playground/Pages/Untitled Page.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..d791bee --- /dev/null +++ b/init, init?, convenience, requiered.playground/Pages/Untitled Page.xcplaygroundpage/Contents.swift @@ -0,0 +1,345 @@ +import UIKit + + +// MARK: L'initialisation concerne les Structure, Class & Enum et s'assure que toute les propriétés soient initialisées + +// MARK: - Structure + +// MARK: Memberwise init : à l'inverse des class, le compilo créer un init pour chacunes des properties de la structure si et seulement si aucun init n'est définit dans la déclaration de cette structure +struct PersonneStruct { + + var name: String + var age: Int +} +let personStruct = PersonneStruct(name: "Lucas", age: 22) + +// MARK: Si on souhaite définir un init "custom" pour la structure et garder le MEMBERWISE init, il faut faire cela dans une extension, en dehors de la déclaration de la structure +// Pour l'exemple je décide que mon custom init ne reçoit qu'une String en paramètre +extension PersonneStruct { + init(name: String) { + self.name = name + self.age = 0 + } +} +let personneStruct = PersonneStruct(name: "Lucas") + +// MARK: A noter qu'on peut créer autant d'init qu'on le souhaite pour une même structure +struct CelsiusStruct { + + var temperature: Double + + init(fromFahrenheit fahrenheit: Double) { + temperature = (fahrenheit - 32.0) / 1.8 + } + + init(fromKelvin kelvin: Double) { + temperature = kelvin - 273.15 + } + + init(_ celsius: Double) { + self.temperature = celsius + } +} +let celsiusFromKStruct = CelsiusStruct(fromKelvin: 500) +let celsiusFromFahStruct = CelsiusStruct(fromFahrenheit: 55) +let celsiusStruct = CelsiusStruct(30) + +// MARK: - Valeurs par défaut pour certaines propriétés +// Si certaines propriétés ont des valeurs par défaut alors le MEMBERWISE init prendra cela en compte automatiquement et générera autant d'init que de situation possible +// Si on définit notre init dans la déclaration alors on peut utilisé des valeurs par défaut pour les arguments de l'init +struct PersonneWithDefaultPropertiesStruct { + + var name = "Lucas" + var age: Int + + // MARK: Exemple d'un init custom en prenant compte de la valeur par défaut de la propriété + // init(name: String = "Lucas", age: Int) { + // self.name = name + // self.age = age + //} +} +let personWithDefaultProperties1Struct = PersonneWithDefaultPropertiesStruct(age: 22) // Lucas, 22 +let personWithDefaultProperties2Struct = PersonneWithDefaultPropertiesStruct(name: "Jacques", age: 50) // Jacques, 50 + +// MARK: - Valeurs par défaut pour toutes les propriétés +// Si toutes les propriétés ont des valeurs par défaut alors le MEMBERWISE init prendra cela en compte automatiquement et générera autant d'init que de situation possible +// Si on définit notre init dans la déclaration alors on peut utilisé des valeurs par défaut pour les arguments +struct JoueurStruct { + + var name: String = "Uknown" + var numero: Int = 3 + + // MARK: Exemple d'un init custom en prenant compte de la valeur par défaut de toutes les propriétés + // init(name: String = "Uknown", numero: Int = 3) { + // self.name = name + // self.numero = numero + // } +} + +let joueurStruct1 = JoueurStruct() // init qui utilise la valeur par défaut pour toutes les propriétés (name & numero) +let joueurStruct2 = JoueurStruct(name: "Lucas") // init qui utilise la valeur par défaut pour la propriété numero +let joueurStruct3 = JoueurStruct(numero: 9) // init qui utilise la valeur par défaut pour la propriété name +let joueurStruct4 = JoueurStruct(name: "Lucas", numero: 90) // init pour ne pas utiliser les valeurs par défaut + +// MARK: - Optional Properties & Memberwise init +// Une propriété optionnel est par défaut initialisé à nil. Ainsi on se retrouve dans la même configuration que pour une valeur par défaut (cf `Valeurs par défaut pour certaines propriété`) +struct JoueurWithOptionalNumeroStruct { + + var name: String + var numero: Int? + + // MARK: Memberwise init sous jacent + // init(name: String, numero: Int? = nil) { + // self.name = name + // self.numero = numero + // } +} +let joueurWithNilNumero1Struct = JoueurWithOptionalNumeroStruct(name: "Lucas") // init qui set numéro à nil (implicit) +let joueurWithNilNumero2Struct = JoueurWithOptionalNumeroStruct(name: "Lucas", numero: nil) // init qui set numéro à nil (explicit) +let joueurWithANumeroStruct = JoueurWithOptionalNumeroStruct(name: "Lucas", numero: 5) // init qui set numéro à Optional(5) + +// MARK: - Failable init : permet d'initialiser ou non un objet en fonction de certaines conditions +// À noter qu'il est préférable de le faire dans la déclaration afin de casser le MEMBERWISE init +struct AnimalStruct { + + let name: String + + init?(name: String) { + guard !name.isEmpty else { return nil } + self.name = name + } +} +let animalFailStruct = AnimalStruct(name: "") // nil +let animalOptionalStruct = AnimalStruct(name: "Lion") // Optional(Animal) + +// MARK: - Initializer Delegation : un init qui appel un autre init afin d'éviter de dupliquer du code +struct SizeStruct { + + var width = 0.0 + var height = 0.0 +} + +struct PointStruct { + var x = 0.0 + var y = 0.0 +} + +struct RectStruct { + + var origin = PointStruct() + var size = SizeStruct() + + init(origin: PointStruct, size: SizeStruct) { + self.origin = origin + self.size = size + } + + // initializer delegation + init(center: PointStruct, size: SizeStruct) { + let oriX = center.x - (size.width / 2) + let oriY = center.y - (size.height / 2) + self.init(origin: PointStruct(x: oriX, y: oriY), size: size) // Appel de l'autre init + } +} +let rectClassique = RectStruct(origin: PointStruct(x: 7, y: 7), size: SizeStruct(width: 7, height: 7)) // init sans delegation +let rectDelegation = RectStruct(center: PointStruct(x: 8, y: 8), size: SizeStruct(width: 7, height: 7)) // init avec delegation + + + + + + + +// MARK: - Class + +// MARK: - Designated init : créer un init pour une class, car ne profite pas du Memberwise init créer par le compilo +// MARK: Designated init & héritage : le call à super.init doit toujours se trouver dans le designated init après l'initation de toutes les propriété de la childClass +class PersonneClass { + + var name: String + var age: Int + + init(name: String, age: Int) { + self.name = name + self.age = age + // super.init() + } +} +let personClass = PersonneClass(name: "Lucas", age: 22) + +// MARK: À noter qu'on peut créer autant de designated init qu'on le souhaite pour la même class +class CelsiusClass { + + var temperature: Double + + init(fromFahrenheit fahrenheit: Double) { + temperature = (fahrenheit - 32.0) / 1.8 + } + + init(fromKelvin kelvin: Double) { + temperature = kelvin - 273.15 + } + + init(_ celsius: Double) { + self.temperature = celsius + } +} +let celsiusFromKClass = CelsiusClass(fromKelvin: 500) +let celsiusFromFahClass = CelsiusClass(fromFahrenheit: 55) +let celsiusClass = CelsiusClass(30) + + + +// MARK: - Valeurs par défaut pour certaines propriétés +// Si certaines propriété ont des valeurs par défaut alors on peut soit créer des init pour puvoir utilisé les valeur par défaut ou les initialisé à la mano +// Ou on peut faire les deux en un grâce aux valeur par défaut des argument de l'init, afin de ne pas cr"er pleins de designated init pour couvrir toues les possibilités. + +class PersonneWithDefaultPropertiesClass { + + var name = "Lucas" + var age: Int + + init(name: String = "Lucas", age: Int) { + self.name = name + self.age = age + } +} +let personWithDefaultProperties1Class = PersonneWithDefaultPropertiesClass(age: 22) // Lucas, 22 +let personWithDefaultProperties2Class = PersonneWithDefaultPropertiesClass(name: "Jacques", age: 50) // Jacques 50 + + +// MARK: - Valeurs par défaut pour toutes les propriétés +// Si toutes les propriétés ont des valeurs par défaut alors on peut les utiliser pour créer un init avec des paramètres par défaut afin de pouvoir initialiser un objet avec d'autre valeurs pour les propriétés +class JoueurClass { + + var name: String = "Uknown" + var numero: Int = 3 + + init(name: String = "Uknown", numero: Int = 3) { + self.name = name + self.numero = numero + } +} +let joueurClass1 = JoueurClass() // init qui utilise la valeur par défaut pour toutes les propriétés (name & numero) +let joueurClass2 = JoueurClass(name: "Lucas") // init qui utilise la valeur par défaut pour la propriété numero +let joueurClass3 = JoueurClass(numero: 9) // init qui utilise la valeur par défaut pour la propriété name +let joueurClass4 = JoueurClass(name: "Lucas", numero: 90) // init pour ne pas utiliser les valeurs par défaut + +// MARK: On peut également ne jamais vouloir changer ses propriétés +// Dans ce là, un init est automatiquement généré pour la class si TOUTES les properties ont une valeur par défaut. Cet init ne prend aucun paramètre +class JoueurClassBis { + + var name: String = "Uknown" + var numero: Int = 3 +} +let joueurBis = JoueurClassBis() + +// MARK: Attention si on souhaite utiliser les valeurs par défaut, càd utiliser l'init sans argument mais que d'autre designated init sont créer alors il faut le rajouter dans la déclaration de la clas +class JoueurClassTer { + + var name: String = "Uknown" + var numero: Int = 3 + + init() { } + + init(name: String, numero: Int) { + self.name = name + self.numero = numero + } +} +let joueurTer = JoueurClassTer() // utilisation des valeurs par défaut +let joueurTer1 = JoueurClassTer(name: "Lucas", numero: 90) // utilisation du designated init qui prend des paramètres + +// MARK: - Optional Properties & Designated init +// Les properties optionnel sont initalisé à nil par défaut +// On peut donc soit créer deux init: soit pour laisser la value à nil soit afin de l'initialiser à la mano +// Tips: on peut faire les deux en mettant en valeur par défaut le paramètre à nil +class JoueurWithOptionalNumeroClass { + + var name: String + var numero: Int? + + init(name: String, numero: Int? = nil) { + self.name = name + self.numero = numero + } +} +let joueurWithNilNumero1Class = JoueurWithOptionalNumeroClass(name: "Lucas") // init qui set numéro à nil (implicit) +let joueurWithNilNumero2Class = JoueurWithOptionalNumeroClass(name: "Lucas", numero: nil) // init qui set numéro à nil (explicit) +let joueurWithANumeroClass = JoueurWithOptionalNumeroClass(name: "Lucas", numero: 5) // init qui set numéro à Optional(5) + + +// MARK: - Convenience init : permet de créer un autre init et d'appellé dans celui cti le designated init +// MARK: Un convenience init doit toujours appeller un designed init +// Si la class hérite d'une autre class, alors le super.init doit toujours être dans le designed init + +// MARK: Remarque : +// 1) Un designed init appelle toujours un designed init de la superclass || delegate up +// 2) Un convenience init appelle toujours appeller un designed init de leur class || delegate across + +class CivilClass { + + var prenom: String + var nomDeFamille: String + var age: Int + + init(prenom: String, nomDeFamille: String, age: Int) { + self.prenom = prenom + self.nomDeFamille = nomDeFamille + self.age = age + } + + convenience init(prenom: String) { + self.init(prenom: prenom, nomDeFamille: "Abijmil", age: 22) + } +} +var designatedClass = CivilClass(prenom: "Lucas", nomDeFamille: "Abijmil", age: 22) +var convenienceClass = CivilClass(prenom: "Lucas") + + +// MARK: - Inherited init où les subclass peuvent hérité des inits de leur superclass : +// - 1) Si la subclass ne définit aucun *designated init* alors elle héritent de tous les *designated inits* de la superclass +// - 2) Si la subclass implémente tous les *designated inits* de la superclass, alors elle hérite de tous les *convenience inits* de la superclass +// => Remarque : l'implémentation des *designated init* d'une super class peut se faire par un *convenience init* de la subclass + + +// MARK: - Failable init : permet d'initialiser ou non un objet en fonction de certaines conditions +class AnimalClass { + + let name: String + + init?(name: String) { + guard !name.isEmpty else { return nil } + self.name = name + } +} +let animalFailClass = AnimalClass(name: "") // nil +let animalOptionalClass = AnimalClass(name: "Lion") // Optional(Animal) + +// MARK: - Requiered init : un init qui doit être implémenté par chaque sublass (utilisation assez rare) +// Les subclass ne sont pas obligé de faire une implémentation explicit du required init du moment qu'ils satisfaient le requierement hérité + +class SomeClass { + + var age: Int + + init(age: Int) { self.age = age } + + required init() { + age = 3 + } +} + +class SomeOtherClass: SomeClass { + + var name: String + + init(name: String, age: Int) { + self.name = name + super.init(age: age) + } + + required init() { + fatalError("init() has not been implemented") + } +} diff --git a/init, init?, convenience, requiered.playground/contents.xcplayground b/init, init?, convenience, requiered.playground/contents.xcplayground new file mode 100644 index 0000000..5da2641 --- /dev/null +++ b/init, init?, convenience, requiered.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/private(set).playground/Contents.swift b/private(set).playground/Contents.swift new file mode 100644 index 0000000..47e6fe9 --- /dev/null +++ b/private(set).playground/Contents.swift @@ -0,0 +1,60 @@ +import Foundation +/*: + # **`private(set)`** + * Permet de rendre le setter en private tout en laissant le getter open pour une propriété d'un type + */ + +/*: + ## Propriété sans `private(set)` + * La propriété (`name`) peut être modifié en dehors de la définition du type : soit par la fonction du type soit par la propriété directemment + */ +struct Person { + var name: String + + mutating func changeName(for name: String) { + self.name = name + } +} + +var individu = Person(name: "Pierre") +print(individu.name) + +// Modification via la propriété du type +individu.name = "Paul" +print(individu.name) + +// Modification via la fonction du type +individu.changeName(for: "Jacques") +print(individu.name) +/*: + ## Ce qu'on aime généralement pour rendre notre code de meilleure qualité + * Modifier la propriété en elle-même **uniquement dans la définition du type** + * Modifier la propriété **uniquement via une fonction si en dehors de la définition du type** + * Permet d'indiquer qu'on est au courant de ces changements => intention de code + + On aurait pu mettre la propriété en `private` mais on ne pourrait plus accéder au getter en dehors de la définition du type + */ + +/*: + ## Pour améliorer ce code, on doit passer le setter en private et laisser le getter en open + * Utilisation du `private(set)` + */ +struct PersonBis { + private(set) var name: String + + mutating func changeName(for name: String) { + self.name = name + } +} + +var personne = PersonBis(name: "Lucas") + +// Modification via la propriété du type impossible : setter non accessible +// lucasBis.name = "Marc" // KO + +// Getter toujours accessible +print(personne.name) + +// Modification via la fonction du type +personne.changeName(for: "David") +print(personne.name) diff --git a/private(set).playground/contents.xcplayground b/private(set).playground/contents.xcplayground new file mode 100644 index 0000000..7388470 --- /dev/null +++ b/private(set).playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/try, throws & rethrows.playground/Pages/introduction.xcplaygroundpage/Contents.swift b/try, throws & rethrows.playground/Pages/introduction.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..ddbe0d6 --- /dev/null +++ b/try, throws & rethrows.playground/Pages/introduction.xcplaygroundpage/Contents.swift @@ -0,0 +1,7 @@ +/*: + +# Try, try?, try!, throws & rethrows + + * [try try? try!](try%20try?%20try!) : Exécuter une fonction qui `throws` + * [rethrows](rethrows) : Exécuter une fonction A qui a comme paramètre une fonction B qui `throws` +*/ diff --git a/try, throws & rethrows.playground/Pages/rethrows.xcplaygroundpage/Contents.swift b/try, throws & rethrows.playground/Pages/rethrows.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..e1358cf --- /dev/null +++ b/try, throws & rethrows.playground/Pages/rethrows.xcplaygroundpage/Contents.swift @@ -0,0 +1,89 @@ +import Foundation +/*: + # **`rethrows` functions** + * À utiliser lorsqu'une fonction A prend comme paramètre une fonction B qui peut `throws` + */ +let data = [1, 2, 3, 4, 5] +/*: **Réimplémentons la fonction `map` pour comprendre `rethrows`** + * Équivaut à la fonction A + * (Utilisation de générique et fonction non optimisée) + */ +extension Sequence { + + func map2(_ transformed: (Element) -> T) -> [T] { + + var result: [T] = [] + + for element in self { + let transformedElement = transformed(element) + result.append(transformedElement) + } + + return result + } +} +//: Création d'une `Enum` pour les `Error` de la fonction B qui `throws` +enum TransformedError: Error { + case uknown +} +/*: Décalaration d'un fonction qui `throws` (pas grand intérêt uniquement pour l'exemple) + * Équivaut à la fonction B + */ +func transformed(_ number: Int) throws -> Int { + if Bool.random() { + return number + } else { + throw TransformedError.uknown + } +} +/*: + * Appel de la fonction A (`map2`) qui appelle la fonction B (`transformed`) + * Génération d'une erreur : `Invalid conversion from throwing function of type '(Int) throws -> Int' to non-throwing function type '(Int) -> Int` + */ +//_ = data.map2 { try transformed($0) } +/*: Le paramètre B dans la fonction `map2` ne `throws` pas, nous devons donc faire les modifications suivantes : + * Ajouter `throws` à la fonction B + * Ajouter `throws` à la fonction A (autrement génération de l'erreur `Errors thrown from here (try transformed(element)) are not handled`) + */ +extension Sequence { + + func map2Prime(_ transformed: (Element) throws -> T) throws -> [T] { + + var result: [T] = [] + + for element in self { + let transformedElement = try transformed(element) + result.append(transformedElement) + } + + return result + } +} + +//: On obtient donc le résultat suivant, lorsque la fonction B throws` +_ = try data.map2Prime { try transformed($0) } +/*: Cependant lorsqu'on appelle une fonction B qui ne `throws` pas on doit ajouter le mot `try` devant la fonction A + * À peu de sens car la fonction B ne `throws` pas donc aucune chance que la fonction A `throws` + */ +_ = try data.map2Prime { $0 * 2 } +/*: Ainsi on peut remplacer le `throws` de la fonction A pour un `rethrows` : + * Permet de ne pas rajouter ce `try` devant la fonction A si la fonction B ne `throws` pas + */ +extension Sequence { + + func map2Second(_ transformed: (Element) throws -> T) rethrows -> [T] { + + var result: [T] = [] + + for element in self { + let transformedElement = try transformed(element) + result.append(transformedElement) + } + + return result + } +} +//: On obtient donc les résultats suivants : +_ = try data.map2Second { try transformed($0) } +_ = data.map2Second { $0 * 2 } +//: [< Previous: `try, try?, try!`](@previous)           [Home](introduction)           [Next >](@next) diff --git a/try, throws & rethrows.playground/Pages/try try? try!.xcplaygroundpage/Contents.swift b/try, throws & rethrows.playground/Pages/try try? try!.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..a429f45 --- /dev/null +++ b/try, throws & rethrows.playground/Pages/try try? try!.xcplaygroundpage/Contents.swift @@ -0,0 +1,101 @@ +import Foundation +/*: + # **`try`, `try?`, `try!` & throws functions** + * Permet d'exécuter une fonction qui peut `throw` et qui peut donc générer des erreurs + */ + +//: Déclaration d'une `Enum` pour connaître la localisation +enum BatmanLocation { + case atHome + case atGym + case onMission +} +var batmanLocation: BatmanLocation = .atHome +//: Déclaration des erreurs grâce à une `Enum` conforme au protocol `Error` +enum BatmanIdentityError: Error { + case atGym + case onMission + case uknown + + var message: String { + switch self { + case .atGym: + return "At gym" + case .onMission: + return "On mission" + case .uknown: + return "Uknown error" + } + } +} + +//: Déclaration d'une fonction qui peut générer des erreurs (`throws`) +func getBatmanIdentity() throws -> String { + if case .atHome = batmanLocation { + return "Bruce Wayne" + } else if case .atGym = batmanLocation { + throw BatmanIdentityError.atGym + } else if case .onMission = batmanLocation { + throw BatmanIdentityError.onMission + } + throw BatmanIdentityError.uknown +} + + +/*: + ## Il existe trois façons pour exécuter une fonction qui `throw` + * `try` : dans un block `do` / `catch`, permet de return le résultat ou l'erreur s'il y en a une + */ + +//: Gestions des erreurs une par une +do { + let batmanIdentity = try getBatmanIdentity() + print(batmanIdentity) +} catch BatmanIdentityError.atGym { + print(BatmanIdentityError.atGym.message) +} catch BatmanIdentityError.onMission { + print(BatmanIdentityError.onMission.message) +} catch { // catch seul = toutes les erreurs possible sauf celles déjà listée au-dessus d'elle + print(BatmanIdentityError.uknown.message) +} +//: Un unique catch pour toutes les erreurs de type BatmanIdentityError +do { + let batmanIdentity = try getBatmanIdentity() + print(batmanIdentity) +} catch let (error as BatmanIdentityError) { + print(error.message) +} +//: On peut combiner la gestions d'erreurs en utilisant la "," +do { + let batmanIdentity = try getBatmanIdentity() + print(batmanIdentity) +} catch BatmanIdentityError.atGym, BatmanIdentityError.onMission { + print("Batman is at gym or on a mission") +} catch { // catch toutes les erreurs sauf celles déjà listés au dessus (atGym et onMission) + print(BatmanIdentityError.uknown.message) +} +//: try sans cas d'erreur spécifié catch toutes les erreurs (sauf celles déjà catch avant) +do { + let identity = try getBatmanIdentity() + print(identity) +} catch BatmanIdentityError.onMission { + print(BatmanIdentityError.onMission.message) +} catch { // catch toutes les erreurs possible sauf celles déjà listés au dessus (onMission ici dans ce cas) + print(BatmanIdentityError.uknown.message) +} +//: Un seul catch qui va catch toutes les erreurs +do { + let identity = try getBatmanIdentity() + print(identity) +} catch { // catch toutes les erreurs + print(BatmanIdentityError.uknown.message) +} +//: * `try?` : return le résultat en optionnel / `nil` en cas d'erreur +let optionnalBatmanIdentity = try? getBatmanIdentity() +print(optionnalBatmanIdentity) +//: Unwrap le résultat optionnel d'un try? avec un if let / guard let +if let nonOptionnalBatmanIdentity = try? getBatmanIdentity() { print(nonOptionnalBatmanIdentity) } +//: * `try!` : return le résultat en unwrappant l'optionnel / `fatalError` en cas d'erreur +let unwrapOptionnalBatmanIdentity = try! getBatmanIdentity() +print(unwrapOptionnalBatmanIdentity) +//: [Home](introduction)           [Next: `rethrows` >](@next) diff --git a/try, throws & rethrows.playground/contents.xcplayground b/try, throws & rethrows.playground/contents.xcplayground new file mode 100644 index 0000000..70f7ded --- /dev/null +++ b/try, throws & rethrows.playground/contents.xcplayground @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file