Swift
が発表されてから、数日経過しましたがすでにたくさんのコードがGithubに上がっています。Swift
を始めたいエンジニアが参考になるサンプルをピックアップしてみました。
自分も今週末に珈琲を片手にコードを読んでみようと思います。
FlappySwift 4,700★
半日くらいで作られたFlappy Birdのクローンです。
SpriteKit
を利用していて、メインのゲームロジックは200行弱ほどで書かれています。
Swift
でシンプルなゲームを作成するのに参考になります。
GameScene.swift 一部抜粋
//
// GameScene.swift
// FlappyBird
//
// Created by Nate Murray on 6/2/14.
// Copyright (c) 2014 Fullstack.io. All rights reserved.
//
import SpriteKit
class GameScene: SKScene {
var bird = SKSpriteNode()
var skyColor = SKColor()
var verticalPipeGap = 150.0
var pipeTextureUp = SKTexture()
var pipeTextureDown = SKTexture()
var movePipesAndRemove = SKAction()
override func didMoveToView(view: SKView) {
// setup physics
self.physicsWorld.gravity = CGVectorMake( 0.0, -5.0 )
// setup background color
skyColor = SKColor(red: 81.0/255.0, green: 192.0/255.0, blue: 201.0/255.0, alpha: 1.0)
self.backgroundColor = skyColor
// ground
var groundTexture = SKTexture(imageNamed: "land")
groundTexture.filteringMode = SKTextureFilteringMode.Nearest
var moveGroundSprite = SKAction.moveByX(-groundTexture.size().width * 2.0, y: 0, duration: NSTimeInterval(0.02 * groundTexture.size().width * 2.0))
var resetGroundSprite = SKAction.moveByX(groundTexture.size().width * 2.0, y: 0, duration: 0.0)
var moveGroundSpritesForever = SKAction.repeatActionForever(SKAction.sequence([moveGroundSprite,resetGroundSprite]))
for var i:CGFloat = 0; i < 2.0 + self.frame.size.width / ( groundTexture.size().width * 2.0 ); ++i {
var sprite = SKSpriteNode(texture: groundTexture)
sprite.setScale(2.0)
sprite.position = CGPointMake(i * sprite.size.width, sprite.size.height / 2.0)
sprite.runAction(moveGroundSpritesForever)
self.addChild(sprite)
}
// skyline
var skyTexture = SKTexture(imageNamed: "sky")
skyTexture.filteringMode = SKTextureFilteringMode.Nearest
var moveSkySprite = SKAction.moveByX(-skyTexture.size().width * 2.0, y: 0, duration: NSTimeInterval(0.1 * skyTexture.size().width * 2.0))
var resetSkySprite = SKAction.moveByX(skyTexture.size().width * 2.0, y: 0, duration: 0.0)
var moveSkySpritesForever = SKAction.repeatActionForever(SKAction.sequence([moveSkySprite,resetSkySprite]))
for var i:CGFloat = 0; i < 2.0 + self.frame.size.width / ( skyTexture.size().width * 2.0 ); ++i {
var sprite = SKSpriteNode(texture: skyTexture)
sprite.setScale(2.0)
sprite.zPosition = -20;
sprite.position = CGPointMake(i * sprite.size.width, sprite.size.height / 2.0 + groundTexture.size().height * 2.0)
sprite.runAction(moveSkySpritesForever)
self.addChild(sprite)
}
・・・
swift-2048 600★
Objective-C
で作られた2048のゲームをSwift
に移植したコードです。
Enum
のパターンマッチを活用している点が参考になります。
メインのロジックは500行弱です。
GameModel.swift 一部抜粋
・・・
// Represents an action applied to a tile; used to generate move orders
enum ActionToken {
case NoAction(source: Int, value: Int)
case Move(source: Int, value: Int)
case SingleCombine(source: Int, value: Int)
case DoubleCombine(source: Int, second: Int, value: Int)
// Get the 'value', regardless of the specific type
func getValue() -> Int {
switch self {
case let .NoAction(_, v): return v
case let .Move(_, v): return v
case let .SingleCombine(_, v): return v
case let .DoubleCombine(_, _, v): return v
}
}
// Get the 'source', regardless of the specific type
func getSource() -> Int {
switch self {
case let .NoAction(s, _): return s
case let .Move(s, _): return s
case let .SingleCombine(s, _): return s
case let .DoubleCombine(s, _, _): return s
}
}
}
・・・
// Convert all action tokens into move orders
func convert(group: ActionToken[]) -> MoveOrder[] {
var moveBuffer = MoveOrder[]()
for (idx, t) in enumerate(group) {
switch t {
case let .Move(s, v):
moveBuffer.append(MoveOrder.SingleMoveOrder(source: s, destination: idx, value: v, wasMerge: false))
case let .SingleCombine(s, v):
moveBuffer.append(MoveOrder.SingleMoveOrder(source: s, destination: idx, value: v, wasMerge: true))
case let .DoubleCombine(s1, s2, v):
moveBuffer.append(MoveOrder.DoubleMoveOrder(firstSource: s1, secondSource: s2, destination: idx, value: v))
default:
// Don't do anything
break
}
}
return moveBuffer
}
・・・
swiftz 300★
Swiftz
は関数型プログラミングのためライブラリです。
便利な機能が追加されています。
JSON
のパースはバリューの型を厳密の精査するようになったので、Objective-C
で記述するよりも冗長になりやすいのですが、このライブラリを利用すると使いやすくなります。
並列処理の記述も、NSOperation/NSOperationQueue
を利用するよりも書きやすいです。
JSON:
let js: NSData = ("[1,\"foo\"]").dataUsingEncoding(NSUTF8StringEncoding,
allowLossyConversion: false)
let lhs: JSValue = JSValue.decode(js)
let rhs: JSValue = JSValue.JSArray([JSValue.JSNumber(1), JSValue.JSString("foo")])
XCTAssert(lhs == rhs)
XCTAssert(rhs.encode() == js)
Concurrency:
// we can delay computations with futures
let x: Future<Int> = Future(exec: gcdExecutionContext, {
sleep(1)
return 4
})
x.result() == x.result() // true, returns in 1 second
// Channels
let chan: Chan<Int> = Chan()
chan.write(1)
chan.write(2) // this could happen asynchronously
let x1 = chan.read()
let x2 = chan.read()
println((x1, x2)) // 1, 2
// we can map and flatMap over futures
x.map({ $0.description }).result() // "4", returns instantly
x.flatMap({ (x: Int) -> Future<Int> in
return Future(exec: gcdExecutionContext, { sleep(1); return x + 1 })
}).result() // sleeps another second, then returns 5
Quick 70★
BDDなテストフレームワークです。
RSpec, Specta, Ginkgoを参考にしてるそうです。記述はRSpecによく似ていて、SPEC
が書きやすそうです。またSwift
の内部DSLとしての可能性も感じます。
import XCTest
class PersonSpec: QuickSpec {
override class var isConcreteSpec: Bool { get { return true } }
override class func exampleGroups() {
describe("Person") {
var person: Person?
beforeEach { person = Person() }
it("is happy") {
XCTAssert(person!.isHappy,
"expected person to be happy by default")
}
describe("greeting") {
context("when the person is unhappy") {
beforeEach { person!.isHappy = false }
it("is lukewarm") {
XCTAssertEqualObjects(person!.greeting, "Oh, hi.",
"expected a lukewarm greeting")
}
}
context("when the person is happy") {
beforeEach { person!.isHappy = true }
it("is enthusiastic") {
XCTAssertEqualObjects(person!.greeting, "Hello!",
"expected an enthusiastic greeting")
}
}
}
}
}
}
まとめ
上記にピックアップしたコード以外にもたくさん面白そうで勉强になるコードがあるので、皆さんも探してみてください。
補足 RSS Reader 50★
筆者が作成した、シンプルなRSSのサンプルです。
cocoapods
のライブラリを利用して、100行程度で実装しているので、最初にアプリ開発の雰囲気をつかむには参考になるかと思います。