Skip to main content
April 1, 2026Noble Desktop Publishing Team/11 min read

Class vs. Instance Properties & Methods

Master Swift class design and object-oriented programming fundamentals

Core Concepts You'll Master

Class vs Instance Properties

Learn the fundamental difference between static class properties that belong to the type itself and instance properties that vary between objects.

Methods and Enumerations

Discover how to integrate enums with classes and create both instance and class methods for different use cases.

Object-Oriented Design

Build practical skills with real-world examples using a Puppy class that demonstrates key programming principles.

Topics Covered in This iOS Development Tutorial:

Master advanced Swift programming concepts including enumerations within classes, instance methods implementation, the critical distinction between class and instance properties, and class method fundamentals — essential skills for professional iOS development.

Exercise Overview

Building on your understanding of enumerations, this tutorial demonstrates how to effectively integrate enumerated types with classes in real-world iOS applications. You'll dive deep into the architectural patterns that separate novice from professional developers: understanding when to use class properties versus instance properties, implementing class methods for factory patterns, and creating robust, scalable code structures that form the backbone of production iOS apps.

Tutorial Learning Path

1

Enum Integration

Start with basic enumerations and learn to incorporate them within class structures

2

Instance Methods

Create and use methods that operate on specific instances of your classes

3

Class Properties

Understand static properties that belong to the class itself, not individual instances

4

Advanced Methods

Build class methods that can generate new instances and perform class-level operations

Getting Started

First, we'll set up your development environment with a new Swift playground to experiment with these concepts safely.

  1. Launch Xcode if it isn't already open.

  2. Go to File > New > Playground.

  3. Under iOS, double–click on Blank.

  4. Navigate into Desktop > Class Files > yourname-iOS App Dev 1 Class.

  5. Save the file as: Class Properties and Methods.playground

  6. Click Create.

Playground Setup Requirements

0/4

Basic Enumerations Redux

Before integrating enumerations with classes, let's refresh the fundamentals. Enumerations in Swift are far more powerful than their counterparts in other languages, offering type safety and raw value associations that make them ideal for modeling discrete states in your applications.

  1. We'll need UIKit objects later in this exercise. Instead of deleting the entire template code, delete only the var str line while preserving the import statement.

  2. Create a simple Gender enumeration with two cases. This demonstrates how enums provide type-safe alternatives to string constants:

    import UIKit
    
    enum Gender {
       case male
       case female
    }
  3. Assign the enumeration to a new variable using explicit type declaration. This pattern ensures clarity in your code and prevents type inference ambiguity:

    case female
    }
    
    var gender: Gender = Gender.female
  4. Test the assignment by typing the variable name:

    var gender: Gender = Gender.female
    gender

    You should see female displayed in the right sidebar, confirming the assignment.

  5. Swift's type inference allows for shorthand notation when the type is already established. This reduces code verbosity while maintaining type safety:

    gender =.male
  6. Verify the change by typing the variable name again:

    gender =.male
    gender

    The right sidebar should now display male, demonstrating successful reassignment.

Enum with Raw Values

When associating String raw values with enum cases, specify the enum type as String and ensure all cases use the same data type. This enables accessing values with .rawValue property.

Using Enumerations Within a Class

