Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2024 EssentialsDeveloper Tools

WWDC24 · 36 min · Essentials / Developer Tools

Xcode essentials

Edit, debug, commit, repeat. Explore the suite of tools in Xcode that help you iterate quickly when developing apps. Discover tips and tricks to help optimize and boost your development workflow.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

Code shown on screen · 24 snippets

Warning and error annotations swift · at 10:26 ↗
#warning("This is a warning annotation")
#error("This is an error annotation")
Mark comments swift · at 10:58 ↗
// MARK: This is a section title
Placeholder swift · at 14:09 ↗
<#placeholder#>
showStarView() swift · at 17:30 ↗
showStarView()
Breakpoint #1 swift · at 17:51 ↗
let task = URLSession.shared.dataTask(with: cloudURL, completionHandler: handleUpdatesFromCloud)
Breakpoint #2 swift · at 17:53 ↗
videos = loadVideosFromCloud()
Swift error breakpoint swift · at 18:17 ↗
let url = try! getVideoResourceFilePath()
Swift error throw swift · at 18:34 ↗
throw URLLoadError.fileNotFound
Conditional breakpoint swift · at 18:59 ↗
cloudURL.scheme == "https"
Print statement in conditional breakpoint swift · at 19:18 ↗
p "Username is \(cloudURL.user())"
guard clause swift · at 19:44 ↗
guard cloudURLs.allSatisfy({ $0. scheme == "https" }),
    session.configuration.networkServiceType == .video else {
    return
}
p session swift · at 19:56 ↗
p session
p first part of guard clause swift · at 19:58 ↗
cloudURLs.allSatisfy({ $0. scheme == "https" })
p second part of guard clause swift · at 20:02 ↗
p session.configuration.networkServiceType == .video
Random star rating swift · at 20:11 ↗
var starRating: Int {
  let randomStarRating = Int.random(n: 1..<5)
  return randomStarRating
}
Converting starRatingPercentage to Int swift · at 21:16 ↗
var starRating: Int {
  return Int((starRatingPercentage * 5).rounded())
}
print statements for debugging swift · at 21:46 ↗
var releaseDate: Date {
    print("🎬 Entering func \(#function) in \(#fileID)...")
    let currentDate = Date()
    let gregorianCal = Calendar(identifier: .gregorian)
    var components = DateComponents()
    components.year = releaseYear
    print("\(#fileID)@\(#line) \(#function): 📅 releaseYear is \(releaseYear)")
    if releaseYear == gregorianCal.component(.year, from: currentDate) {
        components.month = Int(releaseMonth)
        isNewRelease = true
        print("\(#fileID)@\(#line) \(#function): 🆕 this is a new release!")
    }
    if releaseYear < 2000 {
        isClassicMovie = true
        print("\(#fileID)@\(#line) \(#function): 🎻 this one is a classic!")
    }
    let calendar = Calendar(identifier: .gregorian)
    return calendar.date(from: components)!
}
os_log statements for debugging swift · at 22:09 ↗
var releaseDate: Date {
    os_log(.debug, "🎬 Entering func \(#function) in \(#file)...")
    let currentDate = Date()
    let gregorianCal = Calendar(identifier: .gregorian)
    var components = DateComponents()
    components.year = releaseYear
    os_log(.info, "📅 releaseYear is \(releaseYear)")
    if releaseYear == gregorianCal.component(.year, from: currentDate) {
        components.month = Int(releaseMonth)
        isNewRelease = true
        os_log(.info, "🆕 this is a new release!")
    }
    if releaseYear < 2000 {
        isClassicMovie = true
        os_log(.info, "🎻 this one is a classic!")
    }
    let calendar = Calendar(identifier: .gregorian)
    return calendar.date(from: components)!
}
Sample unit tests swift · at 23:19 ↗
import Testing
@testable import Destination_Video

struct DestinationVideo_UnitTests {
    
    private var library = VideoLibrary()

    // Make sure starRating is returning a percentage
    @Test func testStarRating() async throws {
        for video in library.videos {
            #expect(video.info.starRating > 0)
            #expect(video.info.starRating <= 5)
        }
    }
    
    // Make sure the library loads data from the json file
    @Test func testLibraryLoaded() async throws {
        #expect(library.videos.count > 1)
    }

}
Sample UI tests swift · at 24:15 ↗
import XCTest

final class Destination_VideoUITests: XCTestCase {
    
    private var app: XCUIApplication!

    @MainActor override func setUpWithError() throws {
        // UI tests must launch the application that they test.
        app = XCUIApplication()
        app.launch()
        
        // In UI tests it is usually best to stop immediately when a failure occurs.
        continueAfterFailure = false
    }

    @MainActor func testABeach() throws {
        // Tap the button to load the detail view for the "A Beach" video
        let aBeachButton = app.buttons["A Beach"].firstMatch
        aBeachButton.tap()
        
        // Make sure it has a Play Video button after going to that view
        let playButton = app.buttons["Play Video"]
        XCTAssert(playButton.exists)
        
        // Make sure the star rating for this video contains 4 stars to avoid issue we saw previously where it was only a single star because starRating was incorrectly a percentage instead of an Int
        let theRatingView = app.staticTexts["TheRating"]
        XCTAssert(theRatingView.label.contains("⭐️⭐️⭐️⭐️⭐️"))
    }
    
    @MainActor func testMainView() throws {
        // We should have at least 10 buttons for the various videos
        let buttons = app.buttons
        XCTAssert(buttons.count >= 10)
        
        // Check that the most popular videos have buttons for them
        for expectedVideo in ["By the Lake", "Camping in the Woods", "Ocean Breeze"] {
            XCTAssert(app.buttons[expectedVideo].exists)
        }
    }

    @MainActor func testLaunchPerformance() throws {
        if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
            // This measures how long it takes to launch your application.
            measure(metrics: [XCTApplicationLaunchMetric()]) {
                XCUIApplication().launch()
            }
        }
    }
}
Swift Testing tags swift · at 24:19 ↗
@Test(.tags(.stars)) func testStarRating() async throws {
    for video in library.videos {
        #expect(video.info.starRating > 0)
        #expect(video.info.starRating <= 5)
    }
}

@Test(.tags(.library)) func testLibraryLoaded() async throws {
  #expect(library.videos.count > 1)
}

extension Tag {
  @Tag static var stars: Tag
  @Tag static var library: Tag
}
Running xcodebuild test from the command line bash · at 26:35 ↗
xcodebuild test -scheme DestinationVideo
xcodebuild test -scheme DestinationVideo -testPlan TestAllTheThings
xcodebuild test -scheme DestinationVideo -testPlan TestAllTheThings -only-testing "Destination VideoUITests/testABeach"
Missing Code Coverage swift · at 29:03 ↗
func toggleUpNextState(for video: Video) {
    if !upNext.contains(video) {
        // Insert the video at the beginning of the list.
        upNext.insert(video, at: 0)
    } else {
        // Remove the entry with the matching identifier.
        upNext.removeAll(where: { $0.id == video.id })
    }
    // Persist the Up Next state to disk.
    saveUpNext()
}
Code Coverage executed 5 times swift · at 29:19 ↗
init() {
    // Load all videos available in the library.
    videos = loadVideos()
    // The first time the app launches, set the last three videos as the default Up Next items.
    upNext = loadUpNextVideos(default: Array(videos.suffix(3)))
}

Resources