이번 기술 공유를 통해 만든 키노트입니다.

코어 데이터란 무엇일까요?

코어데이터는 iOS 프레임워크입니다. 프레임 워크는 객체 생명주기와 객체 그래프관리에 관련된 솔루션을 제공합니다. 우리가 데이터베이스처럼 사용할 Persistence 객체 그래프 관리에 포함이 됩니다.

객체그래프 관리 내용 추가(https://velog.io/@ssionii/Core-Data%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%84%EB%9D%BC%EB%B3%B4%EC%9E%90-1-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90)

코어 데이터로 있는 기능들입니다. 하지만, 대부분의 아카데미 러너들이 그랬듯이, 이번 키노트에서는 Persistence 사용한 데이터 저장, 읽기, 수정, 삭제에 대해서만 다룰 예정입니다.

코어 데이터의 대부분 기능들은 애플리케이션의 Entity, Property, relationship 사이의 관계를 설명하기 위해 생성하는 스키마에 따라 결정됩니다. CoreData NSManagedObjectModel 인스턴스인 관리 객체 모델 (Managed Object Model)이라는 스키마를 사용합니다. 이러한 모델이 많을 수록 CoreData 좋게 앱을 지원해 있습니다.

우선 지금까지 해왔던 것처럼 프로젝트를 생성해줍니다.

그리고 옵션 설정에서 Use CoreData 선택해줍니다.

그러면 평소에 보던 화면이랑 약간 다른 느낌의 창이 뜹니다. ContentView Hello World 사라졌습니다.

디바이스를 iPhone으로 바꿔주면,

이렇게 타임 스탬프가 찍혀져 있고 추가 수정이 가능해 보이는 리스트가 나옵니다.

또한 네비게이션 영역에 프로젝트 이름과 똑같은 xcdatam…파일이 보입니다.

들어가서 보니 xcodeproj 파일이랑 비슷한 느낌으로 구성되어있는 것을 확인할 있습니다.

자동으로 생성되어 있는 item 엔티티와 timestamp Attribute 확인할 있습니다.

엔티티와 어트리뷰트 설명 추가

Entity를 추가하려면 하단의 add entity 누르면 되고, Attribute 추가하려면 하단의 add atribute 혹은 attribute 테이블 안에 있는 + 버튼을 클릭하면 됩니다.

Entity나 Attribute를 삭제하려면 선택한 상태에서 백스페이스버튼을 눌러주면 됩니다.

엔티티를 누르고 인스펙터창을 보면 일단 먼저 이름 설정칸과 Codegen이라는 칸이 있습니다.

Codegen 설명 추가

Attribute를 선택후 인스펙터 창을 보면 이름과 타입이 있습니다. 애플 공식문서(아카이브)에서는 디폴트인 Optional 사용하는 것을 권장합니다. 왜냐하면 값이 0일때, 없을때를 구분해주기 때문입니다. 여기서 옵셔널은 스위프트 문법이 아닌 SQL에서의 문법입니다.

하단의 에디터 스타일을 변경하면 UML 다이어그램으로 관계를 있습니다. 하지만, relationship을 사용하지 않는다면 쓸모는 없을 것입니다.

우리가 앞으로 진행해야할 과정입니다.

구조체를 생성후, 인스턴스를 만들고 저장하는 과정을 거칩니다.(1 - 2과정)

코어데이터에 저장이 되었는지 불러오기를 통해 확인합니다.(3 - 5과정)

그러기 위해서는 우선 코어 데이터 스택이란 것에 대해서 알고가야 합니다.

NSManagedObjectModel 인스턴스는 properties relationships 포함하여 app's types 설명합니다.

Entity 설명하는 DB 스키마라고 합니다.

Managed object Structure 정의합니다.

NSManagedObjectContext 인스턴스는 app's types 인스턴스에 대한 변화를 추적합니다. 객체를 통해서 create, delete, fetch, update 작업을 있습니다. (core data 메모리에 로드된 상태로 처리되는데, 때의 메모리가 context 의미합니다.)

여기서 Context가 Transaction 이라는 설명도 있었습니다.

NSPersistentStoreCoordinator 인스턴스는 store 에서 app's types 인스턴스를 저장하고 가져옵니다. (Context Container 사이를 통신합니다.)

Persistent storage managed object model 연결합니다.

NSPersistentContainer 인스턴스를 사용해서 model, context, store coordinator 동시에 설정합니다. (전체적으로 관리하는 최상단의 객체)

Core Data Stack 나타내는 필요한 모든 객체를 포함한다.

그리고 우리가 사용하기 편하게 위의 데이터 스택들을 관리해주는 PersistenceManager를 만들어 줍니다.

NearCat:ch에 대한 설명은 여기

우리가 DB용으로 사용하는 CoreData를 이 파일에서 전체적으로 다루겠다! 그런 의미로 생각하면 됩니다.

우선 PersistenetContainer는 다음 코드처럼 만들어집니다.

밑의 코드 내용 설명 보강

NSPersistentContainer 키워드를 사용하여 만들어집니다.

데이터베이스에서 기본적으로 해줘야할 CRUD는 다음처럼 구현합니다.

키워드 위주로만 설명이 될텐데 생성을 담당하는 키워드는 save가 있습니다.

불러오기, 읽기를 담당하는 키워드는 fetch입니다.

rollback 키워드를 통해 업데이트 할 수 있습니다.

delete 키워드를 통해 삭제가 가능합니다.

이번 키노트를 만들면서 많이 도움이 되었던 사이트들입니다.

 

이전 게시물 : SwiftUI Views - Basic Concepts (뷰 기본 개념)

 

 

The View

struct BasicSyntax: View {
    var body: some View {
        Text("Hello World!") // 스크린에 text view를 추가합니다.
    }
}

SwiftUI에서 View들은 View Protocol을 따르는 구조체(Structures) 타입입니다. 구현하기 위해서 단 한가지의 Property만 있으면 되는데, 바로 body Property입니다.
참고) 만약 body가 Property라면, get과 return 요소는 어디로 갔을까요?

 

