프로젝트 기간: 24.03.09 ~
방 탈출을 좋아하는 사람들이 나의 취향을 담은 프로필을 이미지 1장으로 만들 수 있는 서비스.
앱 스토어 링크
블로그 링크
UIKit
, MVVM
, Combine
, Swift Pakage Manager
, Swift Concurrency
, Lottie
, Clean Architecture
회원 가입 | 방탈출 프로필 생성 과정 |
---|---|
Toast Alert | 프로필 이미지 저장 |
---|---|
카카오톡 공유하기 | 방탈출 프로필 내용 편집 |
---|---|
설정 페이지 | 로그아웃 |
---|---|
회원 탈퇴 |
---|
🔒 문제점
이미지로 만들 UIView를 ProfileView로 custom 하여 구현했다. 그 중에서 세로형 화면을 만드는 것에 문제가 생겼다. "저장하기" 버튼을 클릭하면 ProfileView가 UIImage로 변환하여 사용자의 사진첩에 저장된다. 화면에 띄워져야만 rendering을 통해 UIImage로의 저장이 가능한데 3:4 세로형을 만드는 경우 높이를 늘리는 방식이 아닌 넓이를 줄이는 방법을 사용하면, ProfileView에 구현되어 있는 Label의 FontSize로 인해 글자가 잘리고 비율이 맞지 않았다.
-> 그러나 높이를 늘리면 사이즈가 작은 SE의 경우 버튼이 겹쳐서 스크롤을 해야하는 상황이 발생하므로 높이는 고정이어야 했다.
🔑 해결방법
❌ UILabel.autoshrink
자동으로 Label의 크기에 따라 폰트가 일정 비율까지 줄어드는 autoShrink도 고민하였으나, 이렇게 하면 프로필의 각 라인에 따라 폰트 크기가 들쭉날쭉한 문제가 발생했다.
❌ font Size 수정
squareProfileView와 rectangleProfileView를 둘 다 생성하여 들고 있어야 하며 비율에 따라 각 글자들의 사이즈가 조금씩 차이가 나게 된다.
✅ Image로 변환, resize
때문에 뷰가 화면에 display 되기 전에 이미지로 저장하여 해당 image를 resize해 보여주는 방법을 생각했는데, 뷰의 위치가 잡히기 전이라 처음에는 배경색만 Image로 저장되었다. subview인 CardView의 layout이 완성되기 전에 View가 만들어져 버렸기에 ImageView에 빈 이미지가 들어가고 있었다. 때문에 subview들이 다 생성된 후인 viewDidLayoutSubviews()에서 image를 넣어주었다.
버튼을 한번만 눌렀음에도 사진과 같이 팝업화면이 두개 뜨는 문제가 있었다.
🔑 해결방법
현재 이 프로젝트에는 프로필 이미지를 볼 수 있는 순간이 두 번 존재한다.
- 처음에 프로필을 다 생성한 후
- 마이 페이지에서 프로필 카드 버튼을 누른 경우
뷰에는 차이가 있기에 각기 다른 뷰를 만들고 뷰모델만 공유해 줬었는데, 이때 각자 다른 뷰모델을 생성해서 넣어준 것이 아니라 DIManager에서 뷰모델을 하나만 생성해서 그 뷰모델을 두 개의 뷰 모두에 넣어주었다. 때문에 1번 뷰를 지나서 2번 뷰를 온 경우 이미지 저장하기를 누르면 뷰모델이 두 번 작동해서 뷰가 두 번 뜬다. -> 새로운 뷰모델을 생성하여 해결하였다.
🔒 문제점
textField에서 값을 가져올 때 기존 방식이 한박자 느려서 문제가 생겼다.
값이 없는 걸로 인식되어 서버로 넘어가지 않고 오류가 발생하였다.
🔑 해결방법
뷰모델에 @Published var textInput = ""
를 구현하고 receive(on:)
으로 Runloop.main
을 해주었다.
그 후 해당 값을 assign으로 뷰모델에 넘겨주는 방식을 사용했다.
🔒 문제점
카카오톡으로 공유된 프로필 링크를 눌렀을 때 파라미터로 type에는 "profile", value에는 "닉네임값"이 들어와야 했다. 닉네임값이 한글인 경우 카카오에서 자동으로 인코딩을 한 값을 내보내주었기 때문에 다음과 같은 오류가 나왔다.
🔑 해결방법
안드로이드의 경우는 자동으로 다시 한글로 디코딩해주지만, iOS는 아니라서 쿼리 파라미터를 분리하여 딕셔너리를 만들 때 디코딩한 값을 value값으로 넣는 방법으로 해결했다. 이때 removingPercentEncoding를 사용하여 디코딩해주었다.
func queryParams(url: URL) -> Dictionary<String, String> {
var parameters = Dictionary<String, String>()
if let queryComponents = url.query?.components(separatedBy: "&") {
for queryComponent in queryComponents {
let paramComponents = queryComponent.components(separatedBy: "=")
var object: String? = nil
if paramComponents.count > 1 {
object = paramComponents[1].removingPercentEncoding
}
let key = paramComponents[0]
parameters[key] = object
}
}
return parameters
}
🔒 문제점
앱을 한번 실행했을 때는 링크를 타고 들어갔을 때 공유용 프로필 화면이 뜨는데 Suspended 상태에서는 뜨지 않았다.
SceneDelegate에 있는 willConnectTo함수에서 마지막에 self.scene(scene, openURLContexts: connectionOptions.urlContexts)를 불러주었는데, 그럼에도 뜨지 않았다.
기본적으로 앱이 실행되었을 때 window?.rootViewController = SplashView()가 불리는데 이때 SplashView()의 내부에서 작업들이 처리되면서 공유용 프로필 화면이 rootViewController로 설정된 이후에 다시 changeRootViewController()를 통해 화면이 전환되어 보이지 않는 것 같았다.
🔑 해결방법
때문에 분기 처리를 통해 해결했다.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
if connectionOptions.urlContexts.isEmpty {
window?.rootViewController = SplashView()
} else {
DIManager.shared.registerAll()
self.scene(scene, openURLContexts: connectionOptions.urlContexts)
}
window?.makeKeyAndVisible()
}
🔒 문제점
닉네임을 한 번 틀리고 나면 Error를 발행하여 PassthroughSubject가 종료되는 오류가 있었다.
때문에 Error 발생 이후엔 더 이상 다음 버튼이 작동하지 않았다.
PassthroughSubject는 Error를 발행하면 구독을 종료하기에 일어난 문제였다.
🔑 해결방법
고민하다 타입을 PassthroughSubject<Result<Void, Error>,Never>로 바꾸어서 해결했다.
PO_김상희 | Designer_김설아 | Marketer_원성목 |
---|---|---|
iOS_김민송(Mint) | AOS_문장훈 | Backend_김준환 |
---|---|---|
Github Profile | Github Profile | Github Profile |