"Programs should be written for people to read, and only incidentally for machines to execute"
"프로그램은 사람들에게 읽히기 위한 목적으로 만들어져야 하고, 우연히 컴퓨터가 실행할 수 있다면 더욱 좋다."
출처: Structures and Interpretation of Computer Programs 컴퓨터 프로그램의 구조와 해석
영어 | 컨벤션 | |
---|---|---|
O | X | 이질적인 느낌 (ex. Android와 iOS에서 사용되는 단어의 차이) |
X | O | 해석이 오래 걸릴수도 있다. |
O | O | 잘 읽히는 코드 |
잘 읽히는 코드는 영어와 컨벤션을 모두 만족하는 코드이다.
// 주어(view) / 동사(insertSubView) / 목적어(gradientView) / 어떻게(at: 2)
view.insertSubView(gradientView, at: 2)
객체
(주어)가 시작을 하고 메서드가 실행
(동사)하고 파라미터
(목적어)들이 어떻게 사용된다.
위와 같이 코드가 문장처럼 읽히게 된다. 그렇다면 위와 같이 문장처럼 읽히려면 무엇을 지켜야하는가?
// 명사(view) / 동사(insertSubView) / 형용사 + 명사(gradientView) / 전치사(at: 2)
view.insertSubView(gradientView, at: 2)
가장 중요한 첫번째는 올바른 품사를 사용한다. (ex. buyButton = 명사 + 명사)
언어를 배우는 가장 첫 번째 단계는 단어를 알고 그 단어의 품사를 알아야 문장으로 바꿀 수 있는데
품사라는 것은 결국에 단어의 성질을 나타낸다는 부분이 중요하다.
명사를 단독으로 사용하지않고 설명을 붙이게된다.
명사를 꾸밀때는 또 다른 명사
를 붙여서 꾸며주거나 형용사를 붙여서
사용하게된다.
올바른 품사를 사용할 때 가장 걸림돌이 되는 경우가 있는데... 동사가 계속 변형된다는 점이다.
동사 원형(~하다) | 과거형(~했다) | 과거 분사형 |
---|---|---|
request | requested | requested |
make | made | made |
hide | hid | hidden |
- 함수 / 메서드에 사용
- 조동사(can/should/will 등) 뒤에
- e.g. canBecomeFirstResponder
- Life Cycle 관련 delegate
- e.g. didReceive, willAppear, didComplete
- 쓸 일이 없음! 🎉
- 과거분사 = 형용사 라고 생각하면 편하다
- 수동의 의미
- e.g. requestedData, hiddenView
- Bool 변수
- e.g. isHidden, isSelected
명사 | 동사 |
---|---|
request | request |
start | start |
play | play |
- 위 예시와 같이 명사와 동사가 같은 경우도 있음
의미 | 예시 | |
---|---|---|
is + 명사 | ~인가? | isDescandant(of:), isVideo, isFavorite |
is + 현재진행형(~ing) | ~하는 중인가? | isExecuting, isPending |
is + 형용사 | ~인가? ~되었는가? |
isOpaque, isEditable isSelected, isHidden |
can/should/will + 동사원형 | ~할 수 있나?, ~해야하나?, ~할 것인가? | canBecomeFirstResponder |
has + 명사 | ~을 가지고 있는가? | hasVideo, hasiCloudAccount |
has + 과거분사 | ~되었는가? (상태의 지속 강조) | hasConnected, hasEnded |
동사원형 용법 | - | preservesSuperviewLayoutMargins |
❗️ is + 동사원형 |
흔히 하는 실수 | isAuthorize, isDelete, isFind, isAdd |
let album: Album
let albums: [Album]
for album in albums {
}
var album: Album?
let artwork = album.map { downloader.image(from: $0.artworkURL) } // ??
var album: [Album]
let coverSongs = album.map { $0.coverSong } // ??
단수 | 복수 |
---|---|
view | views |
box | boxes |
hash | hashes |
category | categories |
factory | factories |
half | halves |
child | children |
person | people |
index | indexes, indices |
datum | data |
- 외울 필요는 없고 사전을 그때 그때 찾아보면서 진행하자!
// ⛔️
var profileImage: String
var profileImageURL: String
var profileImage: URL
var thumbnail: URL
var thumb: UIImage
var profileImage: UIImage
iOS Framework를 확인해보면 위 경우를 명확하게 사용하는 것을 보여준다.
URL 타입들은 모두 뒤에 URL
단어가 붙어서 정확히 URL 타입을 명시해준다.
var fullSizeImageURL: URL? // PHContentEditingInput
var renderedContentURL: URL // PHContentEditingOutput
var originalURL: URL? // MLMediaObject
var thumbnailURL: URL? // MLMediaObject
var assetURL: URL? // MLMediaItem
var referenceURL: URL // SCNReferenceNode
Image 또한 모두 뒤에 붙어서 타입을 명시해준다.
var referenceImage: ARReferenceImage? // ARImageAnchor
var displaySizeImage: UIImage? // PHContentEditingInput
var fullSizeImage: CIImage // PHLivePhotoEditingContent
var iconImage: NSImage? // MLMediaGroup
var artworkImage: NSImage? // MLMediaObject
위 또한 마찬가지로 프로퍼티에 타입을 다 명시해준다.
var physicalSize: CGSize // ARReferenceImage
var startDate: Date // AVMutable...Group
var creationDate: Date? // PHAsset
var arrivalDate: Date // CLVisit
var adjustmentData: PHAdjustmentData? //PHContentEditingInput
ID vs Id vs identifier
var identifier: AVMetadataIdentifier? { get } // AVMetadataItem
var localIdentifier: String { get } // PHObject
var formatIdentifier: String { get } // PHAdjustmentData
var identifier: String { get } // CLRegion
var identifier: String { get } // MLMediaGroup
var identifier: String { get } // SCNReferenceNode
var identifier: String { get } // NSPersistentStore
var objectID: NSManangeObjectID { get } // NSManageObjectID
var recordID: CKRecordID { get } // CKRecord
var uniqueID: String? { get } // AVMetadataGroup
무조건 identifier로 늘려서 사용하거나 ID의 타입 자체를 새로 만드는 경우인 커스텀 타입에서는 대문자로 사용한다.
그래도 대부분은 늘려서 identifier
를 사용한다.
isHidden vs hidden
var isHidden: Bool { get set }
@property(nonatomic, getter=isHidden) BOOL hidden;
Bool 변수를 작성할 때 is
를 사용해도되고 사용하지 않아도 문법적으로는 문제가 없다.
그래도 애플에서의 컨벤션으로는 앞에 is
를 붙이는 것으로 보인다.
// ⛔️
struct User {
let userID: String
}
let id = user.userID
// ✅
struct User {
let identifier: String
}
let id = user.identifier
// ⛔️
struct ImageDownloader {
func downloadImage(from url: URL) { }
}
let imageDownloader = ImageDownloader()
imageDownloader.downloadImage(from: imageURL)
// ✅
imageDownloader.download(from: imageURL)
imageDownloader.fetch(from: imageURL)
imageManager.download(from: imageURL)
각 프로퍼티와 메서드명을 알맞게 작성하여도 호출부에서는 문장처럼 느껴지지 않을 수 있다.
그러므로 실제로 사용할 때는 문장처럼 보이도록 노력해보자!
Swift는 get을 사용하지 않는 것
이 컨벤션인 것 같다.
// 구현부
func date(from string: String) -> Date?
func anchor(for node: SCNNode) -> ARAnchor?
func distance(from location: CLLocation) -> CLLocationDistance
func track(withTrackID trackID: COMPersistentTrackID) -> AVAssetTrack?
// 호출부
let date = formatter.date(from: dateString)
let imageAnchor = sceneView.anchor(for: node)
let distance = startLocation.distance(from: destination)
let track = asset.track(withTrackID: identifier)
메서드 네이밍에 관해 이야기해보자.
get
fetch
request
execute / perform
통일된 규칙을 정의할 수 없지만, 많이 쓰는 단어들이 위와 같다.
iOS에서보면 위 단어를 명확하게 구분해서 사용한다.
//MARK: - get
//Async - 비동기 작업에서 네이밍 되어짐, completionHandler를 사용
//Fail(실패)할 수 있다. - Error, nil일 가능성이 있는 경우
//User의 권한을 받아올 때 사용된다.
func getPlaylist(with uuid: UUID, creationMetadata: MPMediaPlaylistCreationMetadata?, completionHandler: @escaping (MPMediaPlaylist?, Error?) -> Void)
//MARK: - fetch
//결과를 바로 리턴할때 사용
class func fetchAssets(withLocalIdentifiers identifiers: [String], options: PHFetchOptions?) -> PHFetchResult<PHAsset>
//MARK: - request
//Async - 비동기 작업에서 네이밍 되어짐, resultHandler를 사용
func requestImage(for asset: PHAsset, targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?, resultHandler: @escaping (UIImage?, [AnyHashable: Any]?) -> Void) -> PHImageRequestID
//MARK: - execute / perform
//작업 자체가 request 혹은 클로저에 감싸져 있을 때
func execute(_ request: NSPersistentStoreRequest) throws -> NSPersistentStroeResult
//PHAsset
class func fetchAssets(withLocalIdentifiers identifiers: [String], options: PHFetchOptions?) -> PHFetchResult<PHAsset>
//PHAssetCollection
class func fetchAssets(in assetCollection: PHAssetCollection, options: PHFetchOptions?) -> PHFetchResult<PHAsset>
//NSManageObjectContext
func fetch<T>(_ request: NSFetchRequest<T>) throws -> [T] where T: NSFetchRequestResult
함수의 리턴값으로 결과를 보내주며 작업이 오래걸리지 않고 바로 결과를 알려주는 느낌.
강아지와 공놀이 하는 것을 play fetch
라고 부른다. (물어온다, 당연히 가져와야하는)
- 남이 나의 코드를 읽을 때를 고려
- ⭐️ 동의어 사전 애용(http://www.thesaurus.com)
- 표준 라이브러리 및 프레임워크 읽어보기