Now we'll explore how enumerations integrate with classes to create robust data models. This combination is fundamental to iOS development, providing both type safety and object-oriented structure.

  1. Clean up the test code by deleting the following lines:

    var gender: Gender = Gender.female
    gender =.male
    gender
  2. To enable string interpolation in print statements, we'll add raw values to our enumeration. This pattern is essential when you need to convert enum cases to user-readable strings:

    enum Gender: String {
       case male = "male"
       case female = "female"
    }

    By specifying String as the raw value type, each case must have a corresponding string value, ensuring consistency across your application.

  3. Create a new class that will demonstrate enum integration with object-oriented programming:

    case female = "female"
    }
    
    class Puppy {
    
    }
  4. Define instance properties that will store unique data for each object instance. Notice how the enum becomes a property type:

    class Puppy {
       var name: String
       var gender: Gender
       var breed: String
    }

    This demonstrates how custom enumerations integrate seamlessly with Swift's type system, providing the same benefits as built-in types.

  5. Add an initializer to enable object instantiation. Without this, Swift cannot create instances of your class:

    var breed: String
    
    init(name: String, gender: Gender, breed: String) {
       self.name = name
       self.gender = gender
       self.breed = breed
    }
  6. Create your first puppy instance using the initializer. This demonstrates the clean API that well-designed classes provide:

    class Puppy {

    Code Omitted To Save Space

    }
    
    var puppy1 = Puppy(name: "Jack Sparrow", gender:.male, breed: "Sparrow Terrier")

    Notice how the enum shorthand syntax makes the initialization both concise and readable.

  7. Access the instance properties using dot notation, a fundamental pattern in object-oriented programming:

    var puppy1 = Puppy(name: "Jack Sparrow", gender:.male, breed: "Sparrow Terrier")
    puppy1.name

    The right sidebar should display "Jack Sparrow", confirming successful property access.

  8. Test access to all properties to ensure the object was properly constructed:

    puppy1.name
    puppy1.gender
    puppy1.breed

    All values should appear in the right sidebar, demonstrating complete object functionality.

  9. Create additional instances to build a collection of objects for further testing:

    puppy1.breed
    
    var puppy2 = Puppy(name: "Tinkerbell", gender:.female, breed: "New Yorkshire Terrier")
  10. Add two more instances to create a meaningful dataset:

    var puppy3 = Puppy(name: "Siddhartha", gender:.male, breed: "Lotus Setter")
    
    var puppy4 = Puppy(name: "Spocky", gender:.male, breed: "Vulcan Retriever")

Enum Usage: Standalone vs Class Integration

FeatureStandalone EnumEnum in Class
Declarationvar gender: Gender = .malevar gender: Gender // as property
Access MethodDirect variable accessThrough class instance
InitializationImmediate assignmentVia class initializer
ScopeGlobal or localEncapsulated in class
Recommended: Use enums in classes for better encapsulation and object-oriented design

Using Instance Methods in a Class

Instance methods define the behavior that objects can perform. They're crucial for creating interactive, functional objects that go beyond simple data containers. Let's implement a method that demonstrates how objects can present themselves to users.

  1. Add an instance method that enables objects to introduce themselves. This pattern is common in iOS apps for generating user-facing content:

    init(name: String, gender: Gender, breed: String) {

    Code Omitted To Save Space

    }
    
    func sayHi() {
       print("Hi there! My name is \(name). I am a \(gender.rawValue) \(breed)\n")
    }

    The gender.rawValue property accesses the string value we associated with each enum case, demonstrating the power of raw value enumerations.

  2. Invoke the instance method using dot notation, the standard pattern for calling object methods:

    puppy1.name
    puppy1.gender
    puppy1.breed
    puppy1.sayHi()
  3. Open the Debug area to see print output by clicking the top right middle button show hide debug area.

  4. Verify the output in the Debug area, where you should see Hi there! My name is Jack Sparrow. I am a male Sparrow Terrier.

    The right sidebar shows only Puppy because complex operations like print statements don't display their results there — they appear in the Debug area instead.

  5. Execute the sayHi method for all puppy instances to see how each object maintains its unique state:

    var puppy2 = Puppy(name: "Tinkerbell", gender:.female, breed: "New Yorkshire Terrier")
    puppy2.sayHi()
    
    var puppy3 = Puppy(name: "Siddhartha", gender:.male, breed: "Lotus Setter")
    puppy3.sayHi()
    
    var puppy4 = Puppy(name: "Spocky", gender:.male, breed: "Vulcan Retriever")
    puppy4.sayHi()

    The Debug area should now display four distinct print statements, each reflecting the individual object's properties.

  6. Demonstrate property mutability by modifying an object after creation:

    var puppy4 = Puppy(name: "Spocky", gender:.female, breed: "Vulcan Retriever")
    puppy4.sayHi()

    The print statement immediately reflects Spocky's gender change to female, showing how object properties can be modified at initialization.

  7. Further demonstrate mutability by changing properties after object creation:

    var puppy4 = Puppy(name: "Spocky", gender:.female, breed: "Vulcan Retriever")
    puppy4.gender =.male
    puppy4.sayHi()

    Spocky becomes male again, illustrating how instance properties can be modified throughout an object's lifecycle.

