2021-07-15

iOS App: Queen's game

SwiftRealmRxSwiftMVVMCoreAnimationUICollectionViewdiffable data sources

Summary

We published an native iOS game application to the App store. This app provides a multi-payer game to play between your friends and neighbors. Since this game includes great random factors and customization, each time a player will be provided with variable joys.
This app has a lot of UI factors and data models with a reactive relationship. Thus, we adopted RxSwift and MVVM design patterns to completely separate views, models, and logic.

Release

We published our app to the App Store!πŸŽ‰ App Store Links

App Store screenshots

Motivation

Backgorund

There are enormous unique and awesome games in every country and culture. "King's game" which is called "ηŽ‹ζ§˜γ‚²γƒΌγƒ " in Japan is also one of them. This party game is a sort of combination of, "Simon Says" and "truth or dare", but might be more flexible and thralling. We thought it would be great to introduce this game into a mobile application so that all over the people in world can know, play and share its wonderful experience!

Kings game

What is "King's game"?

The king's game simply consists of two factors, command and obey. Participants decide on one "King" and others(citizens). Every citizen is provided a secret index that only him/herself knows. Then, the King makes a command involving indexes, such as "No. 2 have to make No.3 laugh! 🀣". Finally, each person reveals their index (number), and those who are targeted have to follow the command!

Introducing into App

Tp apply this, we created a game app, which we replace "King" with "Queen" instead. This is because to make this game unique, and there is already a game called "King's cup" in western culture which might cause misunderstanding.

In the app, we randomly select the queen and others(citizens), the same as King's game. What is more, by using the advantage of the application platform, we can also prepare template collections of commands. When a queen chooses the command, targets are randomly selected.

Besides, we can let users edit these commands whatever they want, like a "To-do list" app feature. And of course, those commands are saved permanently in the app so that even after closing the app, users can keep on using their customized commands when restart!

Lastly, we can introduce a lot of fun animation because it's an app and this makes users much more fun!

Queen todo

My contribution

In this work, what I contributed is as follows.

  • As a UI Designer
    • Created UI mockup
    • Created Basic all UI Components
  • As a Developer
    • Created items list editing and adding (Todo) feature
    • Introduced transition animation
    • Implemented custom Search bar
    • Implemented animation with RxSwift

Main specifications

  • Swift: version 5.4
  • iOS: 14.5
  • RxSwift, RxCocoa: To implement reactive functions involving UI interaction.
  • Realm: To save game data persistently.
  • Collection View, diffable data source: To display flexible and beautiful UI connected to data.

Repository

Queen's game repository

Usage

Once you launch the app, users can select whether to start a new game or modifying game settings.

New Game

The basic flow of the game is as follows.

  1. Select the number of players
  2. Enter user names
  3. Choose the queen randomly
  4. The queen chooses the command
  5. Targets described in the command will be chosen randomly
  6. Execute(Obey) the command
game flow

Settings

In the game settings, what we can control is shown below.

  • Edit template commands like a ToDO app
  • Edit some game configuration
  • See play guide (How to play)
  • See privacy & policy
settings

Project management

We used the GitHub project (Kanban feature) for scheduling and followed the agile style. This makes the project complete efficiently with even remote work.

Schedules

Discussion

UI Designing

We created a whole UI mockup both considering light and dark mode with Figma. In addition to creating a style guide, We created UI components that are well suitable for implementing with MVVM design pattern.

Style Guide

style-guide

UI mockup

UI-mockup

UI components

UI components

Dynamic Light and Dark mode.

We also prepared and introduce both light and dark modes to suffice users' demands these days. These light and dark modes correspond to the iOS system's appearance. Thus, as soon as a user switches its app appearance, the app will be reflected without restarting.

dark and light mode

This feature was implemented by using traitCollection and userInterfaceStyle. We implemented a static custom color wrapped by struct.

/// Custom color set used for this project.
/// - Compatible with dark mode.
struct CustomColor {
  
 /// Used for main text color.
 /// - Light mode -> similar to black color
 /// - Dark mode -> similar to white color
 static var text: UIColor {
  return UIColor { (traitCollection: UITraitCollection) -> UIColor in
   return traitCollection.userInterfaceStyle == .light ?
    UIColor(hex: "#251F1F")! : UIColor(hex: "#E1DFDF")!
  }
 }

Front-end animation

For harmony and consistency of UI, this app has a lot of custom animation and UI functions, such as transition, search bar, and loading screen.

Custom search bar

Although the existing UISearchController embedded in Navigation Controller is useful and well-prepared, it is hard to customize in your way. So this time, we decided not to use any UISearchController but only our custom UISearchBar.

search bar

To create a custom UISearchBar, some of the basic features have to be implemented by yourself. For example,

  • Search bar will be displayed and hidden by the user's scrolling.
  • When tapping the search bar, it will focus on it by making other places(views) darker.
  • When tapping the search bar, a keyboard is displayed
  • If you tap outside the keyboard, the keyboard will be dismissed
  • Cancel, enter, and close behavior

It was a bit painstaking to do define, but thankfully, we were able to create our own flexible customized search bar!

extension CommonCommandViewController: UISearchBarDelegate {
  
  // Whenever Text is changed
  func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    viewModel.searchText = searchText
    viewModel.readItems()
    
    // If text is empty -> This is executed when "x" button is click. We want to stop focus.
    if searchText.isEmpty {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            searchBar.resignFirstResponder()
        }
    }
  }