Property Getters

struct Person {
    // Computed read-only property (no set, value is not stored)
    // 읽기 전용 프로퍼티로 설계되었음 (set, value가 저장되지 않음)
    var personType: String {
        get {
            return "human"
	} 
    }
}

Property들은 getter와 setter를 가지고 있을 수 있습니다. 그런데 Property에 setter가 없을때, 그 Property는 read-only property라는 이름을 가집니다. 또한 Property가 value를 저장하지 않을때, computed property라고 부릅니다. Property가 읽힐때마다 값이 계산되거나 생성되기 때문입니다. 위의 예시에서는 personType이 computed read-only property입니다.

 

이 Property는 다음과 같은 두가지 방법으로 더 간단하게 만들 수 있습니다.

// 변경사항1: return 제거
struct Person {
    var personType: String {
	get { 
   	"human"
	}
    }
}

코드의 get 내부에 하나의 expression만 있다면, getter는 자동으로 return을 해줍니다. 따라서 return을 지워도 상관 없습니다.

// 변경사항2: get 제거
    var personType: String {
        "human"
    } 
}

Property가 read-only(setter가 없을 때)인 경우, get을 지울 수 있습니다. 

일단은 이러한 수정방법을 알고 취향껏 하면 됩니다.

 

SwiftUI With Property Getters

이러한 property의 변화는 선택적이기 때문에 이전 SwiftUI문법인 get과 return을 body property에 넣어 작성해도 됩니다. 다음 코드는 아마 당신에게 더 친숙할 것 입니다.

// SwiftUI with the get and return keywords
struct BasicSyntax: View {
    var body: some View {
        get {
            return Text("Hello World!")
        }
    }
}

코드를 다시 한번 들여다보면, some이라는 키워드를 발견할 수 있습니다. 보통, Property의 type을 정의할 때, 이러한 단어를 본적이 없ㅇ르 것입니다.

그렇다면 some 키워드가 하는 것은 어떤 역할일까요?

 

Opaque Types

struct BasicSyntax: View {
    var body: some View {
        Text("Hello World!")
    }
}

