https://simth999wrld.tistory.com/77

 

우리말 퀴즈 앱)3.Alamofier, 네이버 검색 API 사용

https://simth999wrld.tistory.com/76 우리말 퀴즈 앱)2.퀴즈뷰 구성중... https://simth999wrld.tistory.com/75 우리말 퀴즈 앱)1.우리말샘 api https://simth999wrld.tistory.com/74 1차시. OT 간단하게 스터디 일정과 인사후 개인

simth999wrld.tistory.com

 

이제 조금은 기능들을 거의 다 만든것같습니다 ㅎㅎ..

 

이번엔 위젯을 간단하게 만들며 이전에 저장해 두었던 네이버 API키를 코드상에서 지웠습니다!

 


 

API-KEY 숨기기.

import Foundation

struct NaverStorage {
    let naverClientID: String = "Your naverClientID"
    let naverClientSecret: String = "Your naverClientSecret"
}

위처럼 키를 그대로 넣게되면 보안상의 문제가 많아 보입니다.. 별거 아닌 api이긴 하지만 그래도 조금은 신경써주는 것이 좋을것 같네요 ㅎㅎ..

 

Product -> Edit Scheme -> Run 부분의 Arguments에 Enviroment Variables + 를 클릭하여 apikey를 저장해줍니다.

 

이렇게 된다면 환경변수를 사용해서 API Key를 저장하였습니다.

 

- NaverNetwork.swift -

let headers: HTTPHeaders = [
    "X-Naver-Client-Id": ProcessInfo.processInfo.environment["naverClientID"] ?? "",
    "X-Naver-Client-Secret": ProcessInfo.processInfo.environment["naverClientSecret"] ?? ""
]

 

네이버 api를 연동하는 부분의 코드인 헤더 부분에 ProcessInfo.processInfo.environment["API-KEY"] 를 사용해 값을 가져오도록 합니다.

 

이렇게 한다면 코드상에 api키를 저장하지 않고 사용 할 수 있습니다.

 


위젯

다음은 간단한 위젯을 만들어보았습니다.

 

간단한 위젯이니 위젯의 사이즈는 2개로 하였습니다 많은 정보를 알려주는 앱은 아니니까요 

systemSmall과 systemMedium의 크기만 제작하였습니다.

 

위젯을 제작하기전 기본 지식을 알고 가야겠죠..? 하하


위젯 생성은 Xcode -> File -> Target -> Widget Extension을 클릭후 이름을 입력하고 finish를 하면 됩니다.

(finish를 하기전 include Configuration Intent의 체크박스가 있습니다. 이 체크박스는 활성화 하게 된다면 사용자가 위젯 편집이 가능하게 합니다. 따라서 체크 하였습니다 ^_^)

 

위에서 입력한 이름의 위젯 파일로 가게된다면 어질어질한 코드들이 생성이 됩니다..(저도 처음이라 어질어질,,)

여러개의 struct들이 존재합니다!

 

  • Provider :TimelineEntry
  • SimpleEntry : TimelineEntry
  • (위젯이름)EntryView : View
  • (위젯이름) : Widget

이렇게 4개로 이루어져있습니다.

 

Provider

이 구조체는 위젯을 업데이트 할 시기를 WidgetKit에 알리는 역할을 한다고 합니다.

이 구조체엔 여러 메소드가 있습니다. ( placeholder, getSnapshot, getTimeline )

 

func placeholder

데이터를 불러오기 전 getSnapshot에 보여줄 placeholder라고 합니다.

 

func getSnapshot

getSnapshot은 위젯 갤러리에서 위젯을 고를때 보이는 샘플 데이터를 보여줄때 해당 메소드를 호출합니다.

 

func getTimeline

홈 화면에 있는 위젯을 업데이트하는 시간을 구현하는 부분 입니다.

let timeline = Timeline(entries: entries, policy: .atEnd)

 

이부분의 policy 부분에 들어가는 값은

.atEnd - 마지막 date가 끝난 후 타임라인 reloading

.after - 다음 date가 지난 후 타임라인 reloading

.never - 즉시/바로 타임라인 reloading

 

 

SimpleEntry

TimelineEntry를 준수하는 구조체이며 위젯을 표시할 Date를 정하고 그 Data에 표시할 데이터를 나타냅니다.

 

 

(위젯이름)EntryView : View

위젯을 나타낼 뷰의 코드입니다.

@Environment(\.widgetFamily) var family: WidgetFamily 를 사용하여 여러 위젯의 크기에 접근할 수 있습니다.

switch self.family {
    case .systemSmall:
    case .systemMedium:
...

 

 

 

(위젯이름) : Widget

위에서 설명된 메소드들이 호출되는 곳입니다. (+ @main)

struct WordQuizWidget: Widget {
    let kind: String = "WordQuizWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            if #available(iOS 17.0, *) {
                WordQuizWidgetEntryView(entry: entry)
                    .containerBackground(.fill.tertiary, for: .widget)
                    
            } else {
                WordQuizWidgetEntryView(entry: entry)
                    .padding()
                    .background()
            }
        }
        .configurationDisplayName("WordQuizDaily Widget")
        .description("문해력을 높여주는 하루 한단어")
        .supportedFamilies([.systemSmall,
                            .systemMedium])
    }
}

저의 코드입니다.

 

body안의 IntentConfiguration, StaticConfiguration  2개로 설정할 수 있습니다.

IntentConfiguration은 사용자가 위젯에서 Edit을 통해 위젯에 보여지는 내용 변경이 가능합니다.

StaticConfiguration은 사용자가 변경 불가능한 정적 데이터를 보여줍니다.

(저는 단어를 보여주는 위젯이라 Static)

 

kind는 위젯의 ID입니다.

 

entry in 밑의 부분은 위젯에 보여질 뷰 입니다.

 

 


 

이제 위젯에 대한 개념을 알아보았고

저의 앱의 오늘의 단어와 뜻을 넘겨받아 위젯에 보여주는 일만 남았습니다 ^_^

 

ObservedObject를 사용해 변수의 내용을 받아오거나 userdefaults에 저장한 값을 가져오려는 하수같은 생각을 하였습니다만..ㅎㅎ
서치를통해 여러 블로그를 공부한 결과 App Extension Programming Guide를 참고하였고,

 

Extension과 App은 같은 컨테이너를 가지고 있지 않았습니다!

따라서 UserDefaults는 공유되지 않았습니다..

 

하지만 의지의 한국인 다른 방법을 제시해주신 착한 분들이 계셨습니다..

위의 Optional shared container를 사용해서 공유할 수 있었습니다.

 

App Group을 설정하여 데이터를 공유하는 방법이었습니다 ^_^

 

 

 

앱그룹의 컨테이너 이름은 group.(your_app's_bundle_id)로 해야 합니다. (하지만 저는 다르게 해버린걸..)

 

앱부분의 UserDefaults를 저장할 HomeViewModel 부분에서 appGroupId를 맞춰줍니다.

extension UserDefaults {
    static var shared: UserDefaults {
        let appGroupId = "group.wordQuizWidget"
        return UserDefaults(suiteName: appGroupId)!
    }
}

 

Extension부분인 위젯의 코드에서

let customUserDefaults = UserDefaults(suiteName: "group.wordQuizWidget")

으로 appGroupId를 맞춰주면 


shared container를 사용한 UserDefaults를 공유하여 사용할 수 있습니다.

 

위젯 코드

//
//  WordQuizWidget.swift
//  WordQuizWidget
//
//  Created by 정종원 on 3/7/24.
//

import WidgetKit
import SwiftUI

struct Provider: TimelineProvider {
    
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date())
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date())
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        var entries: [SimpleEntry] = []

        // Generate a timeline consisting of five entries an hour apart, starting from the current date.
        let currentDate = Date()
        for hourOffset in 0 ..< 5 {
            let entryDate = Calendar.current.date(byAdding: .minute, value: hourOffset, to: currentDate)!
            let entry = SimpleEntry(date: entryDate)
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}

struct SimpleEntry: TimelineEntry {
    let date: Date
}

struct WordQuizWidgetEntryView : View {
    var entry: Provider.Entry
        
    @Environment(\.widgetFamily) var family: WidgetFamily
    
    var body: some View {
                
        let customUserDefaults = UserDefaults(suiteName: "group.wordQuizWidget")

        
        switch self.family {
            
        case .systemSmall:
            VStack{
                if let storedWord = customUserDefaults?.string(forKey: "TodayWord"),
                   let storedDefinition = customUserDefaults?.string(forKey: "TodayWordDefinition") {
                    
                    Spacer()

                    
                    Text(storedWord)
                        .font(.largeTitle)
                    
                    Spacer()
                    
                    Text(storedDefinition)
                        .font(.footnote)
                    
                    Spacer()
                    
                    
                } else {
                    let _ = print("Nothing Printed")
                }
            }
            
        case .systemMedium:
            VStack{
                if let storedWord = customUserDefaults?.string(forKey: "TodayWord"),
                   let storedDefinition = customUserDefaults?.string(forKey: "TodayWordDefinition") {
                    
                    Spacer()

                    Text(storedWord)
                        .font(.largeTitle)
                    
                    Spacer()
                    
                    Text(storedDefinition)
                        .font(.footnote)

                    
                    Spacer()
                    
                    
                } else {
                    let _ = print("Nothing Printed")
                }
            }
            
        default:
            Text(".default")
            
        }

        }
}

