1. Date Detector(UITextView)
- TextView에서 전화번호, 주소 등을 자동으로 감지하고 이동 기능을 제공.
- editable을 false로 설정해야, 링크를 탭했을때 원하는 기능을 이용할 수 있음.


2. Segue 방식으로 이전 뷰로 돌아가는법, unwind
- unwind: 세그먼트 방식으로 화면전환시 뒤로가기, 기존 VC로 돌아가기
1. 돌아갈 ViewController에서 직접 IBAction을 구현(unwind/segue)

2. PresentedViewController에서 dismiss 기능을 추가할 버튼에, 위에서 구현한 Action Segue를 연결

세그먼트는 이동할 때마다 기존 화면의 위에 새로운 씬이 쌓이는 개념임.
이전 씬으로 돌아갈때 unwind 방식으로 하지 않고 세그를 연결하면, 계속 새로운 화면을 쌓아 올리는 것이다.
이전화면으로 돌아갈때는 세그먼트를 해제하는 unwind로 하기.
3. Fixed width constraints may cause clipping.


1. Label의 width 제약이나 leading, trailing 제약을 ≦ 로 줘서 대응하기.
2. Label의 AutoShrink를 설정해서 해결하기.
3. Label의 adjustsFontSizeToFitWidth = true로 설정해서 폰트가 라벨을 초과할 경우, 라벨의 크기에 딱 맞게 조절하기.
4. viewIsAppearing(_:) - iOS 17.0 +
- WWDC23에서 새로 등장한 개념으로, viewWillAppear가 호출된 후 호출됨.
- 뷰 계층에 뷰컨트롤러의 뷰를 추가한 시점.
- 이 시점에 뷰컨트롤러와 뷰는 갱신된 trait collection을 받게된다.
- 뷰의 displaying과 관련된 작업을 여기서 진행하면 된다.

- Trait과 Geometry는 viewIsAppearing(_:)에서 업데이트되므로, 여기서 뷰를 업데이트 하길,,
공식문서에서 설명하는 viewWillAppear를 사용해야하는 시점은
1. 뷰의 전한이 시작되기 전에 콜백이 필요할 때.
2. 뷰컨트롤러나 뷰의 trait이 필요없는 작업은 여기서 진행.