some이라는 키워드는 opaque type이 반환되는 것을 지정해줍니다. 위의 경우 opaque type은 View입니다. 그래서 왜 이러한 type을  opaque라고 부를까요? opaque라는 영단어의 정의를 보면, "이해하기에 어렵거나 불가능"하다는 뜻이 있습니다. 그리고 opaque type은 value의 type정보와 implementation detail들을 숨기기 때문에 문맥에 상통하는 부분이 있습니다. 이 타입은 확실히 "이해하기 어렵거나 불가능"하지만, 사용 가능함을 의미합니다.

iOS가 화면을 그리기 위해 BasicSyntax가 사용되는 경우, 이 예에서 텍스트 type이 return되는 것을 알 필요가 없습니다. some View가 return되고 있으며 화면을 그리는 데 사용할 수 있다는 것만 알면 됩니다. 그리고 이 body property에서는 View protocol을 따르는 모든 것이 return될 수 있습니다. Opaque Type들에 대해 더 잘 알아보고 싶다면 Swift Programming Language documentation을 참고하면 됩니다. 

Opaque type에 대한 중요한 정보가 더 남아있는데....

 

Opaque Types (some Keyword)

이미 이전 단계에서 body property에서 무엇을 리턴받는지에 대한 답이 View protocol을 따르는 어떠한 것이라고 알게 되었습니다. 그러나 (some 키워드를 사용하여) opaque type을 리턴할 때도 가능한 모든 유형이 동일한 유형이어야 합니다.

대부분의 경우 한 가지 type만 return 합니다. 하지만 다음과 같은 시나리오가 있을 수 있습니다.

struct UnderstandingTheSomeKeyword: View {
    var isYellow = true
    // some 키워드는 우리에게 무엇을 리턴받던, 다음과 같아야한다고 말해줍니다.
    // 1. View protocol을 따라야한다.
    // 2. Return받는 View의 type은 항상 같아야한다.
    var body: some View {
        // ERROR: 함수는 opaque return type으로 선언했지만, body의 return statements 안에는
        //일치하는 하위 type이 없습니다.
        if isYellow {
            return Color.yellow // Color type은 Text type과 매치되지 않습니다.
}
        return Text("No color yellow") // Text type은 color type과 매치되지 않습니다.
    }
}

body property 는 Color와 Text type을 return합니다. 이것은 some 키워드를 위반합니다.

 

Opaque Types Solution

해결 방법은 return된 view들을 모두 동일한 type으로 변경하는 것입니다. 이제 body는 동일한 type의 view(Color)를 반환합니다.

 struct UnderstandingTheSomeKeywordSolution: View {
 var isYellow = true
 // The keyword "some" tells us that whatever we return, it has to:
 // 1. Conform to the View protocol
 // 2. Has to ALWAYS be the same type of View that is returned.
var body: some View {
        return Color.clear
    }
}
이제 body property는 항상 색상 유형을 return합니다. 이것은 some 키워드를 만족시킨다.
 
 

View Containers 

struct Example: View {
    var body: some View {
        VStack {
            Text("Hello World!")
            Text("This Vertical Stack is using a function builder")
        } 
    }
}

지금까지 body는 computed read-only property이며 some View인 하나의 object만 반환할 수 있다는 것을 배웠습니다. 만약 여러 보기를 표시하고 싶다면 어떻게 해야 할까요?

앞에서 "containers" view의 개념에 대해 배웠습니다. 다른 view를 포함할 수 있는 view입니다. body property는 하나의 view만 return할 수 있습니다. body property에서 둘 이상의 view를 return하려고 하면 오류가 발생합니다.

위의 예에서 VStack(Vertical Stack)은 return되는 하나의 view입니다. 그리고 그 VStack은 내부에 두 개의 view가 더 있는 container입니다.

VStack은 "trailing closure"를 사용하는데, 이는 VStack이 실행시킬 initializer로 전달되는 코드 블록이라는 의미입니다. Swift에서 한번쯤 봤을법한 것입니다.

Swift에서 새로운 것은 constructor 내에서 여러 개의 새로운 view를 생성할 수 있는 기능입니다. 이 문제를 시작하기 전에 이 constructor의 작동 방식을 더 잘 이해하도록 합시다.

 

View Container Initialization

Swift에서는 일반적으로 초기화 중에 괄호를 사용하지만, trailing closure 되는 경우에는 괄호가 선택 사항입니다.
그것들을 추가해도 코드는 여전히 정상적으로 작동합니다.