struct WordQuizWidget: Widget {
    let kind: String = "WordQuizWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            if #available(iOS 17.0, *) {
                WordQuizWidgetEntryView(entry: entry)
                    .containerBackground(.fill.tertiary, for: .widget)
                    
            } else {
                WordQuizWidgetEntryView(entry: entry)
                    .padding()
                    .background()
            }
        }
        .configurationDisplayName("WordQuizDaily Widget")
        .description("문해력을 높여주는 하루 한단어")
        .supportedFamilies([.systemSmall,
                            .systemMedium])
    }
}

#Preview(as: .systemSmall) {
    WordQuizWidget()
} timeline: {
    SimpleEntry(date: .now)
}

 

간단하게 systemSmall과 systemMedium 두개를 만들었습니다.

 

 

 

 

 

 

다음에 할것 -> 파이어베이스 연동하여, 앱푸시 기능 -> NotiView에서 시간대 설정 가능하게 하기. 

지금 퀴즈뷰에서 네이버 이미지api로 관련 이미지를 가져오고 있는데 문제가 스포돼버리는 큰 이슈가 있습니다.. 

하지만 우리말샘에서 수어 이미지를 제공 해주더군요! 그래서 그 사진을 띄우면 어떨까... 합니다...
(다른분의 반응을 알고싶다,,ㅎ흐ㅡ긓ㄱ)

 

 

https://gyuios.tistory.com/102

 

iOS) Kakao QRcode Widget 클론코딩 - Widget 데이터 공유 및 뷰 구현(SwiftUI)

내용 카카오톡 QR코드, 프로필 위젯을 만들어보겠다. 위젯과 앱간의 프로필(이름, 이미지) 데이터 공유로 다음과 같은 프로필 위젯 만들기 시작전 위젯은 기능이 제한적이며 interactive 하지도 않

gyuios.tistory.com

https://www.masrinastudio.com/post/securing-api-keys-xcode-guide/

 

Securing Your API Keys in Xcode: Step-by-step Guide | Masrina Studio

Image credit: Photo by Clint Patterson on Unsplash There are a few different ways to securely add an API key to an Xcode project, depending on the level of security you need and the resources you have available. Here are a few methods: Use a Keychain: The

www.masrinastudio.com

https://ios-development.tistory.com/1131

 

[iOS - SwiftUI] 1. 위젯 Widget 사용 방법 개념 (WidgetKit, WidgetFamily)

1. 위젯 Widget 사용 방법 - WidgetKit, WidgetFamily 2. 위젯 Widget 사용 방법 - API 데이터 로드와 위젯UI 업데이트 3. 위젯 Widget 사용 방법 - 위젯 딥링크 구현 방법 (widgetURL) 4. 위젯 Widget 사용 방법 - 위젯 이

ios-development.tistory.com

https://michael-kiley.medium.com/sharing-object-data-between-an-ios-app-and-its-widget-a0a1af499c31

 

Sharing Object Data Between an iOS App and Its Widget

Use Codable and App Groups to get your iOS 14 widget all the data it needs

michael-kiley.medium.com

 

https://simth999wrld.tistory.com/76

 

우리말 퀴즈 앱)2.퀴즈뷰 구성중...

https://simth999wrld.tistory.com/75 우리말 퀴즈 앱)1.우리말샘 api https://simth999wrld.tistory.com/74 1차시. OT 간단하게 스터디 일정과 인사후 개인프로젝트 소개를 하였습니다, ^_^/ 간단하게 아이디어를 정리해

simth999wrld.tistory.com

이번에 구성에 대해 조금더 업그레이드를 해보았는데요,,하하

 

네이버 검색 API를 사용해서 정답 단어의 이미지를 가져오는것 입니다..ㅎ

 

 

 

 

디렉토리 구조를 조금 바꾸었습니다.. (이렇게 하는거 맞나.,.?)

 

조금더 고민한 후에 조금더 보기 편하게 만들어 보겠습니다^_^

 

 

 

 

 

 

 

 

네이버 이미지검색 API

 

일단 네이버 검색 API를 사용하기 위해 네이버 디벨로퍼 회원가입, 키 발급 등을 합니다.

 

https://developers.naver.com/docs/serviceapi/search/image/image.md

 

검색 > 이미지 - Search API

검색 > 이미지 이미지 검색 개요 개요 검색 API와 이미지 검색 개요 검색 API는 네이버 검색 결과를 뉴스, 백과사전, 블로그, 쇼핑, 웹 문서, 전문정보, 지식iN, 책, 카페글 등 분야별로 볼 수 있는 API

developers.naver.com

 

 

 

 

묵념을 검색을 하게된다면 이렇게 최소 10개의 이미지를 가져올 수 있게 됩니다.

 

저는 가장 맨 위의 이미지만을 사용하겠습니다.

 

이 JSON형식에 따라 Swift파일을 만들어 줍니다.

 

 

 

 

 

- NaverImageData.swift -

import Foundation

// MARK: - NaverImageData
struct NaverImageData: Codable {
    let lastBuildDate: String
    let total, start, display: Int
    let items: [NaverItem]
}

// MARK: - NaverItem
struct NaverItem: Codable {
    let title: String
    let link: String //이미지의 URL
    let thumbnail: String
    let sizeheight, sizewidth: String
}

 

이렇게 만들어 주는데 물론 손으로 만드면 좋겠지마는~ 

 

https://app.quicktype.io/

 

Instantly parse JSON in any language | quicktype

 

app.quicktype.io

 

이 사이트에서 여러 타입의 데이터들을 코드로 만들어 줍니다. 편리하죠..?

 

 

클라이언트 아이디와 시크릿 값을 저장하기 위한 파일을 만들어 줍니다.

 

- NaverStorage.swift -

//TODO: - gitignore에 NaverStorage.swift 추가하기..
import Foundation

struct NaverStorage {
    let naverClientID: String = "Your naverClientID"
    let naverClientSecret: String = "Your naverClientSecret"
}

 

 

앗 그전에 까먹은 것이 있네요..

Alamofire를 사용해 HTTP통신을 하여 네이버 API를 사용해야하니 Alamofire또한 Cocopod를 통해 설치해 줍니다.

(킹피셔로 이미지를 보여줄것이니 같이 다운 받아요 ㅎㅎ)

pod 'Alamofire'
pod 'Kingfisher', '~> 7.0'

 

 

pod install을 하였다면,, 이제 HTTP통신을 해야 하겠죠?

 

HTTP 통신에 사용되는 메소드를 편하게 사용하기 위해 enum에 저장하여 사용합니다.

 

- HTTPMethod.swift - 

enum HTTPMethod: String {
    case get = "GET"
    case post = "POST"
    case put = "PUT"
    case head = "HEAD"
    case delete = "DELETE"
    case patch = "PATCH"
    case trace = "TRACE"
    case options = "OPTIONS"
    case connect = "CONNECT"
}

 

 

Alamofire를 사용해 네트워크 통신 부분을 작성 해보겠습니다.

 

- NaverNetwork.swift -

import Foundation
import Alamofire


protocol NaverNetworkDelegate: AnyObject {
    func imageDataUpdated(_ imageData: NaverImageData?)
}

class NaverNetwork: ObservableObject {
    static let shared = NaverNetwork()
    private init() {}
    
    @Published var imageData: NaverImageData?
    
    weak var delegate: NaverNetworkDelegate?
    
    func requestSearchImage(query: String, completion: @escaping () -> Void) {
        let baseURL = "https://openapi.naver.com/v1/search/image"
        
        let headers: HTTPHeaders = [
            "X-Naver-Client-Id": NaverStorage().naverClientID,
            "X-Naver-Client-Secret": NaverStorage().naverClientSecret,
        ]
        
        let parameters: Parameters = [
            "query": query,
            "display": 50
        ]
        
        AF.request(baseURL,
                   method: .get,
                   parameters: parameters,
                   encoding: URLEncoding.default,
                   headers: headers)
        .validate(statusCode: 200...500)
        .responseDecodable(of: NaverImageData.self) { response in
            switch response.result {
            case .success(let data):
                guard let statusCode = response.response?.statusCode else { return }
                if statusCode == 200 {
                    DispatchQueue.main.async {
                        self.imageData = data
                        self.delegate?.imageDataUpdated(data)
                        completion()
                    }
                }
                print("\(#file) > \(#function) :: SUCCESS")
            case .failure(let error):
                print("\(#file) > \(#function) :: FAILURE : \(error)")
            }
        }
    }
}

 

(NaverNetworkDelegate이 뒤에 이미지를 QuizView에서 보여져야 하지만 이미지가 보이지 않았어서 추가하였습니다.. 이부분은 추가로 글쓸 예정!)

 

QuizView에서 보여질 이미지를 위해 QuizViewModel에서 메소드들을 작성해 줍니다.

//MARK: - KoreanWordSearchAPI

// 단어 데이터 처리 메서드
func handleWordData(word: String, wordData: WordData?) {
    if let wordData = wordData {
        wordDataDictionary[word] = wordData
        // 정답 단어의 설명을 가져오기
        if word == correctWord {
            fetchCorrectWordDefinition()
            fetchImageForWord(correctWord)
        }
    } else {
        print("\(word) 단어 데이터를 가져오지 못함 ")
        isLoading = false // 데이터를 가져오지 못한 경우 isLoading을 false로 설정하여 프로그레스 뷰를 숨김
    }

    isLoading = false
}

// 정답 단어의 설명 가져오기
func fetchCorrectWordDefinition() {
    isLoading = true

    guard let wordData = wordDataDictionary[correctWord] else {
        correctWordDefinition = "설명을 가져올 수 없습니다."
        isLoading = false // 작업 완료
        return
    }

    if let firstSense = wordData.channel.item.first?.sense.first {
        correctWordDefinition = firstSense.definition
        print(correctWordDefinition)
    } else {
        correctWordDefinition = "설명을 찾을 수 없습니다."
    }

    isLoading = false // 작업 완료
}