Instance methods perform an action on an instance of a type, accessing and using the specific data of that particular object.
This fundamental concept explains why each puppy can say hi with their own unique information
Debug Area vs Sidebar Output

Complex print statements appear in the Debug area at the bottom of Xcode, while simple values show in the right sidebar. Use print() for detailed formatted output.

The Difference Between Class & Instance Properties

Understanding the distinction between class and instance properties is fundamental to architecting scalable iOS applications. Instance properties store data unique to each object, while class properties (also called static properties or singletons) store data shared across all instances of a class. This pattern is essential for managing global state and implementing factory methods.

  1. Create a class property using the static keyword. This creates a single, shared property that belongs to the class itself rather than any individual instance:

    class Puppy {
       static var puppies = [Puppy]()

    This shared array will contain all Puppy instances, providing a centralized registry that's accessible from anywhere in your application.

  2. Attempt to access the class property incorrectly to understand the distinction:

    puppy1.sayHi()
    
    puppy1.puppies

    This generates an error because puppies belongs to the class, not to individual instances. This distinction prevents confusion about data ownership and scope.

  3. Access the class property correctly by referencing the class name:

    Puppy.puppies
  4. Observe that the error disappears and the right sidebar shows [], indicating an empty array.

    Class properties like this are invaluable for implementing patterns like object pools, caches, or registries that need to be accessible globally while maintaining clean architectural boundaries.

  5. Add an instance property to track each puppy's position in the shared array:

    static var puppies = [Puppy]()
    var puppyIndex: Int
    var name: String

    This creates a red error because our initializer doesn't handle the new property, but we'll resolve this shortly.

  6. Create a computed property that provides a user-friendly numbering system:

    var puppyIndex: Int
    var number: Int { return puppyIndex + 1 }
    var name: String

    Computed properties derive their values from other properties, providing calculated data without storing additional state. This is more efficient and reduces the chance of data inconsistency.

  7. Update the initializer to handle the new property and automatically register each instance:

    self.breed = breed
    puppyIndex = Puppy.puppies.count

    By setting the index to the current array count, each new puppy gets the next available position, starting from 0.

  8. Complete the registration process by adding each new instance to the shared array:

    puppyIndex = Puppy.puppies.count
    Puppy.puppies.append(self)
  9. Test the registration system by accessing puppies through the class property:

    puppy4.sayHi()
    
    Puppy.puppies[0].name

    This demonstrates how class properties enable you to access any instance through a centralized registry.

  10. Copy this line to test access to all registered puppies:

    Puppy.puppies[0].name

  11. Create additional access tests for all instances:

    Puppy.puppies[0].name
    Puppy.puppies[1].name
    Puppy.puppies[2].name
    Puppy.puppies[3].name

    The right sidebar should display all four puppy names, confirming that automatic registration works correctly.

  12. Enhance the sayHi() method to include the computed number property:

    func sayHi() {
       print("Hi there! I'm puppy number \(number), and my name is \(name). I am a \(gender.rawValue) \(breed)\n")
    }

    The Debug area should update to show puppy numbers 1–4, demonstrating how computed properties integrate seamlessly with other object functionality.

Property Types Comparison

FeatureInstance PropertiesClass Properties
Declarationvar name: Stringstatic var puppies = [Puppy]()
Access Patternpuppy1.namePuppy.puppies
UniquenessDifferent per instanceSingle copy for entire class
Use CasesIndividual object dataShared class-wide data
Recommended: Use instance properties for data that varies between objects, class properties for shared data
Common Access Error

Class properties cannot be accessed through instances. Use ClassName.property, not instanceName.property, or you'll get a compilation error.

Class Methods

Class methods operate on the class itself rather than individual instances, making them ideal for factory patterns, utility functions, and operations that affect multiple instances. In production iOS apps, class methods are commonly used for creating objects with specific configurations or performing batch operations.

  1. Create a class method that coordinates behavior across all instances. This demonstrates how class methods can orchestrate complex operations:

    func sayHi() {
       print("Hi there! I'm puppy number \(number), and my name is \(name). I am a \(gender.rawValue) \(breed)\n")
    }
    
    class func listPuppies() {
    
    }
  2. Implement the method to iterate through all registered instances and coordinate their behavior:

    class func listPuppies() {
       print("Puppies, please say hi, politely in turn\n")
       for puppy in puppies { puppy.sayHi() }
       print("Thank you, puppies\n")
    }

    This pattern is powerful for implementing batch operations, data processing, or coordinated UI updates across multiple objects.

  3. Attempt to call the class method incorrectly to reinforce the distinction between class and instance methods:

    Puppy.puppies[3].name
    
    puppy4.listPuppies()

    The error message confirms that static methods belong to the class, not individual instances, maintaining clear architectural boundaries.

  4. Call the class method correctly using the class name:

    Puppy.listPuppies()

    The Debug area should display coordinated output from all puppies, demonstrating how class methods can orchestrate complex behaviors.

  5. Implement a factory method that creates instances with randomized properties. Factory methods are essential for creating objects with complex initialization logic:

    print("Thank you, puppies\n")
    }
    
    class func spawnRandomPuppy() -> Puppy {
    
    }

    The return type annotation indicates this method produces a new Puppy instance, following the factory pattern common in iOS development.

  6. Declare variables for the randomized properties:

    class func spawnRandomPuppy() -> Puppy {
       var randomName: String
       var randomGender: Gender
       var randomBreed: String
    }
  7. Set a consistent name for all randomly generated puppies:

    var randomBreed: String
    
    randomName = "…I think I'm a clone…"
  8. Generate a random number to drive gender selection using iOS's built-in random number generator:

    randomName = "…I think I'm a clone…"
    let randomGenderSeed = arc4random_uniform(2)

    The arc4random_uniform function returns 0 or 1, matching our two enum cases perfectly. This function is part of UIKit, which is why we preserved the import statement.

  9. Use Swift's ternary conditional operator to assign gender based on the random number:

    let randomGenderSeed = arc4random_uniform(2)
    randomGender = randomGenderSeed == 0 ?.female :.male

    The ternary operator provides concise conditional logic: if the seed equals 0, assign female; otherwise, assign male. This pattern is efficient for simple conditional assignments.

  10. Complete the property assignments with a breed that matches the theme:

    randomGender = randomGenderSeed == 0 ?.female :.male
    randomBreed = "…Yeah, I'm definitely a clone :'(…"
  11. Return a new instance using the randomized properties, completing the factory method pattern:

    randomBreed = "…Yeah, I'm definitely a clone :'(…"
    
    return Puppy(name: randomName, gender: randomGender, breed: randomBreed)
  12. Test the factory method by creating a random puppy instance:

    Puppy.listPuppies()
    
    let randomPuppy1 = Puppy.spawnRandomPuppy()

    Notice that we call the class method on the Puppy class itself, not on an instance, following the established pattern.

  13. Verify that the random puppy was automatically registered by checking its number:

    let randomPuppy1 = Puppy.spawnRandomPuppy()
    randomPuppy1.number

Class Method Capabilities

Instance Generation

Create and return new instances of the class with methods like spawnRandomPuppy() that generate objects with random or computed properties.

Collection Operations

Operate on class-wide data like the puppies array, performing actions across all instances such as listing all puppies.

Utility Functions

Provide class-level functionality that doesn't require a specific instance, like random generation or bulk operations.

Key Takeaways

1Instance properties vary between different objects of the same class, while class properties (marked with static) are shared across all instances and belong to the class itself
2Enumerations can be integrated into classes as property types, providing type-safe value options that can include raw values for string conversion
3Instance methods operate on specific objects and can access their unique property values, while class methods operate at the class level and cannot access instance-specific data
4Class properties are accessed using the class name (ClassName.property), not through instances, and represent singleton data shared across all objects
5Computed properties like 'number' derive their values from other properties, enabling user-friendly data presentation without storing redundant information
6Class methods can generate new instances and perform operations on class-wide collections, providing factory-like functionality for object creation
7The self keyword is only required when there's ambiguity between local variables and class properties, otherwise Swift can infer the correct reference
8Array indexing starts at 0, but computed properties can provide more intuitive numbering systems for user-facing data presentation

RELATED ARTICLES