├── QuizChallenge
├── Supporting Files
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── Icons
│ │ │ ├── Contents.json
│ │ │ ├── SocialNetwork
│ │ │ │ ├── Contents.json
│ │ │ │ ├── vk.imageset
│ │ │ │ │ ├── vk.png
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── google.imageset
│ │ │ │ │ ├── search.png
│ │ │ │ │ └── Contents.json
│ │ │ │ └── facebook.imageset
│ │ │ │ │ ├── facebook.png
│ │ │ │ │ └── Contents.json
│ │ │ ├── fun.imageset
│ │ │ │ ├── fun.png
│ │ │ │ └── Contents.json
│ │ │ ├── edit.imageset
│ │ │ │ ├── edit.png
│ │ │ │ └── Contents.json
│ │ │ ├── mind.imageset
│ │ │ │ ├── mind.png
│ │ │ │ └── Contents.json
│ │ │ ├── play.imageset
│ │ │ │ ├── play.png
│ │ │ │ ├── play@2x.png
│ │ │ │ ├── play@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── user.imageset
│ │ │ │ ├── user.png
│ │ │ │ └── Contents.json
│ │ │ ├── close.imageset
│ │ │ │ ├── close.png
│ │ │ │ └── Contents.json
│ │ │ ├── dices.imageset
│ │ │ │ ├── dices.png
│ │ │ │ └── Contents.json
│ │ │ ├── sword.imageset
│ │ │ │ ├── sword.png
│ │ │ │ └── Contents.json
│ │ │ ├── alert.imageset
│ │ │ │ ├── warning-2.png
│ │ │ │ └── Contents.json
│ │ │ ├── avatar.imageset
│ │ │ │ ├── avatar.png
│ │ │ │ └── Contents.json
│ │ │ ├── badge.imageset
│ │ │ │ ├── badge-2.png
│ │ │ │ └── Contents.json
│ │ │ ├── cancel.imageset
│ │ │ │ ├── cancel-2.png
│ │ │ │ └── Contents.json
│ │ │ ├── console.imageset
│ │ │ │ ├── console.png
│ │ │ │ └── Contents.json
│ │ │ ├── hint.imageset
│ │ │ │ ├── creative-2.png
│ │ │ │ └── Contents.json
│ │ │ ├── medal.imageset
│ │ │ │ ├── medal-3.png
│ │ │ │ └── Contents.json
│ │ │ ├── pencil.imageset
│ │ │ │ ├── pencil.png
│ │ │ │ └── Contents.json
│ │ │ ├── puzzle.imageset
│ │ │ │ ├── jigsaw.png
│ │ │ │ └── Contents.json
│ │ │ ├── refresh.imageset
│ │ │ │ ├── refresh.png
│ │ │ │ └── Contents.json
│ │ │ ├── resetTime.imageset
│ │ │ │ ├── clock.png
│ │ │ │ └── Contents.json
│ │ │ ├── swords.imageset
│ │ │ │ ├── swords.png
│ │ │ │ └── Contents.json
│ │ │ ├── trophy.imageset
│ │ │ │ ├── trophy.png
│ │ │ │ └── Contents.json
│ │ │ ├── add-user.imageset
│ │ │ │ ├── add-user.png
│ │ │ │ └── Contents.json
│ │ │ ├── envelope.imageset
│ │ │ │ ├── envelope.png
│ │ │ │ └── Contents.json
│ │ │ ├── errorAlert.imageset
│ │ │ │ ├── cancel.png
│ │ │ │ └── Contents.json
│ │ │ ├── goodgame.imageset
│ │ │ │ ├── goodgame.png
│ │ │ │ └── Contents.json
│ │ │ ├── next.imageset
│ │ │ │ ├── next-page-2.png
│ │ │ │ └── Contents.json
│ │ │ ├── question.imageset
│ │ │ │ ├── question.png
│ │ │ │ └── Contents.json
│ │ │ ├── review.imageset
│ │ │ │ ├── review@2x.png
│ │ │ │ ├── review@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── activeGames.imageset
│ │ │ │ ├── jigsaw-4.png
│ │ │ │ └── Contents.json
│ │ │ ├── adCoins.imageset
│ │ │ │ ├── Play-button.png
│ │ │ │ └── Contents.json
│ │ │ ├── doneAlert.imageset
│ │ │ │ ├── doneAlert.png
│ │ │ │ └── Contents.json
│ │ │ ├── game_type.imageset
│ │ │ │ ├── game_type.png
│ │ │ │ └── Contents.json
│ │ │ ├── logoPuzzle.imageset
│ │ │ │ ├── puzzle-3.png
│ │ │ │ └── Contents.json
│ │ │ ├── settings.imageset
│ │ │ │ ├── controls-2.png
│ │ │ │ └── Contents.json
│ │ │ ├── creativity.imageset
│ │ │ │ ├── creativity.png
│ │ │ │ └── Contents.json
│ │ │ ├── left-arrow.imageset
│ │ │ │ ├── left-arrow.png
│ │ │ │ └── Contents.json
│ │ │ ├── piggy-bank.imageset
│ │ │ │ ├── piggy-bank.png
│ │ │ │ └── Contents.json
│ │ │ ├── confirmAlert.imageset
│ │ │ │ ├── confirmAlert.png
│ │ │ │ └── Contents.json
│ │ │ ├── conversation.imageset
│ │ │ │ ├── conversation.png
│ │ │ │ └── Contents.json
│ │ │ ├── networkAlert.imageset
│ │ │ │ ├── networkAlert.png
│ │ │ │ └── Contents.json
│ │ │ ├── caret-arrow-up.imageset
│ │ │ │ ├── caret-arrow-up.png
│ │ │ │ └── Contents.json
│ │ │ └── questions-circular-button.imageset
│ │ │ │ ├── questions-circular-button.png
│ │ │ │ └── Contents.json
│ │ ├── Colors
│ │ │ ├── Contents.json
│ │ │ ├── MenuColor
│ │ │ │ ├── Contents.json
│ │ │ │ ├── Color-0.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Color-1.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Color-2.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Color-3.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Color-4.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Color-5.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Color-6.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Color-7.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Color-8.colorset
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Color-9.colorset
│ │ │ │ │ └── Contents.json
│ │ │ ├── AppColor.colorset
│ │ │ │ └── Contents.json
│ │ │ ├── LightBlue.colorset
│ │ │ │ └── Contents.json
│ │ │ ├── RoyalColor.colorset
│ │ │ │ └── Contents.json
│ │ │ ├── AppColorMedium.colorset
│ │ │ │ └── Contents.json
│ │ │ ├── AppColorShadow.colorset
│ │ │ │ └── Contents.json
│ │ │ └── RoyalLightColor.colorset
│ │ │ │ └── Contents.json
│ │ ├── Images
│ │ │ ├── Contents.json
│ │ │ ├── Group.imageset
│ │ │ │ ├── Group.png
│ │ │ │ ├── Group@2x.png
│ │ │ │ ├── Group@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── Group2.imageset
│ │ │ │ ├── Group2.png
│ │ │ │ ├── Group2@2x.png
│ │ │ │ ├── Group2@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── Other.imageset
│ │ │ │ ├── images-2.png
│ │ │ │ └── Contents.json
│ │ │ ├── RedRec.imageset
│ │ │ │ ├── RedRec.png
│ │ │ │ └── Contents.json
│ │ │ ├── BlueRec.imageset
│ │ │ │ ├── BlueRec.png
│ │ │ │ └── Contents.json
│ │ │ ├── GrayRec.imageset
│ │ │ │ ├── GrayRec.png
│ │ │ │ └── Contents.json
│ │ │ ├── collage.imageset
│ │ │ │ ├── 4pics-2.png
│ │ │ │ └── Contents.json
│ │ │ ├── GameBack.imageset
│ │ │ │ ├── GameBack.png
│ │ │ │ ├── GameBack@2x.png
│ │ │ │ ├── GameBack@3x.png
│ │ │ │ └── Contents.json
│ │ │ ├── OrangeRec.imageset
│ │ │ │ ├── OrangeRec.png
│ │ │ │ └── Contents.json
│ │ │ ├── PurpleRec.imageset
│ │ │ │ ├── PurpleRec.png
│ │ │ │ └── Contents.json
│ │ │ ├── TestBack.imageset
│ │ │ │ ├── TestBack.png
│ │ │ │ └── Contents.json
│ │ │ ├── TrueFalse.imageset
│ │ │ │ ├── TrueFalse.png
│ │ │ │ └── Contents.json
│ │ │ ├── Background.imageset
│ │ │ │ ├── Background.png
│ │ │ │ └── Contents.json
│ │ │ ├── wordcloud.imageset
│ │ │ │ ├── wordcloud-4.png
│ │ │ │ └── Contents.json
│ │ │ └── iphone_1470012c.imageset
│ │ │ │ ├── iphone_1470012c.jpg
│ │ │ │ └── Contents.json
│ │ ├── tmpFiles
│ │ │ ├── Contents.json
│ │ │ ├── 1.imageset
│ │ │ │ ├── 1.jpg
│ │ │ │ └── Contents.json
│ │ │ ├── 3.imageset
│ │ │ │ ├── 3.jpg
│ │ │ │ └── Contents.json
│ │ │ ├── 4.imageset
│ │ │ │ ├── 4.jpg
│ │ │ │ └── Contents.json
│ │ │ ├── 5.imageset
│ │ │ │ ├── 5.jpg
│ │ │ │ └── Contents.json
│ │ │ ├── 6.imageset
│ │ │ │ ├── 6.jpg
│ │ │ │ └── Contents.json
│ │ │ ├── 7.imageset
│ │ │ │ ├── 7.jpg
│ │ │ │ └── Contents.json
│ │ │ ├── 8.imageset
│ │ │ │ ├── 8.jpg
│ │ │ │ └── Contents.json
│ │ │ ├── 9.imageset
│ │ │ │ ├── 9.jpg
│ │ │ │ └── Contents.json
│ │ │ ├── 10.imageset
│ │ │ │ ├── 10.jpg
│ │ │ │ └── Contents.json
│ │ │ └── 2.imageset
│ │ │ │ ├── Natural-Dog-Law-2-To-dogs,-energy-is-everything.jpg
│ │ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ ├── ItunesArtwork@2x.png
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-20x20@2x-1.png
│ │ │ ├── Icon-App-29x29@2x-1.png
│ │ │ └── Contents.json
│ ├── Info.plist
│ └── AppDelegate.swift
├── ViewModel
│ ├── Login
│ │ ├── AuthController
│ │ │ ├── GoogleAuth.swift
│ │ │ ├── AuthFacadeController.swift
│ │ │ ├── VKAuth.swift
│ │ │ └── FBAuth.swift
│ │ ├── RegistrationViewModel.swift
│ │ ├── ValidationViewModel.swift
│ │ └── LoginViewModel.swift
│ └── PrepareGameViewModel.swift
├── View
│ ├── EmptyView
│ │ ├── EmptyActiveGameView.swift
│ │ └── StartGameCell.swift
│ ├── AdvertisingCell.swift
│ ├── GameView
│ │ ├── TrueFalseView.swift
│ │ ├── SimpleQuestionView.swift
│ │ ├── FourImagesView.swift
│ │ ├── CountdownView.swift
│ │ ├── TrueFalseView.xib
│ │ └── CircleView.swift
│ ├── PrepareGameView
│ │ └── Category
│ │ │ ├── CategoryCell.swift
│ │ │ └── SubcategoryCell.swift
│ ├── BaseView.swift
│ ├── LoginView
│ │ └── WelcomePageCell.swift
│ ├── AnswerCreateView.swift
│ ├── AnswerCreateView.xib
│ └── HistoryGameCell.swift
├── ViewController
│ ├── PrepareGame
│ │ ├── PrepareGameInfo.swift
│ │ ├── OpponentViewController.swift
│ │ ├── TypeGameViewController.swift
│ │ ├── RandomOpponentViewController.swift
│ │ └── FindUserViewController.swift
│ ├── MainViewController.swift
│ ├── GameInformationViewController.swift
│ ├── GameViewController+Koloda.swift
│ ├── Login
│ │ ├── RestorePassViewController.swift
│ │ └── WelcomePage
│ │ │ ├── WelcomeDataSource.swift
│ │ │ └── WelcomeViewController.swift
│ ├── AdvertisingViewController.swift
│ ├── BaseViewController.swift
│ ├── QuestionTypeListViewController.swift
│ ├── Settings
│ │ └── SettingsViewController.swift
│ ├── NavigationViewController.swift
│ └── New Group
│ │ └── ProfileSettingsViewController.swift
├── Helpers
│ ├── Extensions
│ │ ├── UICollectionView+Extension.swift
│ │ ├── UIViewController+Extension.swift
│ │ ├── TextField+Extension.swift
│ │ ├── UISegue.swift
│ │ ├── UIDevice+Extension.swift
│ │ ├── Color+Extension.swift
│ │ ├── UserDefaults+Extension.swift
│ │ └── UIView+Extension.swift
│ ├── AudioManager.swift
│ ├── Logger.swift
│ ├── Categories.json
│ ├── Router.swift
│ └── HelperFile.swift
├── Model
│ ├── Settings.swift
│ ├── TypeDecodable.swift
│ ├── Registration.swift
│ ├── Login.swift
│ ├── Opponent.swift
│ ├── User.swift
│ ├── Statistics.swift
│ ├── Question.swift
│ └── Session.swift
└── Services
│ ├── UserManager.swift
│ ├── Storage
│ └── RealmManager.swift
│ └── Network
│ └── Response.swift
├── .gitignore
├── QuizChallenge.xcworkspace
└── contents.xcworkspacedata
├── README.md
├── Podfile
├── .circleci
└── config.yml
└── QuizChallenge.xcodeproj
└── xcshareddata
└── xcschemes
└── QuizChallenge.xcscheme
/QuizChallenge/Supporting Files/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/MenuColor/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/SocialNetwork/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/fun.imageset/fun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/fun.imageset/fun.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/1.imageset/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/1.imageset/1.jpg
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/3.imageset/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/3.imageset/3.jpg
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/4.imageset/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/4.imageset/4.jpg
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/5.imageset/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/5.imageset/5.jpg
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/6.imageset/6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/6.imageset/6.jpg
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/7.imageset/7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/7.imageset/7.jpg
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/8.imageset/8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/8.imageset/8.jpg
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/9.imageset/9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/9.imageset/9.jpg
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Pods
2 | QuizChallenge.xcodeproj/xcuserdata
3 | QuizChallenge.xcodeproj/project.xcworkspace
4 | Podfile.lock
5 | QuizChallenge.xcworkspace/xcshareddata
6 | QuizChallenge.xcworkspace/xcuserdata
7 |
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/edit.imageset/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/edit.imageset/edit.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/mind.imageset/mind.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/mind.imageset/mind.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/play.imageset/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/play.imageset/play.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/user.imageset/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/user.imageset/user.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/10.imageset/10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/10.imageset/10.jpg
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/close.imageset/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/close.imageset/close.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/dices.imageset/dices.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/dices.imageset/dices.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/play.imageset/play@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/play.imageset/play@2x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/play.imageset/play@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/play.imageset/play@3x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/sword.imageset/sword.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/sword.imageset/sword.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/Group.imageset/Group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/Group.imageset/Group.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/alert.imageset/warning-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/alert.imageset/warning-2.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/avatar.imageset/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/avatar.imageset/avatar.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/badge.imageset/badge-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/badge.imageset/badge-2.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/cancel.imageset/cancel-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/cancel.imageset/cancel-2.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/console.imageset/console.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/console.imageset/console.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/hint.imageset/creative-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/hint.imageset/creative-2.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/medal.imageset/medal-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/medal.imageset/medal-3.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/pencil.imageset/pencil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/pencil.imageset/pencil.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/puzzle.imageset/jigsaw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/puzzle.imageset/jigsaw.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/refresh.imageset/refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/refresh.imageset/refresh.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/resetTime.imageset/clock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/resetTime.imageset/clock.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/swords.imageset/swords.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/swords.imageset/swords.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/trophy.imageset/trophy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/trophy.imageset/trophy.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/Group.imageset/Group@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/Group.imageset/Group@2x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/Group.imageset/Group@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/Group.imageset/Group@3x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/Group2.imageset/Group2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/Group2.imageset/Group2.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/Other.imageset/images-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/Other.imageset/images-2.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/RedRec.imageset/RedRec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/RedRec.imageset/RedRec.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/add-user.imageset/add-user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/add-user.imageset/add-user.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/envelope.imageset/envelope.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/envelope.imageset/envelope.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/errorAlert.imageset/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/errorAlert.imageset/cancel.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/goodgame.imageset/goodgame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/goodgame.imageset/goodgame.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/next.imageset/next-page-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/next.imageset/next-page-2.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/question.imageset/question.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/question.imageset/question.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/review.imageset/review@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/review.imageset/review@2x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/review.imageset/review@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/review.imageset/review@3x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/BlueRec.imageset/BlueRec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/BlueRec.imageset/BlueRec.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/GrayRec.imageset/GrayRec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/GrayRec.imageset/GrayRec.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/Group2.imageset/Group2@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/Group2.imageset/Group2@2x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/Group2.imageset/Group2@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/Group2.imageset/Group2@3x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/collage.imageset/4pics-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/collage.imageset/4pics-2.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/SocialNetwork/vk.imageset/vk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/SocialNetwork/vk.imageset/vk.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/activeGames.imageset/jigsaw-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/activeGames.imageset/jigsaw-4.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/adCoins.imageset/Play-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/adCoins.imageset/Play-button.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/doneAlert.imageset/doneAlert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/doneAlert.imageset/doneAlert.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/game_type.imageset/game_type.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/game_type.imageset/game_type.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/logoPuzzle.imageset/puzzle-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/logoPuzzle.imageset/puzzle-3.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/settings.imageset/controls-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/settings.imageset/controls-2.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/GameBack.imageset/GameBack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/GameBack.imageset/GameBack.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/OrangeRec.imageset/OrangeRec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/OrangeRec.imageset/OrangeRec.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/PurpleRec.imageset/PurpleRec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/PurpleRec.imageset/PurpleRec.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/TestBack.imageset/TestBack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/TestBack.imageset/TestBack.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/TrueFalse.imageset/TrueFalse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/TrueFalse.imageset/TrueFalse.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/creativity.imageset/creativity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/creativity.imageset/creativity.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/left-arrow.imageset/left-arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/left-arrow.imageset/left-arrow.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/piggy-bank.imageset/piggy-bank.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/piggy-bank.imageset/piggy-bank.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/Background.imageset/Background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/Background.imageset/Background.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/GameBack.imageset/GameBack@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/GameBack.imageset/GameBack@2x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/GameBack.imageset/GameBack@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/GameBack.imageset/GameBack@3x.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/wordcloud.imageset/wordcloud-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/wordcloud.imageset/wordcloud-4.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/confirmAlert.imageset/confirmAlert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/confirmAlert.imageset/confirmAlert.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/conversation.imageset/conversation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/conversation.imageset/conversation.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/networkAlert.imageset/networkAlert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/networkAlert.imageset/networkAlert.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/SocialNetwork/google.imageset/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/SocialNetwork/google.imageset/search.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/caret-arrow-up.imageset/caret-arrow-up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/caret-arrow-up.imageset/caret-arrow-up.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/SocialNetwork/facebook.imageset/facebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/SocialNetwork/facebook.imageset/facebook.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/iphone_1470012c.imageset/iphone_1470012c.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Images/iphone_1470012c.imageset/iphone_1470012c.jpg
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/questions-circular-button.imageset/questions-circular-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/Icons/questions-circular-button.imageset/questions-circular-button.png
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/2.imageset/Natural-Dog-Law-2-To-dogs,-energy-is-everything.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a-mkrv/QuizChallenge/HEAD/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/2.imageset/Natural-Dog-Law-2-To-dogs,-energy-is-everything.jpg
--------------------------------------------------------------------------------
/QuizChallenge.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # QuizChallenge
2 | Multiplayer Online Quiz
3 |
4 | #### Current design and implementation
5 |
6 |
7 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewModel/Login/AuthController/GoogleAuth.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GoogleAuth.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 16/03/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class GoogleAuth: Authorizable {
12 |
13 | func authorize(complition: authComplitionData) {
14 |
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/QuizChallenge/View/EmptyView/EmptyActiveGameView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmptyActiveGameView.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 02/05/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | //TODO: Replace to Generic View (with set title, img, button)
12 |
13 | class EmptyActiveGameView: BaseView {
14 |
15 | @IBOutlet weak var button: IBButton!
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/QuizChallenge/View/EmptyView/StartGameCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StartGameCell.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 04/05/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class StartGameCell: UITableViewCell {
12 |
13 | @IBOutlet weak var startGameButton: IBButton!
14 |
15 | override func awakeFromNib() {
16 | super.awakeFromNib()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/fun.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "fun.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/mind.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "mind.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/user.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "user.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "1.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/10.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "10.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "3.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/4.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "4.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/5.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "5.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/6.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "6.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/7.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "7.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/8.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "8.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/9.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "9.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/PrepareGame/PrepareGameInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PrepareGameInfo.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 26/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class PrepareGameInfo {
12 |
13 | var type: TypeGame?
14 | var selectCategory: String?
15 | var selectSubCategory: String?
16 | var opponentName: String?
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/badge.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "badge-2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/cancel.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "cancel-2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/console.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "console.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/hint.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "creative-2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/puzzle.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "jigsaw.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/resetTime.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "clock.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/Other.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "images-2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/RedRec.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "RedRec.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/SocialNetwork/vk.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "vk.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/activeGames.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "jigsaw-4.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/adCoins.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Play-button.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/game_type.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "game_type.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/goodgame.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "goodgame.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/logoPuzzle.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "puzzle-3.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/question.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "question.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/settings.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "controls-2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/BlueRec.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "BlueRec.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/GrayRec.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "GrayRec.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/OrangeRec.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "OrangeRec.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/PurpleRec.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "PurpleRec.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/TestBack.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "TestBack.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/TrueFalse.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "TrueFalse.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/collage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "4pics-2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/View/AdvertisingCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AdvertisingCell.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 23/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class AdvertisingCell: UICollectionViewCell {
12 |
13 | @IBOutlet weak var adIcon: UIImageView!
14 | @IBOutlet weak var nameLabel: UILabel!
15 | @IBOutlet weak var countCoinsLabel: UILabel!
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/conversation.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "conversation.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/creativity.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "creativity.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/left-arrow.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "left-arrow.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/networkAlert.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "networkAlert.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/piggy-bank.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "piggy-bank.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/Background.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Background.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/wordcloud.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "wordcloud-4.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/SocialNetwork/google.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "search.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/caret-arrow-up.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "caret-arrow-up.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/AppColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0x40",
13 | "alpha" : "1.000",
14 | "blue" : "0x7D",
15 | "green" : "0x4C"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/LightBlue.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0x49",
13 | "alpha" : "1.000",
14 | "blue" : "0xD8",
15 | "green" : "0x82"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/RoyalColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0x4C",
13 | "alpha" : "1.000",
14 | "blue" : "0xDD",
15 | "green" : "0x5B"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/SocialNetwork/facebook.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "facebook.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/iphone_1470012c.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "iphone_1470012c.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/AppColorMedium.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0x78",
13 | "alpha" : "1.000",
14 | "blue" : "0xBA",
15 | "green" : "0x85"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/AppColorShadow.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0xD3",
13 | "alpha" : "1.000",
14 | "blue" : "0xE9",
15 | "green" : "0xD7"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/RoyalLightColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0x77",
13 | "alpha" : "1.000",
14 | "blue" : "0xDF",
15 | "green" : "0x63"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/MenuColor/Color-0.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0x7D",
13 | "alpha" : "1.000",
14 | "blue" : "0xE1",
15 | "green" : "0x49"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/MenuColor/Color-1.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0.482",
13 | "alpha" : "1.000",
14 | "blue" : "0.984",
15 | "green" : "0.820"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/MenuColor/Color-2.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0.957",
13 | "alpha" : "1.000",
14 | "blue" : "0.663",
15 | "green" : "0.686"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/MenuColor/Color-3.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0.584",
13 | "alpha" : "1.000",
14 | "blue" : "0.706",
15 | "green" : "0.933"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/MenuColor/Color-4.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0.733",
13 | "alpha" : "1.000",
14 | "blue" : "0.945",
15 | "green" : "0.733"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/MenuColor/Color-5.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0.349",
13 | "alpha" : "1.000",
14 | "blue" : "0.984",
15 | "green" : "0.651"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/MenuColor/Color-6.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0.969",
13 | "alpha" : "1.000",
14 | "blue" : "0.565",
15 | "green" : "0.745"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/MenuColor/Color-7.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0.482",
13 | "alpha" : "1.000",
14 | "blue" : "0.835",
15 | "green" : "0.780"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/MenuColor/Color-8.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0xEF",
13 | "alpha" : "1.000",
14 | "blue" : "0xA0",
15 | "green" : "0x79"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Colors/MenuColor/Color-9.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0x75",
13 | "alpha" : "1.000",
14 | "blue" : "0x71",
15 | "green" : "0xFF"
16 | }
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/tmpFiles/2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Natural-Dog-Law-2-To-dogs,-energy-is-everything.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/review.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "review@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "review@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/QuizChallenge/Helpers/Extensions/UICollectionView+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionView+Extension.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 19/03/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UICollectionView {
12 | func reloadData(completion: @escaping () -> ()) {
13 | UIView.animate(withDuration: 0, animations: {
14 | self.reloadData()
15 | }) { _ in completion() }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/QuizChallenge/Helpers/Extensions/UIViewController+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+Extension.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 22/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIViewController {
12 |
13 | open override func touchesBegan(_ touches: Set, with event: UIEvent?) {
14 | super.touchesBegan(touches, with: event)
15 | self.view.endEditing(true)
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/play.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "play.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "play@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "play@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/close.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "close.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/dices.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "dices.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/edit.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "edit.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/sword.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "sword.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/Group.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Group.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Group@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Group@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/alert.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "warning-2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/avatar.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "avatar.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/medal.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "medal-3.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/next.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "next-page-2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/pencil.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "pencil.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/refresh.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "refresh.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/swords.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "swords.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/trophy.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "trophy.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/Group2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Group2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Group2@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Group2@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/add-user.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "add-user.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/doneAlert.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "doneAlert.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/envelope.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "envelope.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/errorAlert.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "cancel.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Images/GameBack.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "GameBack.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "GameBack@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "GameBack@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/QuizChallenge/View/GameView/TrueFalseView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrueFalseView.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 24/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class TrueFalseView: UIView {
12 |
13 | /*
14 | // Only override draw() if you perform custom drawing.
15 | // An empty implementation adversely affects performance during animation.
16 | override func draw(_ rect: CGRect) {
17 | // Drawing code
18 | }
19 | */
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/confirmAlert.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "confirmAlert.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/View/GameView/SimpleQuestionView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SimpleQuestionView.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 24/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SimpleQuestionView: UIView {
12 |
13 | /*
14 | // Only override draw() if you perform custom drawing.
15 | // An empty implementation adversely affects performance during animation.
16 | override func draw(_ rect: CGRect) {
17 | // Drawing code
18 | }
19 | */
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/Icons/questions-circular-button.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "questions-circular-button.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "template"
23 | }
24 | }
--------------------------------------------------------------------------------
/QuizChallenge/View/PrepareGameView/Category/CategoryCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CategoryCell.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 05/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CategoryCell: UICollectionViewCell {
12 |
13 | @IBOutlet weak var backColorView: GradientView!
14 | @IBOutlet weak var categoryNameLabel: UILabel!
15 |
16 | override func prepareForReuse() {
17 | backColorView.shadowOpacity = 0
18 | backColorView.shadowColor = .clear
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/QuizChallenge/Helpers/Extensions/TextField+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TextField+Extension.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 22/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UITextField {
12 |
13 | func setBottomBorder() {
14 | let bottomBorder = CALayer()
15 | bottomBorder.frame = CGRect(x: 0.0, y: self.frame.size.height - 1, width: self.frame.size.width, height: 1.0);
16 | bottomBorder.backgroundColor = UIColor.black.cgColor
17 | self.layer.addSublayer(bottomBorder)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/QuizChallenge/Model/Settings.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Settings.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 30/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 |
12 | class SettingsModel: Object {
13 | @objc dynamic var id = 1
14 | @objc dynamic var notifications = true
15 | @objc dynamic var saveQuestions = true
16 | @objc dynamic var backgroundSound = true
17 | @objc dynamic var buyPremiumEnabled = true
18 |
19 | override static func primaryKey() -> String? {
20 | return "id"
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/QuizChallenge/Model/TypeDecodable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TypeDecodable.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 30/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct TypeDecodable: Codable {
12 | let typeQuestions: [TypeQuestion]
13 |
14 | enum CodingKeys: String, CodingKey {
15 | case typeQuestions = "type_questions"
16 | }
17 | }
18 |
19 | struct TypeQuestion: Codable {
20 | let name: String
21 | let types: [TypeElement]
22 | }
23 |
24 | struct TypeElement: Codable {
25 | let name, image: String
26 | }
27 |
28 |
29 |
--------------------------------------------------------------------------------
/QuizChallenge/View/BaseView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseView.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 04/05/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class BaseView: UIView {
12 |
13 | var view: UIView!
14 |
15 | override init(frame: CGRect) {
16 | super.init(frame: frame)
17 | addSubviewFromNib()
18 | setupUI()
19 | }
20 |
21 | required init?(coder aDecoder: NSCoder) {
22 | super.init(coder: aDecoder)
23 | addSubviewFromNib()
24 | setupUI()
25 | }
26 |
27 | func setupUI() { }
28 | }
29 |
--------------------------------------------------------------------------------
/QuizChallenge/Model/Registration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Registration.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 19/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 | import ObjectMapper
12 |
13 | class Registration: Object, Mappable, Endpoint {
14 |
15 | @objc dynamic var token = 0
16 |
17 | required convenience init?(map: Map) {
18 | self.init()
19 | }
20 |
21 | func mapping(map: Map) {
22 | token <- map["token"]
23 | }
24 |
25 | static func url() -> String {
26 | return "/users"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/QuizChallenge/View/PrepareGameView/Category/SubcategoryCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SubcategoryCell.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 05/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SubcategoryCell: UICollectionViewCell {
12 |
13 | @IBOutlet weak var imageView: IBImageView!
14 | @IBOutlet weak var nameLabel: UILabel!
15 | @IBOutlet weak var gameButtonView: UIView!
16 |
17 | var callback: EmptyClosure?
18 |
19 | @IBAction func playGame(_ sender: Any) {
20 | callback?()
21 | }
22 |
23 | override func prepareForReuse() {
24 | gameButtonView.isHidden = true
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/QuizChallenge/View/GameView/FourImagesView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FourImagesView.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 24/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class FourImagesView: UIView {
12 |
13 | @IBOutlet weak var image1: UIImageView!
14 | @IBOutlet weak var image2: UIImageView!
15 | @IBOutlet weak var image3: UIImageView!
16 | @IBOutlet weak var image4: UIImageView!
17 |
18 | /*
19 | // Only override draw() if you perform custom drawing.
20 | // An empty implementation adversely affects performance during animation.
21 | override func draw(_ rect: CGRect) {
22 | // Drawing code
23 | }
24 | */
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | project 'QuizChallenge.xcodeproj'
2 |
3 | # Uncomment the next line to define a global platform for your project
4 | # platform :ios, '9.0'
5 |
6 | target 'QuizChallenge' do
7 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
8 | use_frameworks!
9 |
10 | # UI
11 | pod "Koloda"
12 | pod 'TransitionButton'
13 | pod 'AMPopTip'
14 | pod 'SwiftEntryKit', '0.8.8'
15 | pod "SkeletonView"
16 | pod "ViewAnimator"
17 |
18 | # Network, Storage
19 | pod 'Alamofire'
20 | pod 'AlamofireImage'
21 | pod 'RealmSwift'
22 | pod 'AlamofireObjectMapper'
23 | pod 'ObjectMapper'
24 | pod 'RxSwift', '~> 4.0'
25 | pod 'RxCocoa', '~> 4.0'
26 | pod 'Sentry'
27 |
28 | pod 'SwiftyVK'
29 | pod 'FBSDKLoginKit'
30 |
31 | end
32 |
--------------------------------------------------------------------------------
/QuizChallenge/View/LoginView/WelcomePageCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WelcomePageCell.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 04/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class WelcomePageCell: UICollectionViewCell {
12 |
13 | @IBOutlet weak var imageView: UIImageView!
14 | @IBOutlet weak var titleLabel: UILabel!
15 | @IBOutlet weak var descriptionLabel: UILabel!
16 | @IBOutlet weak var startButton: UIButton!
17 |
18 | var callback: EmptyClosure?
19 |
20 | func configLastPage() {
21 | descriptionLabel.isHidden = true
22 | startButton.isHidden = false
23 | }
24 |
25 | @IBAction func pressStart(_ sender: Any) {
26 | self.callback?()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/QuizChallenge/Model/Login.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Login.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 19/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 | import ObjectMapper
12 |
13 | class Login: Object, Mappable, Endpoint {
14 |
15 | @objc dynamic var id = 0
16 | @objc dynamic var token = ""
17 | @objc dynamic var user: User? = nil
18 |
19 | required convenience init?(map: Map) {
20 | self.init()
21 | }
22 |
23 | override static func primaryKey() -> String? {
24 | return "id"
25 | }
26 |
27 | func mapping(map: Map) {
28 | token <- map["token"]
29 | user <- map["user"]
30 | }
31 |
32 | static func url() -> String {
33 | return "/users/login"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/QuizChallenge/Helpers/Extensions/UISegue.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UISegue.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 22/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | //MARK: Custom segue transition
12 | class FadeInPushSegue: UIStoryboardSegue {
13 |
14 | var animated: Bool = true
15 |
16 | override func perform() {
17 |
18 | let sourceViewController = self.source
19 | let destinationViewController = self.destination
20 | let transition: CATransition = CATransition()
21 |
22 | transition.type = CATransitionType.fade
23 | sourceViewController.view.window?.layer.add(transition, forKey: "kCATransition")
24 | sourceViewController.navigationController?.pushViewController(destinationViewController, animated: false)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/QuizChallenge/Helpers/Extensions/UIDevice+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIDevice+Extension.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 30/04/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIDevice {
12 |
13 | enum ScreenType: String {
14 | case iPhoneSE
15 | case iPhone6_7_8
16 | case iPhone6_7_8Plus
17 | case iPhoneX
18 | case Unknown
19 | }
20 |
21 | var screenType: ScreenType {
22 |
23 | switch UIScreen.main.nativeBounds.height {
24 | case 1136:
25 | return .iPhoneSE
26 | case 1334:
27 | return .iPhone6_7_8
28 | case 2208, 1920:
29 | return .iPhone6_7_8Plus
30 | case 2436:
31 | return .iPhoneX
32 | default:
33 | return .Unknown
34 | }
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/MainViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainViewController.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 22/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import TransitionButton
11 |
12 | class MainViewController: CustomTransitionViewController {
13 |
14 | @IBOutlet weak var pointsLabel: UILabel!
15 | @IBOutlet weak var usernameLabel: UILabel!
16 | @IBOutlet weak var activeGamesLabel: UILabel!
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 | Logger.info(msg: "User Token: \(UserManager.shared.userToken)")
21 | }
22 |
23 | override func viewWillAppear(_ animated: Bool) {
24 | navigationController?.interactivePopGestureRecognizer?.isEnabled = true
25 | usernameLabel.text = UserManager.shared.curUser.username
26 | pointsLabel.text = "Points: \(UserManager.shared.curUser.points)"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 |
5 | # Specify the Xcode version to use
6 | macos:
7 | xcode: "10.1"
8 |
9 | steps:
10 | - checkout
11 |
12 | # Install CocoaPods
13 | - run:
14 | name: Install CocoaPods
15 | command: pod install
16 |
17 | # Build the app and run tests
18 | - run:
19 | name: Build and run tests
20 | command: fastlane scan
21 | environment:
22 | SCAN_DEVICE: iPhone 6s
23 | SCAN_SCHEME: WebTests
24 |
25 | # Collect XML test results data to show in the UI,
26 | # and save the same XML files under test-results folder
27 | # in the Artifacts tab
28 | - store_test_results:
29 | path: test_output/report.xml
30 | - store_artifacts:
31 | path: /tmp/test-results
32 | destination: scan-test-results
33 | - store_artifacts:
34 | path: ~/Library/Logs/scan
35 | destination: scan-logs
--------------------------------------------------------------------------------
/QuizChallenge/Model/Opponent.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Opponent.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 15/03/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 | import ObjectMapper
12 |
13 | class Opponent: Object, Mappable, Endpoint {
14 |
15 | @objc dynamic var id = ""
16 | @objc dynamic var username = ""
17 | @objc dynamic var winsGame = 0
18 | @objc dynamic var lossesGame = 0
19 |
20 | override static func primaryKey() -> String? {
21 | return "id"
22 | }
23 |
24 | required convenience init?(map: Map) {
25 | self.init()
26 | }
27 |
28 | func mapping(map: Map) {
29 | id <- map["id"]
30 | username <- map["username"]
31 | winsGame <- map["wins_game"]
32 | lossesGame <- map["losses_game"]
33 | }
34 |
35 | static func url() -> String {
36 | return "/searchOpponent"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/GameInformationViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GameInformationViewController.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 06/03/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class GameInformationViewController: BaseViewController {
12 |
13 | @IBOutlet weak var gamer1ImageView: IBImageView!
14 | @IBOutlet weak var gamer1NameLabel: UILabel!
15 |
16 | @IBOutlet weak var gamer2ImageView: IBImageView!
17 | @IBOutlet weak var gamer2NameLabel: UILabel!
18 |
19 | @IBOutlet weak var gameResultTableView: UITableView!
20 | @IBOutlet weak var nextButton: IBButton!
21 | @IBOutlet weak var scoreLabel: UILabel!
22 |
23 | override func viewDidLoad() {
24 | super.viewDidLoad()
25 | }
26 |
27 | @IBAction func startGame(_ sender: UIButton) {
28 | let vc = CommonHelper.loadViewController(named: "GameSB") as! GameViewController
29 | navigationController?.pushViewController(vc, animated: true)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/GameViewController+Koloda.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GameViewController+Koloda.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 24/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Koloda
11 |
12 | extension GameViewController: KolodaViewDataSource, KolodaViewDelegate {
13 |
14 | func koloda(_ koloda: KolodaView, viewForCardAt index: Int) -> UIView {
15 |
16 | if index == 0 {
17 | let countdownView: CountdownView = .fromNib()
18 | countdownView.startCallback = {
19 | self.startTimer()
20 | self.kolodaView.swipe(arc4random_uniform(2) == 1 ? .left : .right)
21 | }
22 | return countdownView
23 | } else {
24 | return SimpleQuestionView.fromNib()
25 | }
26 | }
27 |
28 | func kolodaNumberOfCards(_ koloda:KolodaView) -> Int {
29 | return 3
30 | }
31 |
32 | func kolodaSpeedThatCardShouldDrag(_ koloda: KolodaView) -> DragSpeed {
33 | return .slow
34 | }
35 |
36 | func kolodaDidRunOutOfCards(_ koloda: KolodaView) {
37 | koloda.reloadData()
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/QuizChallenge/View/GameView/CountdownView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CountdownView.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 24/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CountdownView: UIView {
12 |
13 | @IBOutlet weak var circleView: CircleView!
14 | @IBOutlet weak var timerLabel: UILabel!
15 | @IBOutlet weak var prepareLabel: UILabel!
16 |
17 | var startCallback: EmptyClosure?
18 | var timer: Timer!
19 | var seconds = 3
20 |
21 | override func draw(_ rect: CGRect) {
22 | circleView.animateCircle(duration: 3)
23 | timerLabel.text = String(seconds)
24 |
25 | timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateLabel), userInfo: nil, repeats: true)
26 | }
27 |
28 | @objc func updateLabel() {
29 | seconds -= 1
30 | timerLabel.text = String(seconds)
31 |
32 | if seconds == 0 {
33 | timer.invalidate()
34 | timerLabel.text = "Go"
35 |
36 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
37 | self.startCallback?()
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/QuizChallenge/Model/User.swift:
--------------------------------------------------------------------------------
1 | //
2 | // User.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 24/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 | import ObjectMapper
12 |
13 | protocol Endpoint {
14 | static func url() -> String
15 | }
16 |
17 | class User: Object, Mappable {
18 |
19 | @objc dynamic var id = ""
20 | @objc dynamic var username = ""
21 | @objc dynamic var email = ""
22 | @objc dynamic var realName = ""
23 | @objc dynamic var city = ""
24 | @objc dynamic var age = 0
25 | @objc dynamic var points = 0
26 | @objc dynamic var isDisableAD = false
27 |
28 | override static func primaryKey() -> String? {
29 | return "id"
30 | }
31 |
32 | required convenience init?(map: Map) {
33 | self.init()
34 | }
35 |
36 | func mapping(map: Map) {
37 | id <- map["id"]
38 | username <- map["username"]
39 | email <- map["email"]
40 | realName <- map["real_name"]
41 | city <- map["city"]
42 | age <- map["age"]
43 | points <- map["game_points"]
44 | isDisableAD <- map["is_disable_ad"]
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/PrepareGame/OpponentViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OpponentViewController.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 25/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class OpponentViewController: UIViewController {
12 |
13 | @IBOutlet weak var profileImageView: IBImageView!
14 | @IBOutlet weak var nameLabel: UILabel!
15 | @IBOutlet weak var gamesLabel: UILabel!
16 | @IBOutlet weak var lossesLabel: UILabel!
17 | @IBOutlet weak var winsLabel: UILabel!
18 |
19 | @IBOutlet weak var mainView: IBView!
20 | @IBOutlet weak var progressView: UIProgressView!
21 |
22 | override func viewDidLoad() {
23 | super.viewDidLoad()
24 | }
25 |
26 | override func viewWillAppear(_ animated: Bool) {
27 | let viewPosition = mainView.frame.origin.y
28 | mainView.frame.origin.y = 0
29 |
30 | UIView.springAnimate(animateCompletion: {
31 | self.mainView.frame.origin.y = viewPosition
32 | })
33 | }
34 |
35 | @IBAction func closeView(_ sender: Any) {
36 | self.dismiss(animated: true, completion: nil)
37 | }
38 |
39 | @IBAction func startGame(_ sender: Any) {
40 |
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/QuizChallenge/View/GameView/TrueFalseView.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/QuizChallenge/Helpers/AudioManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AudioManager.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 27/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import AVFoundation
10 |
11 | class AudioManager {
12 |
13 | static let shared = AudioManager()
14 | private init() { }
15 |
16 | private var audioPlayer: AVAudioPlayer?
17 |
18 | func playMusic() {
19 | if isPlaying() {
20 | Logger.error(msg: "Player Already Playing")
21 | return
22 | }
23 |
24 | let fileUrl = Bundle.main.url(forResource: "BackgroundMusic", withExtension: "mp3")
25 |
26 | do {
27 | try audioPlayer = AVAudioPlayer(contentsOf: fileUrl!)
28 | audioPlayer?.numberOfLoops = -1
29 | audioPlayer?.prepareToPlay()
30 | audioPlayer?.play()
31 |
32 | } catch {
33 | Logger.error(msg: "Error playing: " + error.localizedDescription)
34 | }
35 | }
36 |
37 | func stopMusic() {
38 | isPlaying() ? audioPlayer!.stop() : ()
39 | }
40 |
41 | private func isPlaying() -> Bool {
42 | if let player = audioPlayer, player.isPlaying {
43 | return true
44 | }
45 |
46 | return false
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/PrepareGame/TypeGameViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TypeGameViewController.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 26/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ViewAnimator
11 |
12 | enum TypeGame {
13 | case Once
14 | case Tourney
15 | }
16 |
17 | class TypeGameViewController: BaseViewController {
18 |
19 | @IBOutlet weak var quickGameView: IBView!
20 | @IBOutlet weak var tourneyGameView: IBView!
21 | @IBOutlet weak var trainGameView: IBView!
22 |
23 | override func viewDidLoad() {
24 | super.viewDidLoad()
25 | UIView.animate(views: [quickGameView, tourneyGameView, trainGameView], animations: [AnimationType.zoom(scale: 0.5)], animationInterval: 0.13)
26 | }
27 |
28 | @IBAction func quickGamePress(_ sender: Any) {
29 | goToPrepareQuestion(.Once)
30 | }
31 |
32 | @IBAction func tournamentPress(_ sender: Any) {
33 | goToPrepareQuestion(.Tourney)
34 | }
35 |
36 | func goToPrepareQuestion(_ type: TypeGame) {
37 | let prepareGameVC = CommonHelper.loadViewController(from: "Main", named: "PrepareQuestionSB") as! PrepareGameViewController
38 | prepareGameVC.gameInfo.type = type
39 | self.navigationController?.pushViewController(prepareGameVC, animated: true)
40 | }
41 |
42 | deinit {
43 | Logger.mark()
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/QuizChallenge/Services/UserManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserManager.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 12/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class UserManager {
12 |
13 | static let shared = UserManager()
14 |
15 | var curUser: User {
16 | if let user = RealmManager.shared.getObjectByID(Login.self, id: 0)?.user {
17 | return user
18 | }
19 | return User()
20 | }
21 |
22 | var userName: String {
23 | get { return UserDefaults.standard.userName ?? "Unknown" }
24 | set { UserDefaults.standard.userName = newValue }
25 | }
26 |
27 | var isLoggedIn: Bool {
28 | get { return UserDefaults.standard.isLoggedIn ?? false }
29 | set { UserDefaults.standard.isLoggedIn = newValue }
30 | }
31 |
32 | var userToken: String {
33 | get { return UserDefaults.standard.userToken ?? "" }
34 | set { UserDefaults.standard.userToken = newValue }
35 | }
36 |
37 | func logOut(complition: BoolClosure) {
38 | do {
39 | try RealmManager.shared.clearAllData()
40 | UserDefaults.standard.clearAllAppData()
41 | complition(true)
42 | } catch {
43 | Logger.error(msg: "Realm Storage Error: \(error.localizedDescription) \nUnable to cleare data")
44 | complition(false)
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/QuizChallenge/Helpers/Extensions/Color+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color+Extension.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 22/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIColor {
12 |
13 | convenience init(red: Int, green: Int, blue: Int, a: CGFloat = 1.0) {
14 | self.init(
15 | red: CGFloat(red) / 255.0,
16 | green: CGFloat(green) / 255.0,
17 | blue: CGFloat(blue) / 255.0,
18 | alpha: a)
19 | }
20 |
21 | convenience init(hex: Int, a: CGFloat = 1.0) {
22 | self.init(
23 | red: (hex >> 16) & 0xFF,
24 | green: (hex >> 8) & 0xFF,
25 | blue: hex & 0xFF,
26 | a: a
27 | )
28 | }
29 |
30 | static var random: UIColor {
31 | return UIColor(red: .random(in: 0...1),
32 | green: .random(in: 0...1),
33 | blue: .random(in: 0...1),
34 | alpha: 1.0)
35 | }
36 |
37 |
38 | static let mainBlue = UIColor(red:0.18, green:0.42, blue:0.62, alpha:1.00)
39 | static let mainGray = UIColor(red:0.89, green:0.89, blue:0.91, alpha:1.00)
40 | static let royal = UIColor(named: "RoyalColor") ?? .white
41 | static let lightRoyal = UIColor(named: "RoyalLightColor") ?? .white
42 | static let lightRed = UIColor(named: "Color-8") ?? .red
43 | static let errorRed = UIColor(hex: 0xEE5A58)
44 | }
45 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/Login/RestorePassViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RestorePassViewController.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 29/04/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import RxSwift
11 |
12 | class RestorePassViewController: BaseViewController {
13 |
14 | @IBOutlet weak var emailTextField: IBTextField!
15 | @IBOutlet weak var restoreButton: UIButton!
16 | let disposeBag = DisposeBag()
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 | keyboardSubscribe()
21 |
22 | emailTextField.rx.text.orEmpty
23 | .map { $0.count >= 1 }
24 | .share(replay: 1)
25 | .bind(to: restoreButton.rx.isEnabled)
26 | .disposed(by: disposeBag)
27 | }
28 |
29 | override func viewWillDisappear(_ animated: Bool) {
30 | super.viewWillDisappear(animated)
31 | keyboardUnsubscribe()
32 | }
33 |
34 | @IBAction func restorePassword(_ sender: UIButton) {
35 | PopUpHelper.showSimpleAlert(from: self, type: .common, descript: "Check your E-mail and recover your password!", buttonText: "Ok") {
36 | self.navigationController?.popViewController(animated: true)
37 | }
38 | }
39 |
40 | override func keyboardWillShow(_ notification: Notification) {
41 | if view.frame.origin.y == 0 {
42 | if UIDevice().screenType == .iPhoneSE {
43 | view.frame.origin.y -= 35
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/QuizChallenge/Model/Statistics.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Statistics.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 25/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 | import ObjectMapper
12 |
13 | class Statistics: Object, Mappable, Endpoint {
14 |
15 | @objc dynamic var totalGame = 0
16 | @objc dynamic var countLoss = 0
17 | @objc dynamic var countWins = 0
18 |
19 | required convenience init?(map: Map) {
20 | self.init()
21 | }
22 |
23 | func mapping(map: Map) {
24 | totalGame <- map["total_game"]
25 | countLoss <- map["count_loss"]
26 | countWins <- map["count_wins"]
27 | }
28 |
29 | static func url() -> String {
30 | return "https://quizbackend.com/statistics"
31 | }
32 | }
33 |
34 | class TestModel: Object, Mappable, Endpoint {
35 |
36 | @objc dynamic var userId = 0
37 | @objc dynamic var id = 0
38 | @objc dynamic var title = ""
39 | @objc dynamic var completed = false
40 |
41 | required convenience init?(map: Map) {
42 | self.init()
43 | }
44 |
45 | override static func primaryKey() -> String? {
46 | return "id"
47 | }
48 |
49 | func mapping(map: Map) {
50 | userId <- map["userId"]
51 | id <- map["id"]
52 | title <- map["title"]
53 | completed <- map["completed"]
54 | }
55 |
56 | static func url() -> String {
57 | return "https://jsonplaceholder.typicode.com/todos/1"
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/QuizChallenge/View/GameView/CircleView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CircleView.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 24/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CircleView: UIView {
12 |
13 | var circleLayer: CAShapeLayer!
14 |
15 | override init(frame: CGRect) {
16 | super.init(frame: frame)
17 | }
18 |
19 | required init?(coder aDecoder: NSCoder) {
20 | super.init(coder: aDecoder)
21 |
22 | self.backgroundColor = .clear
23 |
24 | let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 10)/2, startAngle: .pi / -2, endAngle: 3/2 * .pi, clockwise: true)
25 |
26 | circleLayer = CAShapeLayer()
27 | circleLayer.path = circlePath.cgPath
28 | circleLayer.fillColor = UIColor.clear.cgColor
29 | circleLayer.strokeColor = UIColor(named: "RoyalColor")?.cgColor
30 | circleLayer.lineWidth = 7.0;
31 | circleLayer.strokeEnd = 0.0
32 |
33 | layer.addSublayer(circleLayer)
34 | }
35 |
36 | func animateCircle(duration: TimeInterval) {
37 | let animation = CABasicAnimation(keyPath: "strokeEnd")
38 |
39 | animation.duration = duration
40 | animation.fromValue = 0
41 | animation.toValue = 1
42 | animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
43 |
44 | circleLayer.strokeEnd = 1
45 | circleLayer.add(animation, forKey: "animateCircle")
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewModel/PrepareGameViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PrepareGameViewModel.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 19/03/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 | import RxCocoa
12 |
13 | class PrepareGameViewModel {
14 |
15 | let disposeBag = DisposeBag()
16 | //let createGameDidTapObservable: Observable
17 |
18 | func searchUserBy(name: String) -> Observable {
19 | return Observable.create{ observer in
20 |
21 | let user: APIParameters = ["opponent": name]
22 | NetworkManager.shared.searchOpponent(with: user)
23 | .subscribe(onNext: { opponent in
24 | observer.onNext(opponent)
25 | }, onError: { error in
26 | observer.onError(error)
27 | }).disposed(by: self.disposeBag)
28 |
29 | return Disposables.create()
30 | }
31 | }
32 |
33 | func createGameWithUser(category: String) -> Observable {
34 |
35 | let params = ["type": "text", "category": category]
36 |
37 | return Observable.create{ observer in
38 |
39 | NetworkManager.shared.createNewGames(with: params)
40 | .subscribe(onNext: { _ in
41 | observer.onNext(.success)
42 | observer.onCompleted()
43 | }, onError: { error in
44 | observer.onError(error)
45 | }).disposed(by: self.disposeBag)
46 |
47 | return Disposables.create()
48 | }
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/QuizChallenge/Model/Question.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Question.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 21/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 | import ObjectMapper
12 |
13 | class Question: Object, Mappable, Endpoint {
14 |
15 | @objc dynamic var id = 0
16 | @objc dynamic var text = ""
17 | @objc dynamic var image = ""
18 | let answers = List()
19 |
20 | override static func primaryKey() -> String? {
21 | return "id"
22 | }
23 |
24 | required convenience init?(map: Map) {
25 | self.init()
26 | }
27 |
28 | func mapping(map: Map) {
29 | id <- map["id"]
30 | text <- map["text"]
31 | image <- map["image"]
32 |
33 | var answers: [Answer]?
34 | answers <- map["answers"]
35 |
36 | if let ansArray = answers {
37 | for answer in ansArray {
38 | self.answers.append(answer)
39 | }
40 | }
41 | }
42 |
43 | static func url() -> String {
44 | return "/questions"
45 | }
46 | }
47 |
48 | class Answer: Object, Mappable {
49 |
50 | @objc dynamic var id = 0
51 | @objc dynamic var text = ""
52 | @objc dynamic var isCorrect = false
53 |
54 | override static func primaryKey() -> String? {
55 | return "id"
56 | }
57 |
58 | required convenience init?(map: Map) {
59 | self.init()
60 | }
61 |
62 | func mapping(map: Map) {
63 | id <- map["id"]
64 | text <- map["text"]
65 | isCorrect <- map["is_correct"]
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/AdvertisingViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AdvertisingViewController.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 23/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class AdvertisingViewController: BaseViewController {
12 |
13 | let advertising = ["Watch Ad", "Share VK", "Share FB", "Share Tweet", "Buy 100", "Buy 300", "Buy 500"]
14 | let adDescription = ["+30 coins", "+30 coins", "+30 coins", "+30 coins", "60 RUB", "150 RUB", "300 RUB"]
15 |
16 | @IBOutlet weak var adCollectionView: UICollectionView!
17 | @IBOutlet weak var coinsCountLabel: UILabel!
18 |
19 | override func viewDidLoad() {
20 | super.viewDidLoad()
21 | }
22 | }
23 |
24 | //MARK: CollectionView Delegates
25 | extension AdvertisingViewController: UICollectionViewDelegate, UICollectionViewDataSource {
26 |
27 | func numberOfSections(in collectionView: UICollectionView) -> Int {
28 | return 1
29 | }
30 |
31 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
32 | return advertising.count
33 | }
34 |
35 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
36 |
37 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AdvertisingCell", for: indexPath as IndexPath) as! AdvertisingCell
38 |
39 | cell.adIcon.image = UIImage(named: "adCoins")
40 | cell.countCoinsLabel.text = adDescription[indexPath.row]
41 | cell.nameLabel.text = advertising[indexPath.row]
42 |
43 | return cell
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/QuizChallenge/Helpers/Extensions/UserDefaults+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserDefaults+Extension.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 27/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | fileprivate let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String
12 |
13 | extension UserDefaults {
14 |
15 | private func createKey(_ key: String) -> String {
16 | return (appName ?? "QuizChallenge") + "-" + key
17 | }
18 |
19 | //FIXME: Refactoring in separate one object (UserManager)
20 | var isLoggedIn: Bool? {
21 | set {
22 | set(newValue, forKey: createKey(#function))
23 | synchronize()
24 | }
25 | get { return bool(forKey: createKey(#function)) }
26 | }
27 |
28 | var userToken: String? {
29 | set {
30 | set(newValue, forKey: createKey(#function))
31 | synchronize()
32 | }
33 | get { return string(forKey: createKey(#function)) }
34 | }
35 |
36 | var userName: String? {
37 | set {
38 | set(newValue, forKey: createKey(#function))
39 | synchronize()
40 | }
41 | get { return string(forKey: createKey(#function)) }
42 | }
43 |
44 | var isShowWelcomeScreen: Bool {
45 | set {
46 | set(newValue, forKey: createKey(#function))
47 | synchronize()
48 | }
49 | get { return bool(forKey: createKey(#function)) }
50 | }
51 |
52 | //MARK: Clear all data
53 | func clearAllAppData() {
54 | UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)
55 | synchronize()
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/QuizChallenge/Model/Session.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Session.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 25/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 | import ObjectMapper
12 |
13 | class Session: Object, Mappable, Endpoint {
14 |
15 | @objc dynamic var id = ""
16 | @objc dynamic var user1ID = ""
17 | @objc dynamic var user2ID = ""
18 | @objc dynamic var startTime = ""
19 | @objc dynamic var finishTime = ""
20 | @objc dynamic var categoryQuestions = ""
21 | @objc dynamic var opponentName = ""
22 | @objc dynamic var opponentImageUrl = ""
23 | @objc dynamic var opponentScore = 0
24 | @objc dynamic var myScore = 0
25 | @objc dynamic var totalRounds = 0
26 | @objc dynamic var endRounds = 0
27 |
28 | required convenience init?(map: Map) {
29 | self.init()
30 | }
31 |
32 | override static func primaryKey() -> String? {
33 | return "id"
34 | }
35 |
36 | func mapping(map: Map) {
37 | id <- map["id"]
38 | user1ID <- map["user1_id"]
39 | user2ID <- map["user2_id"]
40 | startTime <- map["started_at"]
41 | finishTime <- map["finished_at"]
42 | categoryQuestions <- map["category_questions"]
43 | opponentName <- map["opponent_name"]
44 | opponentImageUrl <- map["opponent_image_url"]
45 | opponentScore <- map["opponent_score"]
46 | myScore <- map["my_score"]
47 | totalRounds <- map["total_rounds"]
48 | endRounds <- map["end_rounds"]
49 | }
50 |
51 | static func url() -> String {
52 | return "/games"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/QuizChallenge/Helpers/Logger.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Logger.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 12/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum LogLevel {
12 | case DEBUG, INFO, ERROR, NETWORK, MARK;
13 |
14 | fileprivate func emotionLevel() -> String {
15 | var emotion = ""
16 | switch self {
17 | case .DEBUG:
18 | emotion = "\u{0001F937} "
19 | case .INFO:
20 | emotion = "\u{0001F446} "
21 | case .ERROR:
22 | emotion = "\u{0001F621} "
23 | case .NETWORK:
24 | emotion = "\u{0001F310} "
25 | case .MARK:
26 | emotion = "\u{0001F340} "
27 | }
28 |
29 | return emotion + "\(self)" + " ➯ "
30 | }
31 | }
32 |
33 | class Logger {
34 | static func debug(msg: Any, _ line: Int = #line, _ fileName: String = #file, _ funcName: String = #function, type: LogLevel = .DEBUG) {
35 |
36 | var fullLogMessage = type.emotionLevel()
37 | fullLogMessage += CommonHelper.getCurrentTime() + ": "
38 | fullLogMessage += URL(fileURLWithPath: fileName).deletingPathExtension().lastPathComponent + " ➯ "
39 | fullLogMessage += funcName + ":"
40 | fullLogMessage += "\(line) ➯ "
41 |
42 | print(fullLogMessage, msg)
43 | }
44 |
45 |
46 | static func error(msg: Any, _ line: Int = #line, _ fileName: String = #file, _ funcName: String = #function) {
47 | debug(msg: msg as Any, line, fileName, funcName, type: .ERROR)
48 | }
49 |
50 | static func info(msg: Any, _ line: Int = #line, _ fileName: String = #file, _ funcName: String = #function) {
51 | debug(msg: msg as Any, line, fileName, funcName, type: .INFO)
52 | }
53 |
54 | static func mark(_ line: Int = #line, _ fileName: String = #file, _ funcName: String = #function) {
55 | debug(msg: "MARK", line, fileName, funcName, type: .MARK)
56 | }
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/BaseViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseViewController.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 13/03/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class BaseViewController: UIViewController {
12 |
13 | @IBAction func pressBack(_ sender: UIButton) {
14 | navigationController?.popViewController(animated: true)
15 | }
16 |
17 | func showNetworkUnavailableAlert() {
18 | PopUpHelper.showErrorAlert(from: self, type: .networkUnavailable)
19 | }
20 |
21 | func showWrongAlert() {
22 | PopUpHelper.showErrorAlert(from: self, type: .common, title: "Error", descript: "Something went wrong", buttonText: "Retry")
23 | }
24 | }
25 |
26 | // MARK: - Keyboard Notification Selectors
27 |
28 | extension BaseViewController {
29 |
30 | func keyboardSubscribe() {
31 | NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
32 | NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
33 | }
34 |
35 | func keyboardUnsubscribe() {
36 | NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
37 | NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
38 | }
39 |
40 | @objc func keyboardWillShow(_ notification: Notification) {
41 | if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
42 | if self.view.frame.origin.y == 0{
43 | self.view.frame.origin.y -= keyboardSize.height
44 | }
45 | }
46 | }
47 |
48 | @objc func keyboardWillHide(_ notification: Notification) {
49 | if self.view.frame.origin.y != 0 {
50 | self.view.frame.origin.y = 0
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewModel/Login/RegistrationViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RegistrationViewModel.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 19/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 | import RxCocoa
12 |
13 | class RegistrationViewModel {
14 |
15 | let usernameViewModel = UsernameViewModel()
16 | let emailViewModel = EmailViewModel()
17 | let passwordViewModel = PasswordViewModel()
18 |
19 | let disposeBag = DisposeBag()
20 |
21 | // MARK: - Public methods
22 |
23 | func isValidAllData() -> Observable {
24 |
25 | return Observable.combineLatest(usernameViewModel.validateCredentials(), emailViewModel.validateCredentials(), passwordViewModel.validateCredentials(), usernameViewModel.data, emailViewModel.data, passwordViewModel.data) { $0 && $1 && $2 && $3.count > 0 && $4.count > 0 && $5.count > 0 }
26 | .distinctUntilChanged()
27 | }
28 |
29 | func registration() -> Observable {
30 |
31 | return Observable.create{ observer in
32 |
33 | guard CommonHelper.checkNetworkStatus() else {
34 | observer.onNext(.networkUnavailable)
35 | return Disposables.create()
36 | }
37 |
38 | let userData: APIParameters = ["username": self.usernameViewModel.data.value,
39 | "password": self.passwordViewModel.data.value,
40 | "email": self.emailViewModel.data.value]
41 |
42 | NetworkManager.shared.register(with: userData)
43 | .subscribe(onNext: { _ in
44 | observer.onNext(.success)
45 | }, onError: { error in
46 | Logger.error(msg: "Registration Error: \(error)")
47 | observer.onNext( error as! ResponseState )
48 | }).disposed(by: self.disposeBag)
49 |
50 | return Disposables.create()
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewModel/Login/AuthController/AuthFacadeController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuthFacadeController.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 15/03/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 | import RxCocoa
12 |
13 | typealias authComplitionData = (Result) -> ()
14 |
15 | protocol Authorizable {
16 | func authorize(complition: @escaping authComplitionData)
17 | }
18 |
19 | enum SocialNetwork {
20 | case Facebook
21 | case Vkontakte
22 | case Google
23 | }
24 |
25 | struct AuthData {
26 | var name = ""
27 | var email = ""
28 | var age = 0
29 | var location = ""
30 | var token = ""
31 |
32 | init(name: String = "", email: String = "", age: Int = 0, location: String = "", token: String) {
33 | self.name = name
34 | self.email = email
35 | self.age = age
36 | self.location = location
37 | self.token = token
38 | }
39 | }
40 |
41 | // Facade Login Controller
42 | class AuthFacadeController: NSObject {
43 |
44 | static func doLogin(type: SocialNetwork) -> Observable {
45 | let auth = setupAuthType(type)
46 |
47 | return Observable.create{observer in
48 | auth.authorize(complition: { result in
49 | switch result {
50 | case .error(let error):
51 | observer.onNext(.invalidStatusCode(code: error as! ResponseCode))
52 | case .success:
53 | observer.onNext(.success)
54 | }
55 | observer.onCompleted()
56 | })
57 |
58 | return Disposables.create()
59 | }
60 | }
61 |
62 |
63 | static func setupAuthType(_ type: SocialNetwork) -> Authorizable {
64 |
65 | switch type {
66 | case .Facebook:
67 | return FBAuth()
68 | case .Vkontakte:
69 | return VKAuth()
70 | case .Google:
71 | return GoogleAuth()
72 | }
73 | }
74 |
75 | }
76 |
77 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewModel/Login/ValidationViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ValidationViewModel.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 19/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 | import RxCocoa
12 |
13 | protocol ValidationViewModel {
14 |
15 | // Observables
16 | var data: BehaviorRelay { get set }
17 |
18 | // Validation
19 | func validateCredentials() -> Observable
20 | }
21 |
22 | // MARK: - Username Validate
23 |
24 | class UsernameViewModel : ValidationViewModel {
25 |
26 | var data = BehaviorRelay(value: "")
27 |
28 | func validateCredentials() -> Observable {
29 | return data.asObservable().map({ (username) -> Bool in
30 | let regex = "[A-Z0-9a-z._]{5,}"
31 | let passwordPredicate = NSPredicate(format: "SELF MATCHES %@", regex)
32 | return passwordPredicate.evaluate(with: username) || username.isEmpty
33 | // MARK: isEmpty needs for blue line, if textfield is empty
34 | })
35 | }
36 | }
37 |
38 | // MARK: - Email Validate
39 |
40 | class EmailViewModel : ValidationViewModel {
41 |
42 | var data = BehaviorRelay(value: "")
43 |
44 | func validateCredentials() -> Observable {
45 | return data.asObservable().map({ (email) -> Bool in
46 | let regex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
47 | let passwordPredicate = NSPredicate(format: "SELF MATCHES %@", regex)
48 | return passwordPredicate.evaluate(with: email) || email.isEmpty
49 | })
50 | }
51 | }
52 |
53 | // MARK: - Password Validate
54 |
55 | class PasswordViewModel : ValidationViewModel {
56 |
57 | var data = BehaviorRelay(value: "")
58 |
59 | func validateCredentials() -> Observable {
60 | return data.asObservable().map({ (password) -> Bool in
61 | let regex = "[A-Z0-9a-z._]{5,}"
62 | let passwordPredicate = NSPredicate(format: "SELF MATCHES %@", regex)
63 | return passwordPredicate.evaluate(with: password) || password.isEmpty
64 | })
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewModel/Login/LoginViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginViewModel.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 12/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RxSwift
11 | import RxCocoa
12 |
13 | class LoginViewModel {
14 |
15 | let usernameViewModel = UsernameViewModel()
16 | let passwordViewModel = PasswordViewModel()
17 |
18 | let disposeBag = DisposeBag()
19 |
20 | // MARK: - Public methods
21 |
22 | func isUserNameAndPasswordValid() -> Observable {
23 |
24 | return Observable.combineLatest(usernameViewModel.validateCredentials(), passwordViewModel.validateCredentials(), usernameViewModel.data, passwordViewModel.data) { $0 && $1 && $2.count > 0 && $3.count > 0 }
25 | .distinctUntilChanged()
26 | }
27 |
28 | func socialNetworkAuth(with socialNetworkType: SocialNetwork) -> Observable {
29 | return AuthFacadeController.doLogin(type: socialNetworkType)
30 | }
31 |
32 | func serverNativeLogin() -> Observable {
33 |
34 | return Observable.create{ observer in
35 |
36 | guard CommonHelper.checkNetworkStatus() else {
37 | observer.onNext(.networkUnavailable)
38 | return Disposables.create()
39 | }
40 |
41 | let credentials: APIParameters = ["username": self.usernameViewModel.data.value,
42 | "password": self.passwordViewModel.data.value]
43 |
44 | NetworkManager.shared.doLogin(with: credentials)
45 | .subscribe(onNext: { loginData in
46 | try? RealmManager.shared.storeObject(loginData)
47 | UserManager.shared.userToken = loginData.token
48 | UserManager.shared.userName = loginData.user?.username ?? "Unknown"
49 | observer.onNext(.success)
50 | }, onError: { error in
51 | Logger.error(msg: "Login Error: \(error)")
52 | observer.onNext(error as! ResponseState)
53 | }).disposed(by: self.disposeBag)
54 |
55 | return Disposables.create()
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/Login/WelcomePage/WelcomeDataSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WelcomeDataSource.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 13/03/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | private struct Page {
12 | let title: String
13 | let description: String
14 | let imageName: String
15 | }
16 |
17 | class WelcomeDataSource: NSObject, UICollectionViewDataSource {
18 |
19 | private var pages: [Page] = []
20 |
21 | var countPages: Int {
22 | return pages.count
23 | }
24 |
25 | override init() {
26 | super.init()
27 |
28 | addPage(title: "Get Fun", desc: "Enjoy playing a quiz with friends or random users!", image: "fun")
29 | addPage(title: "Game Type", desc: "Quick play on the road or keen tournament!", image: "game_type")
30 | addPage(title: "Creativity", desc: "Create your own questions for everyone!", image: "creativity")
31 | addPage(title: "Be smarter", desc: "It’s never too late to learn!", image: "mind")
32 | addPage(title: "Good luck", desc: "", image: "goodgame")
33 | }
34 |
35 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
36 | return pages.count
37 | }
38 |
39 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
40 |
41 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "WelcomePageCell", for: indexPath) as! WelcomePageCell
42 |
43 | if indexPath.row == pages.count - 1 {
44 | cell.configLastPage()
45 | cell.callback = {
46 | UserDefaults.standard.isShowWelcomeScreen = true
47 | Router.rootLoginVC()
48 | }
49 | }
50 |
51 | cell.titleLabel.text = pages[indexPath.row].title.uppercased()
52 | cell.descriptionLabel.text = pages[indexPath.row].description
53 | cell.imageView.image = UIImage(named: pages[indexPath.row].imageName)
54 |
55 | return cell
56 | }
57 |
58 | func addPage(title: String, desc: String, image: String) {
59 | pages.append(Page(title: title, description: desc, imageName: image))
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewModel/Login/AuthController/VKAuth.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VKAuth.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 15/03/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyVK
11 |
12 | class VKAuth: SwiftyVKDelegate, Authorizable {
13 |
14 | let VK_APP_ID:String = "6288855"
15 |
16 | init() {
17 | VK.setUp(appId: VK_APP_ID, delegate: self)
18 | }
19 |
20 | func authorize(complition: @escaping authComplitionData) {
21 | if (VK.sessions.default.state != .authorized) {
22 | VK.sessions.default.logIn(
23 | onSuccess: { info in
24 | if let token = info["access_token"] {
25 | let authData = AuthData(token: token)
26 | complition(.success(authData))
27 | } else {
28 | complition(.error(nil))
29 | }
30 | },
31 | onError: { error in
32 | complition(.error(error))
33 | }
34 | )
35 | } else {
36 | VK.sessions.default.logOut()
37 |
38 | authorize(complition: complition)
39 | }
40 | }
41 |
42 | func vkNeedsScopes(for sessionId: String) -> Scopes {
43 | // Called when SwiftyVK attempts to get access to user account
44 | // Should return a set of permission scopes
45 | return [.email]
46 | }
47 |
48 | func vkNeedToPresent(viewController: VKViewController) {
49 | // Called when SwiftyVK wants to present UI (e.g. webView or captcha)
50 | // Should display given view controller from current top view controller
51 | }
52 |
53 | func vkTokenCreated(for sessionId: String, info: [String : String]) {
54 | // Called when user grants access and SwiftyVK gets new session token
55 | // Can be used to run SwiftyVK requests and save session data
56 | }
57 |
58 | func vkTokenUpdated(for sessionId: String, info: [String : String]) {
59 | // Called when existing session token has expired and successfully refreshed
60 | // You don't need to do anything special here
61 | }
62 |
63 | func vkTokenRemoved(for sessionId: String) {
64 | // Called when user was logged out
65 | // Use this method to cancel all SwiftyVK requests and remove session data
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/QuestionTypeListViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // QuestionTypeListViewController.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 26/03/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import RxSwift
11 | import RxCocoa
12 |
13 | class QuestionTypeListViewController: BaseViewController, UITableViewDelegate {
14 |
15 | @IBOutlet weak var tableView: UITableView!
16 |
17 | let qiestionTypeItems: BehaviorRelay<[String]> = BehaviorRelay(value: ["", "All", "Sport", "Animals", "Politics", "Programming"])
18 | let disposeBag = DisposeBag()
19 | var type = PublishSubject()
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 |
24 | qiestionTypeItems.asObservable()
25 | .bind(to: tableView.rx.items) { (tableView, row, element) in
26 | let indexPath = IndexPath(row: row, section: 0)
27 |
28 | if row == 0 {
29 | let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionCustomCell", for: indexPath) as! QuestionCustomCell
30 | cell.type.subscribe(onNext: { [weak self] type in
31 | self?.type.onNext(type)
32 | }).disposed(by: self.disposeBag)
33 | return cell
34 | }
35 | else {
36 | let cell = tableView.dequeueReusableCell(withIdentifier: "QuestionTypeCell", for: indexPath) as! QuestionTypeCell
37 | cell.nameLabel?.text = element
38 | cell.type.subscribe(onNext: { [weak self] type in
39 | self?.type.onNext(type)
40 | }).disposed(by: self.disposeBag)
41 | return cell
42 | }
43 | }
44 | .disposed(by: disposeBag)
45 | }
46 | }
47 |
48 | class QuestionTypeCell: UITableViewCell {
49 |
50 | @IBOutlet weak var nameLabel: UILabel!
51 | let type = PublishSubject()
52 |
53 | @IBAction func chooseType(_ sender: Any) {
54 | self.type.onNext(nameLabel?.text ?? "Nil")
55 | }
56 | }
57 |
58 | class QuestionCustomCell: UITableViewCell {
59 |
60 | @IBOutlet weak var textField: IBTextField!
61 | let type = PublishSubject()
62 |
63 | @IBAction func chooseType(_ sender: Any) {
64 | self.type.onNext(textField?.text ?? "Nil")
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/PrepareGame/RandomOpponentViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RandomOpponentViewController.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 25/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import RxCocoa
11 | import RxSwift
12 |
13 | class RandomOpponentViewController: BaseViewController {
14 |
15 | @IBOutlet weak var activityIndicator: ActivityIndicatorView!
16 | @IBOutlet weak var mainView: IBView!
17 |
18 | var searchUserObservable: Observable = .empty()
19 | var questionCategory = "All"
20 | let disposeBag = DisposeBag()
21 |
22 | override func viewDidLoad() {
23 | super.viewDidLoad()
24 |
25 | searchUserObservable
26 | .subscribe(onNext: { (opponent) in
27 | Logger.debug(msg: opponent.username)
28 | }, onError: { [unowned self] (error) in
29 | Logger.error(msg: error)
30 | switch error as! ResponseState {
31 | case .success: break
32 | case .networkUnavailable:
33 | self.showNetworkUnavailableAlert()
34 | case .invalidStatusCode:
35 | self.showWrongAlert()
36 | }
37 | }).disposed(by: disposeBag)
38 | }
39 |
40 | override func viewWillAppear(_ animated: Bool) {
41 |
42 | activityIndicator.startAnimating()
43 |
44 | let viewPosition = mainView.frame.origin.y
45 | mainView.frame.origin.y = 0
46 |
47 | UIView.springAnimate(animateCompletion: {
48 | self.mainView.frame.origin.y = viewPosition
49 | })
50 | }
51 |
52 | @IBAction func goWaitingToMainScreen(_ sender: Any) {
53 |
54 | let navigationController = self.presentingViewController as? UINavigationController
55 |
56 | self.dismiss(animated: true) {
57 | let _ = navigationController?.popToRootViewController(animated: true)
58 | }
59 | }
60 |
61 | func goToGameInformations() {
62 |
63 | let navigationController = self.presentingViewController as? UINavigationController
64 | self.dismiss(animated: false) {
65 | let gameVC = CommonHelper.loadViewController(from: "Main", named: "GameInfoSB") as! GameInformationViewController
66 | navigationController?.pushViewController(gameVC, animated: true)
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/Settings/SettingsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsViewController.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 26/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SettingsViewController: BaseViewController {
12 |
13 | // MARK: - Outlets
14 |
15 | @IBOutlet weak var profileImage: IBImageView!
16 | @IBOutlet weak var usernameLabel: UILabel!
17 | @IBOutlet weak var backgroundSoundSwitch: UISwitch!
18 | @IBOutlet weak var notificationsSwitch: UISwitch!
19 | @IBOutlet weak var saveQuestionsSwitch: UISwitch!
20 |
21 | // MARK: - Lifecycle
22 |
23 | override func viewDidLoad() {
24 | super.viewDidLoad()
25 | usernameLabel.text = UserManager.shared.curUser.username
26 | setSwitchPositions()
27 | }
28 |
29 | // MARK: - Actions
30 |
31 | @IBAction func switchAction(_ sender: UISwitch) {
32 | do {
33 | try RealmManager.shared.updateSettings(sound: backgroundSoundSwitch.isOn, notify: notificationsSwitch.isOn, saveQuestion: saveQuestionsSwitch.isOn, payButton: true)
34 | } catch {
35 | Logger.error(msg: "Realm Error: \(error.localizedDescription) \nUnable to update storage")
36 | }
37 | }
38 |
39 | @IBAction func resetGameProgress(_ sender: Any) {
40 | // TODO: Reset Game Statistics logic
41 | }
42 |
43 | @IBAction func logOut(_ sender: Any) {
44 | PopUpHelper.showConfirmView(from: self, title: "Log Out", descript: "Are you sure you want to quit?", negativeButton: "Cancel", positiveButton: "Yes", completion: {
45 | Logger.debug(msg: "Press Cancel logout")
46 | }) {
47 | self.doLogOut()
48 | }
49 | }
50 |
51 | // MARK: - Private
52 |
53 | private func setSwitchPositions() {
54 | guard let settings = RealmManager.shared.getObjectByID(SettingsModel.self, id: 1) else {
55 | return
56 | }
57 |
58 | // FIXME: Turn On at start app
59 | //AudioManager.shared.playMusic()
60 | backgroundSoundSwitch.isOn = settings.backgroundSound
61 | notificationsSwitch.isOn = settings.notifications
62 | saveQuestionsSwitch.isOn = settings.saveQuestions
63 | }
64 |
65 | private func doLogOut() {
66 | Logger.info(msg: "Logout process...")
67 | UserManager.shared.logOut {
68 | $0 ? Router.rootLoginVC() : Logger.error(msg: "Error. Unable to logout")
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/NavigationViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationViewController.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 13/03/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | import UIKit
12 |
13 | final class SwipeNavigationController: UINavigationController {
14 |
15 | // MARK: - Private Properties
16 |
17 | fileprivate var duringPushAnimation = false
18 |
19 | // MARK: - Lifecycle
20 |
21 | override init(rootViewController: UIViewController) {
22 | super.init(rootViewController: rootViewController)
23 | }
24 |
25 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
26 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
27 | delegate = self
28 | }
29 |
30 | required init?(coder aDecoder: NSCoder) {
31 | super.init(coder: aDecoder)
32 | delegate = self
33 | }
34 |
35 | override func viewDidLoad() {
36 | super.viewDidLoad()
37 | interactivePopGestureRecognizer?.delegate = self
38 | Logger.info(msg: "Loading Custom Navigation Controller")
39 | }
40 |
41 | deinit {
42 | delegate = nil
43 | interactivePopGestureRecognizer?.delegate = nil
44 | Logger.info(msg: "Deinit Navigation Controller")
45 | }
46 |
47 | // MARK: - Overrides
48 |
49 | override func pushViewController(_ viewController: UIViewController, animated: Bool) {
50 | duringPushAnimation = true
51 |
52 | super.pushViewController(viewController, animated: animated)
53 | }
54 | }
55 |
56 | // MARK: - UINavigationControllerDelegate
57 |
58 | extension SwipeNavigationController: UINavigationControllerDelegate {
59 |
60 | func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
61 | guard let swipeNavigationController = navigationController as? SwipeNavigationController else { return }
62 |
63 | swipeNavigationController.duringPushAnimation = false
64 | }
65 | }
66 |
67 | // MARK: - UIGestureRecognizerDelegate
68 |
69 | extension SwipeNavigationController: UIGestureRecognizerDelegate {
70 |
71 | func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
72 | guard gestureRecognizer == interactivePopGestureRecognizer else {
73 | return true
74 | }
75 |
76 | return viewControllers.count > 1 && duringPushAnimation == false
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleURLTypes
20 |
21 |
22 | CFBundleURLSchemes
23 |
24 | fb615939592191095
25 |
26 |
27 |
28 | CFBundleTypeRole
29 | Editor
30 | CFBundleURLSchemes
31 |
32 | vk1234567890
33 |
34 |
35 |
36 | LSApplicationQueriesSchemes
37 |
38 | vkauthorize
39 | vk6900718
40 |
41 | CFBundleVersion
42 | 1
43 | FacebookAppID
44 | 615939592191095
45 | FacebookDisplayName
46 | QuizChallenge
47 | LSRequiresIPhoneOS
48 |
49 | NSCameraUsageDescription
50 | To set an image profile, you must provide access to the camera.
51 | NSPhotoLibraryUsageDescription
52 | To select an image profile, you must provide access to the gallery.
53 | UILaunchStoryboardName
54 | LaunchScreen
55 | UIMainStoryboardFile
56 | Login
57 | UIRequiredDeviceCapabilities
58 |
59 | armv7
60 |
61 | UIStatusBarStyle
62 | UIStatusBarStyleLightContent
63 | UISupportedInterfaceOrientations
64 |
65 | UIInterfaceOrientationPortrait
66 |
67 | UISupportedInterfaceOrientations~ipad
68 |
69 | UIInterfaceOrientationPortrait
70 | UIInterfaceOrientationPortraitUpsideDown
71 | UIInterfaceOrientationLandscapeLeft
72 | UIInterfaceOrientationLandscapeRight
73 |
74 | UIViewControllerBasedStatusBarAppearance
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/QuizChallenge/View/AnswerCreateView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AnswerCreateView.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 28/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class AnswerCreateView: UIView {
12 |
13 | var view: UIView!
14 | var isFillText = false
15 |
16 | @IBOutlet weak var textView: UITextView!
17 | @IBInspectable var isCorrect: Bool = false {
18 | didSet {
19 | textView.text = isCorrect ? "Correct answer" : "Incorrect answer"
20 | textView.textColor = UIColor.lightGray
21 | view.layer.borderColor = isCorrect ? UIColor.lightRoyal.cgColor : UIColor.lightRed.cgColor
22 | }}
23 |
24 | // MARK: - Setup view
25 |
26 | override init(frame: CGRect) {
27 | super.init(frame: frame)
28 | nibSetup()
29 | }
30 |
31 | required init?(coder aDecoder: NSCoder) {
32 | super.init(coder: aDecoder)
33 | nibSetup()
34 | }
35 |
36 | // MARK: Load Nib
37 |
38 | private func nibSetup() {
39 |
40 | view = getNibView()
41 | view.frame = bounds
42 | uiSetup()
43 | addSubview(view)
44 | }
45 |
46 | // MARK: Prepare UI
47 |
48 | private func uiSetup() {
49 | let insets = UIEdgeInsets(top: textView.frame.height / 2 - 5, left: 0, bottom: 5, right: 0)
50 | textView.textContainerInset = insets
51 | textView.delegate = self
52 |
53 | view.backgroundColor = .clear
54 | view.layer.cornerRadius = 15
55 | view.layer.borderColor = UIColor.lightRed.cgColor
56 | view.layer.borderWidth = 2
57 | view.layer.shadowRadius = 8
58 | view.layer.shadowOpacity = 0.5
59 | view.layer.shadowColor = UIColor.lightGray.cgColor
60 | view.layer.shadowOffset = CGSize(width: 5, height: 5)
61 | }
62 | }
63 |
64 | // MARK: - UITextViewDelegate
65 | extension AnswerCreateView: UITextViewDelegate {
66 |
67 | func textViewDidBeginEditing(_ textView: UITextView) {
68 | if textView.textColor == UIColor.lightGray {
69 | textView.text = nil
70 | textView.textColor = UIColor.black
71 | }
72 | isFillText = true
73 | }
74 |
75 | func textViewDidEndEditing(_ textView: UITextView) {
76 | if textView.text.isEmpty {
77 | resetTextView()
78 | }
79 | }
80 |
81 | func resetTextView() {
82 | textView.text = isCorrect ? "Correct answer" : "Incorrect answer"
83 | textView.textColor = UIColor.lightGray
84 | isFillText = false
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@2x.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@3x.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "size" : "40x40",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-40x40@2x.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@3x.png",
37 | "scale" : "3x"
38 | },
39 | {
40 | "size" : "60x60",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-60x60@2x.png",
43 | "scale" : "2x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@3x.png",
49 | "scale" : "3x"
50 | },
51 | {
52 | "size" : "20x20",
53 | "idiom" : "ipad",
54 | "filename" : "Icon-App-20x20@1x.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@2x-1.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "29x29",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-29x29@1x.png",
67 | "scale" : "1x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@2x-1.png",
73 | "scale" : "2x"
74 | },
75 | {
76 | "idiom" : "ipad",
77 | "size" : "40x40",
78 | "scale" : "1x"
79 | },
80 | {
81 | "idiom" : "ipad",
82 | "size" : "40x40",
83 | "scale" : "2x"
84 | },
85 | {
86 | "idiom" : "ipad",
87 | "size" : "76x76",
88 | "scale" : "1x"
89 | },
90 | {
91 | "idiom" : "ipad",
92 | "size" : "76x76",
93 | "scale" : "2x"
94 | },
95 | {
96 | "idiom" : "ipad",
97 | "size" : "83.5x83.5",
98 | "scale" : "2x"
99 | },
100 | {
101 | "size" : "1024x1024",
102 | "idiom" : "ios-marketing",
103 | "filename" : "ItunesArtwork@2x.png",
104 | "scale" : "1x"
105 | }
106 | ],
107 | "info" : {
108 | "version" : 1,
109 | "author" : "xcode"
110 | }
111 | }
--------------------------------------------------------------------------------
/QuizChallenge/Supporting Files/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 18/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import FBSDKLoginKit
11 | import SwiftyVK
12 |
13 | @UIApplicationMain
14 | class AppDelegate: UIResponder, UIApplicationDelegate {
15 |
16 | var window: UIWindow?
17 |
18 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
19 |
20 | Router.setupRootVC()
21 | FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
22 |
23 | return true
24 | }
25 |
26 | func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
27 |
28 | let sourceApp = options[.sourceApplication] as? String
29 | VK.handle(url: url, sourceApplication: sourceApp)
30 |
31 | FBSDKApplicationDelegate.sharedInstance()?.application(app, open: url, options: options)
32 |
33 | return true
34 | }
35 |
36 | func applicationWillResignActive(_ application: UIApplication) {
37 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
38 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
39 | }
40 |
41 | func applicationDidEnterBackground(_ application: UIApplication) {
42 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
43 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
44 | }
45 |
46 | func applicationWillEnterForeground(_ application: UIApplication) {
47 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
48 | }
49 |
50 | func applicationDidBecomeActive(_ application: UIApplication) {
51 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
52 | }
53 |
54 | func applicationWillTerminate(_ application: UIApplication) {
55 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
56 | }
57 |
58 |
59 | }
60 |
61 |
--------------------------------------------------------------------------------
/QuizChallenge/Services/Storage/RealmManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RealmManager.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 25/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import RealmSwift
11 |
12 | class RealmManager {
13 |
14 | // MARK: - Realm Configuration
15 |
16 | static let shared = RealmManager()
17 |
18 | init() {
19 | realmConfiguration()
20 | }
21 |
22 | func realmConfiguration() {
23 | let config = Realm.Configuration(
24 | schemaVersion: 1,
25 | migrationBlock: { migration, oldSchemaVersion in
26 | switch oldSchemaVersion {
27 | case 1:
28 | break
29 | default:
30 | break
31 | }
32 | })
33 |
34 | Realm.Configuration.defaultConfiguration = config
35 | }
36 |
37 | // MARK: - Main methods (store, remove, get - Object)
38 | func storeObject(_ object: T, withUpdate: Bool = true) throws {
39 | try execute({ realm in
40 | try realm.write {
41 | realm.add(object, update: withUpdate)
42 | }
43 | })
44 | }
45 |
46 | func deleteObject(_ object: T) throws {
47 | try execute({ realm in
48 | try realm.write {
49 | realm.delete(object)
50 | }
51 | })
52 | }
53 |
54 | func getObjectByID(_ type: T.Type, id: Int) -> T? {
55 | if let obj = try? Realm().object(ofType: type, forPrimaryKey: id) {
56 | return obj
57 | }
58 |
59 | return nil
60 | }
61 |
62 | func getObjects(_ type: T.Type) -> [T]? {
63 | if let objs = try? Realm().objects(type).map{ $0 } {
64 | return Array(objs)
65 | }
66 |
67 | return nil
68 | }
69 |
70 | func clearAllData() throws {
71 | try execute({ realm in
72 | try realm.write {
73 | realm.deleteAll()
74 | }
75 | })
76 | }
77 |
78 | func updateSettings(sound: Bool, notify: Bool, saveQuestion: Bool, payButton: Bool) throws {
79 | try execute({ realm in
80 | try realm.write {
81 | let settings = RealmManager.shared.getObjects(SettingsModel.self)?.first
82 | settings?.backgroundSound = sound
83 | settings?.notifications = notify
84 | settings?.saveQuestions = saveQuestion
85 | settings?.buyPremiumEnabled = payButton
86 | }
87 | })
88 | }
89 |
90 | // MARK: - Other supporting methods
91 | private func execute(_ completion: (_ realmObject: Realm) throws -> Void) throws {
92 | try completion(Realm())
93 | }
94 |
95 | func realmUrl() -> URL {
96 | return Realm.Configuration.defaultConfiguration.fileURL!
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/QuizChallenge/Helpers/Categories.json:
--------------------------------------------------------------------------------
1 | {
2 | "type_questions":[
3 | {
4 | "name": "2018",
5 | "types": [
6 | {"name":"Mix", "image":"1"},
7 | {"name":"Events", "image":"2"},
8 | {"name":"World Cup 2018", "image":"3"}
9 | ]
10 | },
11 | {
12 | "name": "Sport",
13 | "types": [
14 | {"name":"Mix", "image":"6"},
15 | {"name":"World Cup 2018", "image":"5"},
16 | {"name":"Football", "image":"4"},
17 | {"name":"Hockey", "image":"3"},
18 | {"name":"MMA", "image":"2"},
19 | {"name":"F1", "image":"1"},
20 | {"name":"Biatlon", "image":"7"},
21 | {"name":"Running", "image":"10"}
22 | ]
23 | },
24 | {
25 | "name": "Movies & Entertainment",
26 | "types": [
27 | {"name":"Mix", "image":"2"},
28 | {"name":"TV Series", "image":"3"},
29 | {"name":"Hollywood", "image":"4"},
30 | {"name":"Russian films", "image":"8"}
31 | ]
32 | },
33 | {
34 | "name": "Travel & Tourism",
35 | "types": [
36 | {"name":"All World", "image":"9"},
37 | {"name":"India", "image":"8"},
38 | {"name":"Russia", "image":"7"},
39 | {"name":"Mountains", "image":"6"}
40 | ]
41 | },
42 | {
43 | "name": "Songs & Music",
44 | "types": [
45 | {"name":"Mix", "image":"1"},
46 | {"name":"Songs 2018", "image":"3"},
47 | {"name":"Other", "image":"5"}
48 | ]
49 | },
50 | {
51 | "name": "IT & Technology",
52 | "types": [
53 | {"name":"Mix", "image":"4"},
54 | {"name":"Computers", "image":"4"},
55 | {"name":"Mobile", "image":"3"},
56 | {"name":"Other", "image":"2"}
57 | ]
58 | },
59 | {
60 | "name": "Food & Kitchen",
61 | "types": [
62 | {"name":"Mix", "image":"1"},
63 | {"name":"Vegetables", "image":"1"},
64 | {"name":"Fruits", "image":"2"},
65 | {"name":"Mushrooms", "image":"2"},
66 | {"name":"Russian", "image":"3"}
67 | ]
68 | },
69 | {
70 | "name": "Food & Kitchen",
71 | "types": [
72 | {"name":"Mix", "image":"1"},
73 | {"name":"Vegetables", "image":"1"},
74 | {"name":"Fruits", "image":"2"},
75 | {"name":"Mushrooms", "image":"2"},
76 | {"name":"Russian", "image":"3"}
77 | ]
78 | },
79 | {
80 | "name": "IT & Technology",
81 | "types": [
82 | {"name":"Mix", "image":"4"},
83 | {"name":"Computers", "image":"4"},
84 | {"name":"Mobile", "image":"3"},
85 | {"name":"Other", "image":"2"}
86 | ]
87 | }
88 | ]
89 | }
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/PrepareGame/FindUserViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FindUserViewController.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 25/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import RxSwift
11 |
12 | class FindUserViewController: BaseViewController {
13 |
14 | // MARK: - UI Outlets
15 |
16 | @IBOutlet weak var userSearchTextField: IBTextField!
17 | @IBOutlet weak var usernameSearchButton: IBButton!
18 |
19 | let disposeBag = DisposeBag()
20 | var gameInfo = PrepareGameInfo()
21 | var viewModel = PrepareGameViewModel()
22 |
23 | // MARK: - Lifecycle
24 |
25 | override func viewDidLoad() {
26 | super.viewDidLoad()
27 | setupRx()
28 | }
29 |
30 | // MARK: - Setup RX
31 |
32 | fileprivate func setupRx() {
33 |
34 | userSearchTextField.rx.controlEvent([.editingDidEndOnExit]).subscribe { _ in
35 | self.userSearchTextField.becomeFirstResponder()
36 | }.disposed(by: disposeBag)
37 |
38 | let usernameValid = userSearchTextField.rx.text.orEmpty
39 | .map { !$0.isEmpty }
40 | .share(replay: 1)
41 |
42 | usernameValid
43 | .bind(onNext: { [unowned self] (isValid) in
44 | self.usernameSearchButton.isEnabled = isValid
45 | self.usernameSearchButton.setTitleColor(isValid ? UIColor(named: "LightBlue") : UIColor.lightGray, for: .normal)
46 | })
47 | .disposed(by: disposeBag)
48 |
49 | // Tap Button
50 | usernameSearchButton.rx.tap
51 | .flatMap { [unowned self] _ -> Observable in
52 | // Demo
53 | let opponentModalVC = CommonHelper.loadViewController(named: "OpponentScreen", isModal: true) as! OpponentViewController
54 | self.present(opponentModalVC, animated: true, completion: nil)
55 |
56 | return self.viewModel.searchUserBy(name: self.userSearchTextField.text!)
57 | }
58 | .subscribe(onNext: { [unowned self] (opponent) in
59 | let opponentModalVC = CommonHelper.loadViewController(named: "OpponentScreen", isModal: true) as! OpponentViewController
60 | self.present(opponentModalVC, animated: true, completion: nil)
61 |
62 | }, onError: { [unowned self] (error) in
63 | switch error as! ResponseState {
64 | case .success: break
65 | case .networkUnavailable:
66 | self.showNetworkUnavailableAlert()
67 | case .invalidStatusCode:
68 | self.showWrongAlert()
69 | }
70 | }).disposed(by: disposeBag)
71 | }
72 |
73 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
74 | if segue.identifier == "RandomSearch" {
75 | if let vc = segue.destination as? RandomOpponentViewController {
76 | vc.questionCategory = gameInfo.selectCategory ?? "All"
77 | vc.searchUserObservable = viewModel.searchUserBy(name: "Debug").asObservable()
78 | // Need to make a request for active games every second
79 | }
80 | }
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/QuizChallenge/Helpers/Router.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Router.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 12/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class Router {
12 |
13 | // MARK: Func for load in AppDelegate
14 | static func setupRootVC(){
15 |
16 | var initialViewController: UIViewController?
17 |
18 | if (UserManager.shared.isLoggedIn) {
19 | initialViewController = CommonHelper.loadViewController(from: "Main", named: "MainSB") as? MainViewController
20 | } else {
21 | if UserDefaults.standard.isShowWelcomeScreen {
22 | Router.rootLoginVC()
23 | } else {
24 | initialViewController = CommonHelper.loadViewController(from: "Login", named: "WelcomeSB") as? WelcomeViewController
25 | }
26 | }
27 |
28 | if let initVC = initialViewController {
29 | let navigationController = SwipeNavigationController(rootViewController: initVC)
30 | navigationController.isNavigationBarHidden = true
31 | navigationController.navigationBar.isTranslucent = true
32 |
33 | let window = UIWindow(frame: UIScreen.main.bounds)
34 | window.backgroundColor = UIColor.white
35 | window.rootViewController = navigationController
36 | window.makeKeyAndVisible()
37 |
38 | let appDelegate = UIApplication.shared.delegate as! AppDelegate
39 | appDelegate.window = window
40 | }
41 | }
42 |
43 | // MARK: Switch root view controller with animate
44 | static func switchAnimateRootVС(_ viewController: UIViewController, duration: TimeInterval = 0.7, options: UIView.AnimationOptions = .transitionCrossDissolve, completion: (() -> Void)? = nil) {
45 |
46 | let navigationController = SwipeNavigationController(rootViewController: viewController)
47 | navigationController.navigationBar.isTranslucent = true
48 | navigationController.navigationBar.isHidden = true
49 |
50 | guard let appWindow = (UIApplication.shared.delegate as! AppDelegate).window else {
51 | completion?()
52 | return
53 | }
54 |
55 | UIView.transition(with: appWindow, duration: duration, options: options, animations: {
56 | let oldState = UIView.areAnimationsEnabled
57 | UIView.setAnimationsEnabled(false)
58 | appWindow.rootViewController = navigationController
59 | UIView.setAnimationsEnabled(oldState)
60 | }) { _ in
61 | completion?()
62 | }
63 | }
64 |
65 | // MARK: Open login vc as a root
66 | static func rootLoginVC() {
67 | if let loginViewController: LoginViewController = CommonHelper.loadViewController(from: "Login", named: "LoginSB") as? LoginViewController {
68 | loginViewController.setViewModel(LoginViewModel())
69 | Router.switchAnimateRootVС(loginViewController)
70 | } else {
71 | Logger.error(msg: "Some error with opening the login screen")
72 | }
73 | }
74 |
75 | // MARK: Open main vc as a root
76 | static func rootMainVC() {
77 | if let mainViewController = CommonHelper.loadViewController(from: "Main", named: "MainSB") as? MainViewController {
78 | Router.switchAnimateRootVС(mainViewController)
79 | } else {
80 | Logger.error(msg: "Some error with opening the main screen")
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/QuizChallenge/Helpers/Extensions/UIView+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+Extension.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 04/02/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIView {
12 |
13 | private func flipViews(fromView: UIView, toView: UIView) {
14 |
15 | let transitionOptions: UIView.AnimationOptions = [.transitionFlipFromRight, .showHideTransitionViews]
16 |
17 | UIView.transition(from: fromView, to: toView, duration: 1, options: transitionOptions) { _ in
18 | fromView.isUserInteractionEnabled = false
19 | toView.isUserInteractionEnabled = true
20 | }
21 | }
22 |
23 | func flip(from: UIView? = nil, to: UIView) {
24 | flipViews(fromView: from ?? self, toView: to)
25 | }
26 |
27 | func shake() {
28 | let shake: CABasicAnimation = CABasicAnimation(keyPath: "position")
29 | shake.duration = 0.1
30 | shake.repeatCount = 2
31 | shake.autoreverses = true
32 |
33 | let fromPoint: CGPoint = CGPoint(x: self.center.x - 5, y: self.center.y)
34 | let fromValue: NSValue = NSValue(cgPoint: fromPoint)
35 |
36 | let toPoint: CGPoint = CGPoint(x: self.center.x + 5, y: self.center.y)
37 | let toValue: NSValue = NSValue(cgPoint: toPoint)
38 |
39 | shake.fromValue = fromValue
40 | shake.toValue = toValue
41 | self.layer.add(shake, forKey: "position")
42 | }
43 |
44 | func addShadowAndRadius(offset: CGSize, color: UIColor, radius: CGFloat, opacity: Float, cornerRadius: CGFloat) {
45 | layer.masksToBounds = false
46 | layer.shadowOffset = offset
47 | layer.shadowColor = color.cgColor
48 | layer.shadowRadius = radius
49 | layer.shadowOpacity = opacity
50 | layer.cornerRadius = cornerRadius
51 |
52 | let backgroundCGColor = backgroundColor?.cgColor
53 | backgroundColor = nil
54 | layer.backgroundColor = backgroundCGColor
55 | }
56 | }
57 |
58 | extension UIView {
59 |
60 | // Spring Animation
61 | static func springAnimate(duration: TimeInterval = 1.0, animateCompletion: @escaping () -> Void, finishCompletion: ((Bool) -> Void)? = nil) {
62 | UIView.animate(withDuration: duration, delay: 0.0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: animateCompletion, completion: finishCompletion)
63 | }
64 | }
65 |
66 | // MARK: Create view from Nib
67 |
68 | extension UIView {
69 |
70 | func getNibView(named nibName: String? = nil) -> UIView {
71 | return createNibView(named: nibName)
72 | }
73 |
74 | func addSubviewFromNib(named nibName: String? = nil) {
75 |
76 | let view = createNibView(named: nibName)
77 | view.frame = self.bounds
78 | addSubview(view)
79 | }
80 |
81 | private func createNibView(named nibName: String? = nil) -> UIView {
82 |
83 | let bundle = Bundle(for: self.classForCoder)
84 |
85 | if let nibName = nibName {
86 | return bundle.loadNibNamed(nibName, owner: self, options: nil)!.last as! UIView
87 | } else {
88 | let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
89 | return nib.instantiate(withOwner: self, options: nil).first as! UIView
90 | }
91 | }
92 |
93 | class func fromNib() -> T {
94 | return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewModel/Login/AuthController/FBAuth.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FBAuth.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 16/03/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import FBSDKCoreKit
11 | import FBSDKLoginKit
12 |
13 |
14 | class FBAuth: Authorizable {
15 |
16 | // MARK: - Facebook
17 | func authorize(complition: @escaping authComplitionData) {
18 |
19 | let facebookLogin: FBSDKLoginManager = FBSDKLoginManager()
20 | facebookLogin.loginBehavior = FBSDKLoginBehavior.native
21 | facebookLogin.logOut() // logout with old credentials
22 |
23 | facebookLogin.logIn(withReadPermissions: ["email"], from: UIViewController()) {
24 | (result, error) in
25 |
26 | guard error == nil else {
27 | Logger.error(msg: error!.localizedDescription)
28 | complition(.error(error))
29 | return
30 | }
31 |
32 | if let fbResult = result {
33 | if fbResult.isCancelled {
34 | Logger.error(msg: "User cancelled login.")
35 | complition(.error(nil))
36 | } else if fbResult.grantedPermissions != nil, fbResult.grantedPermissions.contains("email") {
37 | self.readFacebookLoginResult(complition: { result in
38 | complition(result)
39 | })
40 | }
41 |
42 | } else {
43 | Logger.error(msg: "Error Facebook Login. Check granted permissions")
44 | complition(.error(nil))
45 | }
46 | }
47 | }
48 |
49 | private func readFacebookLoginResult(complition: @escaping authComplitionData) {
50 |
51 | guard FBSDKAccessToken.current() == nil else {
52 | Logger.info(msg: "Token already received")
53 | return
54 | }
55 |
56 | FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, picture.type(large), email, locale, location, birthday, bio"]).start(completionHandler: { (connection, result, error) in
57 |
58 | guard error == nil, let result = result else {
59 | Logger.error(msg: "Facebook parse result is nil or an error occurred")
60 | complition(.error(nil))
61 | return
62 | }
63 |
64 | if let token = FBSDKAccessToken.current().tokenString {
65 | Logger.info(msg: "FB Access token: \(token)")
66 | Logger.info(msg: "FB Response Data: \(result)")
67 |
68 | guard let userDict = result as? [String: Any] else {
69 | complition(.error(nil))
70 | return
71 | }
72 |
73 | let name = userDict["name"] as? String ?? ""
74 | let email = userDict["email"] as? String ?? ""
75 | let age = userDict["birthday"] as? Int ?? 0 // FIXME
76 | let location = userDict["location"] as? String ?? ""
77 |
78 | // Add later
79 | if let picture = userDict["picture"] as? [String:Any] ,
80 | let imgData = picture["data"] as? [String:Any] ,
81 | let _ = imgData["url"] as? String {
82 | }
83 |
84 | let authData = AuthData(name: name, email: email, age: age, location: location, token: token)
85 | complition(.success(authData))
86 | }
87 | })
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/QuizChallenge/View/AnswerCreateView.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/QuizChallenge/Services/Network/Response.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Response.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 15/03/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum ResponseState: Error {
12 | case success
13 | case networkUnavailable
14 | case invalidStatusCode(code: ResponseCode)
15 | }
16 |
17 | public enum ResponseCode: Error {
18 |
19 | case ok
20 | case timeout
21 | case networkUnavailable
22 | case success(statusCode: Int)
23 | case badRequest
24 | case unAuthorized
25 | case forbidden
26 | case notFound
27 | case conflict
28 | case apiError(statusCode: Int)
29 | case serverError(statusCode: Int)
30 | case other(statusCode: Int)
31 | case badMappable
32 |
33 | public init(_ statusCode: Int) {
34 | switch statusCode {
35 | case 200:
36 | self = .ok
37 | case 201 ... 226:
38 | self = .success(statusCode: statusCode)
39 | case 400:
40 | self = .badRequest
41 | case 401:
42 | self = .unAuthorized
43 | case 403:
44 | self = .forbidden
45 | case 404:
46 | self = .notFound
47 | case 409:
48 | self = .conflict
49 | case 400 ... 499:
50 | self = .apiError(statusCode: statusCode)
51 | case 500 ... 527:
52 | self = .serverError(statusCode: statusCode)
53 | case -1001:
54 | self = .timeout
55 | case -1009:
56 | self = .networkUnavailable
57 | default:
58 | self = .other(statusCode: statusCode)
59 | }
60 | }
61 | }
62 |
63 | extension ResponseCode: CustomStringConvertible {
64 |
65 | public var description: String {
66 | return fullDescription
67 | }
68 |
69 | private var textDescription: String {
70 |
71 | switch self {
72 | case .timeout:
73 | return "Request timeout"
74 | case .networkUnavailable:
75 | return "Network Unavailable"
76 | case .ok:
77 | return "200 OK"
78 | case .success(let statusCode):
79 | return "\(statusCode) - Success request"
80 | case .badRequest:
81 | return "400 - Bad request. The entered values are incorrect"
82 | case .unAuthorized:
83 | return "401 - Unauthorized"
84 | case .forbidden:
85 | return "403 - Forbidden. Request is valid, but the server is refusing action"
86 | case .notFound:
87 | return "404 - Not Found"
88 | case .conflict:
89 | return "409 - Conflict"
90 | case .apiError(let statusCode):
91 | return "\(statusCode) - Client errors. Bad request"
92 | case .serverError(let statusCode):
93 | return "\(statusCode) - Server errors"
94 | case .badMappable:
95 | return "Map object error"
96 | case .other(let statusCode):
97 | return "\(statusCode) - Unknown error"
98 | }
99 | }
100 |
101 | private var emojiDescription: String {
102 | switch self {
103 | case .ok, .success:
104 | return "✅"
105 | case .unAuthorized, .forbidden:
106 | return "🔑"
107 | case .badRequest, .notFound, .conflict, .apiError, .timeout, .networkUnavailable:
108 | return "❌"
109 | default:
110 | return "🙈"
111 | }
112 | }
113 |
114 | private var fullDescription: String {
115 | return "\(textDescription) \(emojiDescription)"
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/QuizChallenge/View/HistoryGameCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HistoryGameCell.swift
3 | // QuizChallenge
4 | //
5 | // Created by A.Makarov on 11/03/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SkeletonView
11 |
12 | class HistoryGameCell: UITableViewCell {
13 |
14 | @IBOutlet weak var myImageView: IBImageView!
15 | @IBOutlet weak var myScoreLabel: UILabel!
16 | @IBOutlet weak var opponentImageView: IBImageView!
17 | @IBOutlet weak var opponentScoreLabel: UILabel!
18 | @IBOutlet weak var gameStatusLabel: UILabel!
19 | @IBOutlet weak var pointsLabel: UILabel!
20 | @IBOutlet weak var backView: UIView!
21 |
22 | override func awakeFromNib() {
23 | super.awakeFromNib()
24 | backView.addShadowAndRadius(offset: CGSize(width: 2, height: 2), color: .gray, radius: 2, opacity: 0.18, cornerRadius: 3)
25 | }
26 |
27 | var statusGame: String! {
28 | didSet {
29 | if statusGame == "1" {
30 | gameStatusLabel.text = "Round: \(Int.random(in: 0...10))"
31 | } else {
32 | gameStatusLabel.text = "10 rounds"
33 | }
34 | }
35 | }
36 |
37 | var score: String! {
38 | didSet {
39 |
40 | //FIXME: Temporary solution (stub)
41 | var listScore = (0...1).map{ _ in Int.random(in: 1 ... 20) }
42 | var colors = [UIColor]()
43 |
44 | if listScore[0] > listScore[1] {
45 | colors.append(UIColor(named: "Color-5")!)
46 | statusGame == "0" ? colors.append(UIColor.lightGray) : colors.append(UIColor(named: "Color-8")!)
47 | } else {
48 | statusGame == "0" ? colors.append(UIColor.lightGray) : colors.append(UIColor(named: "Color-8")!)
49 | colors.append(UIColor(named: "Color-5")!)
50 | }
51 |
52 | let att1 = [NSAttributedString.Key.font : UIFont(name: "Futura-Bold", size: 35.0), NSAttributedString.Key.foregroundColor : colors[0]]
53 | let att2 = [NSAttributedString.Key.font : UIFont(name: "Futura-Bold", size: 30.0), NSAttributedString.Key.foregroundColor : UIColor.lightGray]
54 | let att3 = [NSAttributedString.Key.font : UIFont(name: "Futura-Bold", size: 35.0), NSAttributedString.Key.foregroundColor : colors[1]]
55 |
56 | let attributedString1 = NSMutableAttributedString(string: String(listScore[0]), attributes: att1 as [NSAttributedString.Key : Any])
57 | let attributedString2 = NSMutableAttributedString(string:" / ", attributes: att2 as [NSAttributedString.Key : Any])
58 | let attributedString3 = NSMutableAttributedString(string: String(listScore[1]), attributes: att3 as [NSAttributedString.Key : Any])
59 |
60 | attributedString2.append(attributedString3)
61 | attributedString1.append (attributedString2)
62 | pointsLabel.attributedText = attributedString1
63 | }
64 | }
65 |
66 | func startSkeletonAnimate() {
67 | let animation = GradientDirection.topLeftBottomRight.slidingAnimation()
68 | let gradient = SkeletonGradient(baseColor: UIColor.lightRoyal.withAlphaComponent(0.10),
69 | secondaryColor: UIColor.royal.withAlphaComponent(0.25))
70 |
71 | [myImageView, opponentImageView, myScoreLabel, opponentScoreLabel, gameStatusLabel, pointsLabel].forEach(
72 | { SkeletonAppearance.default.multilineHeight = $0?.frame.height ?? 20
73 | $0?.showAnimatedGradientSkeleton(usingGradient: gradient, animation: animation) }
74 | )
75 | }
76 |
77 | func endSkeletonAnimation() {
78 | [myImageView, opponentImageView, myScoreLabel, opponentScoreLabel, gameStatusLabel, pointsLabel].forEach(
79 | { $0?.hideSkeleton() })
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/Login/WelcomePage/WelcomeViewController.swift:
--------------------------------------------------------------------------------
1 | //C
2 | // WelcomeViewController.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 31/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class WelcomeViewController: UIViewController {
12 |
13 | // MARK: - Outlets
14 | @IBOutlet weak var headerView: GradientView!
15 | @IBOutlet weak var bottomView: UIView!
16 | @IBOutlet weak var gradientNameView: GradientView!
17 | @IBOutlet weak var appNameLabel: UILabel!
18 | @IBOutlet weak var collectionView: UICollectionView!
19 | @IBOutlet weak var pageControl: UIPageControl!
20 |
21 | @IBOutlet weak var bottomViewConstraint: NSLayoutConstraint!
22 | @IBOutlet weak var topSpaceHeaderView: NSLayoutConstraint!
23 |
24 | var bottomViewHeight: CGFloat = 46
25 | var pagesDataSource = WelcomeDataSource()
26 | var leftSwipe = UISwipeGestureRecognizer()
27 |
28 | // MARK: - Lifecycle
29 |
30 | override func viewDidLoad() {
31 | super.viewDidLoad()
32 |
33 | leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(leftSwipe(_:)))
34 | leftSwipe.direction = .left
35 | collectionView.addGestureRecognizer(leftSwipe)
36 |
37 | topSpaceHeaderView.constant = -headerView.frame.height
38 | bottomViewConstraint.constant = 0
39 | pageControl.numberOfPages = pagesDataSource.countPages - 1
40 | gradientNameView.mask = appNameLabel
41 | bottomViewHeight = bottomView.frame.height
42 |
43 | // Collection View Config
44 | collectionView.dataSource = pagesDataSource
45 | collectionView.backgroundColor = .clear
46 | collectionView.layer.cornerRadius = 10
47 | collectionView.layoutIfNeeded()
48 |
49 | var height = collectionView.frame.height
50 | if UIDevice().screenType == .iPhoneX {
51 | height -= 60
52 | topSpaceHeaderView.constant -= 50
53 | }
54 |
55 | let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
56 | layout.sectionInset = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0)
57 | layout.itemSize = CGSize(width:collectionView.frame.width, height: height)
58 | layout.minimumInteritemSpacing = 0
59 | layout.minimumLineSpacing = 0
60 | layout.scrollDirection = .horizontal
61 | collectionView!.collectionViewLayout = layout
62 | }
63 |
64 | // MARK: - Actions with Next/Skip buttons
65 |
66 | @IBAction func nextPage(_ sender: Any) {
67 |
68 | // Opening the last page
69 | if pageControl.currentPage == pagesDataSource.countPages - 2 {
70 | bottomViewConstraint.constant = -bottomViewHeight * 2
71 | topSpaceHeaderView.constant = 0
72 | collectionView.removeGestureRecognizer(leftSwipe)
73 |
74 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.55) {
75 | self.gradientNameView.isAnimate = false
76 | }
77 |
78 | UIView.transition(with: appNameLabel, duration: 1.0, options: .transitionCrossDissolve, animations: {() -> Void in
79 | self.view.layoutIfNeeded()
80 | self.appNameLabel.textColor = .white
81 | })
82 | }
83 |
84 | let indexPath = IndexPath(item: pageControl.currentPage + 1, section: 0)
85 | collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
86 | pageControl.currentPage += 1
87 | }
88 |
89 | @IBAction func skipTutorial(_ sender: Any) {
90 | pageControl.currentPage = pagesDataSource.countPages - 1
91 | nextPage(sender)
92 | }
93 |
94 | @objc func leftSwipe(_ gesture: UIGestureRecognizer) {
95 | nextPage(gesture)
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/QuizChallenge.xcodeproj/xcshareddata/xcschemes/QuizChallenge.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
69 |
70 |
71 |
72 |
73 |
74 |
80 |
82 |
88 |
89 |
90 |
91 |
93 |
94 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/QuizChallenge/ViewController/New Group/ProfileSettingsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProfileSettingsViewController.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 26/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ProfileSettingsViewController: UIViewController {
12 |
13 | // MARK: - Outlets & Properties
14 |
15 | @IBOutlet weak var mainView: IBView!
16 | @IBOutlet weak var primarySettingsView: UIView!
17 | @IBOutlet weak var additionalInfoView: UIView!
18 |
19 | @IBOutlet weak var userImageView: IBImageView!
20 | @IBOutlet weak var emailTextField: IBTextField!
21 | @IBOutlet weak var usernameTextField: IBTextField!
22 | @IBOutlet weak var passwordTextField: IBTextField!
23 | @IBOutlet weak var ageTextField: IBTextField!
24 | @IBOutlet weak var nameTextField: IBTextField!
25 | @IBOutlet weak var cityTextField: IBTextField!
26 | @IBOutlet weak var confPasswordTextField: IBTextField!
27 | @IBOutlet weak var changePasswordButton: UIButton!
28 | @IBOutlet weak var generalSaveButton: UIButton!
29 |
30 | var isShowKeyboard = false
31 | var optionalInfoIsShow = false
32 |
33 | // MARK: - Lifecycle
34 |
35 | override func viewDidLoad() {
36 | super.viewDidLoad()
37 | //keyboardNotification()
38 | //setCurrentUserData()
39 |
40 |
41 | }
42 |
43 | override func viewWillAppear(_ animated: Bool) {
44 | let viewPosition = mainView.frame.origin.y
45 | mainView.frame.origin.y = 0
46 | additionalInfoView.isHidden = true
47 |
48 | UIView.springAnimate(animateCompletion: {
49 | self.mainView.frame.origin.y = viewPosition
50 | })
51 | }
52 |
53 | override func viewWillDisappear(_ animated: Bool) {
54 | super.viewWillDisappear(animated)
55 | NotificationCenter.default.removeObserver(self)
56 | }
57 |
58 | func setCurrentUserData() {
59 | let curUser = UserManager.shared.curUser
60 | emailTextField.text = curUser.email
61 | usernameTextField.text = curUser.username
62 | nameTextField.text = curUser.realName
63 | ageTextField.text = String(curUser.age)
64 | cityTextField.text = curUser.city
65 | }
66 |
67 | @IBAction func changeAdditionalInformation(_ sender: UIButton) {
68 | if !optionalInfoIsShow && sender.tag == 1 {
69 | optionalInfoIsShow = true
70 | primarySettingsView.flip(to: additionalInfoView)
71 |
72 | } else if optionalInfoIsShow && sender.tag == 0 {
73 | optionalInfoIsShow = false
74 | additionalInfoView.flip(to: primarySettingsView)
75 | }
76 | }
77 | }
78 |
79 | // MARK: - Keyboard Notification Settings
80 |
81 | extension ProfileSettingsViewController {
82 |
83 | func keyboardNotification() {
84 | NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear), name: UIResponder.keyboardWillHideNotification, object: nil)
85 | NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear), name: UIResponder.keyboardWillShowNotification, object: nil)
86 | }
87 |
88 | @objc func keyboardWillAppear() {
89 | isShowKeyboard = true
90 | }
91 |
92 | @objc func keyboardWillDisappear() {
93 | isShowKeyboard = false
94 | }
95 |
96 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
97 | guard !isShowKeyboard else {
98 | touchesBegan(touches, with: event)
99 | view.endEditing(true)
100 | return
101 | }
102 |
103 | if let touch = touches.first, touch.view != mainView, touch.view != userImageView {
104 | dismiss(animated: true, completion: nil)
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/QuizChallenge/Helpers/HelperFile.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HelperFile.swift
3 | // QuizChallenge
4 | //
5 | // Created by Anton Makarov on 28/01/2019.
6 | // Copyright © 2019 Anton Makarov. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Alamofire
11 |
12 | // MARK: - Closure typealias
13 |
14 | public typealias EmptyClosure = () -> Void
15 | public typealias BoolClosure = (Bool) -> ()
16 | public typealias IntClosure = (Int) -> ()
17 | public typealias StringClosure = (String) -> ()
18 |
19 | // MARK: - PopUp Alert Helper
20 |
21 | class PopUpHelper {
22 |
23 | // Question Alert
24 | static func showConfirmView(from VC: UIViewController, title: String?, descript: String, negativeButton: String, positiveButton: String, completion: @escaping EmptyClosure, secondCompletion: @escaping EmptyClosure) {
25 |
26 | let popUpView = CommonHelper.getPopUpView()
27 | popUpView.showConfirmAlert(title: title, descript: descript, negativeButton: negativeButton, positiveButton: positiveButton, negativeCompletion: completion, positiveCompletion: secondCompletion)
28 |
29 | VC.present(popUpView, animated: true, completion: nil)
30 | }
31 |
32 | // Success simple Alert
33 | static func showSimpleAlert(from VC: UIViewController, type: ModalSimpleType, title: String? = nil, descript: String, buttonText: String, isAutoHide: Bool = true, completion: EmptyClosure? = nil) {
34 |
35 | let popUpView = CommonHelper.getPopUpView()
36 | popUpView.showSimpleAlert(type: type, title: title, descript: descript, buttonText: buttonText, isAutoHide: isAutoHide, completion: completion)
37 |
38 | VC.present(popUpView, animated: true, completion: nil)
39 | }
40 |
41 | // Success error Alert
42 | static func showErrorAlert(from VC: UIViewController, type: ModalErrorType, title: String? = nil, descript: String? = nil, buttonText: String? = nil, isAutoHide: Bool = true, completion: EmptyClosure? = nil) {
43 |
44 | let popUpView = CommonHelper.getPopUpView()
45 | popUpView.showErrorAlert(type: type, title: title, descript: descript, buttonText: buttonText, isAutoHide: isAutoHide, completion: completion)
46 |
47 | VC.present(popUpView, animated: true, completion: nil)
48 | }
49 | }
50 |
51 | // MARK: - Common Helper Class
52 |
53 | class CommonHelper {
54 |
55 | static let networkStateManager = NetworkReachabilityManager()
56 |
57 | // Check Internet connection
58 | static func checkNetworkStatus() -> Bool {
59 | return networkStateManager?.isReachable ?? false
60 | }
61 |
62 | // Load view controller from storyboard
63 | static func loadViewController(from storyboard: String = "Main", named name: String, isModal: Bool = false) -> UIViewController? {
64 | let storyboard = UIStoryboard(name: storyboard, bundle: nil)
65 | let viewController = storyboard.instantiateViewController(withIdentifier: name)
66 | if isModal {
67 | viewController.modalPresentationStyle = .overCurrentContext
68 | }
69 | return viewController
70 | }
71 |
72 | // Load test file with questions
73 | static func loadJsonCategories(from file: String) -> TypeDecodable? {
74 |
75 | if let path = Bundle.main.path(forResource: file, ofType: "json") {
76 | do {
77 | let data = try Data(contentsOf: URL(fileURLWithPath: path))
78 | guard let jsonResult = try? JSONDecoder().decode(TypeDecodable.self, from: data) else {
79 | Logger.error(msg: "Error parse json")
80 | return nil
81 | }
82 |
83 | return jsonResult
84 | } catch {
85 | Logger.error(msg: "Error load json: " + error.localizedDescription)
86 | }
87 | }
88 | return nil
89 | }
90 |
91 | // Current UTC time
92 | static func getCurrentTime() -> String {
93 | let dateFormatter = DateFormatter()
94 | dateFormatter.dateFormat = "yyyy-MM-dd/HH:mm:ss.SSS"
95 | return dateFormatter.string(from: Date())
96 | }
97 |
98 | static func getPopUpView() -> UniversalModalViewController {
99 | return CommonHelper.loadViewController(from: "Modals", named: "UniversalModal", isModal: true) as! UniversalModalViewController
100 | }
101 | }
102 |
--------------------------------------------------------------------------------