//MARK: - NaverSearchAPI

func fetchImageForWord(_ word: String) {
    isLoading = true

    naverNetwork.requestSearchImage(query: word){ [weak self] in
        // 이미지 데이터 로드 완료 시에만 isLoading을 false로 설정
        self?.isLoading = false
    }
}

//MARK: - NaverNetworkDelegate
func imageDataUpdated(_ imageData: NaverImageData?) {
    DispatchQueue.main.async {
        self.imageData = imageData
    }
}
}

 

 

 

 

Kingfisher

킹피셔를 사용하여 이미지를 띄워 줍니다.

//MARK: - 문제의 이미지
VStack{
    if quizViewModel.isLoading {
        ProgressView("Loading...")
            .frame(width: 200, height: 200)
    } else {
        if let imageData = quizViewModel.imageData {
            if let firstImage = imageData.items.first {
                KFImage(URL(string: firstImage.link))
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: 200, height: 200)
            }
        }
    }
}
.padding()

 

 

 

디자인은 잼병이라,, 다른앱을 참고하며 바꿔보도록 하겠습니다.

 

 

 

 

이렇게 이미지를 가져올수 있게 되었습니다!!

 

 

문제 발생..

문제라 함은 2개가 발생 하였는데요...

 

문제 1.

//TODO: - gitignore에 NaverStorage.swift 추가하기..
import Foundation

struct NaverStorage {
    let naverClientID: String = "Your naverClientID"
    let naverClientSecret: String = "Your naverClientSecret"
}

 

이부분은 큰 문제는 아니지만, 조금더 보안을 신경 써준다면 키체인을 사용하면 좋아 보입니다!!!

 

 

문제 2.

 

 

 

 

 

 

 

 

바로 이미지에서 정답을 알수있는 이미지가 나온다는 사실...

관련 이미지를 싹 깃허브에 올려서 가져오는 방법으로 해야 이런 문제가 안날것 같지만..

시간이 많이 걸릴것 같군요 하하..ㅎ 

 

(아이디어 있다면 알려주세요..)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

긴글 읽어주셔서 감사합니다 ^_^🙏

 

 

 

 

 

 

 

 

https://developers.naver.com/docs/serviceapi/search/image/image.md#%EC%9D%B4%EB%AF%B8%EC%A7%80-%EA%B2%80%EC%83%89-%EA%B2%B0%EA%B3%BC-%EC%A1%B0%ED%9A%8C

 

검색 > 이미지 - Search API

검색 > 이미지 이미지 검색 개요 개요 검색 API와 이미지 검색 개요 검색 API는 네이버 검색 결과를 뉴스, 백과사전, 블로그, 쇼핑, 웹 문서, 전문정보, 지식iN, 책, 카페글 등 분야별로 볼 수 있는 API

developers.naver.com

https://github.com/Alamofire/Alamofire

 

GitHub - Alamofire/Alamofire: Elegant HTTP Networking in Swift

Elegant HTTP Networking in Swift. Contribute to Alamofire/Alamofire development by creating an account on GitHub.

github.com

https://velog.io/@dbqls200/iOS-%EB%84%A4%EC%9D%B4%EB%B2%84-%EA%B2%80%EC%83%89%EB%8F%84%EC%84%9C-API-%EC%82%AC%EC%9A%A9%EA%B8%B0

 

[iOS] 네이버 검색(도서) API 사용기

네이버 Open API를 사용해서 책 검색 해보기

velog.io

 

https://simth999wrld.tistory.com/75

 

우리말 퀴즈 앱)1.우리말샘 api

https://simth999wrld.tistory.com/74 1차시. OT 간단하게 스터디 일정과 인사후 개인프로젝트 소개를 하였습니다, ^_^/ 간단하게 아이디어를 정리해보았습니다.. 진행과정을 간간히 올려보도록 노력! 프로

simth999wrld.tistory.com

 

네~! 저번엔 간단히 api를 호출해서 단어의 뜻과 설명등을 가져오는 코드를 작성했습니다,,

현재까지 얼마나 했나,,, 보고아닌 보고를 하기위해 작성하였습니다 ^_^/

 

 

아직 구상을 하는 중입니다만,, 

 

아마 만들어가면서 계속 구상을 할것같아요 하하

 

간단히 4개의 보기를 주고, 문제의 뜻에 대한 단어를 찾는

간단한 뷰를 만들어보았습니다.

 

나름대로 view와 viewModel을 만들어 보았지만,, view의 코드가 깨끗하지 않은점,,

양해 부탁드립니다 ㅎ

 

 

 

 

 

 

 

저번 글에서 URLSession을 통해 api통신을 하는 코드중 수정을 조금 하였습니다.

func searchWord(_ searchWord: String, completion: @escaping (WordData?) -> Void) {
    let urlString = "https://opendict.korean.go.kr/api/search?certkey_no=6282&key=\(myApiKey)&target_type=search&req_type=json&part=word&q=\(searchWord)&sort=dict&start=1&num=10"
    if let url = URL(string: urlString) {
        let session = URLSession(configuration: .default)
        let task = session.dataTask(with: url) { data, response, error in
            if error == nil {
                if let safeData = data {
                    do {
                        let decodedData = try JSONDecoder().decode(WordData.self, from: safeData)
                        DispatchQueue.main.async {
                            self.wordData = decodedData
                            completion(decodedData) // 완료 핸들러 호출
                        }
                    } catch {
                        print(error)
                        completion(nil)
                    }
                }
            } else {
                print(error)
                completion(nil) 
            }
        }
        task.resume()
    }
}

 

차이가 조금 보이시나요? ㅎㅎ

저도 아직 감은 잡히지 않았지많,, 완료 핸들러를 추가하였습니다.

흠 추가한 이유는! 4개의 보기의 단어를 검색해서 가져온 데이터를 저장해 주기위해서 입니다,,

 

추가한 코드를 설명해 드리겠습니다.

 

QuizViewModel.swift

위에서 보여진 앱은 QuizView.swift 입니다.

이 뷰에 보여지는 데이터 추가를 위해 QuizViewModel을 추가하였는데요!

 

프로퍼티

let hardKoreanWords = HardKoreanWords()
let wordNetwork = WordNetwork()

var choiceWords = [String]()
var correctWord: String = ""
var correctWordDefinition: String = ""
var wordDataDictionary = [String: WordData]()

@Published var isLoading = false

 

어려운 단어를 하드코딩해서 넣어둔 hardKoreanWords

우리샘 api를 사용하여 네트워크 통신을 하는 wordNetwork

정답인 단어를 넣어두는 choiceWord

정답인 단어의 뜻을 넣어두는  correctWordDefinition

보기의 단어들을 넣어두는 딕셔너리인wordDataDictionary
등이 있습니다!
(isLoading은 프로그래스를 추가해서 검색하는 시간동안 보여주려 하는 함수이지만,, 아직 작동은 안하는..)

 

 

보기생성 generateChoices()

func generateChoices() {
    choiceWord.append(correctWord)

    while choiceWord.count < 4 {
        let randomWord = hardKoreanWords.hardWords.randomElement()!
        if !choiceWord.contains(randomWord) {
            choiceWord.append(randomWord)
        }
    }
    //보기 섞기
    choiceWord.shuffle()

    //단어 검색 및 데이터 저장
    for word in choiceWord {
        wordNetwork.searchWord(word) { wordData in
            self.handleWordData(word: word, wordData: wordData)
        }
    }
    print(choiceWord)
}

보기를 생성하는 메소드 입니다.

for word in choiceWord를 위해서 앞서 말한 (URLSession을 통해 api통신을 하는 코드중 수정)부분 입니다.

완료 핸들러를 넘겨 받아서 handleWordData를 통해

wordDataDictionary에 보기 단어를 저장합니다.

 

단어 데이터 처리 handleWordData()

func handleWordData(word: String, wordData: WordData?) {
        if let wordData = wordData {
            wordDataDictionary[word] = wordData
            // 정답 단어의 설명을 가져오기
            if word == correctWord {
                fetchCorrectWordDefinition()
            }
        } else {
            print("\(word) 단어 데이터를 가져오지 못함 ")
        }
    }

URLSession을 통해 api통신을 하는 코드를 완료하게 된다면

handleWordData를 호출하여 wordDataDictionary에 저장을 합니다.

 

정답 단어의 설명 가져오기fetchCorrectWordDefinition()

func fetchCorrectWordDefinition() {
    isLoading = true

    // choiceWords 배열에서 랜덤하게 정답 선택
    correctWord = choiceWord.randomElement() ?? ""

    guard let wordData = wordDataDictionary[correctWord] else {
        correctWordDefinition = "설명을 가져올 수 없습니다."
        isLoading = false 
        return
    }

    if let firstSense = wordData.channel.item.first?.sense.first {
        correctWordDefinition = firstSense.definition
        print(correctWordDefinition)
    } else {
        correctWordDefinition = "설명을 찾을 수 없습니다."
    }

    isLoading = false 
}

4개의 단어중 무작위로 정답을 선택합니다.

guard let은 프로그래스뷰를 설정하기 위해 추가를 하였지만,,,ㅎ 아직 성공은 못했습니다.

데이터가 있는 경우 첫번째의 뜻을 correctWordDefinition에 추가합니다. (정렬이 우리말샘순 이여서 첫번째가 가장 적합하였습니다.)

데이터가 없을 경우는 설명을 찾을 수 없습니다 의 내용을 저장합니다.

 

정답 확인 메서드 checkAnswer()