viewIsAppearing(_:) | Apple Developer Documentation
Notifies the view controller that the system is adding the view controller’s view to a view hierarchy.
developer.apple.com
5. Property Wrapper
- Swift 5.1(2019)에서 추가된 기능.
- SwiftUI에서 나오는 @Published, @Binding, @ObservedObject, @State 등이 Property Wrapper임.
- 프로퍼티 자체에 로직을 연결하여 보일러 플레이트 코드를 줄이고, 코드 재사용성을 높여줌.
- 사용예시: UserDefault 관련
@propertyWrapper
struct UserDefault<T> {
let key: String
let defaultValue: T
var wrappedValue: T {
get { UserDefaults.standard.object(forKey: self.key) as? T ?? self.defaultValue }
set { UserDefaults.standard.set(newValue, forKey: self.key) }
}
}
프로퍼티 래퍼로 UserDefaults에 값을 저장하고 불러오는 UserDefault 타입 선언.
final class UserDefaultsManager {
static let shared = UserDefaultsManager()
private let standard = UserDefaults.standard
private init() {}
@UserDefault(key: Constants.UserDefaults.isLoggedIn.key, defaultValue: false)
var isLoggedIn: Bool
@UserDefault(key: Constants.UserDefaults.email.key, defaultValue: "")
var email: String
@UserDefault(key: Constants.UserDefaults.password.key, defaultValue: "")
var password: String
@UserDefault(key: Constants.UserDefaults.nickname.key, defaultValue: "")
var nickname: String
@UserDefault(key: Constants.UserDefaults.save.key, defaultValue: 0)
var saveCount: Int
}
extension UserDefaultsManager {
/// 로그인 여부 정보를 초기화합니다
/// 앱이 종료될 때 호출하세요
func terminateApp() {
isLoggedIn = false
}
}
기존에는 메서드를 통해 UserDefaults에 저장하고 불러오는 로직이 반복됨.
프로퍼티 래퍼를 통해 메서드로 접근하는 것이 아닌, 프로퍼티에 바로 접근 할 수 있게됨.
func saveSignInInformation() {
userDefaultsManager.email = emailTextField.text!
userDefaultsManager.password = passwordTextField.text!
userDefaultsManager.nickname = nicknameTextField.text!
// 저장 버튼 클릭 횟수 저장 기능
// 1. 저장된 횟수 가지고 오기
// 2. 저장된 횟수에 1을 더하기
// 3. 더한 값을 다시 저장함
userDefaultsManager.saveCount += 1
saveResultLabel.text = "\(userDefaultsManager.saveCount)"
}
}
다만 위 예시보다는 문자열의 타당성 검사(regex) 등에 활용하는게 좀 더 합리적이라는 생각이 들었다. 뭔가 위에 코드처럼 인스턴스의 프로퍼티에 직접 접근하는 것 보다는 값을 저장하고 받아오는 행위를 메서드를 통해서 하는게 더 바람직하다는 생각때문에,,?(캡슐화, 은닉화 관점에서)
6. 지연저장 프로퍼티와 타입 프로퍼티
지연저장 프로퍼티: lazy
- lazy 키워드를 통해 선언한 프로퍼티는, 해당 프로퍼티가 실제로 사용되기 전까지는 메모리에 올라가지 않는다.
- 상수는 인스턴스가 생성되기 전에 값을 항상 가지고 있어야 함. 따라서 변수로만 선언 가능.
- lazy 키워드를 사용할 경우 initialize 되기 전에도 self 키워드로 자기자신에 접근할 수 있다.
타입 프로퍼티: static
- 호출하는 순간에 메모리에 올라감.
- 앱을 종료할 때 까지 메모리에 유지됨.
- 메모리의 데이터 영역에 저장됨.
- 지연저장 프로퍼티의 형태로 기본적으로 동작함.
7. 기타
1. String ↔️ ASCII
// ASCII -> Int
Int(UnicodeScalar("A").value)
// Int -> ASCII
String(UnicodeScalar(65)!)
2. 문서화 주석 단축키: command + option + /




3. 스토리보드 UI
Q: 스토리보드를 사용할때 세부적인 속성값은 Interface Builder를 사용하는지, 아니면 코드로 구현하는지.
A: 스토리보드를 사용하더라도 전반적인 레이아웃만 잡아주는 용도로 주로 사용하고, 세부 속성값은 코드로 작성하는 편!
Q: 화면 전환 방식으로 세그먼트를 자주 사용하는지.
A: 케바케, 하지만 값 전달적인 측면에서는 세그보다는 present 방식을 주로 사용한다.
4. 데이터 직렬화와 역직렬화
따로 글로 정리할 예정
5. fatalError()의 사용에 대해서
특히 Cell 재사용시 타입 캐스팅(바인딩)하는 과정에서, 습관적으로 fatalError()를 사용하고 있었다.
과거에 fatalError를 쓰는 이유로, 서비스 단계에서 앱 충돌은 절대 발생하지 않을 것임을 확신하기 위해 fatalError를 통해 개발단계에서 미리 상황에 대처한다... 라는 느낌의 글을 본 적이 있었다. 옵셔널 강제 추출을 절대로 쓰지 않았었지만 그 글을 보고 난 뒤 습관적으로 타입캐스팅(바인딩) 과정에서 실패하는 케이스로 fatalError를 수행해왔던 것 같다.
질문을 통해 fatalError를 쓰는 것은 그렇게 권장할 일은 아니라는 답변을 받을 수 있었다. 옵셔널 강제 추출을 사용하지 않는거랑 같은 맥락이다. 결국은 그마저 앱 충돌을 야기할 수 있는 위험성이다. 따라서, 어떤 형태로든 앱 충돌의 가능성을 차단하는 방향으로 항상 고민하고 코딩하자.

'SeSAC' 카테고리의 다른 글
8.7 ~ 8.11 TIL (0) | 2023.08.15 |
---|---|
7.31 ~ 8.4 TIL (0) | 2023.08.09 |
7.20 ~ 21 TIL (0) | 2023.07.26 |
7.18~19 TIL(새싹 iOS 1,2일차/Two days I Learned) (0) | 2023.07.20 |
SeSAC iOS 3기 시작 (0) | 2023.07.15 |
1. Date Detector(UITextView)
- TextView에서 전화번호, 주소 등을 자동으로 감지하고 이동 기능을 제공.
- editable을 false로 설정해야, 링크를 탭했을때 원하는 기능을 이용할 수 있음.