코드 예제에서 "Change 1"을 참조하십시오.

이러한 변화는 여러분에게 더 친숙해 보이기 시작할지도 모릅니다. 자, 질문은 이것입니다.

VStack은 어떻게 이와 같은 여러개의 View들을 수용하는 방법을 알고 있습니까?

이 내용은 Swift에서 새로운 것입니다. 이를 더 잘 이해하려면 VStack의 initializer를공부해야합니다.

alignment와 spacing 파라미터는 선택 사항이기 때문에 위의 예제에서 볼 수 없습니다. 대신 content 파라미터 앞에 @ViewBuilder 구문이 있음을 유의하세요.

그것이 content 파라미터의 closure내에서 여러 개의 뷰들을 선언할 수 있도록 해주는 것입니다. 

struct Example: View {
       var body: some View {
           VStack {
               Text("Hello World!")
               Text("This Vertical Stack is using a function builder")
           } 
       }
   }
}
   // Change 1 - Add parentheses and parameter name
   struct Example: View {
       var body: some View {
           VStack(content: {
               Text("Hello World!")
               Text("This Vertical Stack is using a function builder")
           })
       }
    } 
}
   // VStack initializer
   init(alignment: HorizontalAlignment = .center, spacing: CGFloat? = nil,
   @ViewBuilder content: () -> Content)

 

@ViewBuilder Parameter Attribute

@ViewBuilder 파라미터 속성을 통해 Swift는 closure 내에서 여러 child view들을 빌드할 수 있습니다.

Closure 내에서 몇 개의 child view를 빌드할 수 있을까요?

관련 기능은 이미 개수가 정해져 있습니다. 우리는 최대 10개의 view만 initialize할 수 있습니다. 여기 예제에서는 11번째 뷰 때문에 오류가 발생합니다.

Child view가 더 필요하면 어떻게 합니까?

사용자 인터페이스에 대해 더 많은 child view를 선언해야 하는 경우 다른 view container(다른 VStack 등)을 사용해야 합니다.

두 번째 예제에서는 텍스트 뷰 10과 텍스트 뷰 11을 포함하기 위해 다른 VStack을 사용합니다.

struct ViewBuilderExample: View {
    var body: some View {
        VStack {
            Text("View 1")
            Text("View 2")
            Text("View 3")
            Text("View 4")
            Text("View 5")
            Text("View 6")
            Text("View 7")
            Text("View 8")
            Text("View 9")
            Text("View 10")
            Text("View 11") // Will cause an error
        }
    }
}
struct ViewBuilderExample: View {
    var body: some View {
        VStack {
            ... // Text views 1 - 5
            Text("View 6")
            Text("View 7")
            Text("View 8")
            Text("View 9")
            VStack { // The VStack is now the 10th view
                Text("View 10")
                Text("View 11")
            }
        }
    }    
}

 

 

이 포스트는 밑의 페이지의 자료를 읽고 나서, 의역이 다소 존재하는 번역글입니다.

 

'IOS' 카테고리의 다른 글

CoreData 기술 공유  (1) 2022.07.05
SwiftUI Views - Basic Concepts  (0) 2022.04.29
SwiftUI Search Bar / 검색 기능 구현하기  (0) 2022.04.28
SwiftUI에서 리스트 동적으로 사용하기 (1)  (0) 2022.04.28
iOS 개인 프로젝트 시작  (0) 2022.04.28

Views And Modifiers 

SwiftUI에서는 View들을 통해 UI를 빌드할 수 있고, Modifier들을 통해 View들을 변경할 수 있습니다.

Containers - Vertical Layout Container 

View들은 container들로 구성될 수 있습니다. 일부 container들은 View들을 한 방향으로 조직화시키는데, 이것을 Stack이라고 합니다. 다음은 Vertical Stack의 예시입니다. SwiftUI에서는 VStack이라고 부릅니다. 이러한 Stack도 Modifier들을 가지고 있는 View입니다. 회색 사각형들은 VStack 내부에 있는 또다른 View입니다. 사각형을 조직화시킨, 눈에 보이지 않는 부분이 VStack입니다.

 

