├── .gitignore ├── Quotes ├── Resources │ └── Fonts │ │ ├── Gotham-Light.otf │ │ ├── Garamond Roman.ttf │ │ ├── Nunito-Regular.ttf │ │ ├── Poppins-Regular.ttf │ │ ├── Vollkorn-Italic.ttf │ │ ├── times new roman.ttf │ │ ├── Freight Sans Book.otf │ │ ├── Montserrat-Regular.otf │ │ ├── OpenSans-Regular.ttf │ │ ├── PlayfairDisplay-Bold.otf │ │ ├── PlayfairDisplay-Black.otf │ │ ├── PlayfairDisplay-Italic.otf │ │ ├── PlayfairDisplay-Regular.otf │ │ ├── PlayfairDisplay-BlackItalic.otf │ │ ├── PlayfairDisplay-BoldItalic.otf │ │ └── DancingScript-VariableFont_wght.ttf ├── SupportingFiles │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── back.imageset │ │ │ ├── back.png │ │ │ ├── back@2x.png │ │ │ ├── back@3x.png │ │ │ └── Contents.json │ │ ├── home.imageset │ │ │ ├── home.png │ │ │ ├── home@2x.png │ │ │ ├── home@3x.png │ │ │ └── Contents.json │ │ ├── logo.imageset │ │ │ ├── logo.png │ │ │ └── Contents.json │ │ ├── none.imageset │ │ │ ├── none.jpeg │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── 100.png │ │ │ ├── 114.png │ │ │ ├── 120.png │ │ │ ├── 128.png │ │ │ ├── 144.png │ │ │ ├── 152.png │ │ │ ├── 16.png │ │ │ ├── 167.png │ │ │ ├── 172.png │ │ │ ├── 180.png │ │ │ ├── 196.png │ │ │ ├── 20.png │ │ │ ├── 216.png │ │ │ ├── 256.png │ │ │ ├── 29.png │ │ │ ├── 32.png │ │ │ ├── 40.png │ │ │ ├── 48.png │ │ │ ├── 50.png │ │ │ ├── 512.png │ │ │ ├── 55.png │ │ │ ├── 57.png │ │ │ ├── 58.png │ │ │ ├── 60.png │ │ │ ├── 64.png │ │ │ ├── 72.png │ │ │ ├── 76.png │ │ │ ├── 80.png │ │ │ ├── 87.png │ │ │ ├── 88.png │ │ │ ├── 1024.png │ │ │ └── Contents.json │ │ ├── close.imageset │ │ │ ├── close.png │ │ │ ├── close@2x.png │ │ │ ├── close@3x.png │ │ │ └── Contents.json │ │ ├── copied.imageset │ │ │ ├── copied.png │ │ │ ├── copied@2x.png │ │ │ ├── copied@3x.png │ │ │ └── Contents.json │ │ ├── create.imageset │ │ │ ├── create.png │ │ │ ├── create@2x.png │ │ │ ├── create@3x.png │ │ │ └── Contents.json │ │ ├── delete.imageset │ │ │ ├── delete.png │ │ │ ├── delete@2x.png │ │ │ ├── delete@3x.png │ │ │ └── Contents.json │ │ ├── ratio.imageset │ │ │ ├── ratio.png │ │ │ ├── ratio@2x.png │ │ │ ├── ratio@3x.png │ │ │ └── Contents.json │ │ ├── art-cat.imageset │ │ │ ├── art-cat.jpg │ │ │ └── Contents.json │ │ ├── explore.imageset │ │ │ ├── explore.png │ │ │ ├── explore@2x.png │ │ │ ├── explore@3x.png │ │ │ └── Contents.json │ │ ├── forward.imageset │ │ │ ├── forward.png │ │ │ ├── forward@2x.png │ │ │ ├── forward@3x.png │ │ │ └── Contents.json │ │ ├── download.imageset │ │ │ ├── download.png │ │ │ ├── download@2x.png │ │ │ ├── download@3x.png │ │ │ └── Contents.json │ │ ├── favourite.imageset │ │ │ ├── favourite.png │ │ │ ├── favourite@2x.png │ │ │ ├── favourite@3x.png │ │ │ └── Contents.json │ │ ├── music-cat.imageset │ │ │ ├── music-cat.jpg │ │ │ └── Contents.json │ │ ├── save-flag.imageset │ │ │ ├── save-flag.png │ │ │ ├── save-flag@2x.png │ │ │ ├── save-flag@3x.png │ │ │ └── Contents.json │ │ ├── history-cat.imageset │ │ │ ├── history-cat.jpg │ │ │ └── Contents.json │ │ ├── nature-cat.imageset │ │ │ ├── nature-cat.jpg │ │ │ └── Contents.json │ │ ├── opacityView.imageset │ │ │ ├── opacityView.png │ │ │ ├── opacityView@2x.png │ │ │ ├── opacityView@3x.png │ │ │ └── Contents.json │ │ ├── success-cat.imageset │ │ │ ├── success-art.jpg │ │ │ └── Contents.json │ │ ├── education-cat.imageset │ │ │ ├── education-cat.jpg │ │ │ └── Contents.json │ │ ├── happiness-cat.imageset │ │ │ ├── happiness-cat.jpg │ │ │ └── Contents.json │ │ ├── home-selected.imageset │ │ │ ├── home-selected.png │ │ │ ├── home-selected@2x.png │ │ │ ├── home-selected@3x.png │ │ │ └── Contents.json │ │ ├── technology-cat.imageset │ │ │ ├── technology-cat.jpg │ │ │ └── Contents.json │ │ ├── explore-selected.imageset │ │ │ ├── explore-selected.png │ │ │ ├── explore-selected@2x.png │ │ │ ├── explore-selected@3x.png │ │ │ └── Contents.json │ │ ├── inspiration-cat.imageset │ │ │ ├── inspiration-cat.jpg │ │ │ └── Contents.json │ │ ├── favourite-selected.imageset │ │ │ ├── favourite-selected.png │ │ │ ├── favourite-selected@2x.png │ │ │ ├── favourite-selected@3x.png │ │ │ └── Contents.json │ │ └── AccentColor.colorset │ │ │ └── Contents.json │ ├── Info.plist │ └── Launch Screen.storyboard ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── Model │ ├── TagsModel.swift │ ├── RealmDB │ │ └── QuotesDB.swift │ ├── QuotesModel.swift │ ├── AuthorsModel.swift │ ├── ImagesModel.swift │ └── UrlImageModel.swift ├── Utils │ ├── Structure.swift │ ├── ImageCache.swift │ ├── Extensions.swift │ └── Constants.swift ├── App │ ├── QuotesApp.swift │ ├── ExploreView.swift │ ├── QuoteDetailView.swift │ ├── CustomTabView.swift │ ├── AuthorDetailView.swift │ ├── ExploreAuthorListView.swift │ ├── FavouriteView.swift │ ├── HomeView.swift │ ├── TagFilteredQuoteListView.swift │ ├── CreatorsView.swift │ └── AuthorQuoteListView.swift ├── View │ ├── DefaultView.swift │ ├── CustomImageView.swift │ ├── CustomTitleView.swift │ ├── AuthorNameView.swift │ ├── TagCardView.swift │ ├── QuoteDetailCardView.swift │ ├── CopyActionView.swift │ ├── CreateActionView.swift │ ├── CustomAnimationView.swift │ ├── AuthorInfoView.swift │ ├── CustomBCOpacityView.swift │ ├── InfoCardView.swift │ ├── ActionCardView │ │ ├── FontSizeView.swift │ │ ├── FontColorView.swift │ │ ├── FontView.swift │ │ ├── PostBackgroundColorView.swift │ │ └── ImageCategoryView.swift │ ├── ExploreAuthorCardView.swift │ ├── FavoriteActionView.swift │ ├── QuoteCardView.swift │ ├── ActionCardView.swift │ ├── QuoteTagsView.swift │ └── CreatorActionView.swift └── ViewModel │ ├── TagsViewModel.swift │ ├── ImagesViewModel.swift │ ├── AuthorsViewModel.swift │ ├── RealmViewModel │ └── QuotesRealmViewModel.swift │ └── QuotesViewModel.swift ├── Quotes.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── dheerajkumarsharma.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── Podfile ├── Quotes.xcworkspace ├── contents.xcworkspacedata ├── xcshareddata │ ├── WorkspaceSettings.xcsettings │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── dheerajkumarsharma.xcuserdatad │ └── WorkspaceSettings.xcsettings ├── Podfile.lock └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | Pods/ 2 | -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/Gotham-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/Gotham-Light.otf -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/Garamond Roman.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/Garamond Roman.ttf -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/Nunito-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/Nunito-Regular.ttf -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/Poppins-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/Poppins-Regular.ttf -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/Vollkorn-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/Vollkorn-Italic.ttf -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/times new roman.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/times new roman.ttf -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Quotes/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/Freight Sans Book.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/Freight Sans Book.otf -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/Montserrat-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/Montserrat-Regular.otf -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/PlayfairDisplay-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/PlayfairDisplay-Bold.otf -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/PlayfairDisplay-Black.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/PlayfairDisplay-Black.otf -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/PlayfairDisplay-Italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/PlayfairDisplay-Italic.otf -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/PlayfairDisplay-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/PlayfairDisplay-Regular.otf -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/PlayfairDisplay-BlackItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/PlayfairDisplay-BlackItalic.otf -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/PlayfairDisplay-BoldItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/PlayfairDisplay-BoldItalic.otf -------------------------------------------------------------------------------- /Quotes/Resources/Fonts/DancingScript-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/Resources/Fonts/DancingScript-VariableFont_wght.ttf -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/back.imageset/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/back.imageset/back.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/home.imageset/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/home.imageset/home.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/logo.imageset/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/logo.imageset/logo.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/none.imageset/none.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/none.imageset/none.jpeg -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/100.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/114.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/120.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/128.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/144.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/152.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/16.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/167.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/172.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/172.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/180.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/196.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/20.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/216.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/216.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/256.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/29.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/32.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/40.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/48.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/50.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/512.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/55.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/57.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/58.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/60.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/72.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/76.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/80.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/87.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/88.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/88.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/back.imageset/back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/back.imageset/back@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/back.imageset/back@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/back.imageset/back@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/close.imageset/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/close.imageset/close.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/copied.imageset/copied.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/copied.imageset/copied.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/create.imageset/create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/create.imageset/create.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/delete.imageset/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/delete.imageset/delete.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/home.imageset/home@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/home.imageset/home@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/home.imageset/home@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/home.imageset/home@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/ratio.imageset/ratio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/ratio.imageset/ratio.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/art-cat.imageset/art-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/art-cat.imageset/art-cat.jpg -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/close.imageset/close@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/close.imageset/close@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/close.imageset/close@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/close.imageset/close@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/explore.imageset/explore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/explore.imageset/explore.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/forward.imageset/forward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/forward.imageset/forward.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/ratio.imageset/ratio@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/ratio.imageset/ratio@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/ratio.imageset/ratio@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/ratio.imageset/ratio@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/copied.imageset/copied@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/copied.imageset/copied@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/copied.imageset/copied@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/copied.imageset/copied@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/create.imageset/create@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/create.imageset/create@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/create.imageset/create@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/create.imageset/create@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/delete.imageset/delete@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/delete.imageset/delete@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/delete.imageset/delete@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/delete.imageset/delete@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/download.imageset/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/download.imageset/download.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/explore.imageset/explore@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/explore.imageset/explore@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/explore.imageset/explore@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/explore.imageset/explore@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/forward.imageset/forward@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/forward.imageset/forward@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/forward.imageset/forward@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/forward.imageset/forward@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/download.imageset/download@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/download.imageset/download@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/download.imageset/download@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/download.imageset/download@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/favourite.imageset/favourite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/favourite.imageset/favourite.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/music-cat.imageset/music-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/music-cat.imageset/music-cat.jpg -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/save-flag.imageset/save-flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/save-flag.imageset/save-flag.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/favourite.imageset/favourite@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/favourite.imageset/favourite@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/favourite.imageset/favourite@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/favourite.imageset/favourite@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/history-cat.imageset/history-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/history-cat.imageset/history-cat.jpg -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/nature-cat.imageset/nature-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/nature-cat.imageset/nature-cat.jpg -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/opacityView.imageset/opacityView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/opacityView.imageset/opacityView.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/save-flag.imageset/save-flag@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/save-flag.imageset/save-flag@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/save-flag.imageset/save-flag@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/save-flag.imageset/save-flag@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/success-cat.imageset/success-art.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/success-cat.imageset/success-art.jpg -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/education-cat.imageset/education-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/education-cat.imageset/education-cat.jpg -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/happiness-cat.imageset/happiness-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/happiness-cat.imageset/happiness-cat.jpg -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/home-selected.imageset/home-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/home-selected.imageset/home-selected.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/opacityView.imageset/opacityView@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/opacityView.imageset/opacityView@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/opacityView.imageset/opacityView@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/opacityView.imageset/opacityView@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/home-selected.imageset/home-selected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/home-selected.imageset/home-selected@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/home-selected.imageset/home-selected@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/home-selected.imageset/home-selected@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/technology-cat.imageset/technology-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/technology-cat.imageset/technology-cat.jpg -------------------------------------------------------------------------------- /Quotes.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/explore-selected.imageset/explore-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/explore-selected.imageset/explore-selected.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/inspiration-cat.imageset/inspiration-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/inspiration-cat.imageset/inspiration-cat.jpg -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/explore-selected.imageset/explore-selected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/explore-selected.imageset/explore-selected@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/explore-selected.imageset/explore-selected@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/explore-selected.imageset/explore-selected@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/favourite-selected.imageset/favourite-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/favourite-selected.imageset/favourite-selected.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/favourite-selected.imageset/favourite-selected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/favourite-selected.imageset/favourite-selected@2x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/favourite-selected.imageset/favourite-selected@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dheerajghub/Quotes-App/HEAD/Quotes/SupportingFiles/Assets.xcassets/favourite-selected.imageset/favourite-selected@3x.png -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'Quotes' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for Quotes 9 | pod 'RealmSwift' 10 | 11 | end 12 | -------------------------------------------------------------------------------- /Quotes.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Quotes.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Quotes.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Quotes.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Quotes/Model/TagsModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TagsModel.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import Foundation 9 | 10 | struct TagsModel: Decodable , Identifiable { 11 | 12 | let id = UUID() 13 | let _id: String 14 | let name: String 15 | let quoteCount: Int 16 | 17 | enum CodingKeys: String, CodingKey { 18 | case _id, name , quoteCount 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "logo.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/none.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "none.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/art-cat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "art-cat.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/history-cat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "history-cat.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/music-cat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "music-cat.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/nature-cat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "nature-cat.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/success-cat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "success-art.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/education-cat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "education-cat.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/happiness-cat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "happiness-cat.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/inspiration-cat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "inspiration-cat.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/technology-cat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "technology-cat.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Quotes.xcodeproj/xcuserdata/dheerajkumarsharma.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Quotes.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 3 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Realm (10.7.6): 3 | - Realm/Headers (= 10.7.6) 4 | - Realm/Headers (10.7.6) 5 | - RealmSwift (10.7.6): 6 | - Realm (= 10.7.6) 7 | 8 | DEPENDENCIES: 9 | - RealmSwift 10 | 11 | SPEC REPOS: 12 | trunk: 13 | - Realm 14 | - RealmSwift 15 | 16 | SPEC CHECKSUMS: 17 | Realm: ed860452717c8db8f4bf832b6807f7f2ce708839 18 | RealmSwift: e31c4ddbcc42ac879313d656b86f9ca539f6f4f4 19 | 20 | PODFILE CHECKSUM: 40fabe5f2d3248a141c836f4d101fb705666b007 21 | 22 | COCOAPODS: 1.10.2 23 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "back.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "back@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "back@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/home.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "home.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "home@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "home@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/close.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "close.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "close@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "close@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/ratio.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ratio.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "ratio@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "ratio@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/Utils/Structure.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Structure.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 30/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ActionViewData: Identifiable { 11 | let id = UUID() 12 | let type: String 13 | let image: UIImage! 14 | let text: String! 15 | let color: Color! 16 | let title: String! 17 | let toggleView: String! 18 | } 19 | 20 | struct CategoryData: Identifiable { 21 | let id = UUID() 22 | let name: String 23 | let image: String 24 | } 25 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/copied.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "copied.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "copied@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "copied@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/create.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "create.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "create@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "create@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/delete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "delete.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "delete@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "delete@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/download.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "download.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "download@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "download@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/explore.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "explore.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "explore@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "explore@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/forward.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "forward.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "forward@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "forward@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/favourite.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "favourite.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "favourite@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "favourite@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/save-flag.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "save-flag.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "save-flag@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "save-flag@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/opacityView.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "opacityView.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "opacityView@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "opacityView@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/home-selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "home-selected.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "home-selected@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "home-selected@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/explore-selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "explore-selected.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "explore-selected@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "explore-selected@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/favourite-selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "favourite-selected.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "favourite-selected@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "favourite-selected@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/App/QuotesApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QuotesApp.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import RealmSwift 9 | import SwiftUI 10 | import Combine 11 | 12 | @main 13 | struct QuotesApp: SwiftUI.App { 14 | var body: some Scene { 15 | WindowGroup { 16 | CustomTabView() 17 | .environment(\.realmConfiguration, Realm.Configuration()) 18 | .onAppear { 19 | print(Realm.Configuration.defaultConfiguration.fileURL) 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/Model/RealmDB/QuotesDB.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QuotesDB.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import RealmSwift 9 | import Foundation 10 | 11 | final class QuotesDB: Object, ObjectKeyIdentifiable { 12 | 13 | @objc dynamic var _id = ObjectId.generate() 14 | @objc dynamic var quoteId = "" 15 | @objc dynamic var tags = "" 16 | @objc dynamic var content = "" 17 | @objc dynamic var author = "" 18 | @objc dynamic var authorSlug = "" 19 | 20 | override static func primaryKey() -> String? { 21 | "_id" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Quotes/View/DefaultView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct DefaultView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | var title: String = "Nothing on list" 15 | 16 | // MARK: BODY - 17 | 18 | var body: some View { 19 | Text(title) 20 | .font(.custom(Constants.fontRegular, size: 20)) 21 | .foregroundColor(.gray) 22 | .padding(.top , 100) 23 | } 24 | } 25 | 26 | struct DefaultView_Previews: PreviewProvider { 27 | static var previews: some View { 28 | DefaultView() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Quotes/Utils/ImageCache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageCache.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 08/07/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | class ImageCache { 11 | var cache = NSCache() 12 | 13 | func get(forKey: String) -> UIImage? { 14 | return cache.object(forKey: NSString(string: forKey)) 15 | } 16 | 17 | func set(forKey: String, image: UIImage) { 18 | cache.setObject(image, forKey: NSString(string: forKey)) 19 | } 20 | } 21 | 22 | extension ImageCache { 23 | private static var imageCache = ImageCache() 24 | static func getImageCache() -> ImageCache { 25 | return imageCache 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Quotes.xcworkspace/xcuserdata/dheerajkumarsharma.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | UseAppPreferences 7 | CustomBuildLocationType 8 | RelativeToDerivedData 9 | DerivedDataLocationStyle 10 | Default 11 | IssueFilterStyle 12 | ShowActiveSchemeOnly 13 | LiveSourceIssuesEnabled 14 | 15 | ShowSharedSchemesAutomaticallyEnabled 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Quotes/Utils/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 01/07/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension View { 11 | func snapshot() -> UIImage { 12 | let controller = UIHostingController(rootView: self) 13 | let view = controller.view 14 | 15 | let targetSize = controller.view.intrinsicContentSize 16 | view?.bounds = CGRect(origin: .zero, size: targetSize) 17 | view?.backgroundColor = .black 18 | 19 | let renderer = UIGraphicsImageRenderer(size: targetSize) 20 | 21 | return renderer.image { _ in 22 | view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Quotes/Model/QuotesModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QuotesModel.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import Foundation 9 | 10 | struct QuotesModel: Decodable , Identifiable{ 11 | 12 | let id = UUID() 13 | var totalCount: Int 14 | var page: Int 15 | var results: [Quote] 16 | 17 | enum CodingKeys: String, CodingKey { 18 | case totalCount, page , results 19 | } 20 | 21 | } 22 | 23 | struct Quote: Decodable , Identifiable { 24 | 25 | let id = UUID() 26 | let tags: [String] 27 | let _id: String 28 | let content: String 29 | let author: String 30 | let authorSlug: String 31 | 32 | enum CodingKeys: String, CodingKey { 33 | case tags, _id , content , author , authorSlug 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Quotes/View/CustomImageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomImageView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 08/07/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CustomImageView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @ObservedObject var urlImageModel = UrlImageModel() 15 | 16 | static var defaultImage = UIImage(named: "logo") 17 | 18 | init(urlString: String?) { 19 | self.urlImageModel.getImage(urlString!) 20 | } 21 | 22 | var body: some View { 23 | Image(uiImage: urlImageModel.image ?? CustomImageView.defaultImage!) 24 | .resizable() 25 | .scaledToFill() 26 | } 27 | } 28 | 29 | //struct CustomImageView_Previews: PreviewProvider { 30 | // static var previews: some View { 31 | // CustomImageView(urlString: "") 32 | // } 33 | //} 34 | -------------------------------------------------------------------------------- /Quotes/Model/AuthorsModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthorsModel.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import Foundation 9 | 10 | struct AuthorsModel: Decodable , Identifiable { 11 | 12 | let id = UUID() 13 | var totalCount: Int 14 | var page: Int 15 | let totalPages: Int 16 | var results: [Author] 17 | 18 | enum CodingKeys: String, CodingKey { 19 | case totalCount, page , totalPages , results 20 | } 21 | 22 | } 23 | 24 | struct Author: Decodable , Identifiable { 25 | 26 | let id = UUID() 27 | let link: String 28 | let bio: String 29 | let description: String 30 | let _id: String 31 | let name: String 32 | let quoteCount: Int 33 | let slug: String 34 | 35 | enum CodingKeys: String, CodingKey { 36 | case link, bio , description , _id , name , quoteCount ,slug 37 | } 38 | 39 | } 40 | 41 | -------------------------------------------------------------------------------- /Quotes/View/CustomTitleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomTitleView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CustomTitleView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Environment(\.colorScheme) var colorScheme 15 | 16 | var titleText: String 17 | 18 | // MARK: BODY - 19 | 20 | var body: some View { 21 | HStack { 22 | Text(titleText.uppercased()) 23 | .foregroundColor(colorScheme == .dark ? Color.white : Color.black.opacity(0.8)) 24 | .font(.footnote) 25 | .bold() 26 | Spacer() 27 | } 28 | .padding(EdgeInsets(top: 10, leading: 30, bottom: 5, trailing: 20)) 29 | } 30 | } 31 | 32 | struct CustomTitleView_Previews: PreviewProvider { 33 | static var previews: some View { 34 | CustomTitleView(titleText: "Author") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Quotes/Model/ImagesModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImagesModel.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 07/07/21. 6 | // 7 | 8 | import Foundation 9 | 10 | struct ImagesModel: Decodable, Identifiable { 11 | 12 | let id = UUID() 13 | var page: Int 14 | var per_page: Int 15 | var total_results: Int 16 | var photos: [PhotosModel] 17 | 18 | enum CodingKeys: String, CodingKey { 19 | case page, per_page , photos , total_results 20 | } 21 | 22 | } 23 | 24 | struct PhotosModel: Decodable, Identifiable { 25 | 26 | let id = UUID() 27 | let width: Int 28 | let height: Int 29 | let src: ImagesContentModel 30 | 31 | enum CodingKeys: String, CodingKey { 32 | case width , height , src 33 | } 34 | 35 | } 36 | 37 | struct ImagesContentModel: Decodable , Identifiable { 38 | 39 | let id = UUID() 40 | let original: String 41 | let medium: String 42 | let small: String 43 | 44 | enum CodingKeys: String, CodingKey { 45 | case original, medium, small 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /Quotes/ViewModel/TagsViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TagsViewModel.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import Foundation 9 | 10 | class TagsViewModel: ObservableObject { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | let tagUrl = Constants.get_tags_url 15 | 16 | @Published var tagsModel: [TagsModel]? 17 | @Published var isLoading: Bool = true 18 | 19 | // MARK: METHODS - 20 | 21 | func fetchTags() { 22 | guard let url = URL(string: tagUrl) else { return } 23 | URLSession.shared.dataTask(with: url) { data, response, error in 24 | guard let data = data else { return } 25 | do { 26 | let tags = try JSONDecoder().decode([TagsModel].self, from: data) 27 | DispatchQueue.main.async { 28 | self.isLoading = false 29 | self.tagsModel = tags 30 | } 31 | } catch { 32 | print("Error description: \(error)") 33 | } 34 | }.resume() 35 | } 36 | 37 | } 38 | 39 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPhotoLibraryAddUsageDescription 6 | App wants photos access. 7 | LSRequiresIPhoneOS 8 | 9 | UIAppFonts 10 | 11 | OpenSans-Regular.ttf 12 | Nunito-Regular.ttf 13 | Poppins-Regular.ttf 14 | DancingScript-VariableFont_wght.ttf 15 | Montserrat-Regular.otf 16 | Freight Sans Book.otf 17 | Gotham-Light.otf 18 | Vollkorn-Italic.ttf 19 | Garamond Roman.ttf 20 | PlayfairDisplay-Black.otf 21 | PlayfairDisplay-BlackItalic.otf 22 | PlayfairDisplay-Bold.otf 23 | PlayfairDisplay-BoldItalic.otf 24 | PlayfairDisplay-Italic.otf 25 | PlayfairDisplay-Regular.otf 26 | 27 | UILaunchScreen 28 | 29 | UIImageName 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Quotes/View/AuthorNameView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthorNameView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct AuthorNameView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | var author: String 15 | @Environment(\.colorScheme) var colorScheme 16 | 17 | // MARK: BODY - 18 | 19 | var body: some View { 20 | HStack { 21 | Text(author) 22 | .font(.custom(Constants.fontItalic, size: 18)) 23 | .padding(EdgeInsets(top: 15, leading: 15, bottom: 15, trailing: 15)) 24 | Spacer() 25 | Image("forward") 26 | .resizable() 27 | .renderingMode(.template) 28 | .foregroundColor(colorScheme == .dark ? .white : .black) 29 | .frame(width: 20, height: 20) 30 | .padding(.trailing , 15) 31 | } 32 | .background(Constants.appSecondary) 33 | .cornerRadius(5) 34 | .padding(EdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15)) 35 | } 36 | } 37 | 38 | // MARK: PREVIEW - 39 | 40 | struct AuthorNameView_Previews: PreviewProvider { 41 | static var previews: some View { 42 | AuthorNameView(author: "Albert Einstien") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Quotes/View/TagCardView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TagCardView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct TagCardView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Environment(\.colorScheme) var colorScheme 15 | 16 | var tagName: String 17 | var quoteCount: Int 18 | 19 | // MARK: BODY - 20 | var body: some View { 21 | HStack { 22 | Spacer() 23 | VStack(alignment: .center, spacing: 0){ 24 | Text(tagName.uppercased()) 25 | .font(.custom(Constants.fontBlack, size: 18)) 26 | Text("Quote count: \(quoteCount)") 27 | .font(.custom(Constants.fontBold, size: 15)) 28 | .foregroundColor(colorScheme == .dark ? Color.white.opacity(0.7) : Color.black.opacity(0.5)) 29 | } 30 | Spacer() 31 | } 32 | .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)) 33 | .frame(height: 100) 34 | .background(Constants.appSecondary) 35 | .cornerRadius(5) 36 | } 37 | } 38 | 39 | struct TagCardView_Previews: PreviewProvider { 40 | static var previews: some View { 41 | TagCardView(tagName: "technology", quoteCount: 1234) 42 | .previewLayout(.sizeThatFits) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Quotes/View/QuoteDetailCardView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QuoteDetailCardView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct QuoteDetailCardView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | var quoteData: Quote 15 | 16 | // MARK: BODY - 17 | 18 | var body: some View { 19 | HStack { 20 | let quoteContent = 21 | Text("\" ") 22 | .font(.custom(Constants.fontBlackItalic, size: 30)) 23 | + Text(quoteData.content) 24 | .font(.custom(Constants.fontBold, size: 25)) 25 | 26 | quoteContent 27 | .padding(EdgeInsets(top: 15, leading: 15, bottom: 15, trailing: 15)) 28 | 29 | Spacer() 30 | } 31 | .background(Constants.appSecondary) 32 | .cornerRadius(5) 33 | .padding(EdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15)) 34 | } 35 | } 36 | 37 | // MARK: PREVIEW - 38 | 39 | struct QuoteDetailCardView_Previews: PreviewProvider { 40 | static var previews: some View { 41 | QuoteDetailCardView(quoteData: Quote(tags: ["wisdom"], _id: "", content: "Wisdom is a kind of knowledge. It is knowledge of the nature, career, and consequences of human values.", author: "Helmut Sigmund", authorSlug: "")) 42 | .previewLayout(.sizeThatFits) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Quotes/View/CopyActionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CopyActionView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CopyActionView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Environment(\.colorScheme) var colorScheme 15 | 16 | @Binding var copiedStateActive: Bool 17 | @Binding var copyViewScale: CGFloat 18 | @Binding var copyViewOpacity: Double 19 | 20 | // MARK: BODY - 21 | 22 | var body: some View { 23 | HStack { 24 | Image("copied") 25 | .resizable() 26 | .renderingMode(.template) 27 | .frame(width: 25, height: 25) 28 | .foregroundColor(colorScheme == .dark ? (copiedStateActive ? .black : .white) : (copiedStateActive ? .white : .black)) 29 | .opacity(copyViewOpacity) 30 | .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)) 31 | .background(colorScheme == .dark ? (copiedStateActive ? .white : Constants.appSecondary) : (copiedStateActive ? .black : Constants.appSecondary)) 32 | .cornerRadius(22.5) 33 | .scaleEffect(copyViewScale) 34 | Spacer() 35 | } 36 | .padding(.leading , 30) 37 | } 38 | } 39 | 40 | //struct CopyActionView_Previews: PreviewProvider { 41 | // static var previews: some View { 42 | // CopyActionView() 43 | // } 44 | //} 45 | -------------------------------------------------------------------------------- /Quotes/View/CreateActionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CreateActionView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 30/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CreateActionView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Environment(\.colorScheme) var colorScheme 15 | 16 | @Binding var creatingStateActive: Bool 17 | @Binding var createViewScale: CGFloat 18 | @Binding var createViewOpacity: Double 19 | 20 | // MARK: BODY - 21 | 22 | var body: some View { 23 | HStack { 24 | Image("create") 25 | .resizable() 26 | .renderingMode(.template) 27 | .frame(width: 25, height: 25) 28 | .foregroundColor(colorScheme == .dark ? .white : (creatingStateActive ? .white : .black)) 29 | .opacity(createViewOpacity) 30 | .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)) 31 | .background(colorScheme == .dark ? (creatingStateActive ? Constants.appBlue : Constants.appSecondary) : (creatingStateActive ? Constants.appBlue : Constants.appSecondary)) 32 | .cornerRadius(22.5) 33 | .scaleEffect(createViewScale) 34 | Spacer() 35 | } 36 | .padding(.leading , 30) 37 | } 38 | } 39 | 40 | //struct CreateActionView_Previews: PreviewProvider { 41 | // static var previews: some View { 42 | // CreateActionView() 43 | // } 44 | //} 45 | -------------------------------------------------------------------------------- /Quotes/View/CustomAnimationView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomAnimationView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 01/07/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CustomAnimationView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @State private var shouldAnimate = false 15 | 16 | // MARK: BODY - 17 | 18 | var body: some View { 19 | HStack { 20 | Circle() 21 | .fill(Color.black) 22 | .frame(width: 20, height: 20) 23 | .scaleEffect(shouldAnimate ? 1.0 : 0.5) 24 | .animation(Animation.easeInOut(duration: 0.5).repeatForever() , value: shouldAnimate) 25 | Circle() 26 | .fill(Color.black) 27 | .frame(width: 20, height: 20) 28 | .scaleEffect(shouldAnimate ? 1.0 : 0.5) 29 | .animation(Animation.easeInOut(duration: 0.5).repeatForever().delay(0.3) , value: shouldAnimate) 30 | Circle() 31 | .fill(Color.black) 32 | .frame(width: 20, height: 20) 33 | .scaleEffect(shouldAnimate ? 1.0 : 0.5) 34 | .animation(Animation.easeInOut(duration: 0.5).repeatForever().delay(0.6) , value: shouldAnimate) 35 | } 36 | .onAppear { 37 | self.shouldAnimate = true 38 | } 39 | } 40 | 41 | } 42 | 43 | struct CustomAnimationView_Previews: PreviewProvider { 44 | static var previews: some View { 45 | CustomAnimationView() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Quotes/View/AuthorInfoView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthorInfoView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct AuthorInfoView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | var authorData: Author 15 | 16 | // MARK: BODY - 17 | 18 | var body: some View { 19 | VStack(alignment: .leading, spacing: 10){ 20 | HStack{ 21 | VStack(alignment: .leading, spacing: 5){ 22 | Text(authorData.name) 23 | .font(.custom(Constants.fontBold, size: 27)) 24 | Text(authorData.description) 25 | .font(.custom(Constants.fontItalic, size: 15)) 26 | Text(authorData.bio) 27 | .font(.custom(Constants.fontRegular, size: 17)) 28 | .padding(.top , 10) 29 | } 30 | Spacer() 31 | } 32 | .padding(EdgeInsets(top: 10, leading: 15, bottom: 10, trailing: 15)) 33 | } 34 | .background(Constants.appSecondary) 35 | .cornerRadius(5) 36 | .padding(EdgeInsets(top: 10, leading: 15, bottom: 10, trailing: 15)) 37 | } 38 | } 39 | 40 | // MARK: PREVIEW - 41 | 42 | struct AuthorInfoView_Previews: PreviewProvider { 43 | static var previews: some View { 44 | AuthorInfoView(authorData: Author(link: "", bio: "", description: "", _id: "", name: "", quoteCount: 2, slug: "")) 45 | .previewLayout(.sizeThatFits) 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /Quotes/View/CustomBCOpacityView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomBCOpacityView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 09/07/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CustomBCOpacityView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Environment(\.colorScheme) var colorScheme 15 | @Binding var bcOpacity: CGFloat 16 | @Binding var hideOpacityView: Bool 17 | 18 | 19 | // MARK: BODY - 20 | 21 | var body: some View { 22 | ZStack(alignment: .topTrailing){ 23 | VStack { 24 | Slider(value: $bcOpacity, in: 0...1) 25 | Text("\(bcOpacity, specifier: "%.1f")") 26 | } //: VSTACK 27 | .padding(EdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15)) 28 | .frame(maxHeight: .infinity) 29 | 30 | Button { 31 | hideOpacityView = true 32 | } label: { 33 | Image("close") 34 | .resizable() 35 | .renderingMode(.template) 36 | .frame(width: 20, height: 20) 37 | .foregroundColor(colorScheme == .dark ? .white : .black) 38 | .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)) 39 | .background(Constants.appSecondary) 40 | .cornerRadius(20) 41 | } 42 | .padding(.trailing , 5) 43 | 44 | } //: ZSTACK 45 | .frame(maxHeight: .infinity) 46 | .background(colorScheme == .dark ? Color.black : Color.white) 47 | } 48 | } 49 | 50 | //struct CustomBCOpacityView_Previews: PreviewProvider { 51 | // static var previews: some View { 52 | // CustomBCOpacityView() 53 | // } 54 | //} 55 | -------------------------------------------------------------------------------- /Quotes/View/InfoCardView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InfoCardView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct InfoCardView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | var title: String 15 | var image: Image 16 | var detailTitle: String = "" 17 | 18 | @Environment(\.colorScheme) var colorScheme 19 | 20 | // MARK: BODY - 21 | 22 | var body: some View { 23 | HStack { 24 | HStack { 25 | Text(title) 26 | .font(.custom(Constants.fontBold, size: 20)) 27 | .padding(EdgeInsets(top: 10, leading: 15, bottom: 10, trailing: 15)) 28 | .lineLimit(1) 29 | Spacer() 30 | Text(detailTitle) 31 | .font(.custom(Constants.fontBold, size: 18)) 32 | .padding(EdgeInsets(top: 5, leading: 15, bottom: 10, trailing: 5)) 33 | .foregroundColor(colorScheme == .dark ? .white : .black.opacity(0.5)) 34 | image 35 | .resizable() 36 | .renderingMode(.template) 37 | .frame(width: 20, height: 20) 38 | .padding(.trailing , 15) 39 | .foregroundColor(colorScheme == .dark ? .white : .black) 40 | } 41 | } 42 | .background(Constants.appSecondary) 43 | .cornerRadius(5) 44 | .padding(EdgeInsets(top: 10, leading: 15, bottom: 10, trailing: 15)) 45 | } 46 | } 47 | 48 | struct InfoCardView_Previews: PreviewProvider { 49 | static var previews: some View { 50 | InfoCardView(title: "Wiki", image: Image(systemName: "link")) 51 | .previewLayout(.sizeThatFits) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Quotes/ViewModel/ImagesViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImagesViewModel.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 07/07/21. 6 | // 7 | 8 | import Foundation 9 | 10 | class ImageViewModel: ObservableObject { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Published var imagesModel: ImagesModel? 15 | @Published var isLoading: Bool = true 16 | 17 | // MARK: METHODS - 18 | 19 | func fetchSearchedImage(for page: Int , search query: String) { 20 | guard let url = URL(string: Constants.image_base_url + "search/?query=\(query)&page=\(page)") else { return } 21 | var request = URLRequest(url: url) 22 | request.httpMethod = "GET" 23 | request.setValue(Constants.pexelsAPIKey, forHTTPHeaderField: "Authorization") 24 | 25 | URLSession.shared.dataTask(with: request) { (data, response, error) in 26 | guard let data = data else { return } 27 | do { 28 | let images = try JSONDecoder().decode(ImagesModel.self, from: data) 29 | print(images) 30 | DispatchQueue.main.async { 31 | if page == 1 { 32 | self.imagesModel = images 33 | self.isLoading = false 34 | } else { 35 | self.isLoading = false 36 | self.imagesModel?.page = images.page 37 | self.imagesModel?.per_page = images.per_page 38 | self.imagesModel?.total_results = images.total_results 39 | self.imagesModel?.photos.append(contentsOf: images.photos) 40 | } 41 | } 42 | } catch { 43 | print("Error description: \(error)") 44 | } 45 | }.resume() 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /Quotes/View/ActionCardView/FontSizeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FontSizeView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 30/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FontSizeView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Environment(\.colorScheme) var colorScheme 15 | 16 | @Binding var fontSize: CGFloat 17 | @Binding var hideFontSizeView: Bool 18 | @Binding var hideActionView: Bool 19 | 20 | // MARK: BODY - 21 | 22 | var body: some View { 23 | ZStack(alignment: .topTrailing){ 24 | VStack { 25 | Slider(value: $fontSize, in: 12...30) 26 | Text("\(fontSize, specifier: "%.0f")") 27 | } //: VSTACK 28 | .padding(EdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15)) 29 | .frame(maxHeight: .infinity) 30 | 31 | Button { 32 | hideActionView = false 33 | hideFontSizeView = true 34 | } label: { 35 | Image("close") 36 | .resizable() 37 | .renderingMode(.template) 38 | .frame(width: 20, height: 20) 39 | .foregroundColor(colorScheme == .dark ? .white : .black) 40 | .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)) 41 | .background(Constants.appSecondary) 42 | .cornerRadius(20) 43 | } 44 | .padding(.trailing , 5) 45 | 46 | } //: ZSTACK 47 | .frame(maxHeight: .infinity) 48 | .background(colorScheme == .dark ? Color.black : Color.white) 49 | } 50 | } 51 | 52 | //struct FontSizeView_Previews: PreviewProvider { 53 | // static var previews: some View { 54 | // FontSizeView() 55 | // } 56 | //} 57 | -------------------------------------------------------------------------------- /Quotes/View/ExploreAuthorCardView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExploreAuthorCardView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ExploreAuthorCardView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | var authorName: String 15 | var authorDescription: String 16 | var quoteCount: Int 17 | 18 | @Environment(\.colorScheme) var colorScheme 19 | 20 | // MARK: BODY - 21 | 22 | var body: some View { 23 | HStack { 24 | VStack(alignment: .leading){ 25 | Text(authorName) 26 | .font(.custom(Constants.fontBlack, size: 23)) 27 | Text(authorDescription) 28 | .font(.custom(Constants.fontItalic, size: 15)) 29 | Text("Quote Count: \(quoteCount)") 30 | .font(.custom(Constants.fontRegular, size: 13)) 31 | .foregroundColor(colorScheme == .dark ? .white.opacity(0.5) : .black.opacity(0.5)) 32 | } 33 | .padding(EdgeInsets(top: 10, leading: 15, bottom: 10, trailing: 15)) 34 | Spacer() 35 | Image("forward") 36 | .resizable() 37 | .renderingMode(.template) 38 | .frame(width: 20, height: 20) 39 | .padding(.trailing, 15) 40 | .foregroundColor(colorScheme == .dark ? .white : .black) 41 | } 42 | .background(Constants.appSecondary) 43 | .cornerRadius(5) 44 | } 45 | } 46 | 47 | // MARK: PREIVIEW - 48 | 49 | struct ExploreAuthorCardView_Previews: PreviewProvider { 50 | static var previews: some View { 51 | ExploreAuthorCardView(authorName: "Albert Einstien", authorDescription: "This is a author description", quoteCount: 1244) 52 | .previewLayout(.sizeThatFits) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Quotes/Model/UrlImageModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UrlImageModel.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 08/07/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | class UrlImageModel: ObservableObject { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | var imageCache = ImageCache.getImageCache() 15 | var urlString: String? 16 | 17 | @Published var image: UIImage? 18 | 19 | // MARK: METHODS - 20 | 21 | func getImage( _ url: String ){ 22 | self.urlString = url 23 | loadImage() 24 | } 25 | 26 | func loadImage() { 27 | if loadImageFromCache() { 28 | return 29 | } 30 | loadImageFromUrl() 31 | } 32 | 33 | func loadImageFromCache() -> Bool { 34 | guard let urlString = urlString else { 35 | return false 36 | } 37 | 38 | guard let cacheImage = imageCache.get(forKey: urlString) else { 39 | return false 40 | } 41 | 42 | image = cacheImage 43 | return true 44 | } 45 | 46 | func loadImageFromUrl() { 47 | guard let urlString = urlString else { 48 | return 49 | } 50 | 51 | let url = URL(string: urlString)! 52 | let task = URLSession.shared.dataTask(with: url, completionHandler: getImageFromResponse(data:response:error:)) 53 | task.resume() 54 | } 55 | 56 | 57 | func getImageFromResponse(data: Data?, response: URLResponse?, error: Error?) { 58 | guard error == nil else { 59 | print("Error: \(error!)") 60 | return 61 | } 62 | guard let data = data else { 63 | print("No data found") 64 | return 65 | } 66 | 67 | DispatchQueue.main.async { 68 | guard let loadedImage = UIImage(data: data) else { 69 | return 70 | } 71 | self.imageCache.set(forKey: self.urlString!, image: loadedImage) 72 | self.image = loadedImage 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Quotes/View/FavoriteActionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FavoriteActionView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FavoriteActionView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Environment(\.colorScheme) var colorScheme 15 | 16 | @Binding var favouriteStateActive: Bool 17 | @Binding var favouriteViewOpacity: Double 18 | @Binding var favouriteViewScale: CGFloat 19 | var isQuoteExist: Bool 20 | 21 | // MARK: BODY - 22 | 23 | var body: some View { 24 | HStack { 25 | Spacer() 26 | Image(isQuoteExist ? "delete" : "favourite-selected") 27 | .resizable() 28 | .renderingMode(.template) 29 | .frame(width: 25, height: 25) 30 | .foregroundColor( 31 | isQuoteExist ? 32 | colorScheme == .dark ? .white : (favouriteStateActive ? .white : .black) 33 | : 34 | colorScheme == .dark ? .white : (favouriteStateActive ? .white : .black) 35 | ) 36 | .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)) 37 | .opacity(favouriteViewOpacity) 38 | .background( 39 | isQuoteExist ? 40 | colorScheme == .dark ? (favouriteStateActive ? .red : Constants.appSecondary) : (favouriteStateActive ? .red : Constants.appSecondary) 41 | : 42 | colorScheme == .dark ? (favouriteStateActive ? Constants.appGreen : Constants.appSecondary) : (favouriteStateActive ? Constants.appGreen : Constants.appSecondary) 43 | ) 44 | .cornerRadius(22.5) 45 | .scaleEffect(favouriteViewScale) 46 | } 47 | .padding(.trailing , 30) 48 | } 49 | } 50 | 51 | //struct FavoriteActionView_Previews: PreviewProvider { 52 | // static var previews: some View { 53 | // FavoriteActionView() 54 | // } 55 | //} 56 | -------------------------------------------------------------------------------- /Quotes/App/ExploreView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExploreView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ExploreView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @ObservedObject var tagsVM = TagsViewModel() 15 | private var gridItemLayout = [GridItem(.flexible()) , GridItem(.flexible())] 16 | 17 | init(){ 18 | self.tagsVM.fetchTags() 19 | } 20 | 21 | // MARK: BODY - 22 | 23 | var body: some View { 24 | ScrollView(.vertical, showsIndicators: false) { 25 | VStack { 26 | NavigationLink(destination: ExploreAuthorListView()) { 27 | InfoCardView(title: "Explore Authors", image: Image("forward")) 28 | } 29 | .buttonStyle(PlainButtonStyle()) 30 | if tagsVM.isLoading { 31 | ProgressView() 32 | .padding(.top , 100) 33 | .progressViewStyle(CircularProgressViewStyle()) 34 | } else { 35 | CustomTitleView(titleText: "Explore quotes with tags") 36 | LazyVGrid(columns: gridItemLayout, spacing: 10) { 37 | if let tags = tagsVM.tagsModel { 38 | ForEach(tags) { tag in 39 | NavigationLink(destination: TagFilteredQuoteListView(tagName: tag.name, quoteCount: tag.quoteCount)) { 40 | TagCardView(tagName: tag.name, quoteCount: tag.quoteCount) 41 | } 42 | .buttonStyle(PlainButtonStyle()) 43 | } 44 | } 45 | } //: LAZYVGRID 46 | .padding(EdgeInsets(top: 5, leading: 15, bottom: 15, trailing: 15)) 47 | } 48 | } //: VSTACK 49 | } //: SCROLLVIEW 50 | } 51 | } 52 | 53 | struct ExploreView_Previews: PreviewProvider { 54 | static var previews: some View { 55 | ExploreView() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Quotes/App/QuoteDetailView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QuoteDetailView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct QuoteDetailView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Environment(\.presentationMode) var presentationMode: Binding 15 | @Environment(\.colorScheme) var colorScheme 16 | 17 | var data: Quote 18 | var btnBack : some View { 19 | Button(action: { 20 | self.presentationMode.wrappedValue.dismiss() 21 | }) { 22 | HStack { 23 | Image("back") 24 | .resizable() 25 | .renderingMode(.template) 26 | .aspectRatio(contentMode: .fit) 27 | .frame(width: 22, height: 22) 28 | .foregroundColor(colorScheme == .dark ? Color.white : Color.black) 29 | } 30 | } 31 | } 32 | 33 | // MARK: BODY - 34 | 35 | var body: some View { 36 | ScrollView(.vertical, showsIndicators: false) { 37 | QuoteDetailCardView(quoteData: data) 38 | .offset(y: 12) 39 | CustomTitleView(titleText: "Author") 40 | .padding(.top , 20) 41 | NavigationLink(destination: AuthorDetailView(authorSlug: data.authorSlug)) { 42 | AuthorNameView(author: data.author) 43 | } 44 | .buttonStyle(PlainButtonStyle()) 45 | CustomTitleView(titleText: "Tags") 46 | .padding(.top , 8) 47 | QuoteTagsView(tags: data.tags) 48 | } 49 | .navigationBarBackButtonHidden(true) 50 | .navigationBarItems(leading: btnBack) 51 | .navigationBarTitle(Text("Quote Details")) 52 | } 53 | } 54 | 55 | // MARK: PREVIEW - 56 | 57 | struct QuoteDetailView_Previews: PreviewProvider { 58 | static var previews: some View { 59 | QuoteDetailView(data: Quote(tags: ["wisdom"], _id: "", content: "Wisdom is a kind of knowledge. It is knowledge of the nature, career, and consequences of human values.", author: "Helmut Sigmund", authorSlug: "")) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Quotes/ViewModel/AuthorsViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthorsViewModel.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import Foundation 9 | 10 | class AuthorsViewModel: ObservableObject { 11 | 12 | let apiURL = Constants.get_authors_url 13 | let authorWithSlugname = Constants.get_author_with_slugname 14 | 15 | @Published var authorsModel: AuthorsModel? 16 | @Published var isLoading: Bool = true 17 | 18 | func fetchAuthors(for page: Int) { 19 | guard let url = URL(string: apiURL + "?page=\(page)") else { return } 20 | URLSession.shared.dataTask(with: url) { (data , response , error) in 21 | guard let data = data else { return } 22 | do { 23 | let authors = try JSONDecoder().decode(AuthorsModel.self, from: data) 24 | DispatchQueue.main.async { 25 | self.isLoading = false 26 | if page == 1 { 27 | self.authorsModel = authors 28 | } else { 29 | self.authorsModel?.page = authors.page 30 | self.authorsModel?.totalCount = authors.totalCount 31 | self.authorsModel?.results.append(contentsOf: authors.results) 32 | } 33 | } 34 | } catch { 35 | print("Error description: \(error)") 36 | } 37 | }.resume() 38 | } 39 | 40 | func fetchAuthorWithSlug( with slugname: String) { 41 | guard let url = URL(string: authorWithSlugname + slugname) else { return } 42 | URLSession.shared.dataTask(with: url) { (data , response , error) in 43 | guard let data = data else { return } 44 | do { 45 | let author = try JSONDecoder().decode(AuthorsModel.self, from: data) 46 | DispatchQueue.main.async { 47 | self.isLoading = false 48 | self.authorsModel = author 49 | } 50 | } catch { 51 | print("Error description: \(error)") 52 | } 53 | }.resume() 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Quotes/View/QuoteCardView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QuoteCardView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct QuoteCardView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Environment(\.colorScheme) var colorScheme 15 | 16 | var isFavouriteQuote: Bool = false 17 | var quoteText: String = "" 18 | var author: String = "" 19 | var authorVisible: Bool = true 20 | var quoteID: String = "" 21 | 22 | // MARK: BODY - 23 | 24 | var body: some View { 25 | ZStack(alignment: .topTrailing){ 26 | VStack { 27 | HStack { 28 | let quoteContent = 29 | Text("\" ") 30 | .font(.custom(Constants.fontBlackItalic, size: 25)) 31 | + Text(quoteText) 32 | .font(.custom(Constants.fontBold, size: 20)) 33 | 34 | quoteContent 35 | .padding(EdgeInsets(top: 15, leading: 15, bottom: authorVisible ? 5 : 15, trailing: 15)) 36 | 37 | Spacer() 38 | } //: HSTACK 39 | if authorVisible { 40 | HStack { 41 | Spacer() 42 | Text("- \(author)") 43 | .font(.custom(Constants.fontItalic, size: 17)) 44 | .padding(EdgeInsets(top: 5, leading: 15, bottom: 15, trailing: 15)) 45 | } //: HSTACK 46 | } 47 | } //: VSTACK 48 | Image("save-flag") 49 | .resizable() 50 | .frame(width: 40, height: 40) 51 | .opacity(isFavouriteQuote ? 1 : 0) 52 | } //: ZSTACK 53 | .background(Constants.appSecondary) 54 | .cornerRadius(5) 55 | .padding(EdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15)) 56 | } 57 | } 58 | 59 | // MARK: PREVIEW - 60 | 61 | struct QuoteCardView_Previews: PreviewProvider { 62 | static var previews: some View { 63 | Text("") 64 | // QuoteCardView(quoteText: "This is test" , author: "Albert Einstien") 65 | // .previewLayout(.sizeThatFits) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | ![platform](https://img.shields.io/badge/platform-iOS-orange?style=flat-square) 4 | 5 | ## Quotes 6 | Quotes App is quotes browsing app which is built with Quotable Free API completely in SwiftUI. You can watch the whole journey of building this app from scratch on my instagram handle here , I've tried to break a whole process of building a Full app in some parts there! I hope you enjoy that! 7 | 8 | ## My Motivation 9 | My motivation behind this app is to able learn and able to teach you guys, how to build a full fledge app using SwiftUI especially how to handle Networking using ViewModels and important property wrappers like ObservedObject, Published , StateObject etc. ( Learn more about these here ) This project is for Intermediate iOS developers ( You need to know the basics! ). And I hope this app helps you to build something great! 10 | 11 | ## Progress Glance 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ## Stuck Somewhere or having trouble ? 25 | Drop me mail @ dheerajsh123456@gmail.com 26 | 27 | ## Do you like my contribution to community? Go Spread a word! 28 | Just give it a star ⭐️ and spread the word! 29 | 30 | ## Credits 31 | **©** **Dheeraj kumar sharma** - *2024* 32 | -------------------------------------------------------------------------------- /Quotes/App/CustomTabView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomTabView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CustomTabView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @State private var selection = 0 15 | 16 | @Environment(\.colorScheme) var colorScheme 17 | 18 | // MARK: BODY - 19 | 20 | var body: some View { 21 | TabView(selection: $selection) { 22 | NavigationView { 23 | HomeView() 24 | .navigationBarTitle(Text("Quotes")) 25 | .navigationBarTitleDisplayMode(.inline) 26 | } 27 | .tabItem { 28 | if selection == 0 { 29 | Image("home-selected") 30 | .renderingMode(.template) 31 | } else { 32 | Image("home") 33 | .renderingMode(.template) 34 | } 35 | Text("") 36 | } 37 | .tag(0) 38 | 39 | NavigationView { 40 | ExploreView() 41 | .navigationBarTitle(Text("Explore")) 42 | .navigationBarTitleDisplayMode(.inline) 43 | } 44 | .tabItem { 45 | if selection == 1 { 46 | Image("explore-selected") 47 | .renderingMode(.template) 48 | } else { 49 | Image("explore") 50 | .renderingMode(.template) 51 | } 52 | Text("") 53 | } 54 | .tag(1) 55 | 56 | NavigationView { 57 | FavouriteView() 58 | .navigationBarTitle(Text("Favorite")) 59 | .navigationBarTitleDisplayMode(.inline) 60 | } 61 | .tabItem { 62 | if selection == 2 { 63 | Image("favourite-selected") 64 | .renderingMode(.template) 65 | } else { 66 | Image("favourite") 67 | .renderingMode(.template) 68 | } 69 | Text("") 70 | } 71 | .tag(2) 72 | } 73 | .accentColor(colorScheme == .dark ? .white : .black) 74 | } 75 | } 76 | 77 | // MARK: PREVIEW - 78 | 79 | struct TabView_Previews: PreviewProvider { 80 | static var previews: some View { 81 | CustomTabView() 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Quotes/View/ActionCardView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActionCardView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 30/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ActionCardView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | var data: ActionViewData 15 | 16 | // MARK: BODY - 17 | 18 | var body: some View { 19 | VStack(alignment: .center, spacing: 7){ 20 | ZStack { 21 | 22 | if data.type == "image" { 23 | // IMAGE OPTION 24 | Image(uiImage: (data.image ?? UIImage(named: "logo"))!) 25 | .resizable() 26 | .renderingMode(.original) 27 | .scaledToFill() 28 | .frame(width: 70, height: 70) 29 | .cornerRadius(35) 30 | .overlay( 31 | RoundedRectangle(cornerRadius: 35) 32 | .stroke(Color.white , lineWidth: 2) 33 | ) 34 | .shadow(color: .black.opacity(0.1), radius: 10, x: 0, y: 5) 35 | } 36 | 37 | if data.type == "color" { 38 | // COLOR OPTION 39 | data.color 40 | .frame(width: 70, height: 70) 41 | .cornerRadius(35) 42 | .overlay( 43 | RoundedRectangle(cornerRadius: 35) 44 | .stroke(Color.white , lineWidth: 2) 45 | ) 46 | .shadow(color: .black.opacity(0.1), radius: 10, x: 0, y: 5) 47 | } 48 | 49 | if data.type == "text" { 50 | // TEXT OPTION 51 | Text(data.text) 52 | .font(.custom(Constants.fontItalic, size: 23)) 53 | .frame(width: 70, height: 70) 54 | .background(Constants.appSecondary) 55 | .multilineTextAlignment(.center) 56 | .cornerRadius(35) 57 | .overlay( 58 | RoundedRectangle(cornerRadius: 35) 59 | .stroke(Color.white , lineWidth: 2) 60 | ) 61 | } 62 | 63 | } 64 | Text(data.title) 65 | .font(.custom(Constants.fontPoppinsRegular, size: 14)) 66 | .multilineTextAlignment(.center) 67 | } 68 | } 69 | } 70 | 71 | struct ActionCardView_Previews: PreviewProvider { 72 | static var previews: some View { 73 | ActionCardView(data: ActionViewData(type: "color", image: nil, text: "", color: nil , title: "", toggleView: "")) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Quotes/ViewModel/RealmViewModel/QuotesRealmViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QuotesRealmViewModel.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import Foundation 9 | import RealmSwift 10 | import Combine 11 | 12 | class QuoteRealmViewModel: ObservableObject { 13 | 14 | @Published var quotes: Results? = nil 15 | 16 | init(){ 17 | let realm = try? Realm() 18 | if let quotes = realm?.objects(QuotesDB.self) { 19 | self.quotes = quotes 20 | } 21 | } 22 | 23 | /// It add quotes to the realmDB 24 | /// - Parameter data: it takes quote model for adding data to realmDB 25 | func addQuotes(_ data: Quote){ 26 | /// Checking for existence of a quote: If exist delete otherwise add! 27 | if checkForQuoteExistence(with: data._id) { 28 | deleteQuote(with: data._id) 29 | } else { 30 | if let realm = quotes?.realm { 31 | try? realm.write { 32 | let quote = QuotesDB() 33 | quote.quoteId = data._id 34 | quote.content = data.content 35 | quote.author = data.author 36 | quote.authorSlug = data.authorSlug 37 | quote.tags = getStringFromArray(data.tags) 38 | realm.add(quote) 39 | } 40 | } 41 | } 42 | } 43 | 44 | /// It Checks for quote Existence in the Realm DB 45 | /// - Parameter quoteId: Quote ID 46 | /// - Returns: it returns true for quote existence otherwise false 47 | func checkForQuoteExistence(with quoteId: String) -> Bool { 48 | let realm = try? Realm() 49 | if let quote = realm?.objects(QuotesDB.self).filter("quoteId == %@" , quoteId) { 50 | if quote.count > 0 { 51 | return true 52 | } else { 53 | return false 54 | } 55 | } 56 | return false 57 | } 58 | 59 | /// It deletes the quote with quoteId 60 | /// - Parameter quoteId: quote unique ID 61 | func deleteQuote(with quoteId: String) { 62 | let realm = try? Realm() 63 | if let quote = realm?.objects(QuotesDB.self).filter("quoteId == %@" , quoteId).first { 64 | try? realm?.write({ 65 | realm?.delete(quote) 66 | }) 67 | } 68 | } 69 | 70 | /// It converts Array of string into string using delemitter 71 | /// - Parameter tags: it takes array of strings 72 | /// - Returns: returns string 73 | func getStringFromArray(_ tags: [String]) -> String{ 74 | var result = "" 75 | for tag in tags { 76 | result.append(contentsOf: "\(tag),") 77 | } 78 | /// removing last extra comma seperator (",") ! 79 | result.removeLast() 80 | return result 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /Quotes/App/AuthorDetailView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthorDetailView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct AuthorDetailView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @ObservedObject var authorsVM = AuthorsViewModel() 15 | 16 | @Environment(\.presentationMode) var presentationMode: Binding 17 | @Environment(\.colorScheme) var colorScheme 18 | 19 | var authorSlug: String 20 | var btnBack : some View { 21 | Button(action: { 22 | self.presentationMode.wrappedValue.dismiss() 23 | }) { 24 | HStack { 25 | Image("back") 26 | .resizable() 27 | .renderingMode(.template) 28 | .aspectRatio(contentMode: .fit) 29 | .frame(width: 22, height: 22) 30 | .foregroundColor(colorScheme == .dark ? Color.white : Color.black) 31 | } 32 | } 33 | } 34 | 35 | init(authorSlug: String){ 36 | self.authorSlug = authorSlug 37 | authorsVM.fetchAuthorWithSlug(with: authorSlug) 38 | } 39 | 40 | // MARK: BODY - 41 | 42 | var body: some View { 43 | ScrollView(.vertical, showsIndicators: false) { 44 | VStack { 45 | if authorsVM.isLoading { 46 | ProgressView() 47 | .padding(.top , 100) 48 | .progressViewStyle(CircularProgressViewStyle()) 49 | } else { 50 | if let authorData = authorsVM.authorsModel?.results { 51 | AuthorInfoView(authorData: authorData[0]) 52 | Link(destination: URL(string: authorData[0].link)!) { 53 | InfoCardView(title: "\(authorData[0].name)'s Wiki", image: Image(systemName: "link")) 54 | } 55 | .foregroundColor(colorScheme == .dark ? .white : .black) 56 | NavigationLink(destination: AuthorQuoteListView(authorSlug: authorData[0].slug , authorName: authorData[0].name , totalQuotes: authorData[0].quoteCount, fromExplore: false)) { 57 | InfoCardView(title: "Author Quotes", image: Image("forward") , detailTitle: "\(authorData[0].quoteCount)") 58 | } 59 | .buttonStyle(PlainButtonStyle()) 60 | } 61 | } 62 | } 63 | } 64 | .navigationBarBackButtonHidden(true) 65 | .navigationBarItems(leading: btnBack) 66 | .navigationBarTitle(Text("Author Details")) 67 | } 68 | } 69 | 70 | // MARK: PREVIEW - 71 | 72 | struct AuthorDetailView_Previews: PreviewProvider { 73 | static var previews: some View { 74 | AuthorDetailView(authorSlug: "albert-einstein") 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Quotes/Utils/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct Constants { 12 | 13 | /// Custom Colors 14 | static let appSecondary = Color.gray.opacity(0.1) 15 | static let appPrimary = Color.black.opacity(0.9) 16 | static let appGreen = Color(red: 0, green: 217/255, blue: 25/255) 17 | static let appBlue = Color(red: 49/255, green: 172/255, blue: 247/255) 18 | 19 | /// Color collection 20 | static let appColor1 = Color(red: 222/255, green: 246/255, blue: 202/255) 21 | static let appColor2 = Color(red: 176/255, green: 254/255, blue: 118/255) 22 | static let appColor3 = Color(red: 245/255, green: 187/255, blue: 0) 23 | static let appColor4 = Color(red: 228/255, green: 217/255, blue: 1) 24 | static let appColor5 = Color(red: 39/255, green: 52/255, blue: 105/255) 25 | static let appColor6 = Color(red: 63/255, green: 52/255, blue: 63/255) 26 | static let appColor7 = Color(red: 244/255, green: 213/255, blue: 141/255) 27 | static let appColor8 = Color(red: 249/255, green: 231/255, blue: 231/255) 28 | static let appColor9 = Color(red: 184/255, green: 184/255, blue: 1) 29 | 30 | /// API urls 31 | static let base_url = "https://api.quotable.io" 32 | static let get_quote_url = "\(base_url)/quotes?page=" 33 | static let get_tags_url = "\(base_url)/tags" 34 | static let get_authors_url = "\(base_url)/authors" 35 | static let get_author_with_slugname = "\(base_url)/authors?slug=" 36 | static let get_randon_quote_url = "\(base_url)/random" 37 | static let get_quotes = "\(base_url)/quotes" 38 | static let get_quotes_for_author = "\(base_url)/quotes?author=" 39 | static let image_base_url = "https://api.pexels.com/v1/" 40 | 41 | /// API Key 42 | static let pexelsAPIKey = "563492ad6f91700001000001a480f8340ffa417e80b3c4227e88f45e" 43 | 44 | 45 | /// Custom Font names 46 | static let fontRegular = "PlayfairDisplay-Regular" 47 | static let fontItalic = "PlayfairDisplay-Italic" 48 | static let fontBold = "PlayfairDisplay-Bold" 49 | static let fontBoldItalic = "PlayfairDisplay-BoldItalic" 50 | static let fontBlack = "PlayfairDisplay-Black" 51 | static let fontBlackItalic = "PlayfairDisplay-BlackItalic" 52 | static let fontGothamLight = "Gotham-Light" 53 | static let fontGaramondRoman = "Garamond-Roman" 54 | static let fontVollkornItalic = "Vollkorn-Italic" 55 | static let fontFreightSansBook = "Freight-SansBook" 56 | static let fontMontserratRegular = "Montserrat-Regular" 57 | static let fontDancingScriptRegular = "DancingScript-Regular" 58 | static let fontOpenSansRegular = "OpenSans-Regular" 59 | static let fontTimesNewRoman = "TimesNewRomanPSMT" 60 | static let fontPoppinsRegular = "Poppins-Regular" 61 | static let fontNunitoRegular = "Nunito-Regular" 62 | } 63 | -------------------------------------------------------------------------------- /Quotes/View/QuoteTagsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QuoteTagsView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct QuoteTagsView: View { 11 | 12 | @Environment(\.colorScheme) var colorScheme 13 | 14 | var tags: [String] 15 | @State private var totalHeight = CGFloat.zero 16 | 17 | var body: some View { 18 | VStack { 19 | GeometryReader { geometry in 20 | self.generateContent(in: geometry) 21 | } 22 | } 23 | .frame(height: totalHeight) 24 | .padding(EdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15)) 25 | } 26 | 27 | private func generateContent(in g: GeometryProxy) -> some View { 28 | var width = CGFloat.zero 29 | var height = CGFloat.zero 30 | 31 | return ZStack(alignment: .topLeading) { 32 | ForEach(tags, id: \.self) { tag in 33 | TagItem(for: tag) 34 | .padding([.horizontal, .vertical], 4) 35 | .alignmentGuide(.leading, computeValue: { d in 36 | if (abs(width - d.width) > g.size.width) 37 | { 38 | width = 0 39 | height -= d.height 40 | } 41 | let result = width 42 | if tag == self.tags.last! { 43 | width = 0 //last item 44 | } else { 45 | width -= d.width 46 | } 47 | return result 48 | }) 49 | .alignmentGuide(.top, computeValue: {d in 50 | let result = height 51 | if tag == self.tags.last! { 52 | height = 0 // last item 53 | } 54 | return result 55 | }) 56 | } 57 | }.background(viewHeightReader($totalHeight)) 58 | } 59 | 60 | private func TagItem(for text: String) -> some View { 61 | Text(text) 62 | .foregroundColor(colorScheme == .dark ? Color.black : Color.white) 63 | .font(.custom(Constants.fontRegular, size: 15)) 64 | .padding(EdgeInsets(top: 5, leading: 10, bottom: 5, trailing: 10)) 65 | .background(colorScheme == .dark ? Color.white : Constants.appPrimary) 66 | .cornerRadius(5) 67 | } 68 | 69 | private func viewHeightReader(_ binding: Binding) -> some View { 70 | return GeometryReader { geometry -> Color in 71 | let rect = geometry.frame(in: .local) 72 | DispatchQueue.main.async { 73 | binding.wrappedValue = rect.size.height 74 | } 75 | return .clear 76 | } 77 | } 78 | } 79 | 80 | struct QuoteTagsView_Previews: PreviewProvider { 81 | static var previews: some View { 82 | QuoteTagsView(tags: ["Technology" , "generic" , "information" , "Techdnology" , "gesneric" , "inforsmation"]) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Quotes/ViewModel/QuotesViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QuotesViewModel.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import Foundation 9 | 10 | class QuotesViewModel: ObservableObject { 11 | 12 | @Published var quoteModel: QuotesModel? 13 | @Published var isLoading: Bool = true 14 | 15 | func fetchQuote(for page: Int) { 16 | guard let url = URL(string: Constants.get_quote_url+"\(page)") else { return } 17 | URLSession.shared.dataTask(with: url) { (data , response , error) in 18 | guard let data = data else { return } 19 | do { 20 | let quote = try JSONDecoder().decode(QuotesModel.self, from: data) 21 | DispatchQueue.main.async { 22 | if page == 1 { 23 | self.quoteModel = quote 24 | self.isLoading = false 25 | } else { 26 | self.isLoading = false 27 | self.quoteModel?.page = quote.page 28 | self.quoteModel?.results.append(contentsOf: quote.results) 29 | } 30 | } 31 | } catch { 32 | print("Error description: \(error)") 33 | } 34 | }.resume() 35 | } 36 | 37 | func fetchQuoteWithAuthorSlug(with slugname: String , for page: Int){ 38 | guard let url = URL(string: Constants.get_quotes_for_author + slugname + "&page=\(page)") else { return } 39 | URLSession.shared.dataTask(with: url) { (data , response , error) in 40 | guard let data = data else { return } 41 | do { 42 | let quote = try JSONDecoder().decode(QuotesModel.self, from: data) 43 | DispatchQueue.main.async { 44 | self.isLoading = false 45 | if page == 1 { 46 | self.quoteModel = quote 47 | } else { 48 | self.quoteModel?.page = quote.page 49 | self.quoteModel?.results.append(contentsOf: quote.results) 50 | } 51 | } 52 | } catch { 53 | print("Error description: \(error)") 54 | } 55 | }.resume() 56 | } 57 | 58 | func fetchQuoteWithTagName(with tagName: String , for page: Int) { 59 | guard let url = URL(string: Constants.get_quotes + "?tags=\(tagName)&page=" + "\(page)") else { return } 60 | URLSession.shared.dataTask(with: url) { (data , response , error) in 61 | guard let data = data else { return } 62 | do { 63 | let quote = try JSONDecoder().decode(QuotesModel.self, from: data) 64 | DispatchQueue.main.async { 65 | if page == 1 { 66 | self.quoteModel = quote 67 | self.isLoading = false 68 | } else { 69 | self.isLoading = false 70 | self.quoteModel?.page = quote.page 71 | self.quoteModel?.results.append(contentsOf: quote.results) 72 | } 73 | } 74 | } catch { 75 | print("Error description: \(error)") 76 | } 77 | }.resume() 78 | } 79 | 80 | } 81 | 82 | -------------------------------------------------------------------------------- /Quotes/App/ExploreAuthorListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExploreAuthorListView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ExploreAuthorListView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Environment(\.presentationMode) var presentationMode: Binding 15 | @Environment(\.colorScheme) var colorScheme 16 | 17 | @ObservedObject var authorVM = AuthorsViewModel() 18 | private var gridItemLayout = [GridItem(.flexible())] 19 | 20 | var btnBack : some View { 21 | Button(action: { 22 | self.presentationMode.wrappedValue.dismiss() 23 | }) { 24 | HStack { 25 | Image("back") 26 | .resizable() 27 | .renderingMode(.template) 28 | .aspectRatio(contentMode: .fit) 29 | .frame(width: 22, height: 22) 30 | .foregroundColor(colorScheme == .dark ? Color.white : Color.black) 31 | } 32 | } 33 | } 34 | 35 | init() { 36 | self.authorVM.fetchAuthors(for: 1) 37 | } 38 | 39 | // MARK: BODY - 40 | 41 | var body: some View { 42 | ScrollView(.vertical, showsIndicators: false) { 43 | LazyVGrid(columns: gridItemLayout, spacing: 12) { 44 | if authorVM.isLoading { 45 | ProgressView() 46 | .padding(.top , 100) 47 | .progressViewStyle(CircularProgressViewStyle()) 48 | } else { 49 | if let authors = authorVM.authorsModel?.results { 50 | ForEach(authors) { author in 51 | NavigationLink(destination: AuthorQuoteListView(authorSlug: author.slug , authorName: author.name , totalQuotes: author.quoteCount , fromExplore: true)) { 52 | ExploreAuthorCardView(authorName: author.name, authorDescription: author.description, quoteCount: author.quoteCount) 53 | } 54 | .buttonStyle(PlainButtonStyle()) 55 | } 56 | if let authorTotalCount = authorVM.authorsModel?.totalCount { 57 | if authorTotalCount > authors.count { 58 | ProgressView() 59 | .padding() 60 | .progressViewStyle(CircularProgressViewStyle()) 61 | .onAppear(perform: { 62 | if let page = authorVM.authorsModel?.page { 63 | self.authorVM.fetchAuthors(for: page + 1) 64 | } 65 | }) 66 | } 67 | } 68 | } 69 | } 70 | } //: LAZYVGRID 71 | .padding(EdgeInsets(top: 10, leading: 15, bottom: 10, trailing: 15)) 72 | } //: SCROLLVIEW 73 | .navigationBarBackButtonHidden(true) 74 | .navigationBarItems(leading: btnBack) 75 | .navigationBarTitle(Text("Explore Authors")) 76 | } 77 | } 78 | 79 | // MARK: - PREVIEW 80 | 81 | struct ExploreAuthorListView_Previews: PreviewProvider { 82 | static var previews: some View { 83 | ExploreAuthorListView() 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Quotes/View/ActionCardView/FontColorView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FontColorView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 30/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FontColorView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Environment(\.colorScheme) var colorScheme 15 | 16 | @Binding var fontColor: Color 17 | @Binding var hideFontColorView: Bool 18 | @Binding var hideActionView: Bool 19 | 20 | let colors: [Color] = [ 21 | Color.white, 22 | Color.black, 23 | Color.yellow, 24 | Color.red, 25 | Constants.appColor1, 26 | Constants.appColor2, 27 | Constants.appColor3, 28 | Constants.appColor4, 29 | Constants.appColor5, 30 | Constants.appColor6, 31 | Constants.appColor7, 32 | Constants.appColor8, 33 | Constants.appColor9 34 | ] 35 | 36 | var body: some View { 37 | ZStack(alignment: .topTrailing){ 38 | ScrollView(.horizontal, showsIndicators: false) { 39 | HStack(alignment: .center, spacing: 10) { 40 | ForEach(colors , id: \.self){ color in 41 | Button { 42 | fontColor = color 43 | } label: { 44 | VStack { 45 | color 46 | .frame(width: 70, height: 70) 47 | .cornerRadius(35) 48 | .overlay( 49 | RoundedRectangle(cornerRadius: 35) 50 | .stroke(.white , lineWidth: 2) 51 | ) 52 | .shadow(color: .black.opacity(0.1), radius: 10, x: 0, y: 5) 53 | Rectangle() 54 | .fill(colorScheme == .dark ? (fontColor == color ? .white : .black ) : (fontColor == color ? .black : .white )) 55 | .frame(width: fontColor == color ? 10 : 5 , height: 5) 56 | .cornerRadius(2.5) 57 | .animation(.easeInOut) 58 | }//: VSTACK 59 | } 60 | } 61 | }//: HSTACK 62 | .padding(EdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15)) 63 | .frame(height: 100) 64 | } //: SCROLLVIEW 65 | .frame(maxHeight: .infinity) 66 | 67 | Button { 68 | hideActionView = false 69 | hideFontColorView = true 70 | } label: { 71 | Image("close") 72 | .resizable() 73 | .renderingMode(.template) 74 | .frame(width: 20, height: 20) 75 | .foregroundColor(colorScheme == .dark ? .white : .black) 76 | .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)) 77 | .background(Constants.appSecondary) 78 | .cornerRadius(20) 79 | } 80 | .padding(.trailing , 5) 81 | } 82 | .frame(maxHeight: .infinity) 83 | .background(colorScheme == .dark ? Color.black : Color.white) 84 | } 85 | } 86 | 87 | //struct FontColorView_Previews: PreviewProvider { 88 | // static var previews: some View { 89 | // FontColorView() 90 | // } 91 | //} 92 | -------------------------------------------------------------------------------- /Quotes/View/ActionCardView/FontView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FontView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 30/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FontView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Environment(\.colorScheme) var colorScheme 15 | 16 | let fontArr = [Constants.fontRegular , Constants.fontGaramondRoman , Constants.fontFreightSansBook , Constants.fontVollkornItalic , Constants.fontGothamLight , Constants.fontMontserratRegular , Constants.fontDancingScriptRegular, Constants.fontOpenSansRegular, Constants.fontTimesNewRoman , Constants.fontPoppinsRegular , Constants.fontNunitoRegular] 17 | 18 | @Binding var fontName: String 19 | @Binding var hideFontView: Bool 20 | @Binding var hideActionView: Bool 21 | 22 | // MARK: BODY - 23 | 24 | var body: some View { 25 | ZStack(alignment: .topTrailing){ 26 | ScrollView(.horizontal, showsIndicators: false) { 27 | HStack(alignment: .center, spacing: 10) { 28 | ForEach(fontArr, id: \.self) { font in 29 | Button { 30 | fontName = font 31 | } label: { 32 | VStack { 33 | Text("Ag") 34 | .font(.custom(font, size: 23)) 35 | .frame(width: 70, height: 70) 36 | .background(Constants.appSecondary) 37 | .multilineTextAlignment(.center) 38 | .cornerRadius(35) 39 | .overlay( 40 | RoundedRectangle(cornerRadius: 35) 41 | .stroke(Color.white , lineWidth: 2) 42 | ) 43 | Rectangle() 44 | .fill(colorScheme == .dark ? (fontName == font ? .white : .black ) : (fontName == font ? .black : .white )) 45 | .frame(width: fontName == font ? 10 : 5 , height: 5) 46 | .cornerRadius(2.5) 47 | .animation(.easeInOut) 48 | }//: VSTACK 49 | } 50 | } 51 | }//: HSTACK 52 | .padding(EdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15)) 53 | .frame(height: 100) 54 | }//: SCROLLVIEW 55 | .frame(maxHeight: .infinity) 56 | 57 | Button { 58 | hideActionView = false 59 | hideFontView = true 60 | } label: { 61 | Image("close") 62 | .resizable() 63 | .renderingMode(.template) 64 | .frame(width: 20, height: 20) 65 | .foregroundColor(colorScheme == .dark ? .white : .black) 66 | .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)) 67 | .background(Constants.appSecondary) 68 | .cornerRadius(20) 69 | } 70 | .padding(.trailing , 5) 71 | } 72 | .frame(maxHeight: .infinity) 73 | .background(colorScheme == .dark ? Color.black : Color.white) 74 | } 75 | } 76 | 77 | //struct FontView_Previews: PreviewProvider { 78 | // static var previews: some View { 79 | // FontView() 80 | // } 81 | //} 82 | -------------------------------------------------------------------------------- /Quotes/View/ActionCardView/PostBackgroundColorView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PostBackgroundColorView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 30/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct PostBackgroundColorView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Environment(\.colorScheme) var colorScheme 15 | 16 | @Binding var backgroundColor: Color 17 | @Binding var hidePostBGColorView: Bool 18 | @Binding var hideActionView: Bool 19 | 20 | let colors: [Color] = [ 21 | Color.white, 22 | Color.black, 23 | Color.yellow, 24 | Color.red, 25 | Constants.appColor1, 26 | Constants.appColor2, 27 | Constants.appColor3, 28 | Constants.appColor4, 29 | Constants.appColor5, 30 | Constants.appColor6, 31 | Constants.appColor7, 32 | Constants.appColor8, 33 | Constants.appColor9 34 | ] 35 | 36 | // MARK: BODY - 37 | 38 | var body: some View { 39 | ZStack(alignment: .topTrailing){ 40 | ScrollView(.horizontal, showsIndicators: false) { 41 | HStack(alignment: .center, spacing: 10) { 42 | ForEach(colors , id: \.self){ color in 43 | Button { 44 | backgroundColor = color 45 | } label: { 46 | VStack { 47 | color 48 | .frame(width: 70, height: 70) 49 | .cornerRadius(35) 50 | .overlay( 51 | RoundedRectangle(cornerRadius: 35) 52 | .stroke(.white , lineWidth: 2) 53 | ) 54 | .shadow(color: .black.opacity(0.1), radius: 10, x: 0, y: 5) 55 | Rectangle() 56 | .fill(colorScheme == .dark ? (backgroundColor == color ? .white : .black ) : (backgroundColor == color ? .black : .white )) 57 | .frame(width: backgroundColor == color ? 10 : 5 , height: 5) 58 | .cornerRadius(2.5) 59 | .animation(.easeInOut) 60 | }//: VSTACK 61 | } 62 | } 63 | }//: HSTACK 64 | .padding(EdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15)) 65 | .frame(height: 100) 66 | } //: SCROLLVIEW 67 | .frame(maxHeight: .infinity) 68 | 69 | Button { 70 | hideActionView = false 71 | hidePostBGColorView = true 72 | } label: { 73 | Image("close") 74 | .resizable() 75 | .renderingMode(.template) 76 | .frame(width: 20, height: 20) 77 | .foregroundColor(colorScheme == .dark ? .white : .black) 78 | .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)) 79 | .background(Constants.appSecondary) 80 | .cornerRadius(20) 81 | } 82 | .padding(.trailing , 5) 83 | } 84 | .frame(maxHeight: .infinity) 85 | .background(colorScheme == .dark ? Color.black : Color.white) 86 | } 87 | } 88 | 89 | //struct PostBackgroundColorView_Previews: PreviewProvider { 90 | // static var previews: some View { 91 | // PostBackgroundColorView() 92 | // } 93 | //} 94 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Launch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Quotes/View/CreatorActionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CreatorActionView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 30/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CreatorActionView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Binding var backgroundColor: Color 15 | @Binding var fontColor: Color 16 | @Binding var fontSize: CGFloat 17 | @Binding var fontName: String 18 | @Binding var backgroundImage: UIImage? 19 | @Binding var imageCategory: String 20 | @Binding var isOpacityButtonAvailable: Bool 21 | 22 | @State private var data = [ActionViewData]() 23 | @State var hidePostBGColorView: Bool = true 24 | @State var hideFontView: Bool = true 25 | @State var hideFontColorView: Bool = true 26 | @State var hideFontSizeView: Bool = true 27 | @State var hideImageCategoryView: Bool = true 28 | @State var hideActionView: Bool = false 29 | 30 | // MARK: BODY - 31 | 32 | var body: some View { 33 | ZStack { 34 | if !hideActionView { 35 | ScrollView(.horizontal, showsIndicators: false) { 36 | HStack(alignment: .center, spacing: 10) { 37 | ForEach(data){ item in 38 | Button { 39 | if item.toggleView == "PostBackgroundColorView" { 40 | hidePostBGColorView.toggle() 41 | hideActionView.toggle() 42 | } 43 | if item.toggleView == "FontView" { 44 | hideFontView.toggle() 45 | hideActionView.toggle() 46 | } 47 | if item.toggleView == "FontSizeView" { 48 | hideFontSizeView.toggle() 49 | hideActionView.toggle() 50 | } 51 | if item.toggleView == "FontColorView" { 52 | hideFontColorView.toggle() 53 | hideActionView.toggle() 54 | } 55 | if item.toggleView == "ImageCategoryView" { 56 | hideImageCategoryView.toggle() 57 | hideActionView.toggle() 58 | isOpacityButtonAvailable = true 59 | } 60 | } label: { 61 | ActionCardView(data: item) 62 | .frame(width: 90, height: 130) 63 | } 64 | } 65 | } 66 | .padding(EdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15)) 67 | } 68 | .frame(maxHeight: .infinity) 69 | .onAppear { 70 | setData() 71 | } 72 | } 73 | 74 | if !hidePostBGColorView { 75 | PostBackgroundColorView(backgroundColor: $backgroundColor, hidePostBGColorView: $hidePostBGColorView , hideActionView: $hideActionView) 76 | } 77 | 78 | if !hideFontView { 79 | FontView(fontName: $fontName, hideFontView: $hideFontView , hideActionView: $hideActionView) 80 | } 81 | 82 | if !hideFontSizeView { 83 | FontSizeView(fontSize: $fontSize, hideFontSizeView: $hideFontSizeView , hideActionView: $hideActionView) 84 | } 85 | 86 | if !hideFontColorView { 87 | FontColorView(fontColor: $fontColor, hideFontColorView: $hideFontColorView , hideActionView: $hideActionView) 88 | } 89 | 90 | if !hideImageCategoryView { 91 | ImageCategoryView(backgroundImage: $backgroundImage, imageCategory: $imageCategory, hideImageCategoryView: $hideImageCategoryView, hideActionView: $hideActionView , isOpacityButtonAvailable: $isOpacityButtonAvailable) 92 | } 93 | 94 | } 95 | .onAppear { 96 | setData() 97 | } 98 | } 99 | 100 | // MARK: FUNCTIONS - 101 | 102 | func setData(){ 103 | data = [ 104 | ActionViewData(type: "color", image: nil , text: "", color: backgroundColor, title: "BG Color" , toggleView: "PostBackgroundColorView"), 105 | ActionViewData(type: "color", image: nil , text: "", color: fontColor, title: "Font Color" , toggleView: "FontColorView"), 106 | ActionViewData(type: "text", image: nil , text: "\(Int(fontSize))", color: nil, title: "Font Size" , toggleView: "FontSizeView"), 107 | ActionViewData(type: "text", image: nil , text: "Ag", color: nil, title: "Font", toggleView: "FontView"), 108 | ActionViewData(type: "image", image: backgroundImage , text: "", color: nil, title: "BC Image", toggleView: "ImageCategoryView") 109 | ] 110 | } 111 | } 112 | 113 | //struct CreatorActionView_Previews: PreviewProvider { 114 | // static var previews: some View { 115 | // CreatorActionView() 116 | // } 117 | //} 118 | -------------------------------------------------------------------------------- /Quotes/App/FavouriteView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FavouriteView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | import MobileCoreServices 10 | 11 | struct FavouriteView: View { 12 | 13 | // MARK: PROPERTIES - 14 | 15 | @ObservedObject var realmModel = QuoteRealmViewModel() 16 | private var gridItemLayout = [GridItem(.flexible())] 17 | 18 | @State var isCreatingQuote: String = "" 19 | @State var isFavouriteActionQuote: String = "" 20 | @State var cardOffset: CGSize = .zero 21 | @State var createViewScale: CGFloat = 0.0 22 | @State var createViewOpacity: Double = 0 23 | @State var creatingStateActive: Bool = false 24 | @State var favouriteStateActive:Bool = false 25 | @State var favouriteViewOpacity: Double = 0 26 | @State var favouriteViewScale: CGFloat = 0 27 | 28 | @State var showingCreatorView: Bool = false 29 | @State var quoteToSend: String = "" 30 | @State var quoteToSendAuthor: String = "" 31 | 32 | // MARK: BODY - 33 | 34 | var body: some View { 35 | ScrollView(.vertical , showsIndicators: false){ 36 | LazyVGrid(columns: gridItemLayout, spacing: 12) { 37 | if let quotes = realmModel.quotes! { 38 | if quotes.count > 0 { 39 | ForEach(quotes){ quote in 40 | ZStack { 41 | if isCreatingQuote == quote.quoteId { 42 | CreateActionView(creatingStateActive: $creatingStateActive, createViewScale: $createViewScale, createViewOpacity: $createViewOpacity) 43 | } 44 | 45 | if isFavouriteActionQuote == quote.quoteId { 46 | FavoriteActionView(favouriteStateActive: $favouriteStateActive, favouriteViewOpacity: $favouriteViewOpacity, favouriteViewScale: $favouriteViewScale, isQuoteExist: true) 47 | } 48 | 49 | QuoteCardView(isFavouriteQuote: false, quoteText: quote.content, author: quote.author, authorVisible: true , quoteID: quote.quoteId) 50 | .offset(CGSize(width: isCreatingQuote == quote.quoteId ? cardOffset.width : .zero , height: .zero)) 51 | .gesture( 52 | DragGesture() 53 | .onChanged { gesture in 54 | if cardOffset.width >= 0 && cardOffset.width < 100 { 55 | isCreatingQuote = quote.quoteId 56 | cardOffset = gesture.translation 57 | createViewOpacity = 1 58 | createViewScale = (cardOffset.width / 100) > 1 ? 1 : (cardOffset.width / 100) 59 | if cardOffset.width >= 95 { 60 | creatingStateActive = true 61 | } else { 62 | creatingStateActive = false 63 | } 64 | } 65 | if cardOffset.width > -100 && cardOffset.width < 0 { 66 | isFavouriteActionQuote = quote.quoteId 67 | cardOffset = gesture.translation 68 | favouriteViewOpacity = 1 69 | favouriteViewScale = CGFloat((abs(cardOffset.width) / 100) > 1 ? 1 : (abs(cardOffset.width) / 100)) 70 | if cardOffset.width <= -95 { 71 | favouriteStateActive = true 72 | } else { 73 | favouriteStateActive = false 74 | } 75 | } 76 | } 77 | .onEnded { _ in 78 | withAnimation(Animation.spring()) { 79 | if cardOffset.width > 95 { 80 | quoteToSend = quote.content 81 | quoteToSendAuthor = quote.author 82 | showingCreatorView.toggle() 83 | } 84 | if cardOffset.width < -95 { 85 | realmModel.addQuotes(Quote(tags: [""], _id: quote.quoteId, content: "", author: "", authorSlug: "")) 86 | } 87 | cardOffset = .zero 88 | isFavouriteActionQuote = "" 89 | isCreatingQuote = "" 90 | favouriteViewOpacity = 0 91 | createViewOpacity = 0 92 | createViewScale = 0 93 | favouriteViewScale = 0 94 | creatingStateActive = false 95 | favouriteStateActive = false 96 | } 97 | } 98 | ) 99 | } //: ZSTACK 100 | } 101 | } else { 102 | DefaultView(title: "Nothing on favorite list") 103 | } 104 | } 105 | 106 | } //: LAZYVGRID 107 | .padding(EdgeInsets(top: 10, leading: 0, bottom: 10, trailing: 0)) 108 | } //: SCROLLVIEW 109 | .fullScreenCover(isPresented: $showingCreatorView) { 110 | CreatorsView(quoteContent: quoteToSend, quoteAuthor: quoteToSendAuthor) 111 | } 112 | } 113 | } 114 | 115 | struct FavouriteView_Previews: PreviewProvider { 116 | static var previews: some View { 117 | FavouriteView() 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Quotes/SupportingFiles/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "40.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "60.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "29.png", 17 | "idiom" : "iphone", 18 | "scale" : "1x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "58.png", 23 | "idiom" : "iphone", 24 | "scale" : "2x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "87.png", 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "29x29" 32 | }, 33 | { 34 | "filename" : "80.png", 35 | "idiom" : "iphone", 36 | "scale" : "2x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "120.png", 41 | "idiom" : "iphone", 42 | "scale" : "3x", 43 | "size" : "40x40" 44 | }, 45 | { 46 | "filename" : "57.png", 47 | "idiom" : "iphone", 48 | "scale" : "1x", 49 | "size" : "57x57" 50 | }, 51 | { 52 | "filename" : "114.png", 53 | "idiom" : "iphone", 54 | "scale" : "2x", 55 | "size" : "57x57" 56 | }, 57 | { 58 | "filename" : "120.png", 59 | "idiom" : "iphone", 60 | "scale" : "2x", 61 | "size" : "60x60" 62 | }, 63 | { 64 | "filename" : "180.png", 65 | "idiom" : "iphone", 66 | "scale" : "3x", 67 | "size" : "60x60" 68 | }, 69 | { 70 | "filename" : "20.png", 71 | "idiom" : "ipad", 72 | "scale" : "1x", 73 | "size" : "20x20" 74 | }, 75 | { 76 | "filename" : "40.png", 77 | "idiom" : "ipad", 78 | "scale" : "2x", 79 | "size" : "20x20" 80 | }, 81 | { 82 | "filename" : "29.png", 83 | "idiom" : "ipad", 84 | "scale" : "1x", 85 | "size" : "29x29" 86 | }, 87 | { 88 | "filename" : "58.png", 89 | "idiom" : "ipad", 90 | "scale" : "2x", 91 | "size" : "29x29" 92 | }, 93 | { 94 | "filename" : "40.png", 95 | "idiom" : "ipad", 96 | "scale" : "1x", 97 | "size" : "40x40" 98 | }, 99 | { 100 | "filename" : "80.png", 101 | "idiom" : "ipad", 102 | "scale" : "2x", 103 | "size" : "40x40" 104 | }, 105 | { 106 | "filename" : "50.png", 107 | "idiom" : "ipad", 108 | "scale" : "1x", 109 | "size" : "50x50" 110 | }, 111 | { 112 | "filename" : "100.png", 113 | "idiom" : "ipad", 114 | "scale" : "2x", 115 | "size" : "50x50" 116 | }, 117 | { 118 | "filename" : "72.png", 119 | "idiom" : "ipad", 120 | "scale" : "1x", 121 | "size" : "72x72" 122 | }, 123 | { 124 | "filename" : "144.png", 125 | "idiom" : "ipad", 126 | "scale" : "2x", 127 | "size" : "72x72" 128 | }, 129 | { 130 | "filename" : "76.png", 131 | "idiom" : "ipad", 132 | "scale" : "1x", 133 | "size" : "76x76" 134 | }, 135 | { 136 | "filename" : "152.png", 137 | "idiom" : "ipad", 138 | "scale" : "2x", 139 | "size" : "76x76" 140 | }, 141 | { 142 | "filename" : "167.png", 143 | "idiom" : "ipad", 144 | "scale" : "2x", 145 | "size" : "83.5x83.5" 146 | }, 147 | { 148 | "filename" : "1024.png", 149 | "idiom" : "ios-marketing", 150 | "scale" : "1x", 151 | "size" : "1024x1024" 152 | }, 153 | { 154 | "filename" : "48.png", 155 | "idiom" : "watch", 156 | "role" : "notificationCenter", 157 | "scale" : "2x", 158 | "size" : "24x24", 159 | "subtype" : "38mm" 160 | }, 161 | { 162 | "filename" : "55.png", 163 | "idiom" : "watch", 164 | "role" : "notificationCenter", 165 | "scale" : "2x", 166 | "size" : "27.5x27.5", 167 | "subtype" : "42mm" 168 | }, 169 | { 170 | "filename" : "58.png", 171 | "idiom" : "watch", 172 | "role" : "companionSettings", 173 | "scale" : "2x", 174 | "size" : "29x29" 175 | }, 176 | { 177 | "filename" : "87.png", 178 | "idiom" : "watch", 179 | "role" : "companionSettings", 180 | "scale" : "3x", 181 | "size" : "29x29" 182 | }, 183 | { 184 | "filename" : "80.png", 185 | "idiom" : "watch", 186 | "role" : "appLauncher", 187 | "scale" : "2x", 188 | "size" : "40x40", 189 | "subtype" : "38mm" 190 | }, 191 | { 192 | "filename" : "88.png", 193 | "idiom" : "watch", 194 | "role" : "appLauncher", 195 | "scale" : "2x", 196 | "size" : "44x44", 197 | "subtype" : "40mm" 198 | }, 199 | { 200 | "filename" : "100.png", 201 | "idiom" : "watch", 202 | "role" : "appLauncher", 203 | "scale" : "2x", 204 | "size" : "50x50", 205 | "subtype" : "44mm" 206 | }, 207 | { 208 | "filename" : "172.png", 209 | "idiom" : "watch", 210 | "role" : "quickLook", 211 | "scale" : "2x", 212 | "size" : "86x86", 213 | "subtype" : "38mm" 214 | }, 215 | { 216 | "filename" : "196.png", 217 | "idiom" : "watch", 218 | "role" : "quickLook", 219 | "scale" : "2x", 220 | "size" : "98x98", 221 | "subtype" : "42mm" 222 | }, 223 | { 224 | "filename" : "216.png", 225 | "idiom" : "watch", 226 | "role" : "quickLook", 227 | "scale" : "2x", 228 | "size" : "108x108", 229 | "subtype" : "44mm" 230 | }, 231 | { 232 | "filename" : "1024.png", 233 | "idiom" : "watch-marketing", 234 | "scale" : "1x", 235 | "size" : "1024x1024" 236 | }, 237 | { 238 | "filename" : "16.png", 239 | "idiom" : "mac", 240 | "scale" : "1x", 241 | "size" : "16x16" 242 | }, 243 | { 244 | "filename" : "32.png", 245 | "idiom" : "mac", 246 | "scale" : "2x", 247 | "size" : "16x16" 248 | }, 249 | { 250 | "filename" : "32.png", 251 | "idiom" : "mac", 252 | "scale" : "1x", 253 | "size" : "32x32" 254 | }, 255 | { 256 | "filename" : "64.png", 257 | "idiom" : "mac", 258 | "scale" : "2x", 259 | "size" : "32x32" 260 | }, 261 | { 262 | "filename" : "128.png", 263 | "idiom" : "mac", 264 | "scale" : "1x", 265 | "size" : "128x128" 266 | }, 267 | { 268 | "filename" : "256.png", 269 | "idiom" : "mac", 270 | "scale" : "2x", 271 | "size" : "128x128" 272 | }, 273 | { 274 | "filename" : "256.png", 275 | "idiom" : "mac", 276 | "scale" : "1x", 277 | "size" : "256x256" 278 | }, 279 | { 280 | "filename" : "512.png", 281 | "idiom" : "mac", 282 | "scale" : "2x", 283 | "size" : "256x256" 284 | }, 285 | { 286 | "filename" : "512.png", 287 | "idiom" : "mac", 288 | "scale" : "1x", 289 | "size" : "512x512" 290 | }, 291 | { 292 | "filename" : "1024.png", 293 | "idiom" : "mac", 294 | "scale" : "2x", 295 | "size" : "512x512" 296 | } 297 | ], 298 | "info" : { 299 | "author" : "xcode", 300 | "version" : 1 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /Quotes/App/HomeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | import MobileCoreServices 10 | import RealmSwift 11 | 12 | struct HomeView: View { 13 | 14 | // MARK: PROPERTIES - 15 | 16 | @Environment(\.colorScheme) var colorScheme 17 | 18 | @ObservedObject var quotesVM = QuotesViewModel() 19 | @ObservedObject var realmModel = QuoteRealmViewModel() 20 | 21 | private var gridItemLayout = [GridItem(.flexible())] 22 | @State var isCopiedQuote: String = "" 23 | @State var isFavouriteActionQuote: String = "" 24 | @State var cardOffset: CGSize = .zero 25 | @State var copyViewScale: CGFloat = 0.0 26 | @State var copyViewOpacity: Double = 0 27 | @State var copiedStateActive: Bool = false 28 | @State var favouriteStateActive:Bool = false 29 | @State var favouriteViewOpacity: Double = 0 30 | @State var favouriteViewScale: CGFloat = 0 31 | 32 | init() { 33 | UINavigationBar.appearance().titleTextAttributes = [.font : UIFont(name: Constants.fontBlack, size: 20)!] 34 | self.quotesVM.fetchQuote(for: 1) 35 | } 36 | 37 | // MARK: BODY - 38 | 39 | var body: some View { 40 | ScrollView(.vertical, showsIndicators: false) { 41 | LazyVGrid(columns: gridItemLayout, spacing: 12) { 42 | if quotesVM.isLoading { 43 | ProgressView() 44 | .padding(.top , 100) 45 | .progressViewStyle(CircularProgressViewStyle()) 46 | } else { 47 | if let quotes = quotesVM.quoteModel?.results { 48 | ForEach(quotes) { quote in 49 | NavigationLink(destination: QuoteDetailView(data: quote)) { 50 | ZStack { 51 | if isCopiedQuote == quote._id { 52 | CopyActionView(copiedStateActive: $copiedStateActive, copyViewScale: $copyViewScale, copyViewOpacity: $copyViewOpacity) 53 | } 54 | 55 | if isFavouriteActionQuote == quote._id { 56 | FavoriteActionView(favouriteStateActive: $favouriteStateActive, favouriteViewOpacity: $favouriteViewOpacity, favouriteViewScale: $favouriteViewScale, isQuoteExist: realmModel.checkForQuoteExistence(with: quote._id)) 57 | } 58 | 59 | QuoteCardView(isFavouriteQuote: realmModel.checkForQuoteExistence(with: quote._id) , quoteText: quote.content , author: quote.author , quoteID: quote._id) 60 | .offset(CGSize(width: isCopiedQuote == quote._id ? cardOffset.width : .zero , height: .zero)) 61 | .gesture( 62 | DragGesture() 63 | .onChanged { gesture in 64 | if cardOffset.width >= 0 && cardOffset.width < 100 { 65 | isCopiedQuote = quote._id 66 | cardOffset = gesture.translation 67 | copyViewOpacity = 1 68 | copyViewScale = (cardOffset.width / 100) > 1 ? 1 : (cardOffset.width / 100) 69 | if cardOffset.width >= 95 { 70 | copiedStateActive = true 71 | } else { 72 | copiedStateActive = false 73 | } 74 | } 75 | if cardOffset.width > -100 && cardOffset.width <= 0 { 76 | isFavouriteActionQuote = quote._id 77 | cardOffset = gesture.translation 78 | favouriteViewOpacity = 1 79 | favouriteViewScale = CGFloat((abs(cardOffset.width) / 100) > 1 ? 1 : (abs(cardOffset.width) / 100)) 80 | if cardOffset.width <= -95 { 81 | favouriteStateActive = true 82 | } else { 83 | favouriteStateActive = false 84 | } 85 | } 86 | } 87 | .onEnded { _ in 88 | withAnimation(Animation.spring()) { 89 | if cardOffset.width > 95 { 90 | UIPasteboard.general.setValue(quote.content, forPasteboardType: kUTTypePlainText as String) 91 | } 92 | if cardOffset.width < -95 { 93 | realmModel.addQuotes(quote) 94 | } 95 | cardOffset = .zero 96 | isFavouriteActionQuote = "" 97 | isCopiedQuote = "" 98 | favouriteViewOpacity = 0 99 | copyViewOpacity = 0 100 | copyViewScale = 0 101 | favouriteViewScale = 0 102 | copiedStateActive = false 103 | favouriteStateActive = false 104 | } 105 | } 106 | ) 107 | } 108 | } 109 | .buttonStyle(PlainButtonStyle()) 110 | } 111 | if let quoteTotalCount = quotesVM.quoteModel?.totalCount { 112 | if quoteTotalCount > quotes.count { 113 | ProgressView() 114 | .padding() 115 | .progressViewStyle(CircularProgressViewStyle()) 116 | .onAppear(perform: { 117 | if let page = quotesVM.quoteModel?.page { 118 | self.quotesVM.fetchQuote(for: page + 1) 119 | } 120 | }) 121 | } 122 | } 123 | } 124 | } 125 | } //: LAZYVGRID 126 | .padding(EdgeInsets(top: 10, leading: 0, bottom: 10, trailing: 0)) 127 | } //: SCROLLVIEW 128 | } 129 | } 130 | 131 | // MARK: PREVIEW - 132 | 133 | struct HomeView_Previews: PreviewProvider { 134 | static var previews: some View { 135 | HomeView() 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Quotes/View/ActionCardView/ImageCategoryView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageCategoryView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 09/07/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ImageCategoryView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @ObservedObject var imageVM = ImageViewModel() 15 | @ObservedObject var urlImageModel = UrlImageModel() 16 | 17 | @Environment(\.colorScheme) var colorScheme 18 | var gridItemLayout = [GridItem(.flexible())] 19 | 20 | @Binding var backgroundImage: UIImage? 21 | @Binding var imageCategory: String 22 | @Binding var hideImageCategoryView: Bool 23 | @Binding var hideActionView: Bool 24 | @Binding var isOpacityButtonAvailable: Bool 25 | 26 | @State var hideImageListView: Bool = true 27 | 28 | let categoryData = [ 29 | CategoryData(name: "none", image: "none"), 30 | CategoryData(name: "nature", image: "nature-cat"), 31 | CategoryData(name: "inspiration", image: "inspiration-cat"), 32 | CategoryData(name: "music", image: "music-cat"), 33 | CategoryData(name: "art", image: "art-cat"), 34 | CategoryData(name: "technology", image: "technology-cat"), 35 | CategoryData(name: "happiness", image: "happiness-cat"), 36 | CategoryData(name: "education", image: "education-cat"), 37 | CategoryData(name: "history", image: "history-cat"), 38 | CategoryData(name: "success", image: "success-cat") 39 | ] 40 | 41 | // MARK: MAIN - 42 | 43 | var body: some View { 44 | if hideImageListView { 45 | ZStack(alignment: .topTrailing){ 46 | ScrollView(.horizontal, showsIndicators: false) { 47 | HStack(alignment: .center, spacing: 10) { 48 | ForEach(categoryData){ category in 49 | Button { 50 | if category.name == "none" { 51 | backgroundImage = nil 52 | hideActionView = false 53 | hideImageCategoryView = true 54 | isOpacityButtonAvailable = false 55 | } else { 56 | imageCategory = category.name 57 | hideImageListView = false 58 | self.imageVM.fetchSearchedImage(for: 1, search: category.name) 59 | } 60 | } label: { 61 | VStack { 62 | Image(category.image) 63 | .resizable() 64 | .renderingMode(.original) 65 | .scaledToFill() 66 | .frame(width: 70, height: 70) 67 | .cornerRadius(35) 68 | .overlay( 69 | RoundedRectangle(cornerRadius: 35) 70 | .stroke(Color.white , lineWidth: 2) 71 | ) 72 | .shadow(color: .black.opacity(0.1), radius: 10, x: 0, y: 5) 73 | Text(category.name.uppercased()) 74 | .font(.custom(Constants.fontPoppinsRegular, size: 14)) 75 | .multilineTextAlignment(.center) 76 | }//: VSTACK 77 | } 78 | } 79 | }//: HSTACK 80 | .padding(EdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15)) 81 | .frame(height: 100) 82 | } //: SCROLLVIEW 83 | .frame(maxHeight: .infinity) 84 | Button { 85 | hideActionView = false 86 | hideImageCategoryView = true 87 | isOpacityButtonAvailable = false 88 | } label: { 89 | Image("close") 90 | .resizable() 91 | .renderingMode(.template) 92 | .frame(width: 20, height: 20) 93 | .foregroundColor(colorScheme == .dark ? .white : .black) 94 | .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)) 95 | .background(Constants.appSecondary) 96 | .cornerRadius(20) 97 | } 98 | .padding(.trailing , 5) 99 | } 100 | .frame(maxHeight: .infinity) 101 | .background(colorScheme == .dark ? Color.black : Color.white) 102 | } else { 103 | ZStack(alignment: .topTrailing){ 104 | ScrollView(.horizontal, showsIndicators: false) { 105 | LazyHGrid(rows: gridItemLayout, spacing: 12) { 106 | if imageVM.isLoading { 107 | ProgressView() 108 | .frame(width: 100, height: 100) 109 | .progressViewStyle(CircularProgressViewStyle()) 110 | } else { 111 | if let imagesData = imageVM.imagesModel?.photos { 112 | ForEach(imagesData){ imageData in 113 | Button { 114 | urlImageModel.getImage(imageData.src.medium) 115 | backgroundImage = urlImageModel.image 116 | hideImageListView = true 117 | } label: { 118 | CustomImageView(urlString: imageData.src.medium) 119 | .frame(width: 100, height: 100) 120 | .cornerRadius(10) 121 | .overlay( 122 | RoundedRectangle(cornerRadius: 10) 123 | .stroke(Color.white , lineWidth: 2) 124 | ) 125 | .shadow(color: .black.opacity(0.1), radius: 10, x: 0, y: 5) 126 | } 127 | } //: LOOP 128 | if let imageTotalCount = imageVM.imagesModel?.total_results { 129 | if imageTotalCount > imagesData.count { 130 | ProgressView() 131 | .padding() 132 | .progressViewStyle(CircularProgressViewStyle()) 133 | .onAppear(perform: { 134 | if let page = imageVM.imagesModel?.page { 135 | self.imageVM.fetchSearchedImage(for: page + 1, search: imageCategory) 136 | } 137 | }) 138 | } 139 | } 140 | } 141 | } 142 | }//: LAZYHGRID 143 | .padding(EdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15)) 144 | } //: SCROLLVIEW 145 | .frame(maxHeight: .infinity) 146 | Button { 147 | hideImageListView = true 148 | } label: { 149 | Image("close") 150 | .resizable() 151 | .renderingMode(.template) 152 | .frame(width: 20, height: 20) 153 | .foregroundColor(colorScheme == .dark ? .white : .black) 154 | .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)) 155 | .background(Constants.appSecondary) 156 | .cornerRadius(20) 157 | } 158 | .padding(.trailing , 5) 159 | } 160 | .frame(maxHeight: .infinity) 161 | .background(colorScheme == .dark ? Color.black : Color.white) 162 | } 163 | 164 | 165 | } 166 | 167 | } 168 | 169 | //struct ImageCategoryView_Previews: PreviewProvider { 170 | // static var previews: some View { 171 | // ImageCategoryView() 172 | // } 173 | //} 174 | -------------------------------------------------------------------------------- /Quotes/App/TagFilteredQuoteListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TagFilteredQuoteListView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | import MobileCoreServices 10 | 11 | struct TagFilteredQuoteListView: View { 12 | 13 | // MARK: PROPERTIES - 14 | 15 | var tagName: String 16 | var quoteCount: Int 17 | 18 | @Environment(\.presentationMode) var presentationMode: Binding 19 | @Environment(\.colorScheme) var colorScheme 20 | 21 | @ObservedObject var quotesVM = QuotesViewModel() 22 | @ObservedObject var realmModel = QuoteRealmViewModel() 23 | 24 | @State var isCopiedQuote: String = "" 25 | @State var isFavouriteActionQuote: String = "" 26 | @State var cardOffset: CGSize = .zero 27 | @State var copyViewScale: CGFloat = 0.0 28 | @State var copyViewOpacity: Double = 0 29 | @State var copiedStateActive: Bool = false 30 | @State var favouriteStateActive:Bool = false 31 | @State var favouriteViewOpacity: Double = 0 32 | @State var favouriteViewScale: CGFloat = 0 33 | 34 | private var gridItemLayout = [GridItem(.flexible())] 35 | 36 | var btnBack : some View { 37 | Button(action: { 38 | self.presentationMode.wrappedValue.dismiss() 39 | }) { 40 | HStack { 41 | Image("back") 42 | .resizable() 43 | .renderingMode(.template) 44 | .aspectRatio(contentMode: .fit) 45 | .frame(width: 22, height: 22) 46 | .foregroundColor(colorScheme == .dark ? Color.white : Color.black) 47 | } 48 | } 49 | } 50 | 51 | init(tagName: String , quoteCount: Int) { 52 | self.tagName = tagName 53 | self.quoteCount = quoteCount 54 | self.quotesVM.fetchQuoteWithTagName(with: tagName, for: 1) 55 | } 56 | 57 | // MARK: BODY - 58 | 59 | var body: some View { 60 | ScrollView(.vertical, showsIndicators: false) { 61 | LazyVGrid(columns: gridItemLayout, spacing: 12) { 62 | if quotesVM.isLoading { 63 | ProgressView() 64 | .padding(.top , 100) 65 | .progressViewStyle(CircularProgressViewStyle()) 66 | } else { 67 | if let quotes = quotesVM.quoteModel?.results { 68 | ForEach(quotes) { quote in 69 | NavigationLink(destination: QuoteDetailView(data: quote)) { 70 | ZStack { 71 | if isCopiedQuote == quote._id { 72 | CopyActionView(copiedStateActive: $copiedStateActive, copyViewScale: $copyViewScale, copyViewOpacity: $copyViewOpacity) 73 | } 74 | 75 | if isFavouriteActionQuote == quote._id { 76 | FavoriteActionView(favouriteStateActive: $favouriteStateActive, favouriteViewOpacity: $favouriteViewOpacity, favouriteViewScale: $favouriteViewScale, isQuoteExist: realmModel.checkForQuoteExistence(with: quote._id)) 77 | } 78 | QuoteCardView(isFavouriteQuote: realmModel.checkForQuoteExistence(with: quote._id), quoteText: quote.content , author: quote.author , quoteID: quote._id) 79 | .offset(CGSize(width: isCopiedQuote == quote._id ? cardOffset.width : .zero , height: .zero)) 80 | .gesture( 81 | DragGesture() 82 | .onChanged { gesture in 83 | if cardOffset.width >= 0 && cardOffset.width < 100 { 84 | isCopiedQuote = quote._id 85 | cardOffset = gesture.translation 86 | copyViewOpacity = 1 87 | copyViewScale = (cardOffset.width / 100) > 1 ? 1 : (cardOffset.width / 100) 88 | if cardOffset.width >= 95 { 89 | copiedStateActive = true 90 | } else { 91 | copiedStateActive = false 92 | } 93 | } 94 | if cardOffset.width > -100 && cardOffset.width < 0 { 95 | isFavouriteActionQuote = quote._id 96 | cardOffset = gesture.translation 97 | favouriteViewOpacity = 1 98 | favouriteViewScale = CGFloat((abs(cardOffset.width) / 100) > 1 ? 1 : (abs(cardOffset.width) / 100)) 99 | if cardOffset.width <= -95 { 100 | favouriteStateActive = true 101 | } else { 102 | favouriteStateActive = false 103 | } 104 | } 105 | } 106 | .onEnded { _ in 107 | withAnimation(Animation.spring()) { 108 | if cardOffset.width > 95 { 109 | UIPasteboard.general.setValue(quote.content, forPasteboardType: kUTTypePlainText as String) 110 | } 111 | if cardOffset.width < -95 { 112 | realmModel.addQuotes(quote) 113 | } 114 | cardOffset = .zero 115 | isFavouriteActionQuote = "" 116 | isCopiedQuote = "" 117 | favouriteViewOpacity = 0 118 | favouriteViewScale = 0 119 | copyViewOpacity = 0 120 | copyViewScale = 0 121 | copiedStateActive = false 122 | favouriteStateActive = false 123 | } 124 | } 125 | ) 126 | } //: ZSTACK 127 | } //: NAVIGATION LINK 128 | .buttonStyle(PlainButtonStyle()) 129 | } //: LOOP 130 | if let quoteTotalCount = quotesVM.quoteModel?.totalCount { 131 | if quoteTotalCount > quotes.count { 132 | ProgressView() 133 | .padding() 134 | .progressViewStyle(CircularProgressViewStyle()) 135 | .onAppear(perform: { 136 | if let page = quotesVM.quoteModel?.page { 137 | self.quotesVM.fetchQuoteWithTagName(with: tagName, for: page + 1) 138 | } 139 | }) 140 | } 141 | } 142 | } 143 | } 144 | } //: LAZYVGRID 145 | .padding(EdgeInsets(top: 15, leading: 0, bottom: 15, trailing: 0)) 146 | } //: SCROLLVIEW 147 | .navigationBarBackButtonHidden(true) 148 | .navigationBarItems(leading: btnBack) 149 | .toolbar { 150 | ToolbarItem(placement: .principal) { 151 | VStack(alignment: .center, spacing: 0){ 152 | Text(tagName.uppercased()) 153 | .font(.custom(Constants.fontBlack, size: 16)) 154 | Text("\(quoteCount)") 155 | .font(.custom(Constants.fontRegular, size: 14)) 156 | .foregroundColor(colorScheme == .dark ? .white.opacity(0.7) : .black.opacity(0.5)) 157 | } 158 | } 159 | } 160 | } 161 | } 162 | 163 | // MARK: PREVIEW - 164 | 165 | struct TagFilteredQuoteListView_Previews: PreviewProvider { 166 | static var previews: some View { 167 | TagFilteredQuoteListView(tagName: "technology", quoteCount: 12) 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /Quotes/App/CreatorsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CreatorsView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 30/06/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CreatorsView: View { 11 | 12 | // MARK: PROPERTIES - 13 | 14 | @Environment(\.presentationMode) var presentationMode 15 | @Environment(\.colorScheme) var colorScheme 16 | 17 | var fixedHeight: CGFloat = UIScreen.main.bounds.width 18 | var quoteContent: String = "" 19 | var quoteAuthor: String = "" 20 | var ratiosArr = [["1" , "1"] , ["4" , "5"] , ["9" , "16"] , ["1.91" , "1"]] 21 | 22 | @State var ratio: [CGFloat] = [1,1] 23 | @State var i = 0 24 | @State var fontColor: Color = .white 25 | @State var backgroundColor: Color = .black 26 | @State var backgroundImage: UIImage? = nil 27 | @State var imageCategory: String = "" 28 | @State var fontSize: CGFloat = 20 29 | @State var fontName: String = Constants.fontRegular 30 | @State var showAuthor: Bool = true 31 | @State var showingAlert = false 32 | @State var showLoader: Bool = false 33 | @State var bcOpacity: CGFloat = 0.7 34 | @State var hideOpacityView:Bool = true 35 | @State var isOpacityButtonAvailable: Bool = false 36 | 37 | init(quoteContent: String , quoteAuthor: String){ 38 | self.quoteContent = quoteContent 39 | self.quoteAuthor = quoteAuthor 40 | UINavigationBar.appearance().titleTextAttributes = [.font : UIFont(name: Constants.fontBold, size: 20)!] 41 | } 42 | 43 | // MARK: BODY - 44 | 45 | var body: some View { 46 | NavigationView { 47 | ZStack { 48 | // MARK: CREATOR VIEW - 49 | VStack { 50 | ZStack(alignment: .bottomLeading){ 51 | ZStack { 52 | QuotePostView 53 | } //: FIRST INNER ZSTACK 54 | .frame(width: fixedHeight, height: fixedHeight) 55 | 56 | /// Background Opacity Button View 57 | 58 | ZStack { 59 | Button { 60 | hideOpacityView.toggle() 61 | } label: { 62 | HStack { 63 | Spacer() 64 | HStack { 65 | Image("opacityView") 66 | .resizable() 67 | .renderingMode(.template) 68 | .frame(width: 25, height: 25) 69 | .foregroundColor(Color.white) 70 | } //: HSTACK 71 | .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)) 72 | .background(Color.black.opacity(0.5)) 73 | .cornerRadius(22.5) 74 | .overlay( 75 | RoundedRectangle(cornerRadius: 22.5) 76 | .stroke(.white , lineWidth: 1) 77 | ) 78 | .scaleEffect(isOpacityButtonAvailable ? 1 : 0) 79 | .animation(.spring() , value: isOpacityButtonAvailable) 80 | } 81 | } 82 | } 83 | .padding(EdgeInsets(top: 15, leading: 15, bottom: 15, trailing: 15)) 84 | 85 | /// Post Ratio View 86 | 87 | ZStack { 88 | Button { 89 | withAnimation(.spring()) { 90 | i = i + 1 91 | if i == ratiosArr.count { 92 | i = 0 93 | } 94 | ratio = [StringToFloat(str: ratiosArr[i][0]) , StringToFloat(str: ratiosArr[i][1])] 95 | } 96 | } label: { 97 | HStack { 98 | Image("ratio") 99 | .resizable() 100 | .renderingMode(.template) 101 | .frame(width: 25, height: 25) 102 | .foregroundColor(Color.white) 103 | 104 | Text("\(ratiosArr[i][0]) : \(ratiosArr[i][1])") 105 | .foregroundColor(.white) 106 | } //: HSTACK 107 | .padding(EdgeInsets(top: 10, leading: 15, bottom: 10, trailing: 15)) 108 | .background(Color.black.opacity(0.5)) 109 | .cornerRadius(22.5) 110 | .overlay( 111 | RoundedRectangle(cornerRadius: 22.5) 112 | .stroke(.white , lineWidth: 1) 113 | ) 114 | } 115 | } //: SECOND INNER ZSTACK 116 | .padding(EdgeInsets(top: 15, leading: 15, bottom: 15, trailing: 15)) 117 | 118 | // DIVDER VIEW 119 | VStack { 120 | Spacer() 121 | Rectangle() 122 | .fill(Color.gray.opacity(0.7)) 123 | .frame(height: 0.7) 124 | } 125 | 126 | } //: OUTER ZSTACK 127 | .frame(width: fixedHeight, height: fixedHeight) 128 | .background(Color.white) 129 | 130 | // MARK: AUTHOR TOGGLE VIEW - 131 | VStack { 132 | Toggle(isOn: $showAuthor){ 133 | Text("Show Author") 134 | .font(.custom(Constants.fontPoppinsRegular, size: 16)) 135 | } 136 | } 137 | .padding(EdgeInsets(top: 5, leading: 20, bottom: 5, trailing: 20)) 138 | 139 | ZStack { 140 | 141 | // MARK: ACTION VIEW - 142 | CreatorActionView(backgroundColor: $backgroundColor, fontColor: $fontColor , fontSize: $fontSize, fontName: $fontName, backgroundImage: $backgroundImage, imageCategory: $imageCategory , isOpacityButtonAvailable: $isOpacityButtonAvailable) 143 | .background(colorScheme == .dark ? Color.black : Color.white) 144 | 145 | // MARK: BACKGROUND OPACITY VIEW - 146 | if !hideOpacityView { 147 | CustomBCOpacityView(bcOpacity: $bcOpacity, hideOpacityView: $hideOpacityView) 148 | .background(colorScheme == .dark ? Color.black : Color.white) 149 | } 150 | 151 | } 152 | 153 | 154 | Spacer() 155 | } //: VSTACK 156 | .navigationBarTitle("Creator View") 157 | .navigationBarTitleDisplayMode(.inline) 158 | .toolbar { 159 | ToolbarItem(placement: .navigationBarTrailing) { 160 | Button { 161 | showingAlert.toggle() 162 | } label: { 163 | Image("close") 164 | .resizable() 165 | .renderingMode(.template) 166 | .scaledToFill() 167 | .frame(width: 20, height: 20) 168 | } 169 | } 170 | 171 | ToolbarItem(placement: .navigationBarLeading) { 172 | Button { 173 | // Taking snapshot of created post and saving to the photos library 174 | let image = QuotePostView.snapshot() 175 | UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil) 176 | showLoader = true 177 | 178 | } label: { 179 | Image("download") 180 | .resizable() 181 | .renderingMode(.template) 182 | .scaledToFill() 183 | .frame(width: 20, height: 20) 184 | } 185 | } 186 | } 187 | .alert(isPresented: $showingAlert) { 188 | Alert( 189 | title: Text("Are you sure?"), 190 | message: Text("Your saved changes will be lost!"), 191 | primaryButton: .destructive(Text("Go Back!")) { 192 | presentationMode.wrappedValue.dismiss() 193 | }, 194 | secondaryButton: .cancel() 195 | ) 196 | } 197 | 198 | // MARK: CUSTOM POPUP VIEW - 199 | if showLoader { 200 | CustomAnimationView() 201 | .frame(width: 130, height: 50) 202 | .background(Color.white) 203 | .cornerRadius(25) 204 | .shadow(color: .black.opacity(0.2), radius: 15, x: 0, y: 7) 205 | .onAppear( perform: { 206 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.3) { 207 | presentationMode.wrappedValue.dismiss() 208 | } 209 | }) 210 | } 211 | 212 | 213 | } //: ZSTACK 214 | } //: NAVIGATION VIEW 215 | .accentColor(colorScheme == .dark ? .white : .black) 216 | } 217 | 218 | // MARK: CUSTOM VIEW - 219 | 220 | var QuotePostView: some View { 221 | ZStack { 222 | // Background Image view 223 | if backgroundImage != nil { 224 | ZStack { 225 | Image(uiImage: backgroundImage!) 226 | .resizable() 227 | .scaledToFill() 228 | .frame(width: calculateWidthRatio(for: fixedHeight - 40, ratio: ratio) > fixedHeight ? fixedHeight - 40 : calculateWidthRatio(for: fixedHeight - 40, ratio: ratio) , height: calculateWidthRatio(for: fixedHeight - 40, ratio: ratio) > fixedHeight ? calculateHeightRatio(for: fixedHeight - 40, ratio: ratio) : fixedHeight - 40) 229 | .clipped() 230 | Rectangle() 231 | .fill(Color.black.opacity(bcOpacity)) 232 | } //: ZSTACK 233 | } 234 | 235 | VStack { 236 | Text(quoteContent) 237 | .foregroundColor(fontColor) 238 | .font(.custom(fontName, size: fontSize)) 239 | .multilineTextAlignment(.center) 240 | .padding() 241 | if showAuthor { 242 | Text("- \(quoteAuthor)") 243 | .foregroundColor(fontColor) 244 | .font(.custom(fontName, size: (fontSize - 7))) 245 | .fixedSize(horizontal: false, vertical: true) 246 | } 247 | }//: VSTACK 248 | } 249 | .frame(width: calculateWidthRatio(for: fixedHeight - 40, ratio: ratio) > fixedHeight ? fixedHeight - 40 : calculateWidthRatio(for: fixedHeight - 40, ratio: ratio) , height: calculateWidthRatio(for: fixedHeight - 40, ratio: ratio) > fixedHeight ? calculateHeightRatio(for: fixedHeight - 40, ratio: ratio) : fixedHeight - 40) 250 | .background(backgroundColor) 251 | .shadow(color: .black.opacity(0.12), radius: 15, x: 0, y: 7) 252 | 253 | } 254 | 255 | // MARK: FUNCTIONS - 256 | 257 | func calculateWidthRatio(for height: CGFloat , ratio: [CGFloat]) -> CGFloat { 258 | let width = height * (ratio[0]/ratio[1]) 259 | return width 260 | } 261 | 262 | func calculateHeightRatio(for width: CGFloat , ratio: [CGFloat]) -> CGFloat { 263 | let height = width * (ratio[1]/ratio[0]) 264 | return height 265 | } 266 | 267 | func StringToFloat(str: String) -> CGFloat { 268 | let string = str 269 | var cgFloat:CGFloat = 0 270 | if let doubleValue = Double(string){ 271 | cgFloat = CGFloat(doubleValue) 272 | } 273 | return cgFloat 274 | } 275 | 276 | } 277 | 278 | struct CreatorsView_Previews: PreviewProvider { 279 | static var previews: some View { 280 | CreatorsView(quoteContent: "", quoteAuthor: "") 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /Quotes/App/AuthorQuoteListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthorQuoteListView.swift 3 | // Quotes 4 | // 5 | // Created by Dheeraj Kumar Sharma on 29/06/21. 6 | // 7 | 8 | import SwiftUI 9 | import MobileCoreServices 10 | 11 | struct AuthorQuoteListView: View { 12 | 13 | // MARK: PROPERTIES - 14 | 15 | @ObservedObject var quotesVM = QuotesViewModel() 16 | @ObservedObject var realmModel = QuoteRealmViewModel() 17 | 18 | @State var isCopiedQuote: String = "" 19 | @State var isFavouriteActionQuote: String = "" 20 | @State var cardOffset: CGSize = .zero 21 | @State var copyViewScale: CGFloat = 0.0 22 | @State var copyViewOpacity: Double = 0 23 | @State var copiedStateActive: Bool = false 24 | @State var favouriteStateActive:Bool = false 25 | @State var favouriteViewOpacity: Double = 0 26 | @State var favouriteViewScale: CGFloat = 0 27 | 28 | var authorSlug: String 29 | var authorName: String 30 | var totalQuotes: Int 31 | var fromExplore: Bool = false 32 | private var gridItemLayout = [GridItem(.flexible())] 33 | 34 | @Environment(\.presentationMode) var presentationMode: Binding 35 | @Environment(\.colorScheme) var colorScheme 36 | 37 | var btnBack : some View { 38 | Button(action: { 39 | self.presentationMode.wrappedValue.dismiss() 40 | }) { 41 | HStack { 42 | Image("back") 43 | .resizable() 44 | .renderingMode(.template) 45 | .aspectRatio(contentMode: .fit) 46 | .frame(width: 22, height: 22) 47 | .foregroundColor(colorScheme == .dark ? Color.white : Color.black) 48 | } 49 | } 50 | } 51 | 52 | init(authorSlug: String , authorName: String , totalQuotes: Int , fromExplore: Bool) { 53 | self.authorSlug = authorSlug 54 | self.authorName = authorName 55 | self.totalQuotes = totalQuotes 56 | self.fromExplore = fromExplore 57 | self.quotesVM.fetchQuoteWithAuthorSlug(with: self.authorSlug, for: 1) 58 | } 59 | 60 | // MARK: BODY - 61 | 62 | var body: some View { 63 | ScrollView(.vertical, showsIndicators: false) { 64 | LazyVGrid(columns: gridItemLayout, spacing: 12) { 65 | if quotesVM.isLoading { 66 | ProgressView() 67 | .padding(.top , 100) 68 | .progressViewStyle(CircularProgressViewStyle()) 69 | } else { 70 | if let quotes = quotesVM.quoteModel?.results { 71 | ForEach(quotes) { quote in 72 | if fromExplore { 73 | NavigationLink(destination: QuoteDetailView(data: quote)) { 74 | ZStack { 75 | if isCopiedQuote == quote._id { 76 | CopyActionView(copiedStateActive: $copiedStateActive, copyViewScale: $copyViewScale, copyViewOpacity: $copyViewOpacity) 77 | } 78 | 79 | if isFavouriteActionQuote == quote._id { 80 | FavoriteActionView(favouriteStateActive: $favouriteStateActive, favouriteViewOpacity: $favouriteViewOpacity, favouriteViewScale: $favouriteViewScale, isQuoteExist: realmModel.checkForQuoteExistence(with: quote._id)) 81 | } 82 | QuoteCardView(isFavouriteQuote: realmModel.checkForQuoteExistence(with: quote._id), quoteText: quote.content, author: authorName, authorVisible: true, quoteID: quote._id) 83 | .offset(CGSize(width: isCopiedQuote == quote._id ? cardOffset.width : .zero , height: .zero)) 84 | .gesture( 85 | DragGesture() 86 | .onChanged { gesture in 87 | if cardOffset.width >= 0 && cardOffset.width < 100 { 88 | isCopiedQuote = quote._id 89 | cardOffset = gesture.translation 90 | copyViewOpacity = 1 91 | copyViewScale = (cardOffset.width / 100) > 1 ? 1 : (cardOffset.width / 100) 92 | if cardOffset.width >= 95 { 93 | copiedStateActive = true 94 | } else { 95 | copiedStateActive = false 96 | } 97 | } 98 | if cardOffset.width > -100 && cardOffset.width < 0 { 99 | isFavouriteActionQuote = quote._id 100 | cardOffset = gesture.translation 101 | favouriteViewOpacity = 1 102 | favouriteViewScale = CGFloat((abs(cardOffset.width) / 100) > 1 ? 1 : (abs(cardOffset.width) / 100)) 103 | if cardOffset.width <= -95 { 104 | favouriteStateActive = true 105 | } else { 106 | favouriteStateActive = false 107 | } 108 | } 109 | } 110 | .onEnded { _ in 111 | withAnimation(Animation.spring()) { 112 | if cardOffset.width > 95 { 113 | UIPasteboard.general.setValue(quote.content, forPasteboardType: kUTTypePlainText as String) 114 | } 115 | if cardOffset.width < -95 { 116 | realmModel.addQuotes(quote) 117 | } 118 | cardOffset = .zero 119 | isFavouriteActionQuote = "" 120 | isCopiedQuote = "" 121 | favouriteViewOpacity = 0 122 | favouriteViewScale = 0 123 | copyViewOpacity = 0 124 | copyViewScale = 0 125 | copiedStateActive = false 126 | favouriteStateActive = false 127 | } 128 | } 129 | ) 130 | } //: ZSTACK 131 | } //: NAVIGATION LINK 132 | .buttonStyle(PlainButtonStyle()) 133 | } else { 134 | ZStack { 135 | if isCopiedQuote == quote._id { 136 | CopyActionView(copiedStateActive: $copiedStateActive, copyViewScale: $copyViewScale, copyViewOpacity: $copyViewOpacity) 137 | } 138 | 139 | if isFavouriteActionQuote == quote._id { 140 | FavoriteActionView(favouriteStateActive: $favouriteStateActive, favouriteViewOpacity: $favouriteViewOpacity, favouriteViewScale: $favouriteViewScale, isQuoteExist: realmModel.checkForQuoteExistence(with: quote._id)) 141 | } 142 | QuoteCardView(isFavouriteQuote: realmModel.checkForQuoteExistence(with: quote._id), quoteText: quote.content, author: "", authorVisible: false, quoteID: quote._id) 143 | .offset(CGSize(width: isCopiedQuote == quote._id ? cardOffset.width : .zero , height: .zero)) 144 | .gesture( 145 | DragGesture() 146 | .onChanged { gesture in 147 | if cardOffset.width >= 0 && cardOffset.width < 100 { 148 | isCopiedQuote = quote._id 149 | cardOffset = gesture.translation 150 | copyViewOpacity = 1 151 | copyViewScale = (cardOffset.width / 100) > 1 ? 1 : (cardOffset.width / 100) 152 | if cardOffset.width >= 95 { 153 | copiedStateActive = true 154 | } else { 155 | copiedStateActive = false 156 | } 157 | } 158 | if cardOffset.width > -100 && cardOffset.width < 0 { 159 | isFavouriteActionQuote = quote._id 160 | cardOffset = gesture.translation 161 | favouriteViewOpacity = 1 162 | favouriteViewScale = CGFloat((abs(cardOffset.width) / 100) > 1 ? 1 : (abs(cardOffset.width) / 100)) 163 | if cardOffset.width <= -95 { 164 | favouriteStateActive = true 165 | } else { 166 | favouriteStateActive = false 167 | } 168 | } 169 | } 170 | .onEnded { _ in 171 | withAnimation(Animation.spring()) { 172 | if cardOffset.width > 95 { 173 | UIPasteboard.general.setValue(quote.content, forPasteboardType: kUTTypePlainText as String) 174 | } 175 | if cardOffset.width < -95 { 176 | realmModel.addQuotes(quote) 177 | } 178 | cardOffset = .zero 179 | isFavouriteActionQuote = "" 180 | isCopiedQuote = "" 181 | favouriteViewOpacity = 0 182 | favouriteViewScale = 0 183 | copyViewOpacity = 0 184 | copyViewScale = 0 185 | copiedStateActive = false 186 | favouriteStateActive = false 187 | } 188 | } 189 | ) 190 | } //: ZSTACK 191 | } //: CONDITION 192 | } 193 | if let quoteTotalCount = quotesVM.quoteModel?.totalCount { 194 | if quoteTotalCount > quotes.count { 195 | ProgressView() 196 | .padding() 197 | .progressViewStyle(CircularProgressViewStyle()) 198 | .onAppear(perform: { 199 | if let page = quotesVM.quoteModel?.page { 200 | self.quotesVM.fetchQuoteWithAuthorSlug(with: self.authorSlug, for: page + 1) 201 | } 202 | }) 203 | } 204 | } 205 | } 206 | } 207 | } //: LAZYVGRID 208 | .padding(EdgeInsets(top: 10, leading: 0, bottom: 10, trailing: 0)) 209 | } //: SCROLLVIEW 210 | .navigationBarBackButtonHidden(true) 211 | .navigationBarItems(leading: btnBack) 212 | .toolbar { 213 | ToolbarItem(placement: .principal) { 214 | VStack(alignment: .center, spacing: 0){ 215 | Text(authorName) 216 | .font(.custom(Constants.fontBlack, size: 16)) 217 | Text("\(totalQuotes)") 218 | .font(.custom(Constants.fontRegular, size: 14)) 219 | .foregroundColor(colorScheme == .dark ? .white.opacity(0.7) : .black.opacity(0.5)) 220 | } 221 | } 222 | } 223 | } 224 | } 225 | 226 | struct AuthorQuoteListView_Previews: PreviewProvider { 227 | static var previews: some View { 228 | AuthorQuoteListView(authorSlug: "" , authorName: "" , totalQuotes: 0, fromExplore: false) 229 | } 230 | } 231 | --------------------------------------------------------------------------------