Mobile game development with SpriteKit (iOS)

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
Mobile game development with SpriteKit (iOS)
Medium
from 1 week to 3 months
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1052
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

Mobile Game Development with SpriteKit (iOS)

SpriteKit—Apple's native 2D framework, built into iOS SDK since version 7. Requires no third-party dependencies, integrates well with GameplayKit for AI, delivers stable 60 fps on iPhone SE second generation with reasonable load. For 2D games moderate complexity—reasonable choice, especially if team already writes Swift without wanting Unity or Godot in project.

Game Architecture: Scenes, Nodes, Physics

Everything in SpriteKit—SKNode tree. SKScene—root container, SKSpriteNode—drawable, SKEmitterNode—particle system, SKLabelNode—text. Common first project mistake—create scenes as "monolith," mixing movement, rendering, sound, UI in one file. At 200 lines already unreadable.

Working structure via component approach with GKComponent from GameplayKit:

class EnemyNode: SKSpriteNode {
    var movementComponent: MovementComponent?
    var healthComponent: HealthComponent?
}

class MovementComponent: GKComponent {
    override func update(deltaTime seconds: TimeInterval) {
        guard let node = entity?.component(ofType: GKSKNodeComponent.self)?.node else { return }
        node.position.y -= CGFloat(150 * seconds)
    }
}

Allows testing MovementComponent in isolation, reuse between enemy types.

Physics engine SpriteKit based on Box2D. SKPhysicsBody three types: circleOfRadius, rectangleOf(size:), bodyWithTexture(_:alphaThreshold:size:)—last generates polygon collider by texture pixels. Practice: bodyWithTexture with alphaThreshold: 0.5 convenient, expensive: on complex textures generation takes time. Cache and reuse:

extension SKPhysicsBody {
    private static var cache: [String: SKPhysicsBody] = [:]

    static func cached(texture: SKTexture, size: CGSize, key: String) -> SKPhysicsBody {
        if let cached = cache[key] {
            return cached.copy() as! SKPhysicsBody
        }
        let body = SKPhysicsBody(texture: texture, alphaThreshold: 0.5, size: size)
        cache[key] = body
        return body.copy() as! SKPhysicsBody
    }
}

Collisions set via categoryBitMask and contactTestBitMask. Common problem—missed collisions at high speed ("tunneling"). Solution: usesPreciseCollisionDetection = true for fast bodies, CPU expensive. Alternative—SKPhysicsWorld.enumerateBodies(alongRayStart:end:using:) for manual ray cast in update(_:).

Texture Atlas and Performance

Draw call—main performance enemy in SpriteKit. Each unique texture—potentially separate draw call. SKTextureAtlas groups sprites into atlas:

let atlas = SKTextureAtlas(named: "Enemies")
let texture = atlas.textureNamed("enemy_run_01")

Xcode compiles atlas automatically from .spriteatlas folder. Rule: everything drawn simultaneously—in one atlas. Check draw calls in Xcode via View → Debug → Statistics while game running.

At SKSpriteNode 64×64 size with 512×512 texture, Metal downscales on GPU each frame. Textures should be close to display size. Xcode Instruments → Metal System Trace shows if GPU overloaded with unnecessary scaling.

Animation via SKAction.animate(with:timePerFrame:):

let frames = (1...8).map { atlas.textureNamed("run_\(String(format: "%02d", $0))") }
let animation = SKAction.animate(with: frames, timePerFrame: 1.0/12.0, resize: false, restore: false)
let loop = SKAction.repeatForever(animation)
character.run(loop, withKey: "running")

withKey: allows stopping or replacing animation via removeAction(forKey:).

Sound: AVAudioEngine Not SKAction.playSoundFileNamed

SKAction.playSoundFileNamed(_:waitForCompletion:) convenient for prototype, not production: no volume control, pause, file decoded each call. For games use AVAudioEngine with AVAudioPlayerNode:

class AudioManager {
    private let engine = AVAudioEngine()
    private var playerNodes: [String: AVAudioPlayerNode] = [:]
    private var audioFiles: [String: AVAudioFile] = [:]

    func preloadSound(named name: String) throws {
        let url = Bundle.main.url(forResource: name, withExtension: "wav")!
        audioFiles[name] = try AVAudioFile(forReading: url)
    }

    func playSound(named name: String) {
        guard let file = audioFiles[name] else { return }
        let node = AVAudioPlayerNode()
        engine.attach(node)
        engine.connect(node, to: engine.mainMixerNode, format: file.processingFormat)
        node.scheduleFile(file, at: nil)
        node.play()
    }
}

Preload sounds in background at scene start, not blocking main thread.

GameplayKit: Enemy AI Without Reinventing Wheel

GKStateMachine perfect for AI states:

class EnemyIdleState: GKState {
    override func isValidNextState(_ stateClass: AnyClass) -> Bool {
        stateClass == EnemyChaseState.self || stateClass == EnemyAttackState.self
    }
}

GKAgent2D with GKGoal implements pursuit, flee, flocking without manual vector math. For procedural levels—GKNoise and GKPerlinNoiseSource.

Common Production Problems

FPS drop with many enemies—usually SKPhysicsBody each with precise colliders. Solution: simplify to circleOfRadius or rectangleOf, exact collision physics only for player.

Memory leaks on scene changeSKScene not freed if uncanceled SKAction with strong references remain. Always call removeAllActions() in willMove(from:).

Textures not unloadingSKTextureAtlas held while any SKSpriteNode uses texture. On level change, replace node textures with SKTexture() before removal, then removeFromParent().

Work Stages

TZ audit: genre, level count, monetization (IAP, ads), target devices, iOS minimum.

Prototype: core gameplay loop first week—decide if SpriteKit fits or need Unity.

Development: scenes, mechanics, physics, AI, sound, UI (separate SKScene or UIKit overlay).

Game Center integration: leaderboards, achievements.

Real device testing: iPhone SE 2gen (weak GPU), iPad Pro (large screen, aspect ratio).

Publication: App Store Connect, rating, metadata.

Timeline

Game Complexity Timeline
Simple casual (1-3 mechanics, 5-10 levels) 2–4 weeks
Medium project (10+ levels, AI enemies, IAP) 1.5–2 months
Full game with content 2–3 months

Depends strongly on content volume (graphics, sound)—ready assets speed development. Creating from scratch—add design time.