func checkAnswer(selectedWord: String) -> Bool {
    return selectedWord == correctWord
}

 

선택된 단어가 정답과 일치하는지 확인하는 메서드 입니다.

 

 

init()

init() {
    correctWord = hardKoreanWords.hardWords.randomElement() ?? ""
    generateChoices()
}

 

클래스의 인스턴스가 생성될때 호출되는 init입니다.

generateChoices()메서드를 호출하여 선택지를 생성하며 단어의 데이터를 검색하고,저장할 수 있도록 합니다.

 

QuizView.swift

VStack{

    Text("QuizView")
        .padding(.top, 50)

    Spacer()

    if quizViewModel.isLoading {
        ProgressView("Loading...")
    } else {
        Image(systemName: "square.and.arrow.up")
        Text(quizViewModel.correctWordDefinition)
    }

    Spacer()

    VStack(){

        HStack {
            ForEach(quizViewModel.choiceWord.prefix(2), id: \.self) { word in
                Button(action: {
                    // 선택된 단어에 대한 동작을 추가하세요
                    isAnswerCorrect = quizViewModel.checkAnswer(selectedWord: word)
                    print("선택된 단어: \(word), 정답?: \(isAnswerCorrect)")
                }) {
                    Text(word)
                        .padding()
                        .background(Color.blue)
                        .foregroundColor(.white)
                        .cornerRadius(8)
                }
                .padding()
            }
        }

        HStack {
            ForEach(quizViewModel.choiceWord.dropFirst(2), id: \.self) { word in
                Button(action: {
                    // 선택된 단어에 대한 동작을 추가하세요
                    isAnswerCorrect = quizViewModel.checkAnswer(selectedWord: word)
                    print("선택된 단어: \(word), 정답?: \(isAnswerCorrect)")
                }) {
                    Text(word)
                        .padding()
                        .background(Color.blue)
                        .foregroundColor(.white)
                        .cornerRadius(8)
                }
                .padding()
            }
        }
    }

    Text(isAnswerCorrect ? "정답입니다!" : "오답입니다.")

    Spacer()
}//VStack

 

quizview에서 간단하게 보기 단어를 4개 보여주고 정답유무를 확인하는 텍스트를 추가하였습니다 하하..

아직

Image(systemName: "square.and.arrow.up")

를 완성하지 않았는데요!

이부분은 네이버 검색API를 활요해서 정답 단어의 관련 이미지를 보여줄 예정입니다 ㅎㅎ..

 

 

이 정도 구현하는것도 어렵네요,, 하하,,,

저의 수준을 잘 파악 할수 있는 시간이 되었습니다 ㅎㅎ

조금은 SwiftUI와 친숙해진 시간이 된것 같네요??!!

 

다음은 네이버 검색 API를 활용한 글을 가져오겠습니다...

이상 보고 끝!(화이팅,,!👏)

https://simth999wrld.tistory.com/74

 

1차시. OT

간단하게 스터디 일정과 인사후 개인프로젝트 소개를 하였습니다, ^_^/ 간단하게 아이디어를 정리해보았습니다.. 진행과정을 간간히 올려보도록 노력! 프로젝트 개요 앱 이름: ??? 프로젝트의 동

simth999wrld.tistory.com

 

위를 바탕으로 개발을 시작하였습니다.

 

1.  시장조사

 

제가 구상한 퀴즈 앱을 구상하기 위해 앱스토어를 둘러보았습니다 하하.. (대충 요런것 둘러봤다는 뜻)

 

2. 뷰 구성

둘러보며 간단하게 저의 앱의 뷰를 구성하였는데요,

 

홈뷰

오늘의 단어와 뜻을 간단하게 알려줌.

퀴즈뷰

퀴즈 시작 시 단어의 뜻과 관련 사진을 알려주고 단어를 맞추는 방식.

정답 여부와 관계없이 TTS로 단어를 읽어줍니다. 

알림뷰

오늘의 단어를 알림을 통해 사용자에게 알려주기

알림 시간 설정 기능

알림 ON/OFF 기능

 

이렇게 3개의 뷰로 구성할것입니다.

+오늘의 단어와 뜻 보여주는 위젯 구성하기.

 

3. 우리말샘 API호출 테스트

단어와 뜻을 가져오기 위해 우리말샘의 API를 사용하였습니다.

 

사이트에서 저의 API키를 받고 openAPI 예시를 "미끼" 로 확인하였습니다.

 

 

위와 같이 JSON을 불러와야 하니 DataModel을 구성하였구요

struct WordData: Codable {
    let channel: Channel
}

struct Channel: Codable {
    let item: [Item]
}

struct Item: Codable {
    let word: String
    let sense: [Sense]
}

struct Sense: Codable {
    let definition: String
    let pos: String?
    let link: String
    let type: String
}

 

 

 

우리말샘 사용을 위해 UrlSession 네트워크 통신을 하였습니담~

class WordNetwork: ObservableObject {
    
    @Published var wordData: WordData?
    
    func searchWord(_ searchWord: String) {
        let urlString = "https://opendict.korean.go.kr/api/search?certkey_no=6282&key=\(YourApiKey)&target_type=search&req_type=json&part=word&q=\(searchWord)&sort=dict&start=1&num=10"
        if let url = URL(string: urlString) {
            let session = URLSession(configuration: .default)
            let task = session.dataTask(with: url) { data, response, error in
                if error == nil {
                    if let safeData = data {
                        do {
                            let decodedData = try JSONDecoder().decode(WordData.self, from: safeData)
                            DispatchQueue.main.async {
                                self.wordData = decodedData
                            }
                        } catch {
                            print(error)
                        }
                    }
                }
            }
            task.resume()
        }
    }
}

 

 

네트워크 통신을 통해 단어를 검색하여 단어와 뜻을 가져올 수 있게 되었습니다.

 

 

 

 

간단하게 버튼을 활용해서 "살림"이란 단어를 검색하여
테스트 하였습니다.

 

 

 

 

 

4. 문제발생

4.1) 시뮬레이터 잘려보이는 현상

 

 

 

 

 

 

 

 

앱을 빌드하니, 시뮬레이터에서 화면이 꽉차지 않는 현상이 있더라군요,,

이는 간단히 앱의 General-AppIcons and Launch Screen

의 Launch Screen File을 main 또는 LaunchScreen으로 하면 된다고 합니다 하하...😅😅

 

 

 

 

 

 

4.2) 고급 단어 어떻게 가져올까...?

고급 단어를 어떻게 가져올까...합니다..

이는 아직 해결은 안됐습니다,,,

그래서! 원시적인 방법으로 문제해결 같아 보이게 하였는데요,,

 

 

 

 

 

 

하드코딩을 하여 조금 어려울만한 단어들을 검색해 넣어두었습니다. ^_^

이 문제에 대해 아이디어가 있으면 알려주세요,,ㅜㅠ

 

 

 

 

 

 

5. 다음 할일

api호출을 위한 테스트를 해보았구요. 이제 기능들을 차차 구현해 나갈 계획입니다.

퀴즈뷰 부터 시작 하려합니다.

예상은 단어의 뜻과 관련 이미지를 보여주고, 4개의 보기중 정답인 단어를 맞추는 식으로 할것같은데,,

보기의 오답 단어는 어떻게 할까~ 하다 정답과 비슷한 유의어를 보여줄것 같습니다!

이것또한 오픈api를 사용 하려 합니다.

정답 단어의 관련 이미지는 네이버 이미지검색 api를 활용하면 좋을것 같습니다 하하~~

어려운 단어 어떻게 가져올지 알려줘 젭알

 

 

 

https://opendict.korean.go.kr/main

 

우리말샘 - 함께 만들고 모두 누리는 우리말 사전

 

opendict.korean.go.kr

https://aiopen.etri.re.kr/guide/Homonym

 

AI API/DATA

JSON parsing을 위해 Gson 라이브러리를 사용하여 제공하고 있습니다. Gson 라이브러리에 대한 자세한 설명은 https://github.com/google/gson 에서 확인 하실 수 있습니다. import java.io.DataOutputStream; import java.io.

aiopen.etri.re.kr

https://developers.naver.com/docs/serviceapi/search/image/image.md#%EC%9D%B4%EB%AF%B8%EC%A7%80-%EA%B2%80%EC%83%89-%EA%B2%B0%EA%B3%BC-%EC%A1%B0%ED%9A%8C

 

검색 > 이미지 - Search API

검색 > 이미지 이미지 검색 개요 개요 검색 API와 이미지 검색 개요 검색 API는 네이버 검색 결과를 뉴스, 백과사전, 블로그, 쇼핑, 영화, 웹 문서, 전문정보, 지식iN, 책, 카페글 등 분야별로 볼 수

developers.naver.com

 

간단하게 스터디 일정과 인사후 개인프로젝트 소개를 하였습니다, ^_^/

간단하게 아이디어를 정리해보았습니다..