2. Segue 방식으로 이전 뷰로 돌아가는법, unwind
- unwind: 세그먼트 방식으로 화면전환시 뒤로가기, 기존 VC로 돌아가기
1. 돌아갈 ViewController에서 직접 IBAction을 구현(unwind/segue)

2. PresentedViewController에서 dismiss 기능을 추가할 버튼에, 위에서 구현한 Action Segue를 연결

세그먼트는 이동할 때마다 기존 화면의 위에 새로운 씬이 쌓이는 개념임.
이전 씬으로 돌아갈때 unwind 방식으로 하지 않고 세그를 연결하면, 계속 새로운 화면을 쌓아 올리는 것이다.
이전화면으로 돌아갈때는 세그먼트를 해제하는 unwind로 하기.
3. Fixed width constraints may cause clipping.


1. Label의 width 제약이나 leading, trailing 제약을 ≦ 로 줘서 대응하기.
2. Label의 AutoShrink를 설정해서 해결하기.
3. Label의 adjustsFontSizeToFitWidth = true로 설정해서 폰트가 라벨을 초과할 경우, 라벨의 크기에 딱 맞게 조절하기.
4. viewIsAppearing(_:) - iOS 17.0 +
- WWDC23에서 새로 등장한 개념으로, viewWillAppear가 호출된 후 호출됨.
- 뷰 계층에 뷰컨트롤러의 뷰를 추가한 시점.
- 이 시점에 뷰컨트롤러와 뷰는 갱신된 trait collection을 받게된다.
- 뷰의 displaying과 관련된 작업을 여기서 진행하면 된다.

- Trait과 Geometry는 viewIsAppearing(_:)에서 업데이트되므로, 여기서 뷰를 업데이트 하길,,
공식문서에서 설명하는 viewWillAppear를 사용해야하는 시점은
1. 뷰의 전한이 시작되기 전에 콜백이 필요할 때.
2. 뷰컨트롤러나 뷰의 trait이 필요없는 작업은 여기서 진행.

