├── .gitignore ├── .swiftlint.yml ├── README.md ├── Social Contributor.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── adamrush.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── adamrush.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── Social Contributor ├── ColorScheme │ └── ColorScheme.swift ├── ContentView.swift ├── CoreData │ ├── Model.xcdatamodeld │ │ └── Model.xcdatamodel │ │ │ └── contents │ └── PersistenceController.swift ├── Extension │ ├── Bundle+Decodable.swift │ ├── Color+HEX.swift │ ├── String+BuildInfo.swift │ ├── URL+Extensions.swift │ ├── URLSession+APICall.swift │ ├── View+Extensions.swift │ └── View+Theme.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── Resources │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── quote │ │ │ ├── Contents.json │ │ │ ├── dark-blue.colorset │ │ │ └── Contents.json │ │ │ └── light-blue.colorset │ │ │ └── Contents.json │ ├── newsletters.json │ ├── no-results-found.json │ └── quotes.json ├── Screens │ ├── Home │ │ ├── Models │ │ │ └── Quote.swift │ │ ├── ViewModels │ │ │ └── HomeViewModel.swift │ │ └── Views │ │ │ ├── Components │ │ │ └── ViewModifiers.swift │ │ │ ├── HomeView.swift │ │ │ └── QuoteView.swift │ ├── Muse │ │ ├── MuseViewModel.swift │ │ └── Views │ │ │ ├── LottieView.swift │ │ │ └── MuseView.swift │ ├── Newsletter │ │ ├── Models │ │ │ └── NewsLetter.swift │ │ ├── NewsLetterListViewModel.swift │ │ └── Views │ │ │ └── NewsLetterListView.swift │ └── Settings │ │ ├── Models │ │ └── Contributor.swift │ │ ├── SettingsLabelStyle.swift │ │ └── Views │ │ └── SettingsView.swift ├── Services │ ├── ContributorsProvider.swift │ └── NewsLetterService.swift ├── Social_ContributorApp.swift └── Utils │ ├── AppReviewRequest.swift │ ├── Constants.swift │ ├── CustomColors.swift │ └── DecoderReport.swift ├── Social ContributorTests ├── Home │ └── HomeViewModelTests.swift └── Social_ContributorTests.swift └── banner.png /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/xcode,swift,macos 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=xcode,swift,macos 4 | 5 | ### macOS ### 6 | # General 7 | .DS_Store 8 | .AppleDouble 9 | .LSOverride 10 | 11 | # Icon must end with two \r 12 | Icon 13 | 14 | 15 | # Thumbnails 16 | ._* 17 | 18 | # Files that might appear in the root of a volume 19 | .DocumentRevisions-V100 20 | .fseventsd 21 | .Spotlight-V100 22 | .TemporaryItems 23 | .Trashes 24 | .VolumeIcon.icns 25 | .com.apple.timemachine.donotpresent 26 | 27 | # Directories potentially created on remote AFP share 28 | .AppleDB 29 | .AppleDesktop 30 | Network Trash Folder 31 | Temporary Items 32 | .apdisk 33 | 34 | ### macOS Patch ### 35 | # iCloud generated files 36 | *.icloud 37 | 38 | ### Swift ### 39 | # Xcode 40 | # 41 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 42 | 43 | ## User settings 44 | xcuserdata/ 45 | 46 | ## Obj-C/Swift specific 47 | *.hmap 48 | 49 | ## App packaging 50 | *.ipa 51 | *.dSYM.zip 52 | *.dSYM 53 | 54 | ## Playgrounds 55 | timeline.xctimeline 56 | playground.xcworkspace 57 | 58 | # Swift Package Manager 59 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 60 | # Packages/ 61 | # Package.pins 62 | # Package.resolved 63 | # *.xcodeproj 64 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 65 | # hence it is not needed unless you have added a package configuration file to your project 66 | # .swiftpm 67 | 68 | .build/ 69 | 70 | # CocoaPods 71 | # We recommend against adding the Pods directory to your .gitignore. However 72 | # you should judge for yourself, the pros and cons are mentioned at: 73 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 74 | # Pods/ 75 | # Add this line if you want to avoid checking in source code from the Xcode workspace 76 | # *.xcworkspace 77 | 78 | # Carthage 79 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 80 | # Carthage/Checkouts 81 | 82 | Carthage/Build/ 83 | 84 | # Accio dependency management 85 | Dependencies/ 86 | .accio/ 87 | 88 | # fastlane 89 | # It is recommended to not store the screenshots in the git repo. 90 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 91 | # For more information about the recommended setup visit: 92 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 93 | 94 | fastlane/report.xml 95 | fastlane/Preview.html 96 | fastlane/screenshots/**/*.png 97 | fastlane/test_output 98 | 99 | # Code Injection 100 | # After new code Injection tools there's a generated folder /iOSInjectionProject 101 | # https://github.com/johnno1962/injectionforxcode 102 | 103 | iOSInjectionProject/ 104 | 105 | ### Xcode ### 106 | 107 | ### Xcode Patch ### 108 | *.xcodeproj/* 109 | !*.xcodeproj/project.pbxproj 110 | !*.xcodeproj/xcshareddata/ 111 | !*.xcworkspace/contents.xcworkspacedata 112 | /*.gcno 113 | **/xcshareddata/WorkspaceSettings.xcsettings 114 | 115 | # End of https://www.toptal.com/developers/gitignore/api/xcode,swift,macos -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | # By default, SwiftLint uses a set of sensible default rules you can adjust: 2 | disabled_rules: # rule identifiers turned on by default to exclude from running 3 | - colon 4 | - comma 5 | - control_statement 6 | - trailing_whitespace 7 | opt_in_rules: # some rules are turned off by default, so you need to opt-in 8 | - empty_count # Find all the available rules by running: `swiftlint rules` 9 | 10 | # Alternatively, specify all rules explicitly by uncommenting this option: 11 | # only_rules: # delete `disabled_rules` & `opt_in_rules` if using this 12 | # - empty_parameters 13 | # - vertical_whitespace 14 | 15 | included: # paths to include during linting. `--path` is ignored if present. 16 | - Social Contributor 17 | excluded: # paths to ignore during linting. Takes precedence over `included`. 18 | - Carthage 19 | - Pods 20 | - Source/ExcludedFolder 21 | - Source/ExcludedFile.swift 22 | - Source/*/ExcludedFile.swift # Exclude files with a wildcard 23 | analyzer_rules: # Rules run by `swiftlint analyze` 24 | - explicit_self 25 | 26 | # configurable rules can be customized from this configuration file 27 | # binary rules can set their severity level 28 | force_cast: warning # implicitly 29 | force_try: 30 | severity: warning # explicitly 31 | # rules that have both warning and error levels, can set just the warning level 32 | # implicitly 33 | line_length: 110 34 | # they can set both implicitly with an array 35 | type_body_length: 36 | - 300 # warning 37 | - 400 # error 38 | # or they can set both explicitly 39 | file_length: 40 | warning: 500 41 | error: 1200 42 | # naming rules can set warnings/errors for min_length and max_length 43 | # additionally they can set excluded names 44 | type_name: 45 | min_length: 4 # only warning 46 | max_length: # warning and error 47 | warning: 40 48 | error: 50 49 | excluded: iPhone # excluded via string 50 | allowed_symbols: ["_"] # these are allowed in type names 51 | identifier_name: 52 | min_length: # only min_length 53 | error: 4 # only error 54 | excluded: # excluded via string array 55 | - id 56 | - URL 57 | - GlobalAPIKey 58 | - url 59 | reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, codeclimate, junit, html, emoji, sonarqube, markdown, github-actions-logging) 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftUI Social Contributor App 2 | 3 | ![alt text](https://github.com/adamrushy/social-swiftui-app/blob/main/banner.png) 4 | 5 | Learning SwiftUI can often be difficult or confusing and despite the many tutorials available it's often practice that will help with your learning. 6 | 7 | ## What is this repository? 8 | 9 | The idea of the Social Contributor App is that anyone can contribute, and we don't have any direction or even idea on what we're building. The idea is that you will contribute something and then somebody else will contribute more. 10 | 11 | We might end up building a brand new social bank, social media or chat forum or we might even build a game. The power is in your hands. 12 | 13 | ## How To Contribute? 14 | 15 | - Fork the repository to your profile 16 | - Clone the repository to your machine 17 | - Install SwiftLint to your machine with [Homebrew](https://brew.sh/) 18 | - `brew install swiftlint` 19 | - Create a new branch 20 | - Add some SwiftUI code 🔥 21 | - Create a PR 22 | 23 | We'll get this merged and then you need to pass the baton to somebody else. Use the Twitter template below to pass the baton: 24 | 25 | ### Twitter Template 26 | 27 | I have just contributed to the @adam9rush Social Contribute SwiftUI app, @person it's now your turn! 28 | 29 | You can follow the instructions on the Github project: 30 | https://github.com/adamrushy/social-swiftui-app 31 | 32 | #SocialSwiftUI 33 | -------------------------------------------------------------------------------- /Social Contributor.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0AD87B87282307FA00904E4E /* MuseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AD87B86282307FA00904E4E /* MuseView.swift */; }; 11 | 234F1C7F288F27CD006248B9 /* Color+HEX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 234F1C7E288F27CD006248B9 /* Color+HEX.swift */; }; 12 | 234F1C81288F29B2006248B9 /* DecoderReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 234F1C80288F29B2006248B9 /* DecoderReport.swift */; }; 13 | 5C66C0932822ECCE00B66FF8 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 5C66C0912822ECCE00B66FF8 /* Model.xcdatamodeld */; }; 14 | 5C66C0952822ECDA00B66FF8 /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C66C0942822ECDA00B66FF8 /* PersistenceController.swift */; }; 15 | 8DA05DA3282703A40005A514 /* NewsLetterListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DA05DA2282703A40005A514 /* NewsLetterListView.swift */; }; 16 | 8DA05DA7282704200005A514 /* NewsLetter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DA05DA6282704200005A514 /* NewsLetter.swift */; }; 17 | 8DA05DAA282704490005A514 /* NewsLetterService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DA05DA9282704490005A514 /* NewsLetterService.swift */; }; 18 | 8DA05DAD2827048C0005A514 /* newsletters.json in Resources */ = {isa = PBXBuildFile; fileRef = 8DA05DAC2827048C0005A514 /* newsletters.json */; }; 19 | 8DA05DAF282705590005A514 /* NewsLetterListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DA05DAE282705590005A514 /* NewsLetterListViewModel.swift */; }; 20 | 8DA05DB1282709170005A514 /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DA05DB0282709170005A514 /* View+Extensions.swift */; }; 21 | 8DA05DB428270A4D0005A514 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DA05DB328270A4D0005A514 /* Constants.swift */; }; 22 | BB3B3A952826DCBC0046DC4B /* AppReviewRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3B3A942826DCBC0046DC4B /* AppReviewRequest.swift */; }; 23 | C42E0620282859DC00C4D532 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E061F282859DC00C4D532 /* URL+Extensions.swift */; }; 24 | C42E062228285DAC00C4D532 /* Contributor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E062128285DAC00C4D532 /* Contributor.swift */; }; 25 | C42E062428285E4800C4D532 /* ContributorsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E062328285E4800C4D532 /* ContributorsProvider.swift */; }; 26 | D3CE07DD2823102600616F6C /* URLSession+APICall.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3CE07DC2823102600616F6C /* URLSession+APICall.swift */; }; 27 | D3CE07DF28231F8C00616F6C /* no-results-found.json in Resources */ = {isa = PBXBuildFile; fileRef = D3CE07DE28231F8C00616F6C /* no-results-found.json */; }; 28 | D3CE07E22823203300616F6C /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = D3CE07E12823203300616F6C /* Lottie */; }; 29 | D3CE07E42823204B00616F6C /* LottieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3CE07E32823204B00616F6C /* LottieView.swift */; }; 30 | D3CE07E62823296A00616F6C /* View+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3CE07E52823296A00616F6C /* View+Theme.swift */; }; 31 | D3CE07E92823336300616F6C /* MuseViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3CE07E82823336300616F6C /* MuseViewModel.swift */; }; 32 | E334B3772822D5BD002E9640 /* Social_ContributorApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E334B3762822D5BD002E9640 /* Social_ContributorApp.swift */; }; 33 | E334B3792822D5BD002E9640 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E334B3782822D5BD002E9640 /* ContentView.swift */; }; 34 | E334B37B2822D5BE002E9640 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E334B37A2822D5BE002E9640 /* Assets.xcassets */; }; 35 | E334B37E2822D5BE002E9640 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E334B37D2822D5BE002E9640 /* Preview Assets.xcassets */; }; 36 | E73361CA2823FF39009D0D9B /* ColorScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = E73361C92823FF39009D0D9B /* ColorScheme.swift */; }; 37 | EA92693F283BD32E0083EC82 /* quotes.json in Resources */ = {isa = PBXBuildFile; fileRef = EA92693E283BD32E0083EC82 /* quotes.json */; }; 38 | EA926945283BD3940083EC82 /* Quote.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA926944283BD3940083EC82 /* Quote.swift */; }; 39 | EA926947283BD3BE0083EC82 /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA926946283BD3BE0083EC82 /* HomeViewModel.swift */; }; 40 | EA926949283BD3D90083EC82 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA926948283BD3D90083EC82 /* HomeView.swift */; }; 41 | EA92694B283BD3EE0083EC82 /* QuoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA92694A283BD3EE0083EC82 /* QuoteView.swift */; }; 42 | EA92694D283BD57E0083EC82 /* Bundle+Decodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA92694C283BD57E0083EC82 /* Bundle+Decodable.swift */; }; 43 | EA92694F283BD7CD0083EC82 /* CustomColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA92694E283BD7CD0083EC82 /* CustomColors.swift */; }; 44 | EA926952283BD8F30083EC82 /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA926951283BD8F30083EC82 /* ViewModifiers.swift */; }; 45 | EA926954283BE0500083EC82 /* HomeViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA926953283BE0500083EC82 /* HomeViewModelTests.swift */; }; 46 | FA0DC4312822F3D5009AC9CC /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0DC4302822F3D5009AC9CC /* SettingsView.swift */; }; 47 | FA0DC4332822F4E3009AC9CC /* SettingsLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0DC4322822F4E3009AC9CC /* SettingsLabelStyle.swift */; }; 48 | FA0DC4362822F730009AC9CC /* String+BuildInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0DC4352822F730009AC9CC /* String+BuildInfo.swift */; }; 49 | FF2309D128255C6500C03D99 /* Social_ContributorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2309D028255C6500C03D99 /* Social_ContributorTests.swift */; }; 50 | /* End PBXBuildFile section */ 51 | 52 | /* Begin PBXContainerItemProxy section */ 53 | FF2309D228255C6500C03D99 /* PBXContainerItemProxy */ = { 54 | isa = PBXContainerItemProxy; 55 | containerPortal = E334B36B2822D5BD002E9640 /* Project object */; 56 | proxyType = 1; 57 | remoteGlobalIDString = E334B3722822D5BD002E9640; 58 | remoteInfo = "Social Contributor"; 59 | }; 60 | /* End PBXContainerItemProxy section */ 61 | 62 | /* Begin PBXFileReference section */ 63 | 0AD87B86282307FA00904E4E /* MuseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuseView.swift; sourceTree = ""; }; 64 | 234F1C7E288F27CD006248B9 /* Color+HEX.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+HEX.swift"; sourceTree = ""; }; 65 | 234F1C80288F29B2006248B9 /* DecoderReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecoderReport.swift; sourceTree = ""; }; 66 | 5C66C0922822ECCE00B66FF8 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = ""; }; 67 | 5C66C0942822ECDA00B66FF8 /* PersistenceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistenceController.swift; sourceTree = ""; }; 68 | 8DA05DA2282703A40005A514 /* NewsLetterListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsLetterListView.swift; sourceTree = ""; }; 69 | 8DA05DA6282704200005A514 /* NewsLetter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsLetter.swift; sourceTree = ""; }; 70 | 8DA05DA9282704490005A514 /* NewsLetterService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsLetterService.swift; sourceTree = ""; }; 71 | 8DA05DAC2827048C0005A514 /* newsletters.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = newsletters.json; sourceTree = ""; }; 72 | 8DA05DAE282705590005A514 /* NewsLetterListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsLetterListViewModel.swift; sourceTree = ""; }; 73 | 8DA05DB0282709170005A514 /* View+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Extensions.swift"; sourceTree = ""; }; 74 | 8DA05DB328270A4D0005A514 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 75 | BB3B3A942826DCBC0046DC4B /* AppReviewRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppReviewRequest.swift; sourceTree = ""; }; 76 | C42E061F282859DC00C4D532 /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = ""; }; 77 | C42E062128285DAC00C4D532 /* Contributor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contributor.swift; sourceTree = ""; }; 78 | C42E062328285E4800C4D532 /* ContributorsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContributorsProvider.swift; sourceTree = ""; }; 79 | D3CE07DC2823102600616F6C /* URLSession+APICall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+APICall.swift"; sourceTree = ""; }; 80 | D3CE07DE28231F8C00616F6C /* no-results-found.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "no-results-found.json"; sourceTree = ""; }; 81 | D3CE07E32823204B00616F6C /* LottieView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LottieView.swift; sourceTree = ""; }; 82 | D3CE07E52823296A00616F6C /* View+Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Theme.swift"; sourceTree = ""; }; 83 | D3CE07E82823336300616F6C /* MuseViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuseViewModel.swift; sourceTree = ""; }; 84 | E334B3732822D5BD002E9640 /* Social Contributor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Social Contributor.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 85 | E334B3762822D5BD002E9640 /* Social_ContributorApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Social_ContributorApp.swift; sourceTree = ""; }; 86 | E334B3782822D5BD002E9640 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 87 | E334B37A2822D5BE002E9640 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 88 | E334B37D2822D5BE002E9640 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 89 | E73361C92823FF39009D0D9B /* ColorScheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorScheme.swift; sourceTree = ""; }; 90 | EA92693E283BD32E0083EC82 /* quotes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = quotes.json; sourceTree = ""; }; 91 | EA926944283BD3940083EC82 /* Quote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Quote.swift; sourceTree = ""; }; 92 | EA926946283BD3BE0083EC82 /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = ""; }; 93 | EA926948283BD3D90083EC82 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; 94 | EA92694A283BD3EE0083EC82 /* QuoteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuoteView.swift; sourceTree = ""; }; 95 | EA92694C283BD57E0083EC82 /* Bundle+Decodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Decodable.swift"; sourceTree = ""; }; 96 | EA92694E283BD7CD0083EC82 /* CustomColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomColors.swift; sourceTree = ""; }; 97 | EA926951283BD8F30083EC82 /* ViewModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifiers.swift; sourceTree = ""; }; 98 | EA926953283BE0500083EC82 /* HomeViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModelTests.swift; sourceTree = ""; }; 99 | FA0DC4302822F3D5009AC9CC /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; 100 | FA0DC4322822F4E3009AC9CC /* SettingsLabelStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsLabelStyle.swift; sourceTree = ""; }; 101 | FA0DC4352822F730009AC9CC /* String+BuildInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+BuildInfo.swift"; sourceTree = ""; }; 102 | FF2309CE28255C6500C03D99 /* Social ContributorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Social ContributorTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 103 | FF2309D028255C6500C03D99 /* Social_ContributorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Social_ContributorTests.swift; sourceTree = ""; }; 104 | /* End PBXFileReference section */ 105 | 106 | /* Begin PBXFrameworksBuildPhase section */ 107 | E334B3702822D5BD002E9640 /* Frameworks */ = { 108 | isa = PBXFrameworksBuildPhase; 109 | buildActionMask = 2147483647; 110 | files = ( 111 | D3CE07E22823203300616F6C /* Lottie in Frameworks */, 112 | ); 113 | runOnlyForDeploymentPostprocessing = 0; 114 | }; 115 | FF2309CB28255C6500C03D99 /* Frameworks */ = { 116 | isa = PBXFrameworksBuildPhase; 117 | buildActionMask = 2147483647; 118 | files = ( 119 | ); 120 | runOnlyForDeploymentPostprocessing = 0; 121 | }; 122 | /* End PBXFrameworksBuildPhase section */ 123 | 124 | /* Begin PBXGroup section */ 125 | 5C66C0962822ECDD00B66FF8 /* CoreData */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 5C66C0912822ECCE00B66FF8 /* Model.xcdatamodeld */, 129 | 5C66C0942822ECDA00B66FF8 /* PersistenceController.swift */, 130 | ); 131 | path = CoreData; 132 | sourceTree = ""; 133 | }; 134 | 8DA05DA82827043C0005A514 /* Services */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 8DA05DA9282704490005A514 /* NewsLetterService.swift */, 138 | C42E062328285E4800C4D532 /* ContributorsProvider.swift */, 139 | ); 140 | path = Services; 141 | sourceTree = ""; 142 | }; 143 | 8DA05DAB282704790005A514 /* Resources */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | E334B37A2822D5BE002E9640 /* Assets.xcassets */, 147 | EA92693E283BD32E0083EC82 /* quotes.json */, 148 | D3CE07DE28231F8C00616F6C /* no-results-found.json */, 149 | 8DA05DAC2827048C0005A514 /* newsletters.json */, 150 | ); 151 | path = Resources; 152 | sourceTree = ""; 153 | }; 154 | 8DA05DB228270A440005A514 /* Utils */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | BB3B3A942826DCBC0046DC4B /* AppReviewRequest.swift */, 158 | 8DA05DB328270A4D0005A514 /* Constants.swift */, 159 | EA92694E283BD7CD0083EC82 /* CustomColors.swift */, 160 | 234F1C80288F29B2006248B9 /* DecoderReport.swift */, 161 | ); 162 | path = Utils; 163 | sourceTree = ""; 164 | }; 165 | D3CE07E72823334C00616F6C /* Muse */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | EA926937283BD1920083EC82 /* Views */, 169 | D3CE07E82823336300616F6C /* MuseViewModel.swift */, 170 | ); 171 | path = Muse; 172 | sourceTree = ""; 173 | }; 174 | E334B36A2822D5BD002E9640 = { 175 | isa = PBXGroup; 176 | children = ( 177 | E334B3752822D5BD002E9640 /* Social Contributor */, 178 | FF2309CF28255C6500C03D99 /* Social ContributorTests */, 179 | E334B3742822D5BD002E9640 /* Products */, 180 | ); 181 | sourceTree = ""; 182 | }; 183 | E334B3742822D5BD002E9640 /* Products */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | E334B3732822D5BD002E9640 /* Social Contributor.app */, 187 | FF2309CE28255C6500C03D99 /* Social ContributorTests.xctest */, 188 | ); 189 | name = Products; 190 | sourceTree = ""; 191 | }; 192 | E334B3752822D5BD002E9640 /* Social Contributor */ = { 193 | isa = PBXGroup; 194 | children = ( 195 | E334B3782822D5BD002E9640 /* ContentView.swift */, 196 | E334B3762822D5BD002E9640 /* Social_ContributorApp.swift */, 197 | 8DA05DB228270A440005A514 /* Utils */, 198 | 8DA05DAB282704790005A514 /* Resources */, 199 | 8DA05DA82827043C0005A514 /* Services */, 200 | E73361C82823FF1E009D0D9B /* ColorScheme */, 201 | FA0DC4342822F727009AC9CC /* Extension */, 202 | FA0DC42F2822F3C7009AC9CC /* Screens */, 203 | 5C66C0962822ECDD00B66FF8 /* CoreData */, 204 | E334B37C2822D5BE002E9640 /* Preview Content */, 205 | ); 206 | path = "Social Contributor"; 207 | sourceTree = ""; 208 | }; 209 | E334B37C2822D5BE002E9640 /* Preview Content */ = { 210 | isa = PBXGroup; 211 | children = ( 212 | E334B37D2822D5BE002E9640 /* Preview Assets.xcassets */, 213 | ); 214 | path = "Preview Content"; 215 | sourceTree = ""; 216 | }; 217 | E73361C82823FF1E009D0D9B /* ColorScheme */ = { 218 | isa = PBXGroup; 219 | children = ( 220 | E73361C92823FF39009D0D9B /* ColorScheme.swift */, 221 | ); 222 | path = ColorScheme; 223 | sourceTree = ""; 224 | }; 225 | EA926935283BD10F0083EC82 /* Settings */ = { 226 | isa = PBXGroup; 227 | children = ( 228 | EA92693C283BD20B0083EC82 /* Models */, 229 | EA926938283BD19F0083EC82 /* Views */, 230 | FA0DC4322822F4E3009AC9CC /* SettingsLabelStyle.swift */, 231 | ); 232 | path = Settings; 233 | sourceTree = ""; 234 | }; 235 | EA926936283BD1440083EC82 /* Newsletter */ = { 236 | isa = PBXGroup; 237 | children = ( 238 | EA92693B283BD1FB0083EC82 /* Models */, 239 | EA926939283BD1EC0083EC82 /* Views */, 240 | 8DA05DAE282705590005A514 /* NewsLetterListViewModel.swift */, 241 | ); 242 | path = Newsletter; 243 | sourceTree = ""; 244 | }; 245 | EA926937283BD1920083EC82 /* Views */ = { 246 | isa = PBXGroup; 247 | children = ( 248 | 0AD87B86282307FA00904E4E /* MuseView.swift */, 249 | D3CE07E32823204B00616F6C /* LottieView.swift */, 250 | ); 251 | path = Views; 252 | sourceTree = ""; 253 | }; 254 | EA926938283BD19F0083EC82 /* Views */ = { 255 | isa = PBXGroup; 256 | children = ( 257 | FA0DC4302822F3D5009AC9CC /* SettingsView.swift */, 258 | ); 259 | path = Views; 260 | sourceTree = ""; 261 | }; 262 | EA926939283BD1EC0083EC82 /* Views */ = { 263 | isa = PBXGroup; 264 | children = ( 265 | 8DA05DA2282703A40005A514 /* NewsLetterListView.swift */, 266 | ); 267 | path = Views; 268 | sourceTree = ""; 269 | }; 270 | EA92693B283BD1FB0083EC82 /* Models */ = { 271 | isa = PBXGroup; 272 | children = ( 273 | 8DA05DA6282704200005A514 /* NewsLetter.swift */, 274 | ); 275 | path = Models; 276 | sourceTree = ""; 277 | }; 278 | EA92693C283BD20B0083EC82 /* Models */ = { 279 | isa = PBXGroup; 280 | children = ( 281 | C42E062128285DAC00C4D532 /* Contributor.swift */, 282 | ); 283 | path = Models; 284 | sourceTree = ""; 285 | }; 286 | EA926940283BD3530083EC82 /* Home */ = { 287 | isa = PBXGroup; 288 | children = ( 289 | EA926943283BD3810083EC82 /* ViewModels */, 290 | EA926942283BD3750083EC82 /* Views */, 291 | EA926941283BD3700083EC82 /* Models */, 292 | ); 293 | path = Home; 294 | sourceTree = ""; 295 | }; 296 | EA926941283BD3700083EC82 /* Models */ = { 297 | isa = PBXGroup; 298 | children = ( 299 | EA926944283BD3940083EC82 /* Quote.swift */, 300 | ); 301 | path = Models; 302 | sourceTree = ""; 303 | }; 304 | EA926942283BD3750083EC82 /* Views */ = { 305 | isa = PBXGroup; 306 | children = ( 307 | EA926950283BD8CD0083EC82 /* Components */, 308 | EA926948283BD3D90083EC82 /* HomeView.swift */, 309 | EA92694A283BD3EE0083EC82 /* QuoteView.swift */, 310 | ); 311 | path = Views; 312 | sourceTree = ""; 313 | }; 314 | EA926943283BD3810083EC82 /* ViewModels */ = { 315 | isa = PBXGroup; 316 | children = ( 317 | EA926946283BD3BE0083EC82 /* HomeViewModel.swift */, 318 | ); 319 | path = ViewModels; 320 | sourceTree = ""; 321 | }; 322 | EA926950283BD8CD0083EC82 /* Components */ = { 323 | isa = PBXGroup; 324 | children = ( 325 | EA926951283BD8F30083EC82 /* ViewModifiers.swift */, 326 | ); 327 | path = Components; 328 | sourceTree = ""; 329 | }; 330 | EA926955283BE19C0083EC82 /* Home */ = { 331 | isa = PBXGroup; 332 | children = ( 333 | EA926953283BE0500083EC82 /* HomeViewModelTests.swift */, 334 | ); 335 | path = Home; 336 | sourceTree = ""; 337 | }; 338 | FA0DC42F2822F3C7009AC9CC /* Screens */ = { 339 | isa = PBXGroup; 340 | children = ( 341 | EA926940283BD3530083EC82 /* Home */, 342 | EA926936283BD1440083EC82 /* Newsletter */, 343 | EA926935283BD10F0083EC82 /* Settings */, 344 | D3CE07E72823334C00616F6C /* Muse */, 345 | ); 346 | path = Screens; 347 | sourceTree = ""; 348 | }; 349 | FA0DC4342822F727009AC9CC /* Extension */ = { 350 | isa = PBXGroup; 351 | children = ( 352 | FA0DC4352822F730009AC9CC /* String+BuildInfo.swift */, 353 | D3CE07DC2823102600616F6C /* URLSession+APICall.swift */, 354 | C42E061F282859DC00C4D532 /* URL+Extensions.swift */, 355 | D3CE07E52823296A00616F6C /* View+Theme.swift */, 356 | 8DA05DB0282709170005A514 /* View+Extensions.swift */, 357 | EA92694C283BD57E0083EC82 /* Bundle+Decodable.swift */, 358 | 234F1C7E288F27CD006248B9 /* Color+HEX.swift */, 359 | ); 360 | path = Extension; 361 | sourceTree = ""; 362 | }; 363 | FF2309CF28255C6500C03D99 /* Social ContributorTests */ = { 364 | isa = PBXGroup; 365 | children = ( 366 | EA926955283BE19C0083EC82 /* Home */, 367 | FF2309D028255C6500C03D99 /* Social_ContributorTests.swift */, 368 | ); 369 | path = "Social ContributorTests"; 370 | sourceTree = ""; 371 | }; 372 | /* End PBXGroup section */ 373 | 374 | /* Begin PBXNativeTarget section */ 375 | E334B3722822D5BD002E9640 /* Social Contributor */ = { 376 | isa = PBXNativeTarget; 377 | buildConfigurationList = E334B3812822D5BE002E9640 /* Build configuration list for PBXNativeTarget "Social Contributor" */; 378 | buildPhases = ( 379 | E334B36F2822D5BD002E9640 /* Sources */, 380 | E334B3702822D5BD002E9640 /* Frameworks */, 381 | E334B3712822D5BD002E9640 /* Resources */, 382 | FFEA1CC428255F2500DF4A9C /* ShellScript */, 383 | ); 384 | buildRules = ( 385 | ); 386 | dependencies = ( 387 | ); 388 | name = "Social Contributor"; 389 | packageProductDependencies = ( 390 | D3CE07E12823203300616F6C /* Lottie */, 391 | ); 392 | productName = "Social Contributor"; 393 | productReference = E334B3732822D5BD002E9640 /* Social Contributor.app */; 394 | productType = "com.apple.product-type.application"; 395 | }; 396 | FF2309CD28255C6500C03D99 /* Social ContributorTests */ = { 397 | isa = PBXNativeTarget; 398 | buildConfigurationList = FF2309D628255C6500C03D99 /* Build configuration list for PBXNativeTarget "Social ContributorTests" */; 399 | buildPhases = ( 400 | FF2309CA28255C6500C03D99 /* Sources */, 401 | FF2309CB28255C6500C03D99 /* Frameworks */, 402 | FF2309CC28255C6500C03D99 /* Resources */, 403 | ); 404 | buildRules = ( 405 | ); 406 | dependencies = ( 407 | FF2309D328255C6500C03D99 /* PBXTargetDependency */, 408 | ); 409 | name = "Social ContributorTests"; 410 | productName = "Social ContributorTests"; 411 | productReference = FF2309CE28255C6500C03D99 /* Social ContributorTests.xctest */; 412 | productType = "com.apple.product-type.bundle.unit-test"; 413 | }; 414 | /* End PBXNativeTarget section */ 415 | 416 | /* Begin PBXProject section */ 417 | E334B36B2822D5BD002E9640 /* Project object */ = { 418 | isa = PBXProject; 419 | attributes = { 420 | BuildIndependentTargetsInParallel = 1; 421 | LastSwiftUpdateCheck = 1330; 422 | LastUpgradeCheck = 1330; 423 | TargetAttributes = { 424 | E334B3722822D5BD002E9640 = { 425 | CreatedOnToolsVersion = 13.3.1; 426 | }; 427 | FF2309CD28255C6500C03D99 = { 428 | CreatedOnToolsVersion = 13.3; 429 | TestTargetID = E334B3722822D5BD002E9640; 430 | }; 431 | }; 432 | }; 433 | buildConfigurationList = E334B36E2822D5BD002E9640 /* Build configuration list for PBXProject "Social Contributor" */; 434 | compatibilityVersion = "Xcode 13.0"; 435 | developmentRegion = en; 436 | hasScannedForEncodings = 0; 437 | knownRegions = ( 438 | en, 439 | Base, 440 | ); 441 | mainGroup = E334B36A2822D5BD002E9640; 442 | packageReferences = ( 443 | D3CE07E02823203300616F6C /* XCRemoteSwiftPackageReference "lottie-ios" */, 444 | ); 445 | productRefGroup = E334B3742822D5BD002E9640 /* Products */; 446 | projectDirPath = ""; 447 | projectRoot = ""; 448 | targets = ( 449 | E334B3722822D5BD002E9640 /* Social Contributor */, 450 | FF2309CD28255C6500C03D99 /* Social ContributorTests */, 451 | ); 452 | }; 453 | /* End PBXProject section */ 454 | 455 | /* Begin PBXResourcesBuildPhase section */ 456 | E334B3712822D5BD002E9640 /* Resources */ = { 457 | isa = PBXResourcesBuildPhase; 458 | buildActionMask = 2147483647; 459 | files = ( 460 | 8DA05DAD2827048C0005A514 /* newsletters.json in Resources */, 461 | EA92693F283BD32E0083EC82 /* quotes.json in Resources */, 462 | E334B37E2822D5BE002E9640 /* Preview Assets.xcassets in Resources */, 463 | E334B37B2822D5BE002E9640 /* Assets.xcassets in Resources */, 464 | D3CE07DF28231F8C00616F6C /* no-results-found.json in Resources */, 465 | ); 466 | runOnlyForDeploymentPostprocessing = 0; 467 | }; 468 | FF2309CC28255C6500C03D99 /* Resources */ = { 469 | isa = PBXResourcesBuildPhase; 470 | buildActionMask = 2147483647; 471 | files = ( 472 | ); 473 | runOnlyForDeploymentPostprocessing = 0; 474 | }; 475 | /* End PBXResourcesBuildPhase section */ 476 | 477 | /* Begin PBXShellScriptBuildPhase section */ 478 | FFEA1CC428255F2500DF4A9C /* ShellScript */ = { 479 | isa = PBXShellScriptBuildPhase; 480 | buildActionMask = 2147483647; 481 | files = ( 482 | ); 483 | inputFileListPaths = ( 484 | ); 485 | inputPaths = ( 486 | ); 487 | outputFileListPaths = ( 488 | ); 489 | outputPaths = ( 490 | ); 491 | runOnlyForDeploymentPostprocessing = 0; 492 | shellPath = /bin/sh; 493 | shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n\nexport PATH=\"$PATH:/opt/homebrew/bin\"\nif which swiftlint > /dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; 494 | }; 495 | /* End PBXShellScriptBuildPhase section */ 496 | 497 | /* Begin PBXSourcesBuildPhase section */ 498 | E334B36F2822D5BD002E9640 /* Sources */ = { 499 | isa = PBXSourcesBuildPhase; 500 | buildActionMask = 2147483647; 501 | files = ( 502 | 234F1C81288F29B2006248B9 /* DecoderReport.swift in Sources */, 503 | FA0DC4312822F3D5009AC9CC /* SettingsView.swift in Sources */, 504 | C42E0620282859DC00C4D532 /* URL+Extensions.swift in Sources */, 505 | E73361CA2823FF39009D0D9B /* ColorScheme.swift in Sources */, 506 | EA926947283BD3BE0083EC82 /* HomeViewModel.swift in Sources */, 507 | D3CE07E92823336300616F6C /* MuseViewModel.swift in Sources */, 508 | EA92694F283BD7CD0083EC82 /* CustomColors.swift in Sources */, 509 | EA926945283BD3940083EC82 /* Quote.swift in Sources */, 510 | 8DA05DA3282703A40005A514 /* NewsLetterListView.swift in Sources */, 511 | 234F1C7F288F27CD006248B9 /* Color+HEX.swift in Sources */, 512 | EA92694D283BD57E0083EC82 /* Bundle+Decodable.swift in Sources */, 513 | EA92694B283BD3EE0083EC82 /* QuoteView.swift in Sources */, 514 | 8DA05DAA282704490005A514 /* NewsLetterService.swift in Sources */, 515 | D3CE07DD2823102600616F6C /* URLSession+APICall.swift in Sources */, 516 | C42E062428285E4800C4D532 /* ContributorsProvider.swift in Sources */, 517 | D3CE07E62823296A00616F6C /* View+Theme.swift in Sources */, 518 | FA0DC4362822F730009AC9CC /* String+BuildInfo.swift in Sources */, 519 | EA926952283BD8F30083EC82 /* ViewModifiers.swift in Sources */, 520 | E334B3792822D5BD002E9640 /* ContentView.swift in Sources */, 521 | 8DA05DA7282704200005A514 /* NewsLetter.swift in Sources */, 522 | BB3B3A952826DCBC0046DC4B /* AppReviewRequest.swift in Sources */, 523 | EA926949283BD3D90083EC82 /* HomeView.swift in Sources */, 524 | 8DA05DB1282709170005A514 /* View+Extensions.swift in Sources */, 525 | E334B3772822D5BD002E9640 /* Social_ContributorApp.swift in Sources */, 526 | 5C66C0932822ECCE00B66FF8 /* Model.xcdatamodeld in Sources */, 527 | 5C66C0952822ECDA00B66FF8 /* PersistenceController.swift in Sources */, 528 | 8DA05DB428270A4D0005A514 /* Constants.swift in Sources */, 529 | D3CE07E42823204B00616F6C /* LottieView.swift in Sources */, 530 | 0AD87B87282307FA00904E4E /* MuseView.swift in Sources */, 531 | FA0DC4332822F4E3009AC9CC /* SettingsLabelStyle.swift in Sources */, 532 | 8DA05DAF282705590005A514 /* NewsLetterListViewModel.swift in Sources */, 533 | C42E062228285DAC00C4D532 /* Contributor.swift in Sources */, 534 | ); 535 | runOnlyForDeploymentPostprocessing = 0; 536 | }; 537 | FF2309CA28255C6500C03D99 /* Sources */ = { 538 | isa = PBXSourcesBuildPhase; 539 | buildActionMask = 2147483647; 540 | files = ( 541 | FF2309D128255C6500C03D99 /* Social_ContributorTests.swift in Sources */, 542 | EA926954283BE0500083EC82 /* HomeViewModelTests.swift in Sources */, 543 | ); 544 | runOnlyForDeploymentPostprocessing = 0; 545 | }; 546 | /* End PBXSourcesBuildPhase section */ 547 | 548 | /* Begin PBXTargetDependency section */ 549 | FF2309D328255C6500C03D99 /* PBXTargetDependency */ = { 550 | isa = PBXTargetDependency; 551 | target = E334B3722822D5BD002E9640 /* Social Contributor */; 552 | targetProxy = FF2309D228255C6500C03D99 /* PBXContainerItemProxy */; 553 | }; 554 | /* End PBXTargetDependency section */ 555 | 556 | /* Begin XCBuildConfiguration section */ 557 | E334B37F2822D5BE002E9640 /* Debug */ = { 558 | isa = XCBuildConfiguration; 559 | buildSettings = { 560 | ALWAYS_SEARCH_USER_PATHS = NO; 561 | CLANG_ANALYZER_NONNULL = YES; 562 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 563 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 564 | CLANG_ENABLE_MODULES = YES; 565 | CLANG_ENABLE_OBJC_ARC = YES; 566 | CLANG_ENABLE_OBJC_WEAK = YES; 567 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 568 | CLANG_WARN_BOOL_CONVERSION = YES; 569 | CLANG_WARN_COMMA = YES; 570 | CLANG_WARN_CONSTANT_CONVERSION = YES; 571 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 572 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 573 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 574 | CLANG_WARN_EMPTY_BODY = YES; 575 | CLANG_WARN_ENUM_CONVERSION = YES; 576 | CLANG_WARN_INFINITE_RECURSION = YES; 577 | CLANG_WARN_INT_CONVERSION = YES; 578 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 579 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 580 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 581 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 582 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 583 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 584 | CLANG_WARN_STRICT_PROTOTYPES = YES; 585 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 586 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 587 | CLANG_WARN_UNREACHABLE_CODE = YES; 588 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 589 | COPY_PHASE_STRIP = NO; 590 | DEBUG_INFORMATION_FORMAT = dwarf; 591 | ENABLE_STRICT_OBJC_MSGSEND = YES; 592 | ENABLE_TESTABILITY = YES; 593 | GCC_C_LANGUAGE_STANDARD = gnu11; 594 | GCC_DYNAMIC_NO_PIC = NO; 595 | GCC_NO_COMMON_BLOCKS = YES; 596 | GCC_OPTIMIZATION_LEVEL = 0; 597 | GCC_PREPROCESSOR_DEFINITIONS = ( 598 | "DEBUG=1", 599 | "$(inherited)", 600 | ); 601 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 602 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 603 | GCC_WARN_UNDECLARED_SELECTOR = YES; 604 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 605 | GCC_WARN_UNUSED_FUNCTION = YES; 606 | GCC_WARN_UNUSED_VARIABLE = YES; 607 | IPHONEOS_DEPLOYMENT_TARGET = 15.4; 608 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 609 | MTL_FAST_MATH = YES; 610 | ONLY_ACTIVE_ARCH = YES; 611 | SDKROOT = iphoneos; 612 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 613 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 614 | }; 615 | name = Debug; 616 | }; 617 | E334B3802822D5BE002E9640 /* Release */ = { 618 | isa = XCBuildConfiguration; 619 | buildSettings = { 620 | ALWAYS_SEARCH_USER_PATHS = NO; 621 | CLANG_ANALYZER_NONNULL = YES; 622 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 623 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 624 | CLANG_ENABLE_MODULES = YES; 625 | CLANG_ENABLE_OBJC_ARC = YES; 626 | CLANG_ENABLE_OBJC_WEAK = YES; 627 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 628 | CLANG_WARN_BOOL_CONVERSION = YES; 629 | CLANG_WARN_COMMA = YES; 630 | CLANG_WARN_CONSTANT_CONVERSION = YES; 631 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 632 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 633 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 634 | CLANG_WARN_EMPTY_BODY = YES; 635 | CLANG_WARN_ENUM_CONVERSION = YES; 636 | CLANG_WARN_INFINITE_RECURSION = YES; 637 | CLANG_WARN_INT_CONVERSION = YES; 638 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 639 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 640 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 641 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 642 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 643 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 644 | CLANG_WARN_STRICT_PROTOTYPES = YES; 645 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 646 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 647 | CLANG_WARN_UNREACHABLE_CODE = YES; 648 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 649 | COPY_PHASE_STRIP = NO; 650 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 651 | ENABLE_NS_ASSERTIONS = NO; 652 | ENABLE_STRICT_OBJC_MSGSEND = YES; 653 | GCC_C_LANGUAGE_STANDARD = gnu11; 654 | GCC_NO_COMMON_BLOCKS = YES; 655 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 656 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 657 | GCC_WARN_UNDECLARED_SELECTOR = YES; 658 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 659 | GCC_WARN_UNUSED_FUNCTION = YES; 660 | GCC_WARN_UNUSED_VARIABLE = YES; 661 | IPHONEOS_DEPLOYMENT_TARGET = 15.4; 662 | MTL_ENABLE_DEBUG_INFO = NO; 663 | MTL_FAST_MATH = YES; 664 | SDKROOT = iphoneos; 665 | SWIFT_COMPILATION_MODE = wholemodule; 666 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 667 | VALIDATE_PRODUCT = YES; 668 | }; 669 | name = Release; 670 | }; 671 | E334B3822822D5BE002E9640 /* Debug */ = { 672 | isa = XCBuildConfiguration; 673 | buildSettings = { 674 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 675 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 676 | CODE_SIGN_STYLE = Automatic; 677 | CURRENT_PROJECT_VERSION = 1; 678 | DEVELOPMENT_ASSET_PATHS = "\"Social Contributor/Preview Content\""; 679 | DEVELOPMENT_TEAM = ""; 680 | ENABLE_PREVIEWS = YES; 681 | GENERATE_INFOPLIST_FILE = YES; 682 | INFOPLIST_KEY_NSAppleMusicUsageDescription = "Social Contributer uses Music access to play music and create a pleasant experience."; 683 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 684 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 685 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 686 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 687 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 688 | LD_RUNPATH_SEARCH_PATHS = ( 689 | "$(inherited)", 690 | "@executable_path/Frameworks", 691 | ); 692 | MARKETING_VERSION = 1.0; 693 | PRODUCT_BUNDLE_IDENTIFIER = "com.swiftlyrush.Social-Contributor"; 694 | PRODUCT_NAME = "$(TARGET_NAME)"; 695 | SWIFT_EMIT_LOC_STRINGS = YES; 696 | SWIFT_VERSION = 5.0; 697 | TARGETED_DEVICE_FAMILY = "1,2"; 698 | }; 699 | name = Debug; 700 | }; 701 | E334B3832822D5BE002E9640 /* Release */ = { 702 | isa = XCBuildConfiguration; 703 | buildSettings = { 704 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 705 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 706 | CODE_SIGN_STYLE = Automatic; 707 | CURRENT_PROJECT_VERSION = 1; 708 | DEVELOPMENT_ASSET_PATHS = "\"Social Contributor/Preview Content\""; 709 | DEVELOPMENT_TEAM = ""; 710 | ENABLE_PREVIEWS = YES; 711 | GENERATE_INFOPLIST_FILE = YES; 712 | INFOPLIST_KEY_NSAppleMusicUsageDescription = "Social Contributer uses Music access to play music and create a pleasant experience."; 713 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 714 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 715 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 716 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 717 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 718 | LD_RUNPATH_SEARCH_PATHS = ( 719 | "$(inherited)", 720 | "@executable_path/Frameworks", 721 | ); 722 | MARKETING_VERSION = 1.0; 723 | PRODUCT_BUNDLE_IDENTIFIER = "com.swiftlyrush.Social-Contributor"; 724 | PRODUCT_NAME = "$(TARGET_NAME)"; 725 | SWIFT_EMIT_LOC_STRINGS = YES; 726 | SWIFT_VERSION = 5.0; 727 | TARGETED_DEVICE_FAMILY = "1,2"; 728 | }; 729 | name = Release; 730 | }; 731 | FF2309D428255C6500C03D99 /* Debug */ = { 732 | isa = XCBuildConfiguration; 733 | buildSettings = { 734 | BUNDLE_LOADER = "$(TEST_HOST)"; 735 | CODE_SIGN_STYLE = Automatic; 736 | CURRENT_PROJECT_VERSION = 1; 737 | DEVELOPMENT_TEAM = Y535846H6P; 738 | GENERATE_INFOPLIST_FILE = YES; 739 | MARKETING_VERSION = 1.0; 740 | PRODUCT_BUNDLE_IDENTIFIER = "com.swiftlyrush.Social-Contributor.Social-ContributorTests"; 741 | PRODUCT_NAME = "$(TARGET_NAME)"; 742 | SWIFT_EMIT_LOC_STRINGS = NO; 743 | SWIFT_VERSION = 5.0; 744 | TARGETED_DEVICE_FAMILY = "1,2"; 745 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Social Contributor.app/Social Contributor"; 746 | }; 747 | name = Debug; 748 | }; 749 | FF2309D528255C6500C03D99 /* Release */ = { 750 | isa = XCBuildConfiguration; 751 | buildSettings = { 752 | BUNDLE_LOADER = "$(TEST_HOST)"; 753 | CODE_SIGN_STYLE = Automatic; 754 | CURRENT_PROJECT_VERSION = 1; 755 | DEVELOPMENT_TEAM = Y535846H6P; 756 | GENERATE_INFOPLIST_FILE = YES; 757 | MARKETING_VERSION = 1.0; 758 | PRODUCT_BUNDLE_IDENTIFIER = "com.swiftlyrush.Social-Contributor.Social-ContributorTests"; 759 | PRODUCT_NAME = "$(TARGET_NAME)"; 760 | SWIFT_EMIT_LOC_STRINGS = NO; 761 | SWIFT_VERSION = 5.0; 762 | TARGETED_DEVICE_FAMILY = "1,2"; 763 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Social Contributor.app/Social Contributor"; 764 | }; 765 | name = Release; 766 | }; 767 | /* End XCBuildConfiguration section */ 768 | 769 | /* Begin XCConfigurationList section */ 770 | E334B36E2822D5BD002E9640 /* Build configuration list for PBXProject "Social Contributor" */ = { 771 | isa = XCConfigurationList; 772 | buildConfigurations = ( 773 | E334B37F2822D5BE002E9640 /* Debug */, 774 | E334B3802822D5BE002E9640 /* Release */, 775 | ); 776 | defaultConfigurationIsVisible = 0; 777 | defaultConfigurationName = Release; 778 | }; 779 | E334B3812822D5BE002E9640 /* Build configuration list for PBXNativeTarget "Social Contributor" */ = { 780 | isa = XCConfigurationList; 781 | buildConfigurations = ( 782 | E334B3822822D5BE002E9640 /* Debug */, 783 | E334B3832822D5BE002E9640 /* Release */, 784 | ); 785 | defaultConfigurationIsVisible = 0; 786 | defaultConfigurationName = Release; 787 | }; 788 | FF2309D628255C6500C03D99 /* Build configuration list for PBXNativeTarget "Social ContributorTests" */ = { 789 | isa = XCConfigurationList; 790 | buildConfigurations = ( 791 | FF2309D428255C6500C03D99 /* Debug */, 792 | FF2309D528255C6500C03D99 /* Release */, 793 | ); 794 | defaultConfigurationIsVisible = 0; 795 | defaultConfigurationName = Release; 796 | }; 797 | /* End XCConfigurationList section */ 798 | 799 | /* Begin XCRemoteSwiftPackageReference section */ 800 | D3CE07E02823203300616F6C /* XCRemoteSwiftPackageReference "lottie-ios" */ = { 801 | isa = XCRemoteSwiftPackageReference; 802 | repositoryURL = "https://github.com/airbnb/lottie-ios"; 803 | requirement = { 804 | kind = upToNextMajorVersion; 805 | minimumVersion = 3.0.0; 806 | }; 807 | }; 808 | /* End XCRemoteSwiftPackageReference section */ 809 | 810 | /* Begin XCSwiftPackageProductDependency section */ 811 | D3CE07E12823203300616F6C /* Lottie */ = { 812 | isa = XCSwiftPackageProductDependency; 813 | package = D3CE07E02823203300616F6C /* XCRemoteSwiftPackageReference "lottie-ios" */; 814 | productName = Lottie; 815 | }; 816 | /* End XCSwiftPackageProductDependency section */ 817 | 818 | /* Begin XCVersionGroup section */ 819 | 5C66C0912822ECCE00B66FF8 /* Model.xcdatamodeld */ = { 820 | isa = XCVersionGroup; 821 | children = ( 822 | 5C66C0922822ECCE00B66FF8 /* Model.xcdatamodel */, 823 | ); 824 | currentVersion = 5C66C0922822ECCE00B66FF8 /* Model.xcdatamodel */; 825 | path = Model.xcdatamodeld; 826 | sourceTree = ""; 827 | versionGroupType = wrapper.xcdatamodel; 828 | }; 829 | /* End XCVersionGroup section */ 830 | }; 831 | rootObject = E334B36B2822D5BD002E9640 /* Project object */; 832 | } 833 | -------------------------------------------------------------------------------- /Social Contributor.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Social Contributor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Social Contributor.xcodeproj/project.xcworkspace/xcuserdata/adamrush.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamrushy/social-swiftui-app/2fa9c87e93cc6e63cc5d599d17695d7a9cc4ccc8/Social Contributor.xcodeproj/project.xcworkspace/xcuserdata/adamrush.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Social Contributor.xcodeproj/xcuserdata/adamrush.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Social Contributor.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Social Contributor/ColorScheme/ColorScheme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorScheme.swift 3 | // Social Contributor 4 | // 5 | // Created by Tunde Adegoroye on 05/05/2022. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | enum AppColorScheme: Int { 12 | case light 13 | case dark 14 | case system 15 | } 16 | 17 | extension AppColorScheme { 18 | 19 | var title: String { 20 | switch self { 21 | case .light: 22 | return "🌞 Light" 23 | case .dark: 24 | return "🌚 Dark" 25 | case .system: 26 | return "📱 System" 27 | } 28 | } 29 | } 30 | 31 | extension AppColorScheme: Identifiable { 32 | var id: Self { self } 33 | } 34 | 35 | extension AppColorScheme: CaseIterable { } 36 | 37 | final class AppColorSchemeManager: ObservableObject { 38 | 39 | @AppStorage("currentColorScheme") 40 | var colorScheme: Int = AppColorScheme.allCases.first!.rawValue // Force unwrap YOLO 🤪 41 | 42 | var currentScheme: ColorScheme? { 43 | guard let scheme = AppColorScheme(rawValue: colorScheme) else { return nil } 44 | switch scheme { 45 | case .light: 46 | return .light 47 | case .dark: 48 | return .dark 49 | default: 50 | return nil 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Social Contributor/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // Social Contributor 4 | // 5 | // Created by Adam Rush on 04/05/2022. 6 | // Edited by Florian Schweizer on 04/05/2022. 7 | // 8 | 9 | import SwiftUI 10 | import MusicKit 11 | 12 | struct ContentView: View { 13 | @State private var showMuseView = false 14 | 15 | var body: some View { 16 | TabView { 17 | HomeView() 18 | .tabItem { 19 | Label("Home", systemImage: "house") 20 | } 21 | 22 | if showMuseView { 23 | MuseView() 24 | .tabItem { 25 | Label("Muse", systemImage: "wand.and.stars.inverse") 26 | } 27 | } 28 | 29 | NewsLetterListView() 30 | .tabItem { 31 | Label("Newsletters", systemImage: "newspaper") 32 | } 33 | 34 | SettingsView() 35 | .tabItem { 36 | Label("Settings", systemImage: "gearshape") 37 | } 38 | .onAppear { 39 | /* 40 | This can be called on any view that would be 41 | appropriate for presenting a Review Request once the 42 | threshold has been met. 43 | */ 44 | AppReviewRequest.requestReviewIfNeeded() 45 | } 46 | } 47 | .task { 48 | await musicKitAuthentication() 49 | } 50 | } 51 | } 52 | 53 | extension ContentView { 54 | private func musicKitAuthentication() async { 55 | do { 56 | // Fetch developer token to see if the current device is simulator or real device. 57 | _ = try await MusicDataRequest.tokenProvider.developerToken(options: .ignoreCache) 58 | 59 | let status = await MusicAuthorization.request() 60 | 61 | if status == .authorized { 62 | showMuseView = true 63 | } else { 64 | // If permission is anything other than `authorized`, do not show `MuseView`. 65 | showMuseView = false 66 | } 67 | } catch { 68 | // If the device is a simulator, do not show `MuseView`. 69 | showMuseView = false 70 | } 71 | } 72 | } 73 | 74 | struct ContentView_Previews: PreviewProvider { 75 | static var previews: some View { 76 | Group { 77 | ContentView() 78 | 79 | ContentView() 80 | .darkMode() 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Social Contributor/CoreData/Model.xcdatamodeld/Model.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Social Contributor/CoreData/PersistenceController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PersistenceController.swift 3 | // Social Contributor 4 | // 5 | // Created by Florian Schweizer on 04/05/2022. 6 | // 7 | 8 | import CoreData 9 | 10 | struct PersistenceController { 11 | static let shared = PersistenceController() 12 | 13 | static var preview: PersistenceController = { 14 | let result = PersistenceController(inMemory: true) 15 | let viewContext = result.container.viewContext 16 | 17 | /* 18 | Use this instance for SwiftUI Previews & Debugging 19 | Fill it with some sample data first :) 20 | */ 21 | 22 | try? viewContext.save() 23 | 24 | return result 25 | }() 26 | 27 | let container: NSPersistentContainer 28 | 29 | init(inMemory: Bool = false) { 30 | container = NSPersistentContainer(name: "Model") 31 | 32 | if inMemory { 33 | container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null") 34 | } 35 | 36 | container.loadPersistentStores(completionHandler: { (_, error) in 37 | if let error = error as NSError? { 38 | fatalError("Unresolved error \(error), \(error.userInfo)") 39 | } 40 | }) 41 | 42 | container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Social Contributor/Extension/Bundle+Decodable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bundle+Decodable.swift 3 | // Social Contributor 4 | // 5 | // Created by Danijela Vrzan on 2022-05-22. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Bundle { 11 | func decode(_ file: String) -> T { 12 | guard let url = self.url(forResource: file, withExtension: nil) else { 13 | fatalError("Failed to locate \(file) in bundle.") 14 | } 15 | 16 | guard let data = try? Data(contentsOf: url) else { 17 | fatalError("Failed to load \(file) from bundle.") 18 | } 19 | 20 | let decoder = JSONDecoder() 21 | 22 | guard let loadedObject = try? decoder.decode(T.self, from: data) else { 23 | fatalError("Failed to decode \(file) from bundle.") 24 | } 25 | 26 | return loadedObject 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Social Contributor/Extension/Color+HEX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Color+HEX.swift 3 | // Social Contributor 4 | // 5 | // Created by Emin Grbo => @emin_ui 6 | // 7 | 8 | import UIKit 9 | import SwiftUI 10 | 11 | // Init color from HEX for both UIColor and Color types. 12 | extension UIColor { 13 | public convenience init?(hex: String) { 14 | var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines) 15 | hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "") 16 | 17 | var rgb: UInt64 = 0 18 | 19 | var red: Double = 0.0 20 | var green: Double = 0.0 21 | var blue: Double = 0.0 22 | var opacity: Double = 1.0 23 | 24 | let length = hexSanitized.count 25 | 26 | guard Scanner(string: hexSanitized).scanHexInt64(&rgb) else { return nil } 27 | 28 | if length == 6 { 29 | red = Double((rgb & 0xFF0000) >> 16) / 255.0 30 | green = Double((rgb & 0x00FF00) >> 8) / 255.0 31 | blue = Double(rgb & 0x0000FF) / 255.0 32 | 33 | } else if length == 8 { 34 | red = Double((rgb & 0xFF000000) >> 24) / 255.0 35 | green = Double((rgb & 0x00FF0000) >> 16) / 255.0 36 | blue = Double((rgb & 0x0000FF00) >> 8) / 255.0 37 | opacity = Double(rgb & 0x000000FF) / 255.0 38 | 39 | } else { 40 | return nil 41 | } 42 | 43 | self.init(red: red, green: green, blue: blue, alpha: opacity) 44 | } 45 | } 46 | 47 | extension Color { 48 | init(hex: String) { 49 | self.init(UIColor(hex: hex) ?? .white) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Social Contributor/Extension/String+BuildInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+BuildInfo.swift 3 | // Social Contributor 4 | // 5 | // Created by Alex Logan on 04/05/2022. 6 | // 7 | 8 | import Foundation 9 | 10 | extension String { 11 | public static func basicBuildInfo() -> String { 12 | let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "" 13 | return version 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Social Contributor/Extension/URL+Extensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | // See more details @ https://www.swiftbysundell.com/articles/constructing-urls-in-swift/#static-urls 4 | extension URL { 5 | init(staticString string: StaticString) { 6 | guard let url = URL(string: "\(string)") else { 7 | preconditionFailure("Invalid static URL string: \(string)") 8 | } 9 | 10 | self = url 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Social Contributor/Extension/URLSession+APICall.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLSession+APICall.swift 3 | // Social Contributor 4 | // 5 | // Created by Yash Shah on 04/05/2022. 6 | // 7 | 8 | import Foundation 9 | 10 | // Useful for Mocking and testing 11 | protocol DataProvider { 12 | func request(url: URL?, type: T.Type) async throws -> T 13 | } 14 | 15 | enum NetworkError: Error { 16 | case badURL 17 | case badResponse 18 | case invalidData 19 | } 20 | 21 | extension URLSession: DataProvider { 22 | // Generic Method to retrieve any form of data 23 | func request(url: URL?, type: T.Type) async throws -> T { 24 | guard let url = url else { 25 | throw NetworkError.badURL 26 | } 27 | 28 | let (data, response) = try await data(for: URLRequest(url: url)) 29 | 30 | guard (response as? HTTPURLResponse)?.statusCode == 200 else { 31 | throw NetworkError.badResponse 32 | } 33 | 34 | do { 35 | return try JSONDecoder().decode(type, from: data) 36 | } catch { 37 | throw NetworkError.invalidData 38 | } 39 | } 40 | } 41 | 42 | // More info at www.hackingwithswift.com 43 | // https://tinyurl.com/3e35jzmr 44 | extension URLSession { 45 | func decode( 46 | _ type: T.Type = T.self, 47 | from url: URL, 48 | keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys, 49 | dataDecodingStrategy: JSONDecoder.DataDecodingStrategy = .deferredToData, 50 | dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate 51 | ) async throws -> T { 52 | let (data, _) = try await data(from: url) 53 | 54 | let decoder = JSONDecoder() 55 | decoder.keyDecodingStrategy = keyDecodingStrategy 56 | decoder.dataDecodingStrategy = dataDecodingStrategy 57 | decoder.dateDecodingStrategy = dateDecodingStrategy 58 | 59 | let decoded = try decoder.decode(T.self, from: data) 60 | return decoded 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Social Contributor/Extension/View+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // View+Extensions.swift 3 | // Social Contributor 4 | // 5 | // Created by Mohammad Azam on 5/7/22. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | extension View { 12 | 13 | func embedInNavigationView() -> some View { 14 | NavigationView { self } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Social Contributor/Extension/View+Theme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // View+Theme.swift 3 | // Social Contributor 4 | // 5 | // Created by Yash Shah on 04/05/2022. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | extension View { 12 | // Force Dark Mode 13 | func darkMode() -> some View { 14 | self.preferredColorScheme(.dark) 15 | } 16 | 17 | // Force Light mode Mode 18 | func lightMode() -> some View { 19 | self.preferredColorScheme(.light) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Social Contributor/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Social Contributor/Resources/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Social Contributor/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Social Contributor/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Social Contributor/Resources/Assets.xcassets/quote/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Social Contributor/Resources/Assets.xcassets/quote/dark-blue.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.200", 9 | "green" : "0.110", 10 | "red" : "0.016" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Social Contributor/Resources/Assets.xcassets/quote/light-blue.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.702", 9 | "green" : "0.431", 10 | "red" : "0.102" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Social Contributor/Resources/newsletters.json: -------------------------------------------------------------------------------- 1 | 2 | 3 | [ 4 | { 5 | "name": "iOS Dev Weekly", 6 | "url": "https://iosdevweekly.com/" 7 | }, 8 | { 9 | "name": "SwiftUI Weekly", 10 | "url": "http://weekly.swiftwithmajid.com/" 11 | }, 12 | { 13 | "name": "iOS Code Review", 14 | "url": "https://newsletter.ioscodereview.com/" 15 | }, 16 | { 17 | "name": "Swift Senpai", 18 | "url": "https://swiftsenpai.com/newsletter/" 19 | }, 20 | { 21 | "name": "Hacking With Swift", 22 | "url": "https://www.hackingwithswift.com/newsletter" 23 | }, 24 | { 25 | "name": "AppCoda Weekly", 26 | "url": "http://digest.appcoda.com/" 27 | }, 28 | { 29 | "name": "AppForce1", 30 | "url": "https://appforce1.net/news/" 31 | }, 32 | { 33 | "name": "RayWenderlich", 34 | "url": "https://www.raywenderlich.com/newsletter" 35 | }, 36 | { 37 | "name": "SwiftLee", 38 | "url": "https://www.avanderlee.com/swiftlee-weekly/" 39 | }, 40 | { 41 | "name": "SarunW", 42 | "url": "https://sarunw.com/newsletter/" 43 | } 44 | ] 45 | -------------------------------------------------------------------------------- /Social Contributor/Resources/no-results-found.json: -------------------------------------------------------------------------------- 1 | {"v":"5.4.4","fr":60,"ip":0,"op":316,"w":642,"h":642,"nm":"NEW sin movs","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":3,"nm":"parent sombra","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":34,"s":[64],"e":[23]},{"i":{"x":[0.437],"y":[0.995]},"o":{"x":[0.7],"y":[0]},"t":92,"s":[23],"e":[-21]},{"i":{"x":[0.358],"y":[1]},"o":{"x":[0.544],"y":[0]},"t":133,"s":[-21],"e":[16]},{"i":{"x":[0.326],"y":[0.985]},"o":{"x":[0.595],"y":[0]},"t":183,"s":[16],"e":[0]},{"t":220}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.222,"y":1},"o":{"x":0,"y":0},"t":34,"s":[240.252,377.045,0],"e":[323.752,265.045,0],"to":[13.917,-18.667,0],"ti":[-5.248,0.545,0]},{"i":{"x":0.453,"y":0.528},"o":{"x":0.785,"y":0},"t":88,"s":[323.752,265.045,0],"e":[334.252,403.045,0],"to":[5.248,-0.545,0],"ti":[-24.248,-17.455,0]},{"i":{"x":0.156,"y":1},"o":{"x":0.423,"y":0.345},"t":133,"s":[334.252,403.045,0],"e":[499.252,415.045,0],"to":[24.248,17.455,0],"ti":[-27.5,-2,0]},{"t":183.03515625}],"ix":2},"a":{"a":0,"k":[47.206,48.054,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.15,0.15,0.15],"y":[0,0,0]},"t":34,"s":[0,0,100],"e":[240,240,100]},{"i":{"x":[0.53,0.53,0.53],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":60,"s":[240,240,100],"e":[240,240,100]},{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.42,0.42,0.42],"y":[0,0,0]},"t":133,"s":[240,240,100],"e":[400,400,100]},{"t":183}],"ix":6}},"ao":0,"ip":34,"op":12647,"st":18,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"LUPA sombra","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[26.162,29.375,0],"ix":2},"a":{"a":1,"k":[{"i":{"x":0.67,"y":1},"o":{"x":0.33,"y":0},"t":133,"s":[59.528,35.656,0],"e":[55.428,43.556,0],"to":[-0.683,1.317,0],"ti":[0.683,-1.317,0]},{"t":183}],"ix":1},"s":{"a":0,"k":[50,50,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.167},"t":35,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}],"e":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.765,"y":0.384},"o":{"x":0.8,"y":0},"t":60,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}],"e":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.21,"y":0.549},"t":84,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}],"e":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0},"t":110,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}],"e":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.765,"y":0.384},"o":{"x":0.167,"y":0},"t":126,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}],"e":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.21,"y":0.549},"t":147,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}],"e":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"t":173}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.792156862745098,0.8470588235294118,0.9137254901960784,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":35,"s":[5.6],"e":[3.6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.8],"y":[0]},"t":60,"s":[3.6],"e":[5.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":84,"s":[5.6],"e":[3.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":110,"s":[3.6],"e":[3.6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0]},"t":126,"s":[3.6],"e":[5.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":147,"s":[5.6],"e":[3.6]},{"t":173}],"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.792156862745098,0.8470588235294118,0.9137254901960784,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[45.074,45],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-42.5,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.883,-1.88],[0,0],[1.874,-1.878],[0,0],[1.884,1.881],[0,0],[-1.874,1.877]],"o":[[1.885,-1.879],[0,0],[1.877,1.874],[0,0],[-1.885,1.878],[0,0],[-1.877,-1.875],[0,0]],"v":[[-15.301,-15.277],[-8.483,-15.273],[15.305,8.476],[15.31,15.269],[15.302,15.277],[8.483,15.273],[-15.305,-8.475],[-15.31,-15.268]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.792156862745098,0.8470588235294118,0.9137254901960784,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[79.622,81.906],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":34,"op":421,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"escalador papel","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[321,161.5,0],"ix":2},"a":{"a":0,"k":[60,60,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":421,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"lupa Outlines 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":34,"s":[64],"e":[23]},{"i":{"x":[0.437],"y":[0.995]},"o":{"x":[0.7],"y":[0]},"t":92,"s":[23],"e":[-21]},{"i":{"x":[0.358],"y":[1]},"o":{"x":[0.544],"y":[0]},"t":133,"s":[-21],"e":[16]},{"i":{"x":[0.326],"y":[0.985]},"o":{"x":[0.595],"y":[0]},"t":183,"s":[16],"e":[0]},{"t":220}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.222,"y":1},"o":{"x":0,"y":0},"t":34,"s":[240.252,377.045,0],"e":[323.752,265.045,0],"to":[13.917,-18.667,0],"ti":[-5.248,0.545,0]},{"i":{"x":0.453,"y":0.528},"o":{"x":0.785,"y":0},"t":88,"s":[323.752,265.045,0],"e":[334.252,403.045,0],"to":[5.248,-0.545,0],"ti":[-24.248,-17.455,0]},{"i":{"x":0.156,"y":1},"o":{"x":0.423,"y":0.345},"t":133,"s":[334.252,403.045,0],"e":[499.252,415.045,0],"to":[24.248,17.455,0],"ti":[-27.5,-2,0]},{"t":183.03515625}],"ix":2},"a":{"a":0,"k":[47.206,48.054,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.15,0.15,0.15],"y":[0,0,0]},"t":34,"s":[0,0,100],"e":[240,240,100]},{"i":{"x":[0.53,0.53,0.53],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":60,"s":[240,240,100],"e":[240,240,100]},{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.42,0.42,0.42],"y":[0,0,0]},"t":133,"s":[240,240,100],"e":[400,400,100]},{"t":183}],"ix":6}},"ao":0,"ip":34,"op":12647,"st":18,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"LUPA rotacion 3D","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[26.162,29.375,0],"ix":2},"a":{"a":0,"k":[48.528,49.656,0],"ix":1},"s":{"a":0,"k":[50,50,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.25,"y":1},"o":{"x":0.167,"y":0.167},"t":202,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[3.438,-0.004],[0.027,3.406],[0.01,3.421],[0.056,3.461],[-3.375,0.031],[-3.421,-0.01],[-3.397,-0.021],[0.014,-3.432],[-0.01,-3.421],[0.024,-3.444],[3.454,-0.013],[3.421,0.011]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[12.204,8.794],[8.792,12.204],[0.01,3.422],[-8.706,12.137],[-12.137,8.707],[-3.421,-0.01],[-12.204,-8.793],[-8.792,-12.204],[-0.01,-3.421],[8.706,-12.137],[12.137,-8.706],[3.421,0.011]],"c":true}]},{"t":232}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.13725490196078433,0.3137254901960784,0.5843137254901961,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[46.174,44.297],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":202,"s":[0],"e":[100]},{"t":205}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.167},"t":35,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}],"e":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.765,"y":0.384},"o":{"x":0.8,"y":0},"t":60,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}],"e":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.21,"y":0.549},"t":84,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}],"e":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0},"t":110,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}],"e":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"i":{"x":0.765,"y":0.384},"o":{"x":0.167,"y":0},"t":126,"s":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}],"e":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.21,"y":0.549},"t":147,"s":[{"i":[[-2.46,0.083],[-0.259,19.058],[1.377,0.116],[0,-19.883]],"o":[[3.106,-0.105],[0.271,-19.881],[-1.276,-0.107],[0,19.882]],"v":[[0.001,36],[2.204,2.371],[0.001,-36],[-3.43,1.971]],"c":true}],"e":[{"i":[[-19.923,0],[0,19.882],[19.923,0],[0,-19.883]],"o":[[19.923,0],[0,-19.883],[-19.923,0],[0,19.882]],"v":[[0.001,36],[36.074,0],[0.001,-36],[-36.074,0]],"c":true}]},{"t":173}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.13725490196078433,0.3137254901960784,0.5843137254901961,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":35,"s":[5.6],"e":[3.6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.8],"y":[0]},"t":60,"s":[3.6],"e":[5.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":84,"s":[5.6],"e":[3.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":110,"s":[3.6],"e":[3.6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0]},"t":126,"s":[3.6],"e":[5.6]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":147,"s":[5.6],"e":[3.6]},{"t":173}],"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[45.074,45],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-42.5,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.883,-1.88],[0,0],[1.874,-1.878],[0,0],[1.884,1.881],[0,0],[-1.874,1.877]],"o":[[1.885,-1.879],[0,0],[1.877,1.874],[0,0],[-1.885,1.878],[0,0],[-1.877,-1.875],[0,0]],"v":[[-15.301,-15.277],[-8.483,-15.273],[15.305,8.476],[15.31,15.269],[15.302,15.277],[8.483,15.273],[-15.305,-8.475],[-15.31,-15.268]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.13725490196078433,0.3137254901960784,0.5843137254901961,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[79.622,81.906],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":34,"op":421,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"line 1 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[299.82,224.817,0],"ix":2},"a":{"a":0,"k":[36.006,1.8,0],"ix":1},"s":{"a":0,"k":[200,200,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.8,1.8],[70.211,1.8]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.6274509803921569,0.6274509803921569,0.6274509803921569,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.15],"y":[0]},"t":17,"s":[0],"e":[100]},{"t":39}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":17,"op":421,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"line 2 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[292.734,278.817,0],"ix":2},"a":{"a":0,"k":[32.463,1.8,0],"ix":1},"s":{"a":0,"k":[200,200,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.8,1.8],[63.125,1.8]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.6274509803921569,0.6274509803921569,0.6274509803921569,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.15],"y":[0]},"t":24,"s":[0],"e":[100]},{"t":46}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":24,"op":428,"st":7,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"line 3 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[258.464,329.218,0],"ix":2},"a":{"a":0,"k":[15.327,1.8,0],"ix":1},"s":{"a":0,"k":[200,200,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.8,1.8],[28.855,1.8]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.6274509803921569,0.6274509803921569,0.6274509803921569,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.15],"y":[0]},"t":31,"s":[0],"e":[100]},{"t":53}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":31,"op":435,"st":14,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"line 4 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[258.464,379.618,0],"ix":2},"a":{"a":0,"k":[15.327,1.8,0],"ix":1},"s":{"a":0,"k":[200,200,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.8,1.8],[28.855,1.8]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.6274509803921569,0.6274509803921569,0.6274509803921569,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.15],"y":[0]},"t":38,"s":[0],"e":[100]},{"t":60}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":38,"op":442,"st":21,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Sombra lupa","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[321,321,0],"ix":2},"a":{"a":0,"k":[321,321,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[-7.5,3.5],[0,0],[0,0],[0,0],[0,0],[-4,0],[0,-2.5],[-29.5,0],[0,15],[0,5]],"o":[[-13,0],[0,0],[0,0],[0,0],[0,0],[4,0],[0,2.5],[29.5,0],[0,-15],[0,-5]],"v":[[412.5,169.5],[231.5,169.5],[202,190.5],[191,254],[191,428.5],[329,428.5],[333.5,434.5],[365.5,478.5],[399,436.5],[399,193]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"w":642,"h":642,"ip":0,"op":421,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"papel bot Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-8.861,353.102,0],"ix":2},"a":{"a":0,"k":[57.389,18.529,0],"ix":1},"s":{"a":0,"k":[200,200,100],"ix":6}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":1,"k":[{"i":{"x":0.35,"y":1},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[114.817,36.647],[1.381,36.647],[1.32,36.478],[114.756,36.478]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[114.756,5.022],[1.32,5.022],[1.32,36.478],[114.756,36.478]],"c":true}]},{"t":45}],"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0.013,-9.145],[0,0],[0,0],[7.994,-2.105],[1.814,0.473],[-0.022,8.366],[0,0],[1.528,0.009],[0,0],[0,0],[0,0],[0,0],[0,0],[0,-1.541],[0,0],[-13.061,-0.011],[0,0],[0,0],[0,0],[-0.609,0.093],[-1.049,0.261],[-0.013,11.099],[0,0],[0,0],[-5.938,0.542],[-0.345,-0.019],[-0.011,-6.47],[0,0],[0,0],[0,1.541],[0,0],[9.485,0.042]],"o":[[0,0],[-9.048,0.552],[0,0],[0,0],[-0.017,8.335],[-1.815,0.473],[-8.019,-2.135],[0,0],[0.011,-1.541],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.527,0],[0,0],[-0.012,13.175],[0,0],[0,0],[0,0],[0.609,0],[1.076,-0.099],[10.716,-2.516],[0,0],[0,0],[0.079,-6.014],[0.344,-0.019],[6.414,0.01],[0,0],[0,0],[1.528,0],[0,0],[0.04,-9.569],[0,0]],"v":[[89.342,-145.931],[88.309,-145.931],[72.188,-128.68],[72.188,-128.419],[72.188,-6.1],[58.594,11.617],[53.059,11.617],[39.481,-6.211],[39.481,-9.933],[36.734,-12.743],[19.195,-12.743],[5.094,-12.743],[-16.645,-12.743],[-39.406,-12.743],[-53.01,-12.743],[-55.777,-9.951],[-55.777,-6.006],[-32.146,17.869],[-32.111,17.869],[-2.276,17.869],[54.109,17.869],[56.268,17.776],[59.46,17.237],[77.777,-6.006],[77.777,-128.586],[77.777,-128.717],[88.365,-140.254],[89.398,-140.254],[101.02,-128.531],[101.02,-115.262],[103.787,-115.262],[106.555,-118.053],[106.555,-128.531],[89.454,-145.931]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.6274509803921569,0.6274509803921569,0.6274509803921569,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[58.74,18.119],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":25,"op":421,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"Papel front Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[42.09,219.148,0],"ix":2},"a":{"a":0,"k":[74.219,89.605,0],"ix":1},"s":{"a":0,"k":[200,200,100],"ix":6}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":1,"k":[{"i":{"x":0.99,"y":1},"o":{"x":0.28,"y":0},"t":10,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[152.276,7.781],[-1.326,7.781],[-1.326,9.031],[152.276,9.031]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[152.276,7.781],[-1.326,7.781],[-1.326,175.031],[152.276,175.031]],"c":true}]},{"t":25}],"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0.012,-9.145],[0,0],[0,0],[7.994,-2.106],[0.894,-0.009],[0,0],[0,0],[0,0],[0,0],[-5.953,-0.005],[0,0],[0,0],[0,0],[-0.61,0.093],[-1.049,0.26],[-0.014,11.099],[0,0],[0,0],[-5.937,0.544],[-0.326,-0.016]],"o":[[0,0],[-9.048,0.551],[0,0],[0,0],[-0.017,8.335],[-0.874,0.227],[0,0],[0,0],[0,0],[0,0],[4.154,3.667],[0,0],[0,0],[0,0],[0.608,0],[1.075,-0.099],[10.716,-2.517],[0,0],[0,0],[0.079,-6.015],[0.328,-0.018],[0,0]],"v":[[68.364,-81.9],[67.331,-81.9],[51.208,-64.649],[51.208,-64.388],[51.208,57.931],[37.614,75.648],[34.951,75.996],[34.951,75.999],[34.882,75.999],[34.811,75.999],[-68.667,75.999],[-53.126,81.9],[-53.089,81.9],[-23.254,81.9],[33.132,81.9],[35.29,81.807],[38.48,81.268],[56.799,58.024],[56.799,-64.556],[56.799,-64.686],[67.385,-76.224],[68.667,-76.224]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.6274509803921569,0.6274509803921569,0.6274509803921569,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[71.074,92.141],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-57.614,-15.262],[-57.614,-11.587]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,3.997],[0,8.289]],"o":[[5.953,0],[0,-32.687],[0,0]],"v":[[-65.219,79.059],[-58.189,68.737],[-58.135,4.655]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-13.063,0.021],[0,0]],"o":[[0,0],[0.021,-13.171],[0,0],[0,0]],"v":[[-58.135,-24.607],[-58.135,-55.195],[-34.467,-79.059],[65.219,-79.059]],"c":false},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"st","c":{"a":0,"k":[0.6274509803921569,0.6274509803921569,0.6274509803921569,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3.6,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[74.219,91.151],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":5,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.02,-13.17],[0,0],[0,0]],"o":[[-13.063,0.021],[0,0],[0,0],[0,0]],"v":[[-6.133,-52.006],[-29.801,-28.142],[-29.801,52.08],[29.801,-52.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.9411764705882353,0.9411764705882353,0.9411764705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[45.83,62.33],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0.02,-13.17],[0,0],[-1.527,0],[0,0],[-0.009,13.153],[0,0],[0,0],[-6.363,0.009],[-0.025,0.001]],"o":[[0,0],[0,0],[-13.062,0.02],[0,0],[0,1.54],[0,0],[13.045,-0.041],[0,0],[0,0],[0.081,-6.417],[0.026,0],[0,0]],"v":[[61.639,-76.59],[61.639,-81.84],[-38.048,-81.84],[-61.714,-57.977],[-61.714,79.05],[-58.947,81.84],[26.406,81.84],[50.018,57.977],[50.018,-64.542],[50.018,-64.673],[61.639,-76.581],[61.714,-76.59]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[77.799,92.164],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":10,"op":421,"st":0,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"Papel top Outlines","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[89.026,94.609,0],"ix":2},"a":{"a":0,"k":[69.157,27.261,0],"ix":1},"s":{"a":0,"k":[200,200,100],"ix":6}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.4,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[133.251,43.755],[8.394,43.755],[8.394,45.207],[133.251,45.207]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[133.324,9.505],[8.466,9.505],[8.394,45.207],[133.251,45.207]],"c":true}]},{"t":10}],"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.544],[-1.545,0],[-0.018,-0.001],[-0.01,-6.471],[0,0],[0,0],[0,1.541],[0,0],[9.485,0.041],[0,0],[0,0],[0.251,-0.027],[0,0]],"o":[[0,1.544],[97.877,0],[6.414,0.011],[0,0],[0,0],[1.527,0],[0,0],[0.041,-9.569],[0,0],[0,0],[-0.254,0.015],[0,0],[-1.544,0]],"v":[[-58.955,-12.455],[-56.155,-9.658],[41.758,-9.658],[53.379,2.066],[53.379,15.334],[56.147,15.334],[58.914,12.543],[58.914,2.066],[41.814,-15.334],[41.703,-15.334],[40.67,-15.334],[39.918,-15.251],[-56.159,-15.251]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.6274509803921569,0.6274509803921569,0.6274509803921569,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[69.205,25.502],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.67,0],[0,-9.44],[0,0],[-1.527,0],[0,0],[0,1.632],[0,0],[9.475,0]],"o":[[16,0],[0,0],[0,1.632],[0,0],[1.528,0],[0,0],[0,-10.128],[-0.642,0]],"v":[[-58.571,-15.544],[-40.571,3.353],[-40.571,13.652],[-37.803,16.61],[56.472,15.79],[59.24,12.833],[59.24,1.73],[42.083,-16.61]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.6274509803921569,0.6274509803921569,0.6274509803921569,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[68.824,26.86],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":421,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"circulito Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[516.83,232.086,0],"ix":2},"a":{"a":0,"k":[7.194,7.198,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.434,0.434,0.67],"y":[1,1,1]},"o":{"x":[0.233,0.233,0.33],"y":[0,0,0]},"t":232,"s":[0,0,100],"e":[240,240,100]},{"i":{"x":[0.196,0.196,0.67],"y":[1,1,1]},"o":{"x":[0.172,0.172,0.33],"y":[0,0,0]},"t":252,"s":[240,240,100],"e":[200,200,100]},{"t":284}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.643,0],[0,1.652],[1.642,0],[0,-1.652]],"o":[[1.642,0],[0,-1.652],[-1.643,0],[0,1.652]],"v":[[0,2.988],[2.976,0],[0,-2.988],[-2.977,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[3.837,0],[0,3.836],[-3.837,0],[0,-3.836]],"o":[[-3.837,0],[0,-3.836],[3.837,0],[0,3.836]],"v":[[0,6.948],[-6.944,0],[0,-6.948],[6.944,0]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.6274509803921569,0.6274509803921569,0.6274509803921569,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[7.194,7.198],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":232,"op":566,"st":145,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"x 2 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[96.613,416.097,0],"ix":2},"a":{"a":0,"k":[6.515,6.515,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.434,0.434,0.67],"y":[1,1,1]},"o":{"x":[0.233,0.233,0.33],"y":[0,0,0]},"t":252,"s":[0,0,100],"e":[240,240,100]},{"i":{"x":[0.196,0.196,0.67],"y":[1,1,1]},"o":{"x":[0.172,0.172,0.33],"y":[0,0,0]},"t":272,"s":[240,240,100],"e":[200,200,100]},{"t":304}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[6.258,3.885],[3.885,6.258],[0.001,2.372],[-3.891,6.264],[-6.264,3.891],[-2.374,-0.001],[-6.258,-3.885],[-3.885,-6.26],[-0.001,-2.374],[3.891,-6.264],[6.264,-3.891],[2.374,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.792156862745098,0.8470588235294118,0.9137254901960784,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.515,6.515],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":252,"op":441,"st":20,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"bg Outlines","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":7,"s":[0],"e":[100]},{"t":49}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[318.455,325.042,0],"ix":2},"a":{"a":0,"k":[119.513,102.602,0],"ix":1},"s":{"a":0,"k":[200,200,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-34.357,-7.157],[-2.441,47.062],[17.957,18.605],[34.584,38.554],[38.389,-25.44],[-24.266,-40.899]],"o":[[79.337,8.029],[3.66,-70.593],[-17.958,-18.605],[-23.057,-25.703],[-30.965,25.202],[24.266,40.898]],"v":[[-7.063,94.323],[115.603,35.774],[39.645,-45.798],[7.22,-76.517],[-84.949,-76.912],[-94.997,22.24]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.9137254901960784,0.9529411764705882,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[119.513,102.602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":421,"st":0,"bm":0}],"markers":[]} -------------------------------------------------------------------------------- /Social Contributor/Resources/quotes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "text": "Time stays long enough for anyone who will use it.", 4 | "author": "Leonardo da Vinci" 5 | }, 6 | { 7 | "text": "Life is trying things, to see if they work.", 8 | "author": "Ray Bradbury" 9 | }, 10 | { 11 | "text": "Leaders spend 5% of their time on the problem and 95% of their time on the solution. Get over it and crush it!", 12 | "author": "Tony Robbins" 13 | }, 14 | { 15 | "text": "The highest level of wisdom is when you not only accept but love adversity.", 16 | "author": "Maxime Lagace" 17 | }, 18 | { 19 | "text": "The best teacher is experience and not through someone's distorted point of view.", 20 | "author": "Jack Kerouac" 21 | }, 22 | { 23 | "text": "Even if I knew that tomorrow the world would go to pieces, I would still plant my apple tree.", 24 | "author": "Martin Luther" 25 | }, 26 | { 27 | "text": "Sometimes it is more important to discover what one cannot do, than what one can do.", 28 | "author": "Lin Yutang" 29 | }, 30 | { 31 | "text": "The most virtuous are those who content themselves with being virtuous without seeking to appear so.", 32 | "author": "Plato" 33 | }, 34 | { 35 | "text": "You are not stuck where you are unless you decide to be.", 36 | "author": "Wayne Dyer" 37 | }, 38 | { 39 | "text": "You know, the more one does the more one can do.", 40 | "author": "Amelia Earhart" 41 | }, 42 | { 43 | "text": "We can have more than we've got because we can become more than we are.", 44 | "author": "Jim Rohn" 45 | }, 46 | { 47 | "text": "Failure means you've now learned another valuable lesson that pushes you one step closer to success.", 48 | "author": "Steve Harvey" 49 | }, 50 | { 51 | "text": "If you don't know, the thing to do is not to get scared, but to learn.", 52 | "author": "Ayn Rand" 53 | }, 54 | { 55 | "text": "The difference between impossible and possible is a willing heart.", 56 | "author": "Lolly Daskal" 57 | }, 58 | { 59 | "text": "Freedom lies in being bold.", 60 | "author": "Robert Frost" 61 | }, 62 | { 63 | "text": "To be yourself in a world that is constantly trying to make you something else is the greatest accomplishment.", 64 | "author": "Ralph Waldo Emerson" 65 | }, 66 | { 67 | "text": "Exceptional thinkers ignore their critics and go about their business making history.", 68 | "author": "John Eliot" 69 | }, 70 | { 71 | "text": "Learn to enjoy every minute of your life. Be happy now. Don't wait for something outside of yourself to make you happy in the future.", 72 | "author": "Earl Nightingale" 73 | }, 74 | { 75 | "text": "The longer we dwell on our misfortunes, the greater is their power to harm us.", 76 | "author": "Voltaire" 77 | }, 78 | { 79 | "text": "Accept responsibility for your life. Know that it is you who will get you where you want to go, no one else.", 80 | "author": "Les Brown" 81 | }, 82 | { 83 | "text": "If you're waiting until you feel talented enough to make it, you'll never make it.", 84 | "author": "Criss Jami" 85 | } 86 | ] 87 | -------------------------------------------------------------------------------- /Social Contributor/Screens/Home/Models/Quote.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Quote.swift 3 | // Social Contributor 4 | // 5 | // Created by Danijela Vrzan on 2022-05-22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Quote: Decodable { 11 | let text: String 12 | let author: String 13 | } 14 | -------------------------------------------------------------------------------- /Social Contributor/Screens/Home/ViewModels/HomeViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeViewModel.swift 3 | // Social Contributor 4 | // 5 | // Created by Danijela Vrzan on 2022-05-22. 6 | // 7 | 8 | import Foundation 9 | 10 | class HomeViewModel: ObservableObject { 11 | @Published var quote: Quote! 12 | var quotes: [Quote] = Bundle.main.decode(Constants.FileName.quotes) 13 | 14 | init() { 15 | getRandomQuote() 16 | } 17 | 18 | func getRandomQuote() { 19 | guard let randomQuote = quotes.randomElement() else { 20 | quote = Constants.placeholderQuote 21 | return 22 | } 23 | quote = randomQuote 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Social Contributor/Screens/Home/Views/Components/ViewModifiers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewModifiers.swift 3 | // Social Contributor 4 | // 5 | // Created by Danijela Vrzan on 2022-05-23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct QuoteViewModifier: ViewModifier { 11 | func body(content: Content) -> some View { 12 | content 13 | .shadow(color: .black, radius: 1, x: 2, y: 2) 14 | .padding(.horizontal, 15) 15 | .foregroundColor(.white) 16 | .font(.headline) 17 | } 18 | } 19 | 20 | extension View { 21 | func quoteViewModifier() -> some View { 22 | modifier(QuoteViewModifier()) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Social Contributor/Screens/Home/Views/HomeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeView.swift 3 | // Social Contributor 4 | // 5 | // Created by Danijela Vrzan on 2022-05-22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct HomeView: View { 11 | @StateObject private var viewModel = HomeViewModel() 12 | 13 | var gradient = Gradient( 14 | colors: [ 15 | Color.lightBlue, 16 | Color.darkBlue 17 | ] 18 | ) 19 | 20 | var body: some View { 21 | VStack(alignment: .leading) { 22 | RadialGradient( 23 | gradient: gradient, 24 | center: .center, 25 | startRadius: 0, 26 | endRadius: 120 27 | ) 28 | .frame( 29 | width: UIScreen.main.bounds.width, 30 | height: 180 31 | ) 32 | .overlay( 33 | QuoteView(viewModel: viewModel) 34 | ) 35 | .navigationTitle(Constants.NavigationTitle.home) 36 | 37 | Spacer() 38 | } 39 | .embedInNavigationView() 40 | .environmentObject(viewModel) 41 | .onAppear { 42 | viewModel.getRandomQuote() 43 | } 44 | } 45 | } 46 | 47 | struct HomeView_Previews: PreviewProvider { 48 | static var previews: some View { 49 | HomeView() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Social Contributor/Screens/Home/Views/QuoteView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QuoteView.swift 3 | // Social Contributor 4 | // 5 | // Created by Danijela Vrzan on 2022-05-22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct QuoteView: View { 11 | @ObservedObject var viewModel: HomeViewModel 12 | 13 | var body: some View { 14 | VStack { 15 | Text(viewModel.quote.text) 16 | .multilineTextAlignment(.center) 17 | .padding(.bottom, 10) 18 | Text("- \(viewModel.quote.author)") 19 | } 20 | .quoteViewModifier() 21 | } 22 | } 23 | 24 | struct QuoteView_Previews: PreviewProvider { 25 | static var previews: some View { 26 | QuoteView(viewModel: HomeViewModel()) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Social Contributor/Screens/Muse/MuseViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // Social Contributor 4 | // 5 | // Created by Yash Shah on 04/05/2022. 6 | // 7 | 8 | import Foundation 9 | import MusicKit 10 | 11 | class MuseViewModel: ObservableObject { 12 | @Published private(set) var tracks: MusicItemCollection = [] 13 | @Published private(set) var player = ApplicationMusicPlayer.shared 14 | 15 | @MainActor func fetchTracks() async throws { 16 | let url = URL(staticString: "https://api.music.apple.com/v1/me/recent/played/tracks") 17 | 18 | let request = MusicDataRequest(urlRequest: .init(url: url)) 19 | let response = try await request.response() 20 | 21 | let tracks = try JSONDecoder().decode(MusicItemCollection.self, from: response.data) 22 | 23 | self.tracks = tracks 24 | } 25 | 26 | func play(_ track: Track) { 27 | player.queue = ApplicationMusicPlayer.Queue(for: tracks, startingAt: track) 28 | 29 | Task { 30 | do { 31 | try await player.play() 32 | } catch { 33 | print("Failed to prepare to play with error: \(error).") 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Social Contributor/Screens/Muse/Views/LottieView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LottieView.swift 3 | // Social Contributor 4 | // 5 | // Created by Yash Shah on 04/05/2022. 6 | // 7 | 8 | import SwiftUI 9 | import Lottie 10 | 11 | struct LottieView: UIViewRepresentable { 12 | let lottieFile: String 13 | 14 | let animationView = AnimationView() 15 | 16 | func makeUIView(context: Context) -> some UIView { 17 | let view = UIView(frame: .zero) 18 | 19 | animationView.animation = Animation.named(lottieFile) 20 | animationView.contentMode = .scaleAspectFit 21 | animationView.loopMode = .playOnce 22 | animationView.play() 23 | 24 | view.addSubview(animationView) 25 | 26 | animationView.translatesAutoresizingMaskIntoConstraints = false 27 | 28 | NSLayoutConstraint.activate([ 29 | animationView.heightAnchor.constraint(equalTo: view.heightAnchor), 30 | animationView.widthAnchor.constraint(equalTo: view.widthAnchor) 31 | ]) 32 | 33 | return view 34 | } 35 | 36 | func updateUIView(_ uiView: UIViewType, context: Context) { 37 | } 38 | } 39 | 40 | struct LottieView_Previews: PreviewProvider { 41 | static var previews: some View { 42 | Group { 43 | LottieView(lottieFile: "no-results-found") 44 | 45 | LottieView(lottieFile: "no-results-found") 46 | .darkMode() 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Social Contributor/Screens/Muse/Views/MuseView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MuseView.swift 3 | // Social Contributor 4 | // 5 | // Created by Rudrank Riyam on 05/05/22. 6 | // 7 | 8 | import SwiftUI 9 | import MusicKit 10 | 11 | struct MuseView: View { 12 | @StateObject private var viewModel = MuseViewModel() 13 | @State private var alertPresented = false 14 | @State private var alertMessage = "" 15 | 16 | var body: some View { 17 | NavigationView { 18 | Group { 19 | if viewModel.tracks.isEmpty { 20 | VStack { 21 | LottieView(lottieFile: "no-results-found") 22 | .frame(height: UIScreen.main.bounds.height/2, alignment: .top).padding(.top) 23 | Text("Sorry.\nWe have nothing to play for you.") 24 | .multilineTextAlignment(.center) 25 | .lineLimit(nil) 26 | .font(.title2.weight(.semibold)) 27 | .foregroundColor(.primary) 28 | 29 | Button { 30 | Task { await reloadData() } 31 | } label: { 32 | Text("Try Again") 33 | } 34 | .buttonStyle(.bordered) 35 | .padding() 36 | 37 | Spacer() 38 | } 39 | } else { 40 | List { 41 | ForEach(viewModel.tracks) { track in 42 | Button { 43 | viewModel.play(track) 44 | } label: { 45 | trackRow(track) 46 | } 47 | .buttonStyle(.plain) 48 | } 49 | } 50 | .refreshable { await reloadData() } 51 | } 52 | } 53 | .navigationTitle("Muse") 54 | } 55 | .navigationViewStyle(.stack) 56 | .task { await reloadData() } 57 | .alert("Oops! Something Went Wrong.", isPresented: $alertPresented) { 58 | Button("Cancel", action: {}) 59 | Button("Try again") { 60 | Task { await reloadData() } 61 | } 62 | } message: { 63 | Text(alertMessage) 64 | } 65 | 66 | } 67 | 68 | private func reloadData() async { 69 | do { 70 | try await viewModel.fetchTracks() 71 | } catch { 72 | alertPresented.toggle() 73 | alertMessage = error.localizedDescription 74 | } 75 | } 76 | 77 | private func trackRow(_ track: Track) -> some View { 78 | HStack { 79 | if let artwork = track.artwork { 80 | ArtworkImage(artwork, height: 50) 81 | .cornerRadius(8) 82 | } 83 | 84 | VStack(alignment: .leading) { 85 | Text(track.title) 86 | .font(.headline) 87 | 88 | Text(track.artistName) 89 | .font(.subheadline) 90 | } 91 | } 92 | .padding(8) 93 | } 94 | } 95 | 96 | // Lottie File credit - https://assets8.lottiefiles.com/packages/lf20_3qzrm0wa.json 97 | 98 | struct MuseView_Previews: PreviewProvider { 99 | static var previews: some View { 100 | Group { 101 | MuseView() 102 | 103 | MuseView() 104 | .darkMode() 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Social Contributor/Screens/Newsletter/Models/NewsLetter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NewsLetter.swift 3 | // Social Contributor 4 | // 5 | // Created by Mohammad Azam on 5/7/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct NewsLetter: Decodable { 11 | let name: String 12 | let url: URL 13 | } 14 | -------------------------------------------------------------------------------- /Social Contributor/Screens/Newsletter/NewsLetterListViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NewsLetterListViewModel.swift 3 | // Social Contributor 4 | // 5 | // Created by Mohammad Azam on 5/7/22. 6 | // 7 | 8 | import Foundation 9 | 10 | class NewsLetterListViewModel: ObservableObject { 11 | 12 | @Published var newsLetters: [NewsLetterViewModel] = [] 13 | 14 | func populateAllNewsLetters() { 15 | 16 | let newsLetters = NewsLetterService().load(Constants.FileName.newsletters) 17 | self.newsLetters = newsLetters.map(NewsLetterViewModel.init) 18 | } 19 | 20 | } 21 | 22 | struct NewsLetterViewModel: Identifiable { 23 | 24 | private var newsLetter: NewsLetter 25 | 26 | init(newsLetter: NewsLetter) { 27 | self.newsLetter = newsLetter 28 | } 29 | 30 | var id: UUID { 31 | return UUID() 32 | } 33 | 34 | var name: String { 35 | newsLetter.name 36 | } 37 | 38 | var url: URL { 39 | newsLetter.url 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Social Contributor/Screens/Newsletter/Views/NewsLetterListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NewsLetterListView.swift 3 | // Social Contributor 4 | // 5 | // Created by Mohammad Azam on 5/7/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct NewsLetterListView: View { 11 | 12 | @StateObject private var viewModel = NewsLetterListViewModel() 13 | 14 | var body: some View { 15 | 16 | List(viewModel.newsLetters) { newsLetter in 17 | HStack { 18 | Link(newsLetter.name, destination: newsLetter.url) 19 | Spacer() 20 | Image(systemName: "chevron.right") 21 | } 22 | } 23 | .navigationTitle(Constants.NavigationTitle.newsletters) 24 | .embedInNavigationView() 25 | .listStyle(.plain) 26 | .onAppear { 27 | viewModel.populateAllNewsLetters() 28 | } 29 | } 30 | } 31 | 32 | struct NewsLetterListView_Previews: PreviewProvider { 33 | static var previews: some View { 34 | NewsLetterListView() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Social Contributor/Screens/Settings/Models/Contributor.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | // Github Contributors for this app 4 | // https://api.github.com/repos/adamrushy/social-swiftui-app/contributors 5 | struct Contributor: Codable { 6 | let id: Int 7 | let login: String 8 | let avatarUrl: URL 9 | let htmlUrl: URL 10 | } 11 | -------------------------------------------------------------------------------- /Social Contributor/Screens/Settings/SettingsLabelStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsLabelStyle.swift 3 | // Social Contributor 4 | // 5 | // Created by Alex Logan on 04/05/2022. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct SettingsLabelStyle: LabelStyle { 12 | // Base all sizing on the current accessibility scale so everyone can read it. 13 | @ScaledMetric var accessibilityScale: CGFloat = 1 14 | 15 | var cornerRadius: CGFloat { 16 | 6 * accessibilityScale 17 | } 18 | var iconFrameSize: CGFloat { 19 | 28 * accessibilityScale 20 | } 21 | 22 | let backgroundColor: Color 23 | 24 | func makeBody(configuration: Configuration) -> some View { 25 | HStack(spacing: 12) { 26 | RoundedRectangle(cornerRadius: cornerRadius) 27 | .frame(width: iconFrameSize, height: iconFrameSize, alignment: .center) 28 | .foregroundColor(backgroundColor) 29 | .overlay( 30 | configuration.icon 31 | .font(font) 32 | .foregroundColor(.white) 33 | ) 34 | configuration.title 35 | .layoutPriority(1) 36 | .font(font) 37 | .foregroundColor(.primary) 38 | .frame(maxWidth: .infinity, alignment: .leading) 39 | Spacer() 40 | } 41 | } 42 | 43 | var font: Font { 44 | .body.weight(.medium) 45 | } 46 | } 47 | 48 | struct SettingsLabelStyle_Previews: PreviewProvider { 49 | static var previews: some View { 50 | Group { 51 | List { 52 | Label("Contribute", systemImage: "person.badge.plus") 53 | .labelStyle(SettingsLabelStyle(backgroundColor: .orange)) 54 | Label("Help", systemImage: "questionmark.circle.fill") 55 | .labelStyle(SettingsLabelStyle(backgroundColor: .blue)) 56 | Label("Contact", systemImage: "mail") 57 | .labelStyle(SettingsLabelStyle(backgroundColor: .pink)) 58 | } 59 | 60 | List { 61 | Label("Contribute", systemImage: "person.badge.plus") 62 | .labelStyle(SettingsLabelStyle(backgroundColor: .orange)) 63 | Label("Help", systemImage: "questionmark.circle.fill") 64 | .labelStyle(SettingsLabelStyle(backgroundColor: .blue)) 65 | Label("Contact", systemImage: "mail") 66 | .labelStyle(SettingsLabelStyle(backgroundColor: .pink)) 67 | }.darkMode() 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Social Contributor/Screens/Settings/Views/SettingsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsView.swift 3 | // Social Contributor 4 | // 5 | // Created by Alex Logan on 04/05/2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct SettingsView: View { 11 | 12 | @EnvironmentObject var colorSchemeManager: AppColorSchemeManager 13 | @StateObject private var contributorsProvider: ContributorsProvider 14 | 15 | // This init is here to mute the following warning: 16 | // "Expression requiring global actor 'MainActor' cannot appear in default-value 17 | // expression of property '_contributorsProvider'; this is an error in Swift 6" 18 | // when using just: 19 | // @StateObject private var contributorsProvider = ContributorsProvider() 20 | // See more info: 21 | // https://tinyurl.com/5h2v9a75 22 | // https://twitter.com/andresr_develop/status/1509287460961927186 23 | init() { 24 | _contributorsProvider = StateObject(wrappedValue: ContributorsProvider()) 25 | } 26 | 27 | var body: some View { 28 | NavigationView { 29 | List { 30 | general 31 | about 32 | contribution 33 | } 34 | .navigationTitle(Text("Settings")) 35 | } 36 | .task { 37 | await contributorsProvider.fetchContributors() 38 | } 39 | } 40 | 41 | } 42 | 43 | struct SettingsView_Previews: PreviewProvider { 44 | static var previews: some View { 45 | Group { 46 | SettingsView() 47 | 48 | SettingsView() 49 | .darkMode() 50 | } 51 | .environmentObject(AppColorSchemeManager()) 52 | } 53 | } 54 | 55 | private extension SettingsView { 56 | 57 | var general: some View { 58 | Section("General") { 59 | 60 | Picker(selection: $colorSchemeManager.colorScheme) { 61 | ForEach(AppColorScheme.allCases) { item in 62 | Text(item.title) 63 | .tag(item.rawValue) 64 | } 65 | } label: { 66 | Text("App Theme") 67 | } 68 | } 69 | } 70 | 71 | var contribution: some View { 72 | Section(content: { 73 | Button(action: { 74 | let githubUrl = Constants.URLPath.gitHubProject 75 | UIApplication.shared.open(githubUrl) 76 | }, label: { 77 | NavigationLink { 78 | EmptyView() 79 | } label: { 80 | Label("Github", systemImage: "chevron.left.forwardslash.chevron.right") 81 | .labelStyle(SettingsLabelStyle(backgroundColor: .black.opacity(0.7))) 82 | } 83 | }) 84 | 85 | Button(action: { 86 | let contributorsUrl = Constants.URLPath.gitHubContributors 87 | UIApplication.shared.open(contributorsUrl) 88 | }, label: { 89 | NavigationLink { 90 | EmptyView() 91 | } label: { 92 | Label( 93 | contributorsProvider.contributors.isEmpty ? 94 | "Contributors" : contributorsProvider.contributors, 95 | systemImage: "person.3" 96 | ) 97 | .labelStyle(SettingsLabelStyle(backgroundColor: .black.opacity(0.7))) 98 | .lineLimit(3) 99 | } 100 | }) 101 | 102 | }, header: { 103 | Text("Contribution & Contributors") 104 | }, footer: { 105 | Text("This app is an open source project started by @Adam9Rush, with contributions open.") 106 | }) 107 | } 108 | 109 | var about: some View { 110 | Section("About") { 111 | Label("Version " + String.basicBuildInfo(), systemImage: "terminal.fill") 112 | .labelStyle(SettingsLabelStyle(backgroundColor: .blue)) 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Social Contributor/Services/ContributorsProvider.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @MainActor final class ContributorsProvider: ObservableObject { 4 | @Published var contributors: String = "" 5 | 6 | func fetchContributors() async { 7 | 8 | do { 9 | let url = Constants.URLPath.gitHubContributors 10 | let response = try await URLSession.shared.decode( 11 | [Contributor].self, 12 | from: url, 13 | keyDecodingStrategy: .convertFromSnakeCase 14 | ) 15 | let names: [String] = response.map { $0.login } 16 | contributors = names.joined(separator: ", ") 17 | } catch { 18 | // Add error handling 🤔 19 | print("ERROR: \(error)") 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Social Contributor/Services/NewsLetterService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NewsLetterService.swift 3 | // Social Contributor 4 | // 5 | // Created by Mohammad Azam on 5/7/22. 6 | // 7 | 8 | import Foundation 9 | 10 | class NewsLetterService { 11 | 12 | func load(_ fileName: String) -> [NewsLetter] { 13 | 14 | guard let path = Bundle.main.path(forResource: fileName, ofType: "json") else { 15 | fatalError("newsletters file is not found!") 16 | } 17 | 18 | do { 19 | let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) 20 | let newsletters = try JSONDecoder().decode([NewsLetter].self, from: data) 21 | return newsletters 22 | } catch { 23 | return [] 24 | } 25 | 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /Social Contributor/Social_ContributorApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Social_ContributorApp.swift 3 | // Social Contributor 4 | // 5 | // Created by Adam Rush on 04/05/2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct Social_ContributorApp: App { 12 | 13 | @StateObject private var colorSchemeManager = AppColorSchemeManager() 14 | 15 | let persistenceController = PersistenceController.shared 16 | 17 | var body: some Scene { 18 | WindowGroup { 19 | 20 | // This will silence the constraints errors displayed on the output window 21 | // swiftlint:disable: redundant_discardable_let 22 | let _ = UserDefaults.standard.set(false, forKey: "_UIConstraintBasedLayoutLogUnsatisfiable") 23 | 24 | ContentView() 25 | .environment(\.managedObjectContext, persistenceController.container.viewContext) 26 | .environmentObject(colorSchemeManager) 27 | .preferredColorScheme(colorSchemeManager.currentScheme) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Social Contributor/Utils/AppReviewRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created for Social Contributor 3 | // by Stewart Lynch on 2022-05-07 4 | // Using Swift 5.0 5 | // 6 | // Follow me on Twitter: @StewartLynch 7 | // Subscribe on YouTube: https://youTube.com/StewartLynch 8 | // 9 | 10 | import SwiftUI 11 | import StoreKit 12 | 13 | enum AppReviewRequest { 14 | // Adjust this threshold as you see fit. 15 | static var threshold = 3 16 | @AppStorage("runSinceLastRequest") static var runsSinceLastRequest = 0 17 | @AppStorage("version") static var version = "" 18 | static func requestReviewIfNeeded() { 19 | runsSinceLastRequest += 1 20 | guard let appBuild = Bundle 21 | .main 22 | .object(forInfoDictionaryKey: "CFBundleVersion") as? String else { return } 23 | guard let appVersion = Bundle 24 | .main 25 | .object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String else { return } 26 | let thisVersion = "\(appVersion).\(appBuild)" 27 | #if DEBUG 28 | print("Run Count: \(runsSinceLastRequest)/\(threshold)") 29 | print("Version: \(thisVersion)") 30 | #endif 31 | // Only present if new version 32 | guard thisVersion != version else { runsSinceLastRequest = 0; return } 33 | // Only present review if threshold has been met 34 | guard runsSinceLastRequest >= threshold else { return } 35 | if let scene = UIApplication 36 | .shared 37 | .connectedScenes 38 | .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene { 39 | SKStoreReviewController.requestReview(in: scene) 40 | version = thisVersion 41 | runsSinceLastRequest = 0 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Social Contributor/Utils/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // Social Contributor 4 | // 5 | // Created by Mohammad Azam on 5/7/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Constants { 11 | static let placeholderQuote = Quote( 12 | text: "If you're waiting until you feel talented enough to make it, you'll never make it.", 13 | author: "Criss Jami" 14 | ) 15 | 16 | struct FileName { 17 | static let newsletters = "newsletters" 18 | static let quotes = "quotes.json" 19 | } 20 | 21 | struct NavigationTitle { 22 | static let home = "Welcome" 23 | static let settings = "Settings" 24 | static let newsletters = "Newsletters" 25 | } 26 | 27 | struct URLPath { 28 | static let gitHubContributors = URL( 29 | staticString: "https://api.github.com/repos/adamrushy/social-swiftui-app/contributors" 30 | ) 31 | static let gitHubProject = URL( 32 | staticString: "https://github.com/adamrushy/social-swiftui-app" 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Social Contributor/Utils/CustomColors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomColors.swift 3 | // Social Contributor 4 | // 5 | // Created by Danijela Vrzan on 2022-05-23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension Color { 11 | static let lightBlue = Color("light-blue") 12 | static let darkBlue = Color("dark-blue") 13 | } 14 | -------------------------------------------------------------------------------- /Social Contributor/Utils/DecoderReport.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DecoderReport.swift 3 | // Social Contributor 4 | // 5 | // Created by Emin Grbo -> @emin_ui 6 | // 7 | 8 | import Foundation 9 | 10 | /// This method would get any data, and provide you with a more specific info on WHY decode failed. 11 | /// Too often when fetching remote JSON you fail the see the response because something changed in the model. 12 | /// This way, you will have some insight into which key was invalid. 13 | /// I know, there are lots of new lines, but it really gelps you spot the message 😅 14 | func decoderReport(data: Data, expecing: T) { 15 | do { 16 | let _ = try JSONDecoder().decode(T.self, from: data) 17 | print("\n\n👍ALL GOOD\n\n") 18 | } catch DecodingError.keyNotFound( let key, let context) { 19 | print("\n\n⛔️FAILED TO DECODE\n\n") 20 | print("could not find key \(key) in JSON: \(context.debugDescription)") 21 | } catch DecodingError.valueNotFound( let type, let context) { 22 | print("\n\n⛔️FAILED TO DECODE\n\n") 23 | print("could not find type \(type) in JSON: \(context.debugDescription)") 24 | } catch DecodingError.typeMismatch( let type, let context) { 25 | print("\n\n⛔️FAILED TO DECODE\n\n") 26 | print("type mismatch for type \(type) in JSON: \(context.debugDescription)") 27 | } catch DecodingError.dataCorrupted( let context) { 28 | print("\n\n⛔️FAILED TO DECODE\n\n") 29 | print("data found to be corrupted in JSON: \(context.debugDescription)") 30 | } catch let error as NSError { 31 | print("\n\n⛔️FAILED TO DECODE\n\n") 32 | print(String(data: data, encoding: String.Encoding.utf8) ?? "NIL") 33 | print("\n\n —————————— ") 34 | NSLog("Error in read(from:ofType:) domain = \(error.domain), description= \(error.localizedDescription)") 35 | } 36 | return 37 | } 38 | -------------------------------------------------------------------------------- /Social ContributorTests/Home/HomeViewModelTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeViewModelTests.swift 3 | // Social ContributorTests 4 | // 5 | // Created by Danijela Vrzan on 2022-05-23. 6 | // 7 | 8 | import XCTest 9 | @testable import Social_Contributor 10 | 11 | class HomeViewModelTests: XCTestCase { 12 | var viewModel: HomeViewModel! 13 | 14 | override func setUpWithError() throws { 15 | try super.setUpWithError() 16 | viewModel = HomeViewModel() 17 | } 18 | 19 | override func tearDownWithError() throws { 20 | try super.tearDownWithError() 21 | viewModel = nil 22 | } 23 | 24 | //MARK: Tests 25 | func test_quote_notNil_whenVMInitialized() { 26 | XCTAssertNotNil(viewModel.quote) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Social ContributorTests/Social_ContributorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Social_ContributorTests.swift 3 | // Social ContributorTests 4 | // 5 | // Created by Mikaela Caron on 5/6/22. 6 | // 7 | 8 | import XCTest 9 | 10 | class Social_ContributorTests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | } 15 | 16 | override func tearDownWithError() throws { 17 | // Put teardown code here. This method is called after the invocation of each test method in the class. 18 | } 19 | 20 | func testExample() throws { 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamrushy/social-swiftui-app/2fa9c87e93cc6e63cc5d599d17695d7a9cc4ccc8/banner.png --------------------------------------------------------------------------------