진행과정을 간간히 올려보도록 노력!

 

  1. 프로젝트 개요
    • 앱 이름: ???
    • 프로젝트의 동기: 청소년 문해력 문제 있어 (80.9%, 2532명)
    • 목적: 문해력 높이기
    • 대상: 청소년
  2. 기술 스택 및 도구 선택프레임워크: SwiftUI, Alamofire도구: Xcode, Cocoapods..?SwiftUI 및 Kingfisher를 사용하기로 결정한 이유: (킹피셔,알라모파이어,rxswift 국밥3대장이라함…)
  3. 기타: Git, GitHub (깃 업데이트 안하니 이제부터라도 씨앗심기…)
  4. 라이브러리: Kingfisher
  5. 언어: Swift
  6. 프로젝트 구조 설계 MVVM..?
  7. API 통신 구현
    • 통신 클래스와 모듈 구현
  8. Kingfisher 및 SwiftUI를 활용한 이미지 표시 구현
    • 어디서 무료 이미지 끌어오는지..?
  9. 퀴즈 로직 구현
    • 우리말샘 API로 받아온 데이터를 활용하여 퀴즈 로직을 구현합니다.
    • 사용자 입력을 처리하고 정답 여부를 확인하는 로직을 작성합니다.
  10. UI/UX 디자인 및 구현
    • 앱의 사용자 인터페이스 및 사용자 경험 구현
    • 사용자에게 편리한 환경을 제공
  11. 테스트 및 디버깅(안해봄..)
  12. 문서화 및 포트폴리오 작성
  13. 앱출시..?

무료API모음

https://github.com/dl0312/open-apis-korea

알라모파이어를 사용해서 우리말샘api 사용.

https://opendict.korean.go.kr/main

 




이렇게 작성해서 이야기를 나누었구요 ^_^

+ TTS기능과 위젯, 매일 알림을 설정해서 단어의 뜻을 알려주는 기능들을 추가하면 좋을것같습니다..

시작전에 앱스토어에 올라와있는 관련 퀴즈 앱을 구경하면서 뷰를 구상해 보겠습니다 ^_^/

ToDo
✅ 조금은 부지런해진 나
✅ 감사해 🙏

 

매일 나태해 지는 나를 어떡하면 조금 부지런하게 살아갈수 있을지를 고민하던중

공부하는 환경을 바꾸기로 했습니다..하하 

바로 스터디 카페에 출첵을 하는것이죠!

이제 시간도 정해서 매일 오는 것으로 합시다 하하.하..ㅎ..ㅇ..

 

배운점

강의를 들으며 AppDelegate에 있는 코드를 뜯어보고있었습니다.
눈에 안들어오는 document까지,,

//SQLite DB
lazy var persistentContainer: NSPersistentContainer = {

    let container = NSPersistentContainer(name: "DataModel")
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {

            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    return container
}()

// MARK: - Core Data Saving support

func saveContext () {
    //context는 git의 스테이징area와 바슷
    let context = persistentContainer.viewContext
    if context.hasChanges {
        do {
            try context.save()
        } catch {

            let nserror = error as NSError
            fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
        }
    }
}

 

 

1. persistentContainer 변수.

- NSPersistentContainer는 데이터 모델과 저장소를 관리합니다.

- lazy var로 선언되며 처음 접근할때만 초기화됩니다.

- loadPersistentStores를 호출하여 데이터모델을 로드하고 영구 저장소를 설정합니다.

 

2. saveContext 함수

- saveContext함수는 데이터 변경을 저장소에 반영하는 역할을 합니다.

- viewContext는 github의 스테이징 영역과 유사한 개념으로 데이터를 변경하고 업데이트 할 수 있는 영역이다.

- context.hasChanges를 통해 현제 컨텍스트에 변경 사항이 있는지 확인하고, 변경사항이 있을경우

context.save()를 호출하여 저장소에 변경을 저장합니다.

 

 

 

CoreData CRUD

let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

context를 AppDelegate의 persistentContainer로 부터 가져와서 사용한다.

 

- Create

let newItem = Item(context: self.context)
newItem.title = textField.text!
newItem.done = false
newItem.parentCategory = self.selectedCategory //Item과 Category의 정보저장
self.itemArray.append(newItem)

self.saveItems()

 

새로운 Item 객체를 생성합니다.

이 객체가 데이터에 할당된 후에 context.save()를 통해 CoreData에 저장됩니다.

 

-Read

    func loadItems(with request: NSFetchRequest<Item> = Item.fetchRequest(), predicate: NSPredicate? = nil) {
        do {
            itemArray = try context.fetch(request)
        } catch {
            print("Error fetching data from context \(error)")
        }
    }

loadItems메서드는 CoreData에서 데이터를 가져와 itemArray에 할당합니다.

NSPredicate를 통해 득정 조건에 따라 데이터를 필텉링합니다.

 

-Update

//MARK: - TableView Delegate Method
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    itemArray[indexPath.row].done = !itemArray[indexPath.row].done

    //DB에 저장
    saveItems()
}

func saveItems() {

    do {
        try context.save()
    } catch {
        print("Error saving context \(error)")
    }

    DispatchQueue.main.async {
        self.tableView.reloadData()
    }
}

itemArray[indexPath.row].done = !itemArray[indexPath.row].done
으로 변경이 된다.

context.save()로 변경사항을 DB에 저장한다.

 

-Delete

//context delete부터 먼저 제거해야함!
context.delete(itemArray[indexPath.row])
itemArray.remove(at: indexPath.row)

삭제시에는 context.delete에 삭제될 객체를 넣어주어야 context에 삭제사항이 반영된다.

context에 삭제사항을 반영하고 객체를 삭제하는 순서로 이루어져야함!

 

 

 

 

 

 

https://inuplace.tistory.com/1005

 

[CoreData] 기초개념 (1): Entity, CRUD

CoreData란? Apple에서 제공하는 데이터관리 프레임워크로, 데이터를 유지 및 캐싱할 수 있을 뿐 아니라 Cloudkit을 사용해 여러 장치에 데이터를 동기화하는 것도 가능하다. 내부적으로 SQLite를 사용

inuplace.tistory.com

https://80000coding.oopy.io/72aca705-64d9-4be6-aaf5-34c1def4223a

 

(Swift) 코어 데이터 이해하기

"아래 모든 내용은 애플 공식 문서를 참고하여 정리 및 구현한 내용입니다."

80000coding.oopy.io

생각과 감정

오늘까지 졸업유예기간이다.. 유예는 하지 않겠다,,, wOO yEaH~,,,,

 

'TIL' 카테고리의 다른 글

TIL_20240116 iOS에서 데이터를 저장하는 방법  (0) 2024.01.16
TIL_20240110 Swift_AppSandbox, NSCoder  (0) 2024.01.10
TIL_20240109 Swift_SingletonPattern  (0) 2024.01.09
TIL_20240103  (0) 2024.01.05
TIL_20231226  (0) 2023.12.26

ToDo
✅ 운동
✅ 취지직...준비 
✅ 공부

 

글을 쓴지 조금 지났군요 하하 ^_^ 갑작스레 잡힌 면접을 준비하느라 공부는 살짝 미뤄뒀습니다.

솔루션 회사에 면접을 보고왔습니다만,, 여러 느낌은 제껴두고 면접관님에게 감사한 느낌이 듭니다. 

물론 합불을 떠나서 개발자로써 좋은 조언들을 해주시고 분위기도 편안하게 해주셔서 너무 감사드려요 ^_^/

하지만 저는 손에 땀이 너무 많이나더군요 하하.. 긴장했는지 발을 살짝 떨었는데 들킨거같은 느낌ㅋ

 

하튼~ 취준을 할때마다 느끼는 거지만 생각을 하면 할수록 머리가 복잡해집니다..~~

그래서 이제부터 스터디 카페에서 공부를 하려구요..ㅎ

타자를 좀 우다다다 치고싶지만 다른 공부하시는 분들을 위해 살살 누르는중..ㅋ

 

마저못한 CoreData와 데이터를 저장하는 방법들을 후다닥 알아봅시다~

 

배운점

UserDefaults

- 정보를 저장하고 검색하는 가장 일반적이고 편안한 방법

- 사용자 기본설정인 작은 정보 (ex. dark or ligh mode, volume크기(소리) 등) 를 저장할때 사용.

- 일반적으로 앱에 설정 화면이 있는 경우 UserDefaults가 적합

- 이는 Key-Value 로 property plist 또는 plist로서 disk에 저장된다.

- AppData > Library > Preferences 안에 plist 파일이 있는데 이게 표준 UserDefaults의 database 파일

 

Keychain

- 이는 disk에 있는 특수한 파일

- 암호화하고 보안을 유지해야 하는 작은정보 (ex. 암호, 자격증명, 토큰 등) 를 저장할때 사용

- 모든 것이 Data로 저장되어야 함. 따라서 해당 유형으로 직렬화 할 수 있는 object와 value만 전달해야함.

- 참고로 Keychain Sharing 을 활성화하려면 keychain에 저장한 값을 같은 팀에서 개발한 다른 앱과 공유 가능.

