Swift

    [WatchOS] Tuist로 WatchOS XCode Project 생성하기

    1. WatchOS 개발을 위한 첫 단계 이전까지 iOS App 개발을 위해서 Tuist를 사용했지만, WatchOS App Target으로 Tuist를 생성한 적이 없어서 꽤나 어려웠다. iOS처럼 단순하게 아래처럼 생성하게 된 기본 Project.swift 파일로 destination을 iOS에서 WatchOS로만 바꾸면 해결될 것으로 기대했다. Project는 생성되었을 지 몰라도 정상적으로 빌드 및 실행이 되지 않았다. tuist init {projectName} 이런 삽질을 시간이 지나 까먹어서(..) 다시 하지 않는 불상사를 저지르지 않고자 기록한다! 결론만 보고 싶다면 가장 최 하단에 코드를 남겨 놓으니 참고해주세요! 2. Project.swift 최대한 간결하게 Project.swift를 ..

    [iOS] 유저에게 앱스토어 리뷰 요청을 해보자

    1. 요구사항 체크 리뷰 요청 기능에 대해서는 꽤나 오랜 시간동안 팀 내에서 이야기가 나왔어요. 별점 5점을 받고자 하는 것도 있었지만, 실제로 유저가 사용하면서 느끼고 있는 생각을 듣고 싶었거든요. 그래서 백업 기능 이후에 리뷰 요청 기능에 대해서 개발하자고 팀원과 이야기 하게 되었고, 제가 백업 기능 개발을 하고 있는 시간에 디자이너 팀원들은 리뷰 요청 기능을 먼저 기획/디자인을 시작했어요. 1-1. UI BottomSheet을 통해서 리뷰 요청 팝업이 뜰 수 있도록 하는 요구사항이 핵심입니다! 어느 하나의 뷰에 종속되지 않았기 때문에, 실제로 어떤 뷰에서 띄워야 하는 지가 가장 중요한 요구사항이라고 생각했는데요. 그러기 위해서는 UX Flow를 참고해 어떤 뷰에서 떠야 하는지 자세히 체크해야, Bo..

    [iOS] 출시 후 첫 신규기능 <온보딩> 업데이트 후기

    1. 출시 2달 어땠는지? 메뉴얼이 출시된지 약 2달이 지났습니다. 부끄럽지만 매일 4명에서 많게는 13명(!) 정도가 우리 앱을 사용해주고 계십니다. 유료로 출시하게 되면서 내부적으로 조금 더 책임감을 가지고 개발하게 되는 선순환이 되기는 했지만, 아무래도 무료로 출시할 때보다는 유저 유입 자체가 적을 수밖에 없습니다. 신규 유입은 홍보로 해결할 수 있다고 생각 했는데요. 메뉴얼을 설치하고 사용하게 되면서 얼마나 우리 앱에 남아줄까? 하는게 근본적인 해결책이었습니다. 메뉴얼은 "작성한 일기를 다시 읽는 경험"을 가장 중요하게 생각하는 서비스입니다. 다시 읽기 위해서는 어느정도 작성한 일기가 있어야 하는데, 이 부분이 다소 미흡했던 것 같아요. 우리 서비스에서 진입하고 "이정도면 추천 해줄 일기가 많이 ..

    [iOS] 메뉴얼(Menual)은 RIBs를 어떻게 활용했을까?

    1. 메뉴얼은 어떤 구조를 가져야할까 메뉴얼은 RIBs 아키텍쳐를 도입한 첫 번째 사이드프로젝트였기 때문에, 가장 오랜 시간 고민을 했던 것은 "어떻게 구조를 짜야하는가?" 였습니다. 기존에 진행했던 ViewController-ViewModel을 설계하는 것과는 사뭇 다른 개념이었기 때문에, 독립적이고 기능 단위로 만들기 위해서 더욱 노력했습니다. 또, '게시글 작성'과 '게시글 수정'과 같은 비슷한 기능을 하는 것은 하나의 RIB으로 재활용하고 싶었습니다. RIBs를 도입하기 전에, 이 아키텍쳐를 어떻게 활용하면 가장 효율적이게 사용할 수 있을지 고민했던 것 같습니다. 1-1. 기능 단위로 분류해보자 메뉴얼을 작성하고 확인하기 위해서 어떤 RIB이 필요할 지, 먼저 큰 기능 단위로 분류하고자 했습니다...

    디모다모 앱스토어 리젝 대응기

    홍익대학교 디지털미디어디자인 졸업작품으로 어플리케이션을 개발하고 있습니다. 12월 18일 온라인 졸업전시 기간에 맞추어 어플리케이션 1차 런칭을 준비하고 있는데, 어플리케이션의 상태(?)를 점검하고자 테스트 릴리즈를 앱스토어 심사에 맡겨보았습니다. 약 3달 동안 어플리케이션의 기조를 잡는데 집중했다면, 지금은 실제로 출시가 가능한 상황인지 점검하고자 했습니다. 애플이 과연 어떤 답을 줄지도 굉장히 걱정되기도 했습니다. 아무래도 상용화를 목적으로 하는 첫 번째 어플리케이션이기도 했고, 관련 경험이 전무했기 때문일지도 모르겠습니다. 앱스토어 첫 번째 심사를 진행하면서 겪었던 리젝 사유 대응에 대해서 포스팅하고자 합니다. 앱스토어 심사 기간 앱스토어 심사 기간에는 24시간 또는 48시간 이내에 모든 심사가 진..

    [Swift] 프로퍼티(Property)

    프로퍼티 접근자 메소드를 자동으로 생성하는 기술입니다. let으로 생성했을 경우에는 getter 기능만 제공하는 것이고, var로 생성했을 경우에는 getter, (optional) setter 두 가지 기능을 모두 제공하는 것입니다. Stored Property - 저장형 프로퍼티 단순히 값을 저장하고 있는 프로퍼티 Enum 에서 사용 불가능 Computed Property - 계산형 프로퍼티 var를 사용합니다. newValue로 setter에서 새로운 값을 사용할 수 있습니다. (변수명 지정도 가능합니다) 프로퍼티를 가장한 메소드입니다 간단한 메소드를 계산형 프로퍼티로 사용하면, 코드의 가독성에 도움이 됩니다. 메소드로 기능을 제공할 경우 struct Timer { let id: Int let st..

    [Swift] Enum2

    Enum없이 분기하고자 할때 이미지의 확장자별로 분기하고자 했는데 확장자가 늘 일정하지 않을 수 있습니다. "jpg"로 입력되었으면 했지만, "JPG", "JPEG", "jpeg" 등 다양하게 입력이 되는데 이 부분을 모두 분기 처리하기에는 굉장히 번거롭습니다. func getAvatarImageFilename(for fileExtension: String) -> String? { swtich { case "jpg": return "avatar.jpg" case "bmp": return "avatar.bmp" case "gif": return "avatar.gif" // 단일 실패의 처리는 optional default: return nil } } if let result = getAvatarImageF..

    [Swift] 상속

    Swift에 대해서 개인적으로 공부하고 기록한 포스팅입니다. 구조체를 활용한 상속? 중복된 속성이 존재할 때, 객체 지향 설계에서는 중복된 속성을 부모 클래스를 통해 캡슐화가 가능합니다. 스위프트에서 상속을 사용하기 위해서는 구조체가 아닌 클래스를 이용해야 합니다. (Call by Ref) 아래와 같이 상속을 위해서는 구조체를 활용할 수 없습니다. struct User { let email: String let password: String let joinDate: Date var level: Int var exp: Int } struct Admin { let email: String let password: String let joinDate: Date var logs: [String] } 클래스를 활..

    [Swift] Tuple

    Tuple(튜플) 다양한 값의 묶음 임시적으로 사용할 때는 유용하지만, 범용적으로 사용될 경우 구조체나 클래스를 이용해야 합니다. func foo() -> (Stirng, Int) { return("Tom", 42) } var result = foo() print("\(result.0) / \(result.1)") var (name, age) = foo() print("\(name) / \(age)") func foo2() -> (name: String, age: Int) { return (name: "Tom", age: 42) } var result2 = foo2() print("\(result2.name) / \(result2.age)") var (name2, age2) = foo2() print("..

    [Swift] Enum

    Struct를 활용해서 채팅 메세지를 만든다고 가정해보겠습니다. 채팅메세지에는 아래와 같은 형식을 가지고 있습니다. 일반적인 텍스트 메세지 채팅 참가 메세지 채팅 탈퇴 메세지 위와 같은 메세지 형태를 Struct로 표현했을 경우와 Enum으로 표현했을 경우에는 어떤 차이점이 있는지 확인해보겠습니다. 1. Struct struct Message { let userId: Int let contents: String? let data: Date let hasJoined: Bool let hasLeft: Bool } let joinMessage = Message(userId: 1, contents: nil, date: Date(), hasJoined: true, hasLeft: false) let textMes..