viewIsAppearing(_:) | Apple Developer Documentation
Notifies the view controller that the system is adding the view controller’s view to a view hierarchy.
developer.apple.com
5. Property Wrapper
- Swift 5.1(2019)에서 추가된 기능.
- SwiftUI에서 나오는 @Published, @Binding, @ObservedObject, @State 등이 Property Wrapper임.
- 프로퍼티 자체에 로직을 연결하여 보일러 플레이트 코드를 줄이고, 코드 재사용성을 높여줌.
- 사용예시: UserDefault 관련
@propertyWrapper
struct UserDefault<T> {
let key: String
let defaultValue: T
var wrappedValue: T {
get { UserDefaults.standard.object(forKey: self.key) as? T ?? self.defaultValue }
set { UserDefaults.standard.set(newValue, forKey: self.key) }
}
}
프로퍼티 래퍼로 UserDefaults에 값을 저장하고 불러오는 UserDefault 타입 선언.
final class UserDefaultsManager {
static let shared = UserDefaultsManager()
private let standard = UserDefaults.standard
private init() {}
@UserDefault(key: Constants.UserDefaults.isLoggedIn.key, defaultValue: false)
var isLoggedIn: Bool
@UserDefault(key: Constants.UserDefaults.email.key, defaultValue: "")
var email: String
@UserDefault(key: Constants.UserDefaults.password.key, defaultValue: "")
var password: String
@UserDefault(key: Constants.UserDefaults.nickname.key, defaultValue: "")
var nickname: String
@UserDefault(key: Constants.UserDefaults.save.key, defaultValue: 0)
var saveCount: Int
}
extension UserDefaultsManager {
/// 로그인 여부 정보를 초기화합니다
/// 앱이 종료될 때 호출하세요
func terminateApp() {
isLoggedIn = false
}
}
기존에는 메서드를 통해 UserDefaults에 저장하고 불러오는 로직이 반복됨.
프로퍼티 래퍼를 통해 메서드로 접근하는 것이 아닌, 프로퍼티에 바로 접근 할 수 있게됨.
func saveSignInInformation() {
userDefaultsManager.email = emailTextField.text!
userDefaultsManager.password = passwordTextField.text!
userDefaultsManager.nickname = nicknameTextField.text!
// 저장 버튼 클릭 횟수 저장 기능
// 1. 저장된 횟수 가지고 오기
// 2. 저장된 횟수에 1을 더하기
// 3. 더한 값을 다시 저장함
userDefaultsManager.saveCount += 1
saveResultLabel.text = "\(userDefaultsManager.saveCount)"
}
}
다만 위 예시보다는 문자열의 타당성 검사(regex) 등에 활용하는게 좀 더 합리적이라는 생각이 들었다. 뭔가 위에 코드처럼 인스턴스의 프로퍼티에 직접 접근하는 것 보다는 값을 저장하고 받아오는 행위를 메서드를 통해서 하는게 더 바람직하다는 생각때문에,,?(캡슐화, 은닉화 관점에서)
6. 지연저장 프로퍼티와 타입 프로퍼티
지연저장 프로퍼티: lazy
- lazy 키워드를 통해 선언한 프로퍼티는, 해당 프로퍼티가 실제로 사용되기 전까지는 메모리에 올라가지 않는다.
- 상수는 인스턴스가 생성되기 전에 값을 항상 가지고 있어야 함. 따라서 변수로만 선언 가능.
- lazy 키워드를 사용할 경우 initialize 되기 전에도 self 키워드로 자기자신에 접근할 수 있다.
타입 프로퍼티: static
- 호출하는 순간에 메모리에 올라감.
- 앱을 종료할 때 까지 메모리에 유지됨.
- 메모리의 데이터 영역에 저장됨.
- 지연저장 프로퍼티의 형태로 기본적으로 동작함.
7. 기타
1. String ↔️ ASCII
// ASCII -> Int
Int(UnicodeScalar("A").value)
// Int -> ASCII
String(UnicodeScalar(65)!)
2. 문서화 주석 단축키: command + option + /




3. 스토리보드 UI
Q: 스토리보드를 사용할때 세부적인 속성값은 Interface Builder를 사용하는지, 아니면 코드로 구현하는지.
A: 스토리보드를 사용하더라도 전반적인 레이아웃만 잡아주는 용도로 주로 사용하고, 세부 속성값은 코드로 작성하는 편!
Q: 화면 전환 방식으로 세그먼트를 자주 사용하는지.
A: 케바케, 하지만 값 전달적인 측면에서는 세그보다는 present 방식을 주로 사용한다.
4. 데이터 직렬화와 역직렬화
따로 글로 정리할 예정
5. fatalError()의 사용에 대해서
특히 Cell 재사용시 타입 캐스팅(바인딩)하는 과정에서, 습관적으로 fatalError()를 사용하고 있었다.
과거에 fatalError를 쓰는 이유로, 서비스 단계에서 앱 충돌은 절대 발생하지 않을 것임을 확신하기 위해 fatalError를 통해 개발단계에서 미리 상황에 대처한다... 라는 느낌의 글을 본 적이 있었다. 옵셔널 강제 추출을 절대로 쓰지 않았었지만 그 글을 보고 난 뒤 습관적으로 타입캐스팅(바인딩) 과정에서 실패하는 케이스로 fatalError를 수행해왔던 것 같다.
질문을 통해 fatalError를 쓰는 것은 그렇게 권장할 일은 아니라는 답변을 받을 수 있었다. 옵셔널 강제 추출을 사용하지 않는거랑 같은 맥락이다. 결국은 그마저 앱 충돌을 야기할 수 있는 위험성이다. 따라서, 어떤 형태로든 앱 충돌의 가능성을 차단하는 방향으로 항상 고민하고 코딩하자.

'SeSAC' 카테고리의 다른 글
8.7 ~ 8.11 TIL (0) | 2023.08.15 |
---|---|
7.31 ~ 8.4 TIL (0) | 2023.08.09 |
7.20 ~ 21 TIL (0) | 2023.07.26 |
7.18~19 TIL(새싹 iOS 1,2일차/Two days I Learned) (0) | 2023.07.20 |
SeSAC iOS 3기 시작 (0) | 2023.07.15 |