- ( 실제로 앱에서 키체인을 사용하려 한다면 좋은 라이브러리를 검색하여 그 라이브러리가 대신 작업을 해야 시간과 스트레스에 도움이 된다합니다 ^_^ 

 

File Sytem

- 저장해야하는 항목이 UserDefaults, Keychain에 해당하지 않는 경우 파일시스템 사용.

- 컴퓨터의 파일 시스템과 똑같이 작동하며, 경로와 URL을 사용하여 디스크의 리소스를 식별.

- 암호화 해야 하지만 키체인에 저장할 수 없는 대용량 파일의 경우 파일에 쓸 때 적절한 암호화 옵션을 사용하는 것이 좋음.

- 사용자의 진행 상황을 저장하기 위한 파일을 Library 폴더에 만들 수 있음.

- 앱의 파일 시스템은 샌드박스 처리됩니다. (링꾸)

 

언제사용?

- 웹에서 다운로드 한 이미지를 저장해서, 로딩 시간을 단축할 때는 Library/Caches 폴더 사용

- 앱에서 사용자가 콘텐츠를 만들 수 있도록 허용하는 경우 Document 폴더 사용 (ex. 사용자가 내보낼 수 있는 CSV 파일의 위치)

다만 동영상과 사진을 편집하는 경우 CameraRoll이 더 나은 선택.

 

sqlite

- 데이터를 저장하는 장소 이상으로 내부 데이터베이스의 사실상 표준.

- Library 폴더는 일반적으로 SQLite 데이터베이스를 저장하기에 가장 좋은 장소.

- 잘 구조화된 수많은 데이터가 있을때 사용하기 좋음

- 스토리지 기능 외에도 SQL 데이터베이스가 있으면 효율적인 검색이 가능하고 복잡한 쿼리를 작성하여 흥미로운 인사이트를 얻을 수 있음

 

CoreData

- Core Data는 iOS가 제공하는 ORM (Objected - Relational Mapping) 프레임 워크

- 데이터를 저장하고 관리하기 위한 프레임워크 즉 데이터를 조작하는 방법

- CoreData는 해당 기기에 데이터를 저장하므로 오프라인에서도 동작 가능하며,icloud에 데이터 공유 가능.

- SQL을 쓸 일 없이 오롯이 object - oriented 방식으로만 데이터를 다룰 수 있음. 데이터는 Object로 표현되며, 이러한 Object가 관계를 형성하어 Object Graphs를 이루고 이를 관리하는 프레임 워크가 바로 Core Data

- CoreData도 내부적으로는 SQL을 이용하여 데이터를 저장하지만, 개발자는 Xcode에 내장된 데이터 모델 에디터를 통해 데이터의 타입, 관계(Graphical Relationship)를 지정하고 코드로 관련 클래스를 수정할 수 있음.

- 우리는 Context에 의해 관리되는 DataModel을 만듦. 그리고 Context는 데이터 저장 및 검색을 담당하는 StorePresistor와 상호작용함.

- 여러 엔티티와 관계가 있는 복잡한 오브젝트 모델을 추적해야 할 때 좋음

- SQLite보다 더 빠르게 기록을 가져올 수 있지만, 더 많은 메모리와 저장 공간을 사용.

 

 

앞으로의 계획선언

CoreData부분은 더욱 알아봐야 할 것 같습니다. (persistentcontainer, context)등 아직 이해가 덜된것이 많군요

 

https://betterprogramming.pub/5-ways-to-store-user-data-in-your-ios-app-595d61c89667

 

5 Ways To Store User Data in Your iOS App

Choose the right storage based on your requirements

betterprogramming.pub

https://github.com/sujinnaljin/TIL/blob/master/Swift/5%20Ways%20To%20Store%20User%20Data%20in%20Your%20iOS%20App.md

https://80000coding.oopy.io/72aca705-64d9-4be6-aaf5-34c1def4223a

 

'TIL' 카테고리의 다른 글

TIL_20240119 iOS)CoreData로 부숴지기.. CRUD  (0) 2024.01.19
TIL_20240110 Swift_AppSandbox, NSCoder  (0) 2024.01.10
TIL_20240109 Swift_SingletonPattern  (0) 2024.01.09
TIL_20240103  (0) 2024.01.05
TIL_20231226  (0) 2023.12.26

ToDo
✅ 운동
✅ 공부

어제 하루 루틴을 조금 예상해보았지만.. 한두시간씩 밀리는 감이 있습니다 하하..

호다닥 공부를 해보겠습니다.

 

배운점

공부를 조금 진행하면서 간단한 앱을 만든적이 있습니다.

ToDoApp입니다. 하하

이 앱을 만들던중, 오늘 할일과 할일을 체크하는 두개의 기능이 있었지만, 앱을 껐다 키면 데이터가 모두 날아가,

UserDefaults를 사용해서 데이터를 저장하였습니다.

하지만 데이터를 저장하고 불러오는것은 성공하였지만, Todo의 체크여부가 셀이 늘어날수록 엉키고, 셀을 삭제했을때도 순서가 맞지않는 문제를 겪어, 체크박스를 커스텀해 제작을 했습니다!

 

하지만 공부를 하던중 이 이유를 알았습니다.. 바로 ReusableCell 때문이더군요! 테이블뷰를 스크롤 하여 내리면 새로 생기는 셀이 스크롤하며 보이지 않는 셀로 재활용 되어 나타나 체크가 되어있는 이슈였습니다.. 하하

이걸 알았다면 체크박스를 직접만드는 삽질은 안해도,, 또한 UserDefaults는 작은 정보만 저장을 하도록 추천을하고 UserDefaults 싱글톤으로 관리됩니다. 또한 UserDefaults를 사용하면 기본 자료형의 딕셔너리만 사용 할 수 있습니다.

따라서 데이터를 저장하는 방법을 NSCoder를 사용하여 바꾸었습니다. 


데이터를 plist로 저장하고 불러오기를 하기전 Sandbox의 개념을 알아보았습니다.

 

App Sandbox

: 앱이 손상된 경우 손상을 방지하기 위해 macOS 앱의 시스템 및 사용자 데이터에 대한 엑세스를 제한합니다.

 

프로그램을 보호하는 보안 모델로 보입니다!

 

 

 

앱을 다운받아 실행하면 이러한 화면을 많이 접합니다.

또한 Sandbox는 커널수준에서 시행되는 접근 제어 기술 이라고 합니다.

 

커널?? 메모리에 상주하는 운영체제의 부분

소프트웨어가 컴퓨터에서 실행되기 위해서는 메모리에 그 프로그램이 올라가있어야 합니다.

전원을 키자마자 OS또한 메모리에 올라갑니다.
하지만 OS 매우 커서 필요한 부분부분을 올리게 되는데 이때의 영역을 커널 이라고 합니다.

 

 

 

 

 

 

왼쪽 그림이 샌드박스가 없는 것이며 오른쪽은 샌드박스를 사용하는 앱의 경우 입니다.

 

왼쪽의 경우

앱을 실행하는 사용자는 모든 권한을 가지며, 사용자가 접근할 수 있는 모든 리소스에 접근을 할 수 있게 됩니다.

이는 보안의 허점이 있을 경우 앱의 취약점이 공격당할 수 있으며 공격자는 사용자가 수행할 수 있는 모든 작업을 수행할 수 있게 됩니다.

아주 악용하기 너무 좋아보이는걸요??

 

따라서 보안뿐 아닌 접근 문제또한 해결하기 위해 AppSandbox를 사용하는것 입니다.

 

1. AppSandbox를 사용하면 앱이 시스템과 상호작용 하는 방식을 설명합니다. ( 접근 권한을 앱에 부여하는것)

2. AppSandbox룰 사용하면 갤러리, 열기, 저장 등등 사용자 상호작용을 통해 앱에 투명하게 추가 접근 권한을 부여합니다.

 

App Sandbox의 원칙

:  App Sandbox는 각 앱마다 취약한 리소스에 대한 액세스를 제한함으로써 공격자로 인한 사용자 데이터 도난, 손상 또는 삭제 또는 시스템 하드웨어 해킹에 대한 마지막 방어선을 제공합니다.

하드웨어, 네트워크 연결, 앱 데이터, 사용자파일 등과 같은 리소스를 사용하기 위해서는 그 의도를 명시해야 합니다.

 

App Sandbox의 구조

샌드박스는 위와같은 내부 컨테이너로 볼 수 있습니다.

샌드박스에는 여러 디렉토리가 포함되어 있습니다.

 

Bundle Countainer

이곳은 앱의 번들 입니다. 이 디렉토리에는 앱 바이너라와 앱에서 사용되는 모든 리소스가 포함되어 있습니다.

이 디렉토리에는 write를 할 수 없습니다. 이 디렉토리에 write를 하면 서명이 변경되어 앱이 실행되지 않습니다.