pop up transition

The default transition animation is nice but limited. To make the transition more game-like, we created a natural pop-up menu. We used UIViewControllerAnimatedTransitioning and UIViewControllerTransitioningDelegate for customizing modal animation.

pop up menu
// The delegatee who will do the transition. We will send this to the delegator.
class PopUpTransitioningDelegatee: NSObject, UIViewControllerTransitioningDelegate { }

extension PopUpTransitioningDelegatee {
  // Tells delegate What kind if animation transitioning do you want to use when presenting ?
  func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {

    PopUpTransitioning.shared.presenting = true
    return PopUpTransitioning.shared
  }

  // Tells delegate What kind of animation transitioning do you want to use when dismissing?
  func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    PopUpTransitioning.shared.presenting = false
    return PopUpTransitioning.shared
  }
}

Back-end

This app includes many reactive scenes. For instance, when the number of players reaches the maximum, it will auto-disable the plus button. For another example, after a countdown, the app will show a different view on the screen.

Therefore, we adopted a view model (MVVM) pattern and reactive framework which is RxSwift.

Disabling button depends on user action

We added a feature that a user can choose how many players join the game. Following the original King's game spec, we wanted to set the maximum and the minimum number of players.

#plyaers

So we needed features like as follows,

  • When a user tap plus or minus button, it'll also reflect the label soon
  • When the number of players (#player) reaches max or min, don't let the user tap anymore

This was the exact opportunity to adopt RXswift. We made each button, label, and variable for #player reactive as reactive relation.

  • When a plus or minus button is tapped, we bind it to #player
  • When #player changes, it emits its value and the label subscribes to it.
  • When #player reaches max or min, we bind it as boolean to button's enable
// View model
  let numOfPlayers: BehaviorRelay = BehaviorRelay(value: 5)
let vm = ViewModel()

// when button tapped -> increment #player
plusButton.rx.tap
  .subscribe { [weak self] _ in
    self?.vm.numOfPlayers.accept((self?.vm.numOfPlayers.value)! + 1)
  }
  .disposed(by: vm.disposeBag)

// when #player changes -> bind to UI (label)
vm.numOfPlayers
  .map{String($0)}
  .bind(to: playerCountLabel.rx.text)
  .disposed(by: vm.disposeBag)

// when #player changes and reach to limit, -> bind to UI (disable the button)
vm.numOfPlayers
  .map {$0 < 9}
  .bind(to: plusButton.rx.isEnabled)
  .disposed(by: vm.disposeBag)

Countdown animation

For another example, we created a countdown screen, in which each second the label changes, and after reaching 0 seconds, the whole view is replaced.

countdown

For this feature, we use onNext and onComplete. Each second, we send onNext and bind to UILabel. After finishing the countdown, we emit onComplete, which triggers a next view replacing.

// View model
  
  var timer = Timer()
  var countdownTime = Int(Settings.shared.queenWaitingSeconds) // Singleton

  // Subject
  let rxCountdownTime = PublishSubject<Int?>()
  
  func countdown() {
    self.timer = Timer.scheduledTimer(
      withTimeInterval: 1,
      repeats: true,
      block: { [weak self] timer in
        self?.countdownTime -= 1
        // Each second, send time
        self?.rxCountdownTime.onNext(self?.countdownTime)
        if self?.countdownTime == -1 {
          // When it's 0, send finish.
          self?.rxCountdownTime.onCompleted()
        }
      })
  }


// view controller

  self.viewModel.countdown()
  self.viewModel.rxCountdownTime
    .subscribe(onNext: { [weak self] time in
      guard let time = time else { return }
      DispatchQueue.main.async {
        self?.countdownStackView.countdownLabel.text = String(time)
      }
    },
    onCompleted: {
      self.replaceView() // after count down, replace view
    })
    .disposed(by: disposeBag)

Todo feature

This game contains initial template command so that users only have to select them. Of course, a user can add new commands or edit them later. This is a sort of Todo list feature. We utilized it by using a UICollectionView with difffable datasource, which elegantly displays the differences of items. In addition, Realm was used to keep items as persistence data.

Furthermore, we added a searching(filtering) feature, which leads the user to comfortably find their item.

todo list

This is an example code. The ViewModel reads stored items considering search text. The viewController subscribes snapshot and it reflects the UI.

// View model

  /// Read items from realm and update snapshot (and update UI)
  func readItems() {
    let items: [Command]
    
    if searchText == "" {
      items = Array(
        realm.objects(Command.self)
          .sorted(byKeyPath: "detail", ascending: true)
      )
    } else {
      items = Array(
        realm.objects(Command.self)
          .filter("detail CONTAINS[c] %@", searchText)
          .sorted(byKeyPath: "detail", ascending: true)
      )
    }
    
    updateSnapshot(newItems: items)
  }
// view controller
    viewModel.snapshotSubject
      .subscribe (onNext: { [unowned self] snapshot in
        self.dataSource.apply(snapshot, animatingDifferences: true)
      })
      .disposed(by: viewModel.disposeBag)

Future work

What we're planning to do next is as follows.

  • Publish in the app store (Coming soon! We are preparing documents now.)
  • Implement authentication by using firebase so that user can store their commands and history to cloud store
  • Add play card graphic to select the Queen

Team member

Tak(me) (cookie777)

Daiki (DaiSugi01)

Takayasu (TakayasuNasu)

Works