Horizontal Layout Container 

이것은 가로로 조직화시킬수 있는 또다른 Stack입니다. SwiftUI에서는 이러한 Horizon Stack을 HStack이라고 부릅니다.

 

Depth Layout Container 

또다른 Stack은 당신의 View를 다른 View들의 위로 조직화시킵니다. 이러한 Stack을 Depth Stack 혹은 ZStack이라고 부릅니다.

 

Grid Layout Container 

SwiftUI의 ver.2에서, grid container view가 소개되었습니다. VStack과 HStack을 하나로 합친 Stack입니다. 

 

 

Relationships - Parent & Child 

프로그래밍에서는 종종 object의 계층(hierachy)을 부모와 자식 관계로 표현합니다. 여기서도 비슷한 컨셉으로 표현이 될 것입니다. 예를 들어 위의 HStack 사진에서, HStack은 회색 rectangle View를 세 개 포함하고 있었습니다. 이 경우에는 HStack이 부모(parent)가, rectangle View가 자식(child)이 되는 것입니다. 

한가지 알면 좋은 상식이 있습니다. 자신의 자식이 없는 child View들을 애플은 "leaf views"라고 부르기로 했습니다.

Relationships - And Modifiers 

어떤 modifier들은 부모 View위에 설정되어 그 container 안의 자식 View에게도 영향을 주기도 합니다. 즉 VStack으로 쌓인 두개의 Rectangle을 생각해보면, VStack의 폰트 사이즈를 설정하면 Rectangle의 폰트도 같은 사이즈가 됩니다.만약 자식 View의 폰트 사이즈를 변경하고 싶다면 override를 통해 할 수 있습니다.

 

다음 게시물 : SwiftUI Views - Understanding the Syntax (뷰 문법 이해하기)

 

 

 

이 포스트는 밑의 페이지의 자료를 읽고 나서, 의역이 다소 존재하는 번역글입니다.

리스트에서 막히는 부분이 생겨서 빠르게 서치바부터 끝냈다.

https://www.appcoda.com/swiftui-search-bar/

 

Building a Search Bar in SwiftUI | Mastering SwiftUI

Unlike UIKit, SwiftUI doesn't come with a built-in control for search bar. In this tutorial, let's try to build a SwiftUI version of search bar.

www.appcoda.com

이 사이트를 참고했다. 

//
//  SearchBar.swift
//  SwiftDictionary
//
//  Created by Tempnixk on 2022/04/28.
//

import SwiftUI
 
struct SearchBar: View {
    @Binding var text: String
 
    @State private var isEditing = false
 
    var body: some View {
        HStack {
 
            TextField("Search ...", text: $text)
                .padding(7)
                .padding(.horizontal, 25)
                .background(Color(.systemGray6))
                .cornerRadius(8)
                .overlay(
                    HStack {
                        Image(systemName: "magnifyingglass")
                            .foregroundColor(.gray)
                            .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
                            .padding(.leading, 8)
                 
                        if isEditing {
                            Button(action: {
                                self.text = ""
                            }) {
                                Image(systemName: "multiply.circle.fill")
                                    .foregroundColor(.gray)
                                    .padding(.trailing, 8)
                            }
                        }
                    }
                )
                .padding(.horizontal, 10)
                .onTapGesture {
                    self.isEditing = true
                }
 
            if isEditing {
                Button(action: {
                    self.isEditing = false
                    self.text = ""
 
                }) {
                    Text("Cancel")
                }
                .padding(.trailing, 10)
                .transition(.move(edge: .trailing))
                .animation(.default)
            }
        }
    }
}

struct SearchBar_Previews: PreviewProvider {
    static var previews: some View {
        SearchBar(text: .constant(""))
    }
}

우선 서치바의 틀만 만들어두었고, 나중에 컨텐츠가 생겼을때 단어 목록 추천, 혹은 검색 후 링크까지 구현해야 할 것 같다.

다시 리스트 만드러 가야겠다...

'IOS' 카테고리의 다른 글

SwiftUI Views - Understanding the Syntax  (0) 2022.04.30
SwiftUI Views - Basic Concepts  (0) 2022.04.29
SwiftUI에서 리스트 동적으로 사용하기 (1)  (0) 2022.04.28
iOS 개인 프로젝트 시작  (0) 2022.04.28
Xcode 화면 구성  (0) 2022.04.27