(API를 사용하면 앱 번들에 저장된 모든 리소스에 대한 읽기 전용 액세스 권한을 얻을 수 있습니다.

 

DataCountainer

사용자가 텍스트 이미지 등을 저장하고 앱의 런타임 중에 생성된 데이터등 앱의 데이터를 저장합니다.

 

Document : 이 디렉토리를 사용하여 사용자 생성 콘텐츠를 저장합니다.

(iCloud를 사용하여 백업 가능)

 

Libary : 사용자 데이터 파일이 아닌 모든 파일의 최상위 디렉토리 입니다.

 

Temp : 이 디렉토리를 사용하여 앱 실행 간에 유지될 필요가 없는 임시 파일을 작성합니다.

(iCloud를 사용하여 백업 불가)

 

iCloud Container

런타임에 접근을 요청할 수 있는 추가 컨테이너 디렉토리입니다.

 

App Sandbox의 장점

- Sandbox는 앱의 데이터와 정보를 비공개 환경으로 보호하고 유지합니다.

- Sandbox는 시스템 리소스를 손상으로부터 보호함으로써 잠재적인 해커로부터 발생할 수 있는 피해를 최소화 할 수 있습니다.

- iOS or iPadOS 기기에 앱이 설치되면 시스템은 해당 앱에 대한 고유한 디렉토리를 생성합니다.

 

 

 


따라서 App Sandbox덕분에 앱이 수명 내내 파일에 저장된 데이터를 읽기,쓰기를 할수 있습니다.

 

 

ReusableCell을 사용하지 않기 위해 데이터 모델을 만들어주었습니다.

import Foundation

class Item: Codable {
    var title: String = ""
    var done: Bool = false
}

 

할일인 title과 체크여부를 알려주는 done을 작성하였습니다.

 

Codable을 사용하여 plist로 [Item]타입의 배열을 저장하려 합니다.

 

plist가 저장되는 장소는

let dataFilePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("Items.plist")
print(dataFilePath)

위의 코드로 알 수 있습니다.

 

//    addButtonPreesed에서 plist를 저장하게된다면 title은 저장이됨, 하지만 done 즉 체크를 판단하는 부울은 저장이 안됨
//    이는 didSelectRowAt안에서또한 저장을 해야함. 따라서 같은 코드를 복붙X 함수를 만들어 사용
func saveItems() {

    let encoder = PropertyListEncoder()

    do {
        //데이터 인코딩
        let data = try encoder.encode(itemArray)
        //dataFilePath에 items.plist 생성
        try data.write(to: dataFilePath!)
    } catch {
        print("Error encoding item array, \(error)")
    }

    //테이블뷰에 itemArray의 내용의 셀을 보기위한 리로드
    self.tableView.reloadData()
}

//plist에 저장된 data 가져오기
func loadItems() {
    if let data = try? Data(contentsOf: dataFilePath!) {
        let decoder = PropertyListDecoder()
        do {
            //데이터파일 패스에서 데이터를 디코딩하는 메서드
            itemArray = try decoder.decode([Item].self, from: data)
        } catch {
            print("Error decoding item array, \(error)")
        }
    }
}

 

PropertyListEncoder와 PropertyListDecoder

PropertyListEncoder는 Encodable 프로토콜을 따르는 데이터 타입을 PropertyList형식으로 인코딩 할 수 있게 됩니다.

PropertyListDecoder는 반대로 Data를 Decodable 프로토콜을 따르는 타입으로 디코딩 할 수 있도록 해줍니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

https://medium.com/techiepedia/demystifying-ios-ipados-sandbox-with-swift-code-8c9222d19380

 

Demystifying iOS/iPadOS SandBox with Swift Code

Hello everyone in this article we are going to see what is iOS Sand Box and how does it works and how developers can access their app’s…

medium.com

https://developer.apple.com/documentation/security/app_sandbox#//apple_ref/doc/uid/TP40011183-CH1-SW1

 

App Sandbox | Apple Developer Documentation

Restrict access to system resources and user data in macOS apps to contain damage if an app becomes compromised.

developer.apple.com

 

생각과 감정

어.. 요즘 갑자기 모든것이 2배속으로 느껴질때가 있더군요 검색을 해보니 스트레스 때문이라던가~ 저는 아니라고 생각해왔지만

막상 받긴 받나 봅니다 허허,, 구래도 정진... 해야겠지..?

부지런하려 노력한 나에게 감사해...🙏

 

 

'TIL' 카테고리의 다른 글

TIL_20240119 iOS)CoreData로 부숴지기.. CRUD  (0) 2024.01.19
TIL_20240116 iOS에서 데이터를 저장하는 방법  (0) 2024.01.16
TIL_20240109 Swift_SingletonPattern  (0) 2024.01.09
TIL_20240103  (0) 2024.01.05
TIL_20231226  (0) 2023.12.26

ToDo
✅ 운동 clear~
✅ 나태지옥 빠져나가기
✅ 졸업신청인가 유예인가?

✅ Swift singleton pattern 공부

 

후 오픽 점수를 IM2를 받아 졸업을 할수 있게 되었습니다만.....

과연 졸업을 하는것이 맞는것인가? 졸업유예를 하는것이 맞는것인가?? 허허..

어떻게 생각하시나요?

저는 아직 모르겠습니다만...

둘다 이후에 행적은 비슷할것 같지만 어떤것이 더욱 유리할까요~!~! 유리...라,,,,

이득? 흠... 뭐 비슷비슷 할것 같습니다만~!~

유예신청 기간에 생각좀 해보겠습니다..ㅎ

 

하루 루틴을 예상하고 내일부터 지켜보도록 하겠습니다. (자세한 일정은 추가하지 않는다.. 나는 극Pㄷ,,)

8시쯤 느지막하게 기상 후 식사

9시 운동출발

11시 집도착

12시 점식먹고

13시 유데미 강의 때리기 (틈틈히 쉬기)

16시 cs공부

18시 or 19시 저녁

~휴식

22시  인턴/신입 공고 구경 코테한문제 때리기?

24시 or 01시 취침

 

이렇게 보니 흠 너무 계획이 난장판이군요...

조금더 자세하게 정리하고 지켜나가 보겠습니다. 일단 저건 초안 ㅋ

배운점

UserDefaults를 이전에 간단한 TODO앱을 만들때 공부하였습니다만!

오늘 Udemy강의에서도 todo앱을 만들면서 싱글톤 패턴이 나와

싱글톤패턴을 공부해보았습니다..하하

 

싱글톤 패턴(Singleton pattern)

특정 용도로 객체를 하나만 생성하여, 공용으로 사용하고 싶을 때 사용하는 디자인 유형. 이라고 정의되어있습니다.

또는 한 개의 클래스로 만드는 객체는 단 한개여야만 한다는 규칙을 가진 디자인 패턴 입니다.

 

프로그램 전체에 단 하나의 전역 객체를 만들어놓고 다른곳에서 이 하나의 객체에 접근하는것 입니다.

 

EX)

URLSession.shared : 네트워크 처리를 할 때 URLSession 객체를 이용하는데, 이미 만들어져 있는 shared 객체에 접근해 메서드를 수행한다.

UserDefaults.standard : UserDefault에서 기본적으로 공유하고 있는 standard 저장소에 접근해 쓰고 읽기 위해 만들어져 있는 standard 에 접근해 메서드를 수행한다.

 

이 위 두예시가 싱글톤 패턴을 기반으로 만들어졌다고 생각하면 된다 합니다 ㅎㅎ

 

import UIKit

//Car 클래스는 빨간색 색상을 가지고, static let으로 만들어진 Car()의 객체인 singletonCar를 만듭니다.
class Car {
    var colour = "Red"
    
    //싱글톤 인스턴스를 저장하는 static상수
    static let singletonCar = Car()
}

let myCar = Car.singletonCar
myCar.colour = "Blue"

//인스턴스 공유 
let yourCar = Car.singletonCar
print(yourCar.colour) //yourCar도 같은 싱글톤 인스턴스를 참조하므로 "Blue"가 출력됩니다.

class A {
    init() {
        Car.singletonCar.colour = "Broun"
    }
}

class B {
    init() {
        print(Car.singletonCar.colour)
    }
}

let a = A()
let b = B()

//A클래스의 인스턴스를 생성하며 색상을 Brown으로 변경됩니다.
//B클래스의 인스턴스를 생성하면서 색상을 출력합니다. "Brown"출력

 

 

결과적으로 싱글톤 패턴을 사용하면 언제나 동일한 Car객체에 접근할 수 있으며, 객체의 상태를 전역적으로 관리할 수 있습니다.

위의 코드에서 볼 수 있듯이 여러 클래스에서 해당 싱글톤 인스턴스를 공유하고 상태를 변경할 수 있음을 보여줍니다.

 

위와같이 강의를 보며 싱글톤 패턴을 이해하였는데요! 

다른 블로그를 구경하며 다른 예제를 보며 이해를 도왔습니다.

 

class UserInfo {
    var id: String?
    var password: String?
    var name: String?
}

 

User의 정보를 저장하는 UserInfo라는 클래스를 선언합니다.

 

//A ViewController
let userInfo = UserInfo()
userInfo.id = "Sodeul"

//B ViewController
let userInfo = UserInfo()
userInfo.password = "123"


//C ViewController
let userInfo = UserInfo()
userInfo.name = "Sodeul"

 

A,B,C ViewController에서 각각의 UserInfo객체를 만들어서 저장하면,

각 인스턴스의 프로퍼티에만 저장이되며 데이터는 공유할 수 없게 됩니다.

 

따라서 UserInfo에 대한 인스턴스는 최초 생성될 때 딱 한번만 생성해서 전역으로 선언하고 이후에 이 인스턴스만 접근 가능하게하자

--> 이 말이 Singleton Pattern 입니다.

 

따라서 

class UserInfo {
    static let shared = UserInfo()

    var id: String?
    var password: String?
    var name: String?
    
    private init() { }
}

 

static을 이용해 인스턴스를 저장할 shared라는 프로퍼티를 생성합니다.

이후에 혹시 init를 호출해 인스턴스를 또 생성하는 것을 막기위해 init의 접근제어자를 private로 지정합니다.

 

위와같이 싱글톤을 만들수 있습니다.

 

외부에서의 접근은 

//A ViewController
let userInfo = UserInfo.shared
userInfo.id = "Sodeul"

//B ViewController
let userInfo = UserInfo.shared
userInfo.password = "123"
 
 
//C ViewController
let userInfo = UserInfo.shared
userInfo.name = "Sodeul"

 

어느 ViewController에서든 shared라는 프로퍼티에 접근하면 하나의 인스턴스를 공유합니다.

 

그럼 싱글톤패턴에 대해 알아봤으니 장/단점 또한 알아 보겠습니다 ㅎㅎ

 

싱글톤 패튼의 장/단점

장점

- 메모리를 단 한번만 사용한다.

해당 객체는 단 한번만 만들어지기 때문에 메모리 관리가 편하다는 장점이 있습니다.

 

- 객체 접근 시간이 줄어든다.

다시 메모리를 할당하고, 초기화(init)하는 시간이 줄어드니 한번 만들어 두기만 하면

다시 접근할 때 매번 객체를 만드는 것보다 조금이나마 시간이 빠르다.

 

- 전역 번위에서 상태, 데이터 전달이 쉬워진다.

어디에서든 접근할 수 있기 때문에 데이터를 전달할 수 있다.

 

단점

 

- 테스트가 힘들다.

싱극톤 클래스는 초기화를 private로 선언합니다.

