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