먼저 내가 구현하려고 하는 카테고리뷰는 다음과 같은 식의 연락처 화면이다.

https://gigas-blog.tistory.com/46

 

Swift 4 TableView Section

iPhone의 연락처 앱처럼 구분을 하여 목록을 보여주고 싶을 때가 있습니다. 블로그나 유튜브를 찾아봐도 TableView의 Section 구분만 나오는게 아닌 특정 기능까지 포함해서 나오기에 복잡해서 어려움

gigas-blog.tistory.com

구현해야 할것들

- 알파벳순 정렬 / 섹션 구분

- 없는 알파벳 섹션은 안보이도록

- 스크롤 하면서 알파벳 인덱스 쉽게 찾을 수 있도록?

- 들어가는 컨텐츠가 많다보니까 데이터 관리를 편하게 해야할듯

이러한 구현 목록을 보니, 우선 리스트를 만들면서 시작해야 할 것 같다.

그러나, 목록이 72개, 섹션까지 합치면 84개에 달하니, 리스트를 동적으로 사용해야 할 것 같다.

 

1. 첫번째 시도

https://medium.com/ivymobility-developers/creating-simple-tableview-in-swiftui-1f9e8e464782

 

Creating Simple TableView in SwiftUI

Static Lists and Dynamic Lists

medium.com

여기에서 마지막에 나오는 방식처럼 구조체를 만들고 배열 기반 리스트를 사용하려고 한다.

우선 섹션만 나타나는 리스트를 만들어보자.

//
//  ContentView.swift
//  SwiftDictionary
//
//  Created by Tempnixk on 2022/04/28.
//

import SwiftUI

struct Section: Identifiable {
  let id    : Int
  let name  : String
  let size  : String
}

var sectionList = [
    Section(id: 0, name: "Collection Types", size: "4 bytes"),
    Section(id: 1, name: "Conditional Statements", size: "1 byte"),
    Section(id: 2, name: "Data", size: "4 bytes"),

    Section(id: 3, name: "Enum", size: "4 bytes"),
    Section(id: 4, name: "Function", size: "8 bytes"),
    Section(id: 5, name: "Loops", size: "8 bytes"),
    Section(id: 6, name: "Methods", size: "8 bytes"),
    Section(id: 7, name: "Operations", size: "8 bytes"),
    Section(id: 8, name: "Optional", size: "8 bytes"),
    Section(id: 9, name: "Properties", size: "8 bytes"),
    Section(id: 10, name: "Structure / Classes", size: "8 bytes"),
    Section(id: 11, name: "Type", size: "8 bytes"),
    Section(id: 12, name: "Double", size: "8 bytes"),
  ]

struct ContentView: View {
    var body: some View {
        List(sectionList) { Section in
          HStack {
            Text(Section.name)
            Text(Section.size)
          }
        }
    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

다 만들고 나니 섹션 부분은 헤더? 라는 것으로 대체할 수 있다고 한다. 즉, 컨텐츠 리스트만 만들고 나중에 작업하는게 가능한 것이었다.

JSON 파일 통해서 리스트 구현? 한번 해봐야겠다.

 
사전 앱 만들기를 주제로 프로젝트 시작하려고 한다.
 
스케치로 만든 이번 프로젝트의 Hi-fi 프로토타입이다.
 
 

Sketch

Sketch

www.sketch.com

구현해야할 목록은 다음과 같다.

카테고리뷰

1. 서치바

2. 리스트

3. 스크롤뷰

4. 음성인식(?)

딕트뷰

1. 북마크 기능

2. 컨텐츠 작성

그 외

-북마크 추가한 내용을 동시에 북마크뷰 리스트에 추가하기

- 같은뷰의 같은 버튼이 다른 동작을 하도록 ( 딕트 뷰에서 뒤로가기 네비게이션 링크가 넘어갈때, 카테고리뷰에서 시작했으면 카테고리뷰로, 북마크뷰에서 시작했으면 북마크뷰로 넘어가도록) 구현

 

+ Recent posts