SeSAC
iOS
- 번들 아이디, Bundle Identifier
- IBOutlet Collection 연결 시 weak 수식어를 사용하지 않는 이유
- Alpha vs Opacity
- Image Rendering Mode, Original vs Template
- Raw Strings
TIL
week1
week2
week3
week1
week2
week3
사실 이전에도 BaseViewController를 만들어서 서브클래싱하는 작업을 해왔었는데 어느 순간 프로토콜을 쓰는 게 좋다는 주변 말이 많이 들렸다. 그래서 그런가라는 생각을 나도 덩달아 했었는데, 상속을 하는 구조가 무작정 나쁘다는 생각을 하는 것이 오히려 잘못되지 않았나라는 생각이 들었다. 어차피 상속은 하나의 클래스만 할 수 있고, 모든 컨트롤러에서 공통적으로 쓰여야 하는 내용이라면 하나의 BaseViewController로 상속받아 사용하는 것이 더 낫지 않나 생각이 든다. 개인적으로 그 외적인 것들을 프로토콜로 관리하는 것이 더 맞는 것 같다. 상속과 합성을 적절히 사용하는 것이 가장 적절한 것 같은데 그 정도를 찾기가 정말 어려운 것 같다.
class BaseView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
configureUI()
setConstraints()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") // 런타임 오류
}
func configureUI() {}
func setConstraints() {}
}
코드 베이스로 작업을 할 경우 Controller의 역할, 부담을 좀 더 덜어주기 위해 UI 객체를 한 번 더 덜어내는 작업을 수행한다. 간단하게 말해서 RootView를 교체해주는 작업을 해주는 것인데 RootView를 갈아 끼워주는 작업을 LoadView 메서드에서 수행한다.
override func loadView() {
print(self.view!) // 신기하다.. 여기서 print찍으면 호출이 안 됨
self.view = WriteView()
// print(self.view)
}
self.view에 view를 할당하지 않고, self.view를 호출하면 loadView 메서드가 무한 호출된다.
NotificationCenter.default.post(
name: Notification.Name("Post"),
object: nil,
userInfo: ["name": nameTextField.text!, "value": 123456]
)
NotificationCenter.default.addObserver(
self,
selector: #selector(action),
name: Notification.Name("Post"),
object: nil
)
@objc func action(_ notification: NSNotification) {
if let name = notification.userInfo?["name"] as? String {
self.nameButton.setTitle(name, for: .normal)
}
}
TIL Done day34
의식의 흐름대로 몇 자 적으면서
새싹 과정을 시작하고 벌써 30번째 TIL이다. (1, 2일차 TIL은 어쩌다 보니 빼먹게 되었지만) 참 시간 빠르다... 처음에는 완벽하게 쓰고 싶어서 미루다가 정말 많이 밀렸었는데 이러다가 하나도 못 쓸 것 같아서 간단하게 필기해 뒀던 거 짤막하게 올리는 형태로 바꾸었다. 나중에 다시 찾아볼 수 있는 키워드를 모아놓았다고 생각하면 충분히 좋은 자산이 되지 않을까 싶다. (30일차까지 그래도 몇 자 적으려고 노력한 것은 칭찬한다.)
여기서 자체 피드백을 하자면, TIL은 그날 그날 쓰는게 베스트인 것 같고 키워드가 있다면 그에 대해 고민했던 나의 생각을 최대한 많이 적는 방식으로 보완해나가려고 한다. 그리고 코드 조각도 많이 모아놓으면 좋겠다고 생각이 든다. 사실 30일차까지의 TIL은 내용을 채워넣기에 급급했던 것 같고, 깊이가 많이 부족한 것 같다. 그동안 뭔가 자기 합리화하면서 너무 많이 나태해졌던 것 같고 시간도 막 쓴 것 같다. 조금 더 엄격하게 나를 통제하고, 중간 중간 복습 못 했던 내용 꼭 조금씩이라도 보완해나가보자.
🏞 UIImagePickerController - 커스텀 불가, 사진 촬영 🎥 AVFoundation - 커스텀 (미디어 관련한 세부 처리) 🌆 PHPickerViewController - iOS 14이상, 갤러리 접근, 갤러리 저장, 다중 선택
자세한 작동 방법은 공식 문서를 참고하도록 하자.
let picker = YPImagePicker()
picker.didFinishPicking { [unowned picker] items, _ in
if let photo = items.singlePhoto {
print(photo.fromCamera) // Image source (camera or library)
print(photo.image) // Final image selected by the user
print(photo.originalImage) // original image selected by the user, unfiltered
print(photo.modifiedImage) // Transformed image, can be nil
print(photo.exifMeta) // Print exif meta data of original image.
self.imageView.image = photo.image
}
picker.dismiss(animated: true, completion: nil)
}
present(picker, animated: true, completion: nil)
iOS 14.0 이상 부터는 갤러리 접근, 저장 같은 경우는 PHPickerViewController를 이용하도록 하자.
guard UIImagePickerController.isSourceTypeAvailable(.camera) else {
return
}
picker.sourceType = .camera
picker.allowsEditing = true // 편집화면 등장
present(picker, animated: true)
guard UIImagePickerController.isSourceTypeAvailable(.photoLibrary) else {
return
}
picker.sourceType = .photoLibrary
picker.allowsEditing = false // 편집화면 X
present(picker, animated: true)
if let image = imageView.image {
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
기본적으로 UIImagePickerController는 네비게이션 컨트롤러를 상속 받고 있다. 그래서 아래에 UINavigationControllerDelegate도 함께 채택해주고 있음.
extension CameraViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
// UIImagePickerController - 사진을 선택하거나 카메라 촬영 직후 실행
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
// - 원본, 편집, 메타 데이터 등 infoKey
// - 갤러리 상에 있는 이미지는 originalImage
if let image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
self.imageView.image = image
dismiss(animated: true)
}
}
// UIImagePickerController 5. 취소 버튼 클릭 시
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
// code
}
}
문자열이 아닌 파일, 이미지, PDF 파일 자체가 그대로 전송 되지 않는다. 그래서 파일을 텍스트 형태로 인코딩하여 서버로 전송해야 한다. 결국 이 형태도 body에 담아보내는 post 요청의 한 형태라고 볼 수 있다.
import Alamofire
import SwiftyJSON
@IBAction func clovaFaceButtonTapped(_ sender: UIButton) {
let url = "https://openapi.naver.com/v1/vision/celebrity"
let headers: HTTPHeaders = [
"X-Naver-Client-Id": "-",
"X-Naver-Client-Secret": "-",
"Content-Type": "multipart/form-data"
]
// UIImage를 텍스트 형태(바이너리 타입)로 변환해서 전달
// MIME Type
guard let imageData = imageView.image?.jpegData(compressionQuality: 0.5) else { return }
AF.upload(multipartFormData: { multipartFormData in
multipartFormData.append(imageData, withName: "image")
}, to: url, headers: headers)
.validate(statusCode: 200...500)
.responseData { response in
switch response.result {
case .success(let value):
let json = JSON(value)
print(json)
case .failure(let error):
print(error)
}
}
}
어떤 파일의 종류가 전송되는지 명시해줄 필요가 있다. 여러 파일을 업로드 해야 하는 경우라면 헤더에 Content-Type을 multipart/form-data로 명시해주어야 한다.
// pngData()라는 메서드도 있다.
// 크기가 큰 이미지를 서버로 보내려고 하면 통신이 안되는 경우도 있는데 아래와 같이 압축해서 보내면 해결할 수 있다.
guard let imageData = imageView.image?.jpegData(compressionQuality: 0.5) else { return }
TIL Done day30
urlString ▶️ url ▶️ image
url로 바꾸고 또는 image로 바꾸는 과정을 어디에서 진행하는 것이 좋을까?
마찬가지로 서버 통신을 셀에서 할 경우 셀이 꺼내질 때마다 요청이 들어가 call 수가 증가할 수 있다.
swift에서 구현하기
그 동안 막연하게도 타입 어노테이션(명시)가 컴파일 속도가 더 빠르다고 생각했던지라 아무 생각 없이 타입 명시를 하는 습관을 들이고 있었다. 그러나 최근 듣고 있는 교육 과정에서 멘토님께서 타입 추론을 하는 것이 더 나아보인다라는 말씀을 해주셨고, 왜인지 궁금했던지라 여쭤보게 되었다. 답변 주신 것으로는 컴파일러의 성능이 점점 더 개선되고 있고, 타입 추론이 컴파일 속도가 더 빠르다는 실험 결과도 있다고 답변주셨다.
실제로 아래에 첨부한 링크를 보면 간단한 실험 결과를 확인해볼 수 있다.
let a = "hello, world!" // type is inferred
let b = String("hello, world!") // type is inferred from String(...) and then passed to the root (the constant b)
let c: String = .init("hello, world!") // type inference is not required
let d: String = "hello, world!" // type inference is not required
결과를 보면 1번(타입 추론)을 한 것이 속도가 가장 빨랐다.
Benchmark #1: xcrun swiftc -typecheck a.swift
Time (mean ± σ): 175.7 ms ± 3.5 ms [User: 82.9 ms, System: 81.9 ms]
Range (min … max): 171.0 ms … 182.8 ms 16 runs
Benchmark #1: xcrun swiftc -typecheck b.swift
Time (mean ± σ): 224.8 ms ± 2.8 ms [User: 131.1 ms, System: 81.7 ms]
Range (min … max): 220.2 ms … 228.2 ms 13 runs
Benchmark #1: xcrun swiftc -typecheck c.swift
Time (mean ± σ): 672.3 ms ± 8.0 ms [User: 568.3 ms, System: 93.7 ms]
Range (min … max): 662.4 ms … 685.1 ms 10 runs
Benchmark #1: xcrun swiftc -typecheck d.swift
Time (mean ± σ): 213.3 ms ± 2.0 ms [User: 119.8 ms, System: 81.6 ms]
Range (min … max): 210.2 ms … 216.5 ms 13 runs
나는 타입을 추론하는데 더 많은 리소스가 들 것이라고 그냥 생각하고 있었는데 타입 추론이 속도가 더 빠르다니... 물론 실험 결과 중 1가지이긴 하지만 되게 신기했다. 덧붙여서 멘토님께서는 ***타입을 명시하게 되면 컴파일러가 명시된 타입과 초깃값을 비교하는 작업이 추가되어서 더 느리고, 애초에 타입 명시와 타입 추론 속도 차이 자체도 밀리세컨드 단위 차이로 미세하다***라고 말씀 해주셨다.
컴파일 성능 자체에 영향을 크게 주는 것이 아니라면, 타입 명시를 하나하나 해주는 것은 정말 비효율적인 작업일 것이다. 타입 추론을 하는 방향으로 코드 작성 습관을 바꾸도록 해야겠다. 그리고 확실한 근거 없이 막연히 무엇이 더 좋겠다라고 생각했던 나 자신을 반성하고자 한다.
Done Swift쇼핑 목록 리스트에 Realm Database 구현하기
220824
특성에 맞게 어느 것이 호출되는 것이 더 적절한지 확인해본다.
셀 레이아웃을 잡을 때는 contentView위에 구현해주자.
데이터를 저장한 파일들의 집합체
present를 overCurrentContext, overFullScreen로 실행하고, dismiss하면 viewWillAppear가 실행되지 않는다.
// A.swift
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print(#function)
// 화면 갱신은 화면 전환 코드 및 생명 주기 실행 점검이 필요하다.
// present, overCurrentContext, overFullScreen > viewWillAppear가 실행되지 않는다.
// RootView가 그대로 남아있기 때문이다.
}
...
@objc func plusButtonClicked() {
let vc = WriteViewController()
vc.modalPresentationStyle = .fullScreen
present(vc, animated: true)
}
// 다음도 잘 구분할 수 있으면 클로저의 이해를 도울 수 있다.
func example() {}
example // 함수 자체
example() // 함수 호출
클로저 헤더와 클로저 바디 부분을 잘 이해하자.
{ (매개 변수) -> 리턴 타입 in // 클로저 헤더
실행 구문 // 클로저 바디
}
// 코드를 생략하지 않고 클로저 구문 씀, 함수의 매개변수 내에 클로저가 그대로 들어간 형태
// => 인라인 클로저
getStudyWithMe(study: { () -> () in
print("주말에도 공부하기")
})
// 함수 뒤에 클로저가 실행
// => 트레일링 클로저(= 후행 클로저)
getStudyWithMe() { () -> () in
print("주말에도 공부하기")
}
let result = { (number: Int) -> String in
return "행운의 숫자는 \(number)입니다."
}
randomNumber(result: { (number: Int) -> String in
return "행운의 숫자는 \(number)입니다."
})
randomNumber(result: { (number) in
return "행운의 숫자는 \(number)입니다."
})
randomNumber(result: { (number) in
"행운의 숫자는 \(number)입니다."
})
randomNumber(result: {
"행운의 숫자는 \($0)입니다."
})
randomNumber {
"행운의 숫자는 \($0)입니다."
}
이후에 공부는 해야겠지만 너무 얽매이지 말자.
Type Annotation을 이용하기로 하자. 자세한 내용은 블로그 글 참고 바람.
역할을 분리해서 함수를 작성하자.
UITableViewController만 사용할 수 있으면 얼마나 편할까? 그러나 UITableViewController를 사용하는 경우에는 좀 더 유연한 화면 구성이 어렵다. 이미 화면 전체가 테이블뷰로 가득 차 있어 영역의 크기 조정이 어렵기 때문이다. (이 말인 즉슨 RootView가 UITableView라는 것이다.) 그렇기에 TableView만 따로 올려서 화면을 구성하는 경우가 더 많다.
저장 프로퍼티에서 사용된다. 프로퍼티의 값이 변경될 때마다 호출된다.
var nickname: String {
willSet(newNickname) {
print("유저 닉네임이 \(nickname)에서 \(newNickname)로 변경될 예정이에요")
}
didSet {
print("유저 닉네임 변경 완료!!!! \(oldValue) -> \(nickname)으로 바뀜!")
}
}
static var totalOrderCount = 10 {
willSet {
print("총 주문 건수가 \(totalOrderCount)에서 \(newValue)로 변경될 예정입니다.")
}
didSet {
print("총 주문 건수가 \(oldValue)에서 \(totalOrderCount)로 변경되었습니다.")
}
}
TIL Done day18
SE-0200 Enhancing String Literals Delimiters to Support Raw Text
많은 다른 언어와 마찬가지로 Swift는 이스케이프 문자( \ )를 사용해서 문자열 리터럴 내에서 후속 문자의 특별한 해석을 만듭니다. 이스케이프 문자 시퀀스는 문자열 구분 기호(큰 따옴표), 이스케이프 문자(백 슬래시), 문자열 보간 등에 대한 문자 집합을 나타냅니다.
사실 이스케이프 문자를 사용하는 것은 너무 귀찮은 일이고, 문자열 리터럴 안에 많은 이스케이프 시퀀스가 포함되어 있으면 읽기 어렵습니다. 이를 Apple에서 알아주었는지 Swift 5부터는 조금 더 단순하고 깔끔하게 문자열 리터럴 구문을 작성할 수 있게 되었습니다.
해시(#) 구분 기호를 사용해서 단순하고 깔끔한 구문을 작성할 수 있습니다.
let rawStrings = "Hello, \"Raw Strings\"."
let rawStrings = #"Hello, "Raw Strings"."#
Output)
Hello, "Raw Strings".
Hello, "Raw Strings".
Raw String에서는 모든 문자와 이스케이프 시퀀스가 그대로 출력이 됩니다. 줄바꿈도 하고 싶고, 그냥 백 슬래시도 쓰고 싶은데 어떡하죠? 두 번째 코드처럼 작성하면 문제를 해결할 수 있습니다.
let escapeStrings = #"줄을 바꾸고 싶어요. \n\n 여기서 부터요."#
// output)
// 줄을 바꾸고 싶어요. \n\n 여기서 부터요.
let escapeStrings = #"줄을 바꾸고 싶어요. \#n\#n여기서 부터요."#
// output)
// 줄을 바꾸고 싶어요.
//
// 여기서 부터요.
해시 기호의 개수는 사용하는 부분에서 동일하게 맞춰주기만 한다면 문제가 없습니다. 특별한 이유가 없다면 1개만 쓰는 것이 낫겠죠?
var onlineService = "WhaleOn"
var finalNotice = ##"온라인 라이브 수업은 \##(onlineService)으로 진행합니다."##
// output)
// 온라인 라이브 수업은 WhaleOn으로 진행합니다.
시작과 끝에 해시 기호를 붙여주면 됩니다.
let number: Int = 2
let multiline = #"""
This is
SeSAC \#(number).
"""#
How to use raw strings in Swift 5 아티클을 읽어보면 정규식을 사용할 때에도 Raw String이 유용하게 사용될 수 있다는 것을 확인할 수 있습니다. 기존에 정규식을 작성해 본 사람이라면 백 슬래시()가 많이 사용되는 것을 경험해보셨을 것입니다. 문자열 리터럴 내에서 백 슬래시를 올바르게 사용하려면 또 하나의 백 슬래시를 붙여줘야 했었죠. 이러한 귀찮음, 복잡함을 해시(#) 기호를 붙임으로서 해결할 수 있습니다.
let regex = try NSRegularExpression(pattern: "\\\\\\([^)]+\\)")
let regex = try NSRegularExpression(pattern: #"\\\([^)]+\)"#)
기존 정규식의 사용되던 백 슬래시의 수를 절반으로 줄일 수 있고, 또한 가져온 정규식 표현을 그대로 사용할 수 있다는 장점이 있습니다.
Done Assignment day7setUpError() throws
tearDownWithErrors() throws
함수, 매개변수, 반환값
func greeting(to name: string = "손님")
// 오버로딩 가능
// greeting()
// greeting(to:)
|클래스|구조체| |:--|:--| |참조 타입|값 타입| |상속 o|상속 x| |타입 캐스팅 가능|-| |타입 캐스팅에서 에러가 발생하면 런타임 에러 발생|| |ARC|| ||멤버와이즈 이니셜라이저|
변수 -> 프로퍼티 함수 -> 메서드
데이터와 연관되어 있는 대부분의 녀석들, 근간이 되는 프레임워크
옵셔널에 대해 아는대로 설명하시오.
Product Name Short blurb about what your product does. One to two paragraph statement about your product and what it does. Installation OS X & Linux:
Duration A simple Swift package for measuring and reporting the time taken for operations. It is derived from a version for Playgrounds that I blogged
The debugger tool for iOS developer. Display logs, network request, device informations, crash logs while using the app. Easy accessible with its bubble head button ?? . Easy to integrate in any apps, to handle development or testing apps easier. First version, there is plenty of room for improvement.
Buglife is an awesome bug reporting SDK & web platform for iOS apps. Here's how it works: User takes a screenshot, or stops screen recording User anno
Dotzu In-App iOS Debugging Tool With Enhanced Logging, Networking Info, Crash reporting And More. The debugger tool for iOS developer. Display logs, n
Buglife is an awesome bug reporting SDK & web platform for iOS apps. Here's how it works: User takes a screenshot, or stops screen recording User anno
MeterReporter Lightweight MetricKit-based diagnostics reporting. MeterReporter will capture MetricKit payloads and relay them to a backend. It uses Me
Instabug iOS SDK Instabug is an in-app feedback and bug reporting tool for mobile apps. With just a simple shake, your users or beta testers can repor
Daily News Hey ! Daily News is a news app with good looking user interface ! Apps architecture is MVVM and used RxSwift for binding. Architecture I pr
View Project On Devpost: Built With: PubChem's REST API How To Install Chem Aware: Prerequiste: Latest Version of Xcode and Simulators installed The a
?????? Common categories for daily development. Such as UIKit, Foundation, QuartzCore, Accelerate, OpenCV and more.
News This project is demo project for newyork time apis news feed. Generally thi
Taskaholic An open-source task management app for daily general operations, sepa
HydrationCompanion SwiftUI iOS app for tracking daily hydration, logs to HealthKit! Screenshots: Home view: see all intake logs and progress Settings
Scrumdinger (Work in progress) Apple's SwiftUI Essentials Series An iOS app that helps users manage their daily scrums. To help keep scrums short and
WordleAverage Adds an "Average Guesses" statistic to the viral daily word game W
A simple to use yet very powerful tool for the tech heads looking for daily dose of tech news. This app is built using SwiftUI and also uses catalyst to run on MacOs as well.
Skredvarsel (Avalanche warning) app is an iOS, iPadOS, and macOS application that provides daily avalanche warnings from the Norwegian Avalanche Warning Service API
Sasha is an easy-to-use CLI app for routine designer tasks. Features • Using • Installing • Author • License Features Icon slicing for different platf
Currently developing an App for watchOS 8.5 to help order your tasks daily. The app is a simple approach to the actual Apple App Remainders to become an improved version of it