Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Cherrish-iOS/Cherrish-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

/* Begin PBXBuildFile section */
5131A5B82F120ED2004214DF /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 5131A5B72F120ED2004214DF /* Alamofire */; };
51DB7DE72F211C9D003095AC /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 51DB7DE62F211C9D003095AC /* Lottie */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand Down Expand Up @@ -41,6 +42,7 @@
buildActionMask = 2147483647;
files = (
5131A5B82F120ED2004214DF /* Alamofire in Frameworks */,
51DB7DE72F211C9D003095AC /* Lottie in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -84,6 +86,7 @@
name = "Cherrish-iOS";
packageProductDependencies = (
5131A5B72F120ED2004214DF /* Alamofire */,
51DB7DE62F211C9D003095AC /* Lottie */,
);
productName = "Cherrish-iOS";
productReference = 51B5D3182F0514390090B8B4 /* Cherrish-iOS.app */;
Expand Down Expand Up @@ -115,6 +118,7 @@
minimizedProjectReferenceProxies = 1;
packageReferences = (
5131A5B62F120ED2004214DF /* XCRemoteSwiftPackageReference "Alamofire" */,
51DB7DE52F211C9D003095AC /* XCRemoteSwiftPackageReference "lottie-ios" */,
);
preferredProjectObjectVersion = 77;
productRefGroup = 51B5D3192F0514390090B8B4 /* Products */;
Expand Down Expand Up @@ -378,6 +382,14 @@
minimumVersion = 5.11.0;
};
};
51DB7DE52F211C9D003095AC /* XCRemoteSwiftPackageReference "lottie-ios" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/airbnb/lottie-ios";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 4.6.0;
};
};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
Expand All @@ -386,6 +398,11 @@
package = 5131A5B62F120ED2004214DF /* XCRemoteSwiftPackageReference "Alamofire" */;
productName = Alamofire;
};
51DB7DE62F211C9D003095AC /* Lottie */ = {
isa = XCSwiftPackageProductDependency;
package = 51DB7DE52F211C9D003095AC /* XCRemoteSwiftPackageReference "lottie-ios" */;
productName = Lottie;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 51B5D3102F0514390090B8B4 /* Project object */;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

import SwiftUI

import Lottie

struct SplashView: View {
@EnvironmentObject private var appCoordinator: AppCoordinator

private let userDefaultService: UserDefaultService

init(userDefaultService: UserDefaultService = DefaultUserDefaultService()) {
self.userDefaultService = userDefaultService
}
Expand All @@ -27,7 +29,8 @@ struct SplashView: View {
endPoint: .bottomTrailing
)

Image(.appicon)
LottieView(animationName: "splash", loopMode: .playOnce)
.frame(width: 130.adjustedW, height: 154.adjustedH)
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// Lottie+.swift
// Cherrish-iOS
//
// Created by 이나연 on 1/21/26.
//

import SwiftUI
import Lottie

struct LottieView: UIViewRepresentable {
let animationName: String
let loopMode: LottieLoopMode

func makeUIView(context: Context) -> UIView {
let view = UIView(frame: .zero)

let animationView = LottieAnimationView(name: animationName)
animationView.loopMode = loopMode
animationView.play()

animationView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(animationView)

NSLayoutConstraint.activate([
animationView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
animationView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
animationView.topAnchor.constraint(equalTo: view.topAnchor),
animationView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])

return view
}

func updateUIView(_ uiView: UIView, context: Context) {}
Comment on lines +15 to +35
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "Lottie+.swift" -type f

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 141


🏁 Script executed:

fd "Lottie\+\.swift" -type f

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 240


🏁 Script executed:

cat -n "./Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/Lottie+.swift"

Repository: TEAM-Cherrish/Cherrish-iOS

Length of output: 1331


🌐 Web query:

UIViewRepresentable updateUIView lifecycle SwiftUI best practices

💡 Result:

Key points & best practices for UIViewRepresentable lifecycle (makeUIView / updateUIView)

  1. Lifecycle basics
  • makeUIView(...) is called once to create the UIKit view. updateUIView(_:context:) is called repeatedly whenever SwiftUI considers the representable’s inputs/state changed; after makeUIView it will be called at least once. Use dismantleUIView(...) for cleanup. (developer.apple.com)
  1. Treat updateUIView as a sync/diff method
  • Don’t recreate subviews or do heavy allocation in updateUIView. Apply only the minimal updates needed (set properties, update models, call setNeedsLayout). That keeps SwiftUI diffing fast and avoids flicker. (sarunw.com)
  1. Use Coordinator for delegates, actions and two‑way bindings
  • Put delegate/dataSource handlers and callbacks in Context.coordinator so delegate lifetime and bridging are correct. Use @Binding / @ObservedObject on the SwiftUI side and propagate changes in updateUIView or via the coordinator. (ios-swift-explorations.hashnode.dev)
  1. Be explicit about what triggers updates
  • SwiftUI calls updateUIView when inputs change; if you need a full rebuild, change the view’s .id(...) to force re‑creation. Don’t rely on implicit side effects—drive updates from bindings/observable objects. (stackoverflow.com)
  1. Threading and performance
  • All UIKit work must run on the main thread. Offload heavy processing (parsing, layout calculations) to background threads and apply final UI updates in updateUIView on the main thread. Profile with Instruments if performance is a concern. (peerdh.com)
  1. Cleanup & appearance hooks
  • Use dismantleUIView to remove observers/notifications. Complement representable lifecycle with SwiftUI’s onAppear/onDisappear in the parent view when appropriate. (fatbobman.com)

Short checklist when implementing:

  • makeUIView: create and configure once.
  • updateUIView: apply incremental changes only; read bindings/observables here.
  • Coordinator: implement delegates, send events back to SwiftUI.
  • Use .id to force recreate only when necessary.
  • Keep UIKit updates on main thread; heavy work off the main thread.
  • Clean up in dismantleUIView and/or onDisappear.

If you want, I can show a compact example demonstrating these patterns.

Citations:


updateUIView가 비어 있어 애니메이션 속성 변경이 반영되지 않음

animationName 또는 loopMode가 변경되어도 updateUIView가 구현되지 않았으므로 UI에 반영되지 않습니다. 특히 동일한 LottieView를 다른 파라미터로 재사용할 때 버그가 발생할 수 있습니다.

Coordinator 패턴을 사용하여 animationView 참조를 유지한 후, updateUIView에서 애니메이션과 루프 모드를 갱신해야 합니다.

🛠️ 제안 수정
 struct LottieView: UIViewRepresentable {
     let animationName: String
     let loopMode: LottieLoopMode

+    func makeCoordinator() -> Coordinator { Coordinator() }
+
     func makeUIView(context: Context) -> UIView {
         let view = UIView(frame: .zero)

         let animationView = LottieAnimationView(name: animationName)
+        context.coordinator.animationView = animationView
         animationView.loopMode = loopMode
         animationView.play()

         animationView.translatesAutoresizingMaskIntoConstraints = false
         view.addSubview(animationView)

         NSLayoutConstraint.activate([
             animationView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
             animationView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
             animationView.topAnchor.constraint(equalTo: view.topAnchor),
             animationView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
         ])

         return view
     }

-    func updateUIView(_ uiView: UIView, context: Context) {}
+    func updateUIView(_ uiView: UIView, context: Context) {
+        guard let animationView = context.coordinator.animationView else { return }
+        if animationView.animation?.name != animationName {
+            animationView.animation = LottieAnimation.named(animationName)
+        }
+        animationView.loopMode = loopMode
+        animationView.play()
+    }
+
+    final class Coordinator {
+        var animationView: LottieAnimationView?
+    }
 }
🤖 Prompt for AI Agents
In `@Cherrish-iOS/Cherrish-iOS/Presentation/Global/Extension/Lottie`+.swift around
lines 15 - 35, makeUIView currently creates a new LottieAnimationView but
updateUIView is empty, so changes to animationName or loopMode aren’t applied;
implement the Coordinator pattern to store a persistent reference (e.g.,
coordinator.animationView) when creating the view in makeUIView, assign the
created LottieAnimationView to that coordinator property, and then implement
updateUIView to compare and update the coordinator.animationView: if
animationName changed load/set the new LottieAnimationView animation, update
loopMode, and restart/play as needed so parameter changes are reflected without
recreating the whole SwiftUI view.

}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,33 +1,153 @@
{
"images" : [
{
"filename" : "app icon.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
"filename" : "40.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "app icon 1.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
"filename" : "60.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"filename" : "29.png",
"idiom" : "iphone",
"scale" : "1x",
"size" : "29x29"
},
{
"filename" : "58.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "87.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"filename" : "80.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "120.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"filename" : "57.png",
"idiom" : "iphone",
"scale" : "1x",
"size" : "57x57"
},
{
"filename" : "114.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "57x57"
},
{
"filename" : "120.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"filename" : "180.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"filename" : "20.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"filename" : "40.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "29.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"filename" : "58.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "40.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"filename" : "80.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "50.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "50x50"
},
{
"filename" : "100.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "50x50"
},
{
"filename" : "72.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "72x72"
},
{
"filename" : "144.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "72x72"
},
{
"filename" : "76.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"filename" : "152.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"filename" : "167.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"filename" : "app icon 2.png",
"idiom" : "universal",
"platform" : "ios",
"filename" : "1024.png",
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions Cherrish-iOS/Cherrish-iOS/Resources/Lottie/splash.json

Large diffs are not rendered by default.