├── .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 | 
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 |
--------------------------------------------------------------------------------