이렇게 한다면 테스트용 Mock 객체를 만들어내기 어려워진다.

 

- 의존성을 만들어낸다.

어디에서나 접근을 할 수 있으니 어떤 객체와 연결되어 있는지 다 찾아내기 힘들어진다.

 

- 멀티 스레드 환경에서 위험할 수 있다.

멀티 스레드 환경에선 싱글톤 객체가 2개가 생기는 문제가 발생할 수 있다.

또한 싱글톤 객체 내부의 데이터를 동기적으로 처리하지 않으면 여러 객체가 동시에 사용되다 꼬여버릴수 있다.

 

 


테스트용 Mock 객체..?? 

: Swift에서 Mock 객체는 주로 단위 테스트를 수행할 떄 사용되는 가상의 객체라고 합니다!

이 객체는 실제 객체를 모방하여 특정 동작을 시뮬레이션 하거나 메서드 호출을 기록하고 검증하는데 사용된다 하군요.

주로 의존성 주입 패턴과 함께 사용되어 코드의 격리성을 유지하고 테스트 가능한 코드를 작성하는데 도움이 된다고 합니다.

(링크)

 

생각과 감정

생각과 감정이라.. 하하 위에서 다 말을 해버려서 훠훠

 

앞으로의 계획선언

이제 더는 학생이 아닐수도..? 하지만 배움엔 끝이없죠 매일 꾸준이 공부하다 보면 좋은 기회가 있을거라 생각합니다 ㅎㅎ

항상 열심히 정진 하겠습니다.

오늘만큼은 부지런했던 나에게 감사해...🙏

 

 

 

 

https://stackoverflow.com/questions/137975/what-are-drawbacks-or-disadvantages-of-singleton-pattern

 

What are drawbacks or disadvantages of singleton pattern?

The singleton pattern is a fully paid up member of the GoF's patterns book, but it lately seems rather orphaned by the developer world. I still use quite a lot of singletons, especially for factory

stackoverflow.com

 

https://babbab2.tistory.com/66

 

Swift) 싱글톤 패턴(Singleton Pattern)

안녕하세요 :) 소들입니다 이번 포스팅에선 싱글톤 패턴이 무엇이고, Swift에선 어떻게 사용되는지에 대해 알아보려고 해요!!! 왜 네트워크 하다가 갑자기 SingleTon이냐 하면 내 마음임 모든 포스팅

babbab2.tistory.com

 

https://medium.com/hcleedev/swift-singleton-%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4-b84cfe57c541

 

Swift: Singleton, 싱글톤 패턴

Singleton 패턴은 무엇인지, 장단점을 알아보고, Semaphore를 이용한 사용 예시까지 살펴보자.

medium.com

 

'TIL' 카테고리의 다른 글

TIL_20240116 iOS에서 데이터를 저장하는 방법  (0) 2024.01.16
TIL_20240110 Swift_AppSandbox, NSCoder  (0) 2024.01.10
TIL_20240103  (0) 2024.01.05
TIL_20231226  (0) 2023.12.26
TIL_20231223  (0) 2023.12.23

ToDo
✅ udemy swiftUI강의
✅ 운동
✅ 나태한 나를 반성하기.

 

흠 오늘은 어제 유데미 강의를 보며 클론한 코드를 다시 하나씩 뜯어보며 

사용된 내용들을 잘근잘근 씹어서 삼켜보도록 하겠습니다..

 

배운점

Hacker News를 볼수있는 간단한 앱을 클론 했습니다만,,~~

networking, URLSession, Json decode등을 알수 있었쥐만,,,~~

네트워킹 부분에서 https://news.ycombinator.com/ 의 제목, id 등등을 가져와야 했습니다.

이 과정에서 항상 썼던 

class NetworkManager: ObservableObject {
    
    @Published var posts = [Post]()
    
    func fetchData() {
        if let url = URL(string: "https://hn.algolia.com/api/v1/search?tags=front_page") {
            let session = URLSession(configuration: .default)
            let task = session.dataTask(with: url) { data, response, error in
                if error == nil {
                    let decoder = JSONDecoder()
                    if let safeData = data {
                        do {
                            let results = try decoder.decode(Results.self, from: safeData)
                            DispatchQueue.main.async {
                                self.posts = results.hits
                            }
                        } catch {
                            print(error)
                        }
                    }
                }
            }
            task.resume()
        }
    }
}

 

위의 fetchData()의 함수부분을 생각없이 코드를 집어넣었지만 한줄 한줄 뜯어 보다보니, 잊고있었던

async await의 개념을 공부하게 되었습니다.

 

async await을 공부하기 위해 이 블로그를 보며 공부를 하는 중이었지만,, 처음 보는 부분이라 이해가 잘 되지 않더라구요..
그래서! 기억난 async swift에서 발표하신 내용을 확인후 다시 블로그를 보며 공부 했습니다...
(진짜 이 발표 이해 굳굳으로 됨... 발표도 그렇고 블로거 수진님...정말 캄사함다.. 저도 블로그 짜집기 하며 정리를 해보겠슴다..) 

 

DispatchQueue.main.async

 

Swift의 코드는 한줄의 코드가 문장처럼 읽히게 작성 한다라고 어디서 주워 들었던것 같네요..ㅎㅎ
(궁금하면 다시 코드 컨벤션을 찾압 보겠습니다.)

 

하튼 DispatchQueue 큐로 보낸다는 뜻인것 같군요.

main 메인 으로 보낸다는것 같군요.

async 방식으로 한다는것 아닐까요?

메인 디스패치큐에 비동기로 task를 보낸다는것 같군요!!

 

네... ~~인것 같아요 ㅋㅋ

 

이글은 누구에게 async await을 알려주는 글이 아닌 오로지 저의 이해를 위한 글이니 다른

사람에 편의는 봐주지 않겠습니다..(하하..봐줘,,)

 

 

비동기(async)

메인 스레드가 자기한태 쌓여있는 다음 일을 실행한다.. 다음 라인을 실행한다 라구요..

queue에 보낸 작업은 생각하지 않고 바로 그 다음일을 한다 입니다..

 

동기(sync)

queue에 보낸 작업이 완료 될 때까지 기다린 후 다음 줄로 넘어갑니다.

 

queue.async { task } 또는 queue.sync { task }로 task를 queue로 보냈습니다.

queue에 쌓여있는 task들을 다른 스레드에 넣어 일을 해야겠습니다.

 

------------------------------------------------------------------------------

여기서 두개의 큐의 방식이 나오게 됩니다.

 

Serial(직렬)

serial queue는 한대의 스레드에서 처리하는 큐 입니다.

(여러개의 스레드가 있어도 한개의 스레드만 사용을 하는것 입니다.)

 

Concurrent(동시)

concurrent queue는 여러개의 스레드에서 처리하는 큐 입니다.

 

두개의 큐 방식중 어떤것을 사용해야 하는가??( 난 몰루? 항상 빠른것이 짱임 ㅋㅋ)

순서가 중요한 작업이면 serial queue, 순서가 중요하지 않고 빨리 끝내야 하는 작업이면 concurrent queue방식을 

사용 해야 한다고 하네요 껄껄

 

async sync, serial concurrent를 두개씩 묶어 생각하지 않고 별도의 개념으로 받아드려요,,,

 

async, sync -> 작업을 보내는 시점에서 기다릴지 말지에 대해 다루는것

 

serial, concurrent -> 큐에 보내진 작업을 여러개의 스레드에 보낼지 한개의 스레드로 보낼지에 대해 다루는 것

 

 

 

Dispatch Queue의 종류

 

1. main Queue

: 오직 한개만 존재하는 큐, Serial의 특성을 가짐, 이곳에 할당된 task는 메인 스레드에서 처리(UI 업데이트 내용 처리)

 

2. global Queue

: concurrent의 특성을 가진 큐, QoS(qualith of service)에 따라 여러개의 종류로 나뉨.

 

QoS(qualith of service) 6종에 대해 또한 알아야 겠습니다.

 

- userInteractive

사용자와 직접 상호작용 하는 작업 ( UI업데이트, 애니메이션 등)

메인스레드에서 처리하면 많은 로드가 걸리는 작업을 userInteractive에서 처리하여

바로 동작하는 것처럼 보이게함.

 

- userInitiated

클릭과 같은 즉각적인 결과가 필요한 작업(저장된 문서열기 등..)

userInteractive보다는 조금 오래걸리고 유저가 어느정도 인지하고 있음

 

- default

일반적인 작업

 

- utility

progress bar와 함께 길게 싱행되는 작업 (데이터 다운로드 등,,,)

 

- background

유저가 인지하지 않는 시간이 덜 중요한 작업(동기화 및 백업 등,,,)

 

- unspecified 

Qos 정보가 없음 을 나타냄. (거의 사용할일 없다십니다 하하)

 

 

3. custom Queue

: 커스텀으로 생성, 디폴트로 Serial의 특서을 가진 큐 but Concurrent로 설정이 가능하다.

 

앞으로의 계획선언

요즘 공부에대한 목적을 잃고 다시 나태지옥에 빠지는 듯한 기분이 듭니다.. 

그래서 ios공부와 같이 플러터또한 시작해볼까 합니다.. 핳하...

뭐라도 하면서 흥미를 붙여야 좋지 않을까 합니다 하하..

'TIL' 카테고리의 다른 글

TIL_20240110 Swift_AppSandbox, NSCoder  (0) 2024.01.10
TIL_20240109 Swift_SingletonPattern  (0) 2024.01.09
TIL_20231226  (0) 2023.12.26
TIL_20231223  (0) 2023.12.23
TIL_20231221  (0) 2023.12.21

+ Recent posts