ハッカソンでiOSアプリを24時間で作るためのアーキテクチャや事前準備、実装手順


概要

この記事は、もともとiOSDCで話そうと思ってたけど不採択、でも俺コンにて拾っていただいて発表することになった発表のQiita版です。

スライドは以下
https://speakerdeck.com/fujikawakei/hatukasondeiketeruapuriwo-24shi-jian-debao-su-shi-zhuang-surufang-fa

ハッカソンのアプリ全体の話はSpajam2017優秀賞「嫌われAIの命名」の発想プロセスからiOSアプリ実装まで で詳しく書いてありますので、実装以外の部分も興味ある方はそちらも覗いてみてください。

この記事の内容

  • ハッカソンのアーキテクチャはこれでいい
  • ハッカソンでの事前準備しとこう
  • 実装手順はこれが良さそう

ハッカソンでのアーキテクチャ

テストしやすくするために疎結合にしたり、メンテナンス性の低いFatViewControllerを作らないために、iOSアプリのアーキテクチャを工夫することは大事だと思っています。

Swiftエンジニアを2年弱やった僕もアーキテクチャ選定って気持ちよく仕事する上で大事だよなと思ったりしてます。

しかし、ハッカソンは短時間で実装し、イケてる感じに見せかけ、感動させれば勝つことができます。 特に流行りのフレームワーク、アーキテクチャを取り入れるメリットはそれほどありません。

ということで、ハッカソン用のアーキテクチャを考えました。

爆速!ハッカソン用アーキテクチャ V&M

その名はV&M(ViewController & Manager)

Spajamではこんな感じで作りました。

画面間で共有したい要素は全てManagerに突っ込みます。他の必要なことはViewControllerに突っ込みます。細かいことは気にしない。分かり易ければ良いのです。

Manager(シングルトン)の例

textFieldに入力した値、生成したNickName, screenShotは全てこのマネージャーに突っ込みます。まさにマネージャー。全ての大切なpropertyをマネジメントしてくれます。

NickNameManager.swift
class NickNameManager {

    private init(){

    }
    static var shared =  NickNameManager()

    var screenName = ""
    var nickName = ""
    var screenShot: UIImage?
}

ViewControllerでのManager使い方例

VCはManagerさんの力を借りて値を投げたり受け取ったりします。

InputNameViewController

textFieldに入った値をシングルトンのmanagerに渡すだけ!簡単!!!

InputNameViewController.swift
class InputNameViewController: UIViewController {

    @IBAction func buttonTapped(_ sender: Any) {

        guard let screenName = textField.text else { return }
        NickNameManager.shared.screenName = screenName
    }
}

ResultViewController

できたnicknameはManagerに保持しておいてResultViewControllerに表示!!簡単!!

ResultViewController.swift

class ResultViewController: UIViewController {

    var nickName: String {
        let name = NickNameManager.shared.nickName
        return name
    }

    @IBOutlet weak var resultLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        resultLabel.text = nickName
    }
}

事前準備

画面遷移、音楽再生、動画再生あたりを先に用意しておきます。音楽や動画を使って、リズムよく画面を遷移していくアプリが僕たちのチームのスタイルに合っていたからです。

画面遷移

ScreenTransitionManagerってやつを用意しときます。下からmodalとかクロスディゾルブで表示とか、横にpushで画面遷移をメソッドを呼べばできるように、あとはVCとidentifierを入れればすぐに画面遷移のメソッドが作れるようにしておきました。

class ScreenTransitionManager {

    static let shared = ScreenTransitionManager()
    private init(){

    }

    private var targetViewController: UIViewController? {
        // 現在一番上にあるViewControllerを取得する
        var tc = UIApplication.shared.keyWindow?.rootViewController
        while tc?.presentedViewController != nil {
            tc = tc!.presentedViewController
        }
        return tc
    }

    /// 横にスライド表示
    private func show(_ controller: UIViewController){
        targetViewController?.show(controller, sender: targetViewController)
    }

    /// 新しいnavigationControllerでモーダル表示
    /// modalPresentationStyleやmodalTransitionStyleを設定しなければ下から出てくる
    private func presentModalWithNavigation(_ controller: UIViewController){
        let nc = UINavigationController(rootViewController: controller)
        targetViewController?.present(nc, animated: true, completion: nil)
    }

    /// クロスディゾルブでスーッとモーダル表示
    private func showTranslucenceView(_ controller: UIViewController) {
        controller.modalPresentationStyle = .overFullScreen
        controller.modalTransitionStyle = .crossDissolve
        targetViewController?.present(controller, animated: true, completion: nil)
    }

    // ログインに飛ぶ
    func goToLogin(){
        let storyboard = UIStoryboard(name: "Login", bundle: nil)
        let controller = storyboard.instantiateViewController(withIdentifier: "LoginViewController")
        show(controller)
    }

}

音楽再生class


class AudioPlayer: NSObject, AVAudioPlayerDelegate {

    static let shared = AudioPlayer()
    private override init(){
    }

    var audioPlayer: AVAudioPlayer?

    enum MusicName: String {
        case loopmusic
    }

    func playMusic(_ name: MusicName){

    }

    func stop(){
        audioPlayer?.stop()
    }
}

動画再生class

AVPlayerView

// レイヤーをAVPlayerLayerにする為のラッパークラス.
final class AVPlayerView: UIView {

    func setPlayer(_ player: AVPlayer) {
        let layer = self.layer as! AVPlayerLayer
        layer.player = player
    }
}

ViewDidLoadにコピペで貼ったやつ

// パスからassetを生成.
let path = Bundle.main.path(forResource: "movie_parts", ofType: "mp4") ?? ""
let fileURL = URL(fileURLWithPath: path)
let avAsset = AVURLAsset(url: fileURL, options: nil)

// AVPlayerに再生させるアイテムを生成.
let playerItem = AVPlayerItem(asset: avAsset)
// AVPlayerを生成.
videoPlayer = AVPlayer(playerItem: playerItem)

// 動画再生完了の監視
NotificationCenter.default.addObserver(self, selector: #selector(MovieViewController.movieEnd(_:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)

実装手順

最後に実装手順。皆さんはアプリを作るときにどこから作るでしょうか?

通信周りから? Modelから? Viewから? (僕は喉から)

私はハッカソンでも普段の業務でもViewと画面遷移から作ります。それはデザイナーが何もできない時間を減らすためと、できてる感を出すためです。

ハッカソンで大事なのがこのできてる感。 とりあえず動くアプリを作っておけば、時間が足りなくなって裏の実装が間に合わなかったとしてもとりあえず動くアプリを持って発表に望むことができます。

まず動くことはSpajamでも評価として高そうでした。ぶっちゃけ中身がなくてもいい動画、いい音楽、いい感じに動くアプリがあれば、それだけで ポイントを稼ぐことができると思います。

そこに技術点を加えていくために裏側の実装を頑張るイメージです。

まとめ

V&Mは仕事では使っちゃダメだけど、ハッカソンで爆速実装するにはこの実装はよかったです!

ハッカソン出る方は参考にしてみてくださいw

Spajam2017優秀賞「嫌われAIの命名」の発想プロセスからiOSアプリ実装までも読んでね!