Skip to content

Modernize Swift codelab - #248

Open
morganchen12 wants to merge 2 commits into
masterfrom
mc/modernize
Open

Modernize Swift codelab#248
morganchen12 wants to merge 2 commits into
masterfrom
mc/modernize

Conversation

@morganchen12

@morganchen12 morganchen12 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

DO_NOT_MERGE: codelab text coming soon

@wiz-9635d3485b

Copy link
Copy Markdown

Wiz Scan Summary

Scanner Findings
Vulnerability Finding Vulnerabilities -
Data Finding Sensitive Data -
Secret Finding Secrets -
IaC Misconfiguration IaC Misconfigurations -
SAST Finding SAST Findings 1 Info
Software Management Finding Software Management Findings -
Total 1 Info

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try the Wiz Code extension for VS Code, JetBrains, or Visual Studio.

@morganchen12
morganchen12 requested a review from peterfriese July 31, 2026 20:41

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request modernizes the FriendlyChat iOS codelab by migrating the Swift starter and completed projects from UIKit, Storyboards, and CocoaPods to SwiftUI, Swift Package Manager, and Firebase iOS SDK v12+. Key changes include the removal of legacy view controllers and storyboards, the introduction of SwiftUI views and modern MVVM view models, and updates to the test script. The review feedback highlights several areas for improvement, including handling Google Storage URIs in image loading, preventing out-of-bounds scrolling when messages are empty, replacing deprecated text modifiers, ensuring thread safety for main-actor-isolated state mutations in database observers, and using bash arrays in the test script to avoid word-splitting bugs.

Comment on lines +64 to +70
} else {
let storageRef = Storage.storage().reference(withPath: imageUrl)
if let data = try? await storageRef.data(maxSize: 5 * 1024 * 1024),
let downloadedImage = UIImage(data: data) {
self.image = downloadedImage
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using Storage.storage().reference(withPath:) directly with imageUrl will fail if the URL is a full Google Storage URI (starting with gs://). To ensure robustness and support both relative paths and full URIs, check the prefix and use reference(forURL:) when appropriate.

    } else {
      let storageRef: StorageReference
      if imageUrl.hasPrefix("gs://") {
        storageRef = Storage.storage().reference(forURL: imageUrl)
      } else {
        storageRef = Storage.storage().reference(withPath: imageUrl)
      }
      if let data = try? await storageRef.data(maxSize: 5 * 1024 * 1024),
         let downloadedImage = UIImage(data: data) {
        self.image = downloadedImage
      }
    }

Comment on lines +61 to +65
.onChange(of: messageViewModel.messages.count) { _ in
withAnimation(.easeInOut) {
scrollViewReader.scrollTo(messageViewModel.messages.count - 1, anchor: .bottom)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When messageViewModel.messages is empty, messages.count - 1 evaluates to -1. Attempting to scroll to an invalid index of -1 can cause unexpected behavior or console warnings. Adding a guard check prevents this issue.

            .onChange(of: messageViewModel.messages.count) { count in
              guard count > 0 else { return }
              withAnimation(.easeInOut) {
                scrollViewReader.scrollTo(count - 1, anchor: .bottom)
              }
            }

Comment on lines +45 to +46
.autocapitalization(.none)
.disableAutocorrection(true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The .autocapitalization(_:) and .disableAutocorrection(_:) modifiers are deprecated in iOS 16.0. Since the deployment target is set to iOS 16.0, use the modern .textInputAutocapitalization(_:) and .autocorrectionDisabled(_:) modifiers instead.

Suggested change
.autocapitalization(.none)
.disableAutocorrection(true)
.textInputAutocapitalization(.never)
.autocorrectionDisabled(true)

Comment on lines +54 to +55
.autocapitalization(.none)
.disableAutocorrection(true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The .autocapitalization(_:) and .disableAutocorrection(_:) modifiers are deprecated in iOS 16.0. Since the deployment target is set to iOS 16.0, use the modern .textInputAutocapitalization(_:) and .autocorrectionDisabled(_:) modifiers instead.

Suggested change
.autocapitalization(.none)
.disableAutocorrection(true)
.textInputAutocapitalization(.never)
.autocorrectionDisabled(true)

Comment on lines +32 to +40
refHandle = dbRef.observe(.childAdded) { [weak self] snapshot in
guard let self = self,
var dict = snapshot.value as? [String: Any] else { return }
dict["id"] = snapshot.key
if let data = try? JSONSerialization.data(withJSONObject: dict),
let message = try? JSONDecoder().decode(FriendlyMessage.self, from: data) {
self.messages.append(message)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since FriendlyMessageViewModel is isolated to @MainActor, mutating self.messages directly inside the escaping closure of dbRef.observe can lead to data races and compiler errors under strict concurrency checking (Swift 6). Wrapping the state mutation in a Task { @MainActor in ... } block ensures thread safety.

    refHandle = dbRef.observe(.childAdded) { [weak self] snapshot in
      guard let self = self,
            var dict = snapshot.value as? [String: Any] else { return }
      dict["id"] = snapshot.key
      if let data = try? JSONSerialization.data(withJSONObject: dict),
         let message = try? JSONDecoder().decode(FriendlyMessage.self, from: data) {
        Task { @MainActor in
          self.messages.append(message)
        }
      }
    }

Comment thread test.sh
Comment on lines +7 to +11
if [ -d "${DIR}/FriendlyChat${LANGUAGE}.xcworkspace" ]; then
BUILD_TARGET="-workspace ${DIR}/FriendlyChat${LANGUAGE}.xcworkspace"
else
BUILD_TARGET="-project ${DIR}/FriendlyChat${LANGUAGE}.xcodeproj"
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using unquoted string variables for command arguments like BUILD_TARGET can lead to word-splitting bugs if paths contain spaces. Using a bash array is the standard and robust way to handle multi-part command arguments safely.

Suggested change
if [ -d "${DIR}/FriendlyChat${LANGUAGE}.xcworkspace" ]; then
BUILD_TARGET="-workspace ${DIR}/FriendlyChat${LANGUAGE}.xcworkspace"
else
BUILD_TARGET="-project ${DIR}/FriendlyChat${LANGUAGE}.xcodeproj"
fi
if [ -d "${DIR}/FriendlyChat${LANGUAGE}.xcworkspace" ]; then
BUILD_TARGET=(-workspace "${DIR}/FriendlyChat${LANGUAGE}.xcworkspace")
else
BUILD_TARGET=(-project "${DIR}/FriendlyChat${LANGUAGE}.xcodeproj")
fi

Comment thread test.sh

(xcodebuild \
-workspace ${DIR}/FriendlyChat${LANGUAGE}.xcworkspace \
${BUILD_TARGET} \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When using a bash array for BUILD_TARGET, expand it correctly as "${BUILD_TARGET[@]}" to preserve arguments with spaces.

Suggested change
${BUILD_TARGET} \
"${BUILD_TARGET[@]}" \

@morganchen12
morganchen12 removed the request for review from peterfriese July 31, 2026 20:43
@peterfriese

peterfriese commented Jul 31, 2026

Copy link
Copy Markdown

Apart from my comment about Observation, I'd recommend installing the following skills for up-to-date SwiftUI / Swift Concurrency / view design insights:

and/or using the Xcode 27 skills

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants