하지마이씌

https://simth999wrld.tistory.com/82

위 게시물 이후 3개월 후 군여 훠훠 ㅋ

저는 잘 살아있습니다 아니 죽어있나..? 아무튼 새해 복 다들 많이 받으시길 바랍니다! 누가읽을진 모르겠지만..(매크로 쳐내)

개발을 공부하고 있냐구 말하신다면... 그럼요 요즘 잘 진행하고 있는 것 같습니 다? 

iOS에 대해 조금씩 공부하면서 요즘은 플러터도 조금씩 맛보는 중입니다요~ 예제코드 돌려보면서 이런저런 앱들 만들어 보는즁..

 

이번년도엔 취직을 할 수 있을까여? 

     ㄴ 몰루?

     ㄴ ㅋㅋ 빨리하셈;;

     ㄴ (작성자) 아 가능;;!!

 

제가 조그만한 개인 프로젝트를 진행중입니다. 메트로놈 앱입니다. 제가 밴드를 하고있어서 메트로놈을 사용하는데 조금씩 불편한곳이 있더라구요?? 제 입맛에 따라 만들어 보고 싶어서 만들고있습니다.

여기서 모두 같은 bpm을 동시간대에 같이 보는 기능이 있어숴!! Swift로 서버를 만들 수 있는 Vapor로 프로젝트를 진행하는 중입니다요.

이 이야기는 vapor에대한 내용..

 

배운점

Vapor?

vapor는 Swift로 작성된 서버 사이드 웹 프레임워크로 백엔드, 웹 앱 Api 및 HTTP 서버를 구출할 수 있게 됩니다!

 

설치방법

https://docs.vapor.codes/ko/install/macos/

설치방법은 위 링크 참조 해서 따라하셔용~

 

프로젝트 생성

vapor가 성공적으로 설치가 되었다면 밑의 명령으로 새로운 프로젝트를 생성할 수 있습니다~

vapor new 프로젝트이름 -n

 

이후 프로젝트 이름으로된 폴더에 이동해

swift run

의 명령어로 vapor를 실행할 수 있습니다.

 

폴더구조

├── Public
├── Sources
│ ├── App
│ │ ├── Controllers
│ │ ├── Migrations
│ │ ├── Models
│ │ ├── configure.swift
│ │ ├── entrypoint.swift
│ │ └── routes.swift
├── Tests
│ └── AppTests
└── Package.swift

 

크아악 폴더구조는 링크 에 들어가면 아주 친절하게 정리가 되어있지만! 저도 공부를 해야하니 조금 끄적여 보겠습니다.

 

Public

정적 파일을 저장하는 폴더.

일반적으로 이미지, 스타일시트, 브라우저 스크립트 등이 포함됩니다.

FileMiddleware가 활성화 되면 자동으로 서빙됨.

Tests

XCTest 기반의 단위 테스트 코드가 위치하는 폴더

Sources

프로젝트의 모든 Swift 소스파일이 들어있음.

최상위 폴더인 App은 SPM manifest에 선언된 대로 모듈을 반영함

Sources/App

모든 애플리케이션의 로직이 있는곳

Sources/App/Controller

애플리케이션의 로직을 그룹화 할수 있는 곳.

대부분의 컨드롤러는 요청을 수락하고 응답을 반환하는 많은 함수가 있음.

Sources/App/DTOs

링크

DTOs는 Data Transper Objects라는 뜻

데이터 전송 및 변환을 명확하고 안전하게 관리하기 위해 있다고해.

DTO는 클라이언트와 서버 간 데이터를 주고받을 때 사용되는 객체를 말해

Sources/App/Migrations

Fluent 데이터 베이스 마이그레이션 파일들이 위치

데이터베이스 스키마 변경사항을 관리

테이블 생성, 수정등의 마이그레이션 정의

Sources/App/Models

데이터 구조를 정의하는 파일이 위치.

Content 프로토콜을 준수하는 구조체가 위치함

Content 프로토콜
-> Json 자동 변환 지원
-> 요청, 응답 핸들링 간소화 : 클라이언트가 보낸 JSON 데이터를 구조체로 쉽게 받을 수 있음.
-> 다양한 데이터 포멧 지원
-> validatable를 사용하여 데이터의 유효성을 검증할 수 있음.

따라서
Vapor에서 데이터 모델을 HTTP 요청/응답 처리에 쉽게 사용할 수 있도록 해주는 핵심 기능입니다. 이를 통해 데이터 변환, 유효성 검증, 다양한 포맷 지원 등 많은 작업이 간소화되고, 코드의 유지보수성이 높아집니다.

 

Sources/App/configure.swift

이파일엔 configure(_:) 함수가 포함되어 있음.

앱의 설정을 담당

앱 구동에 필요한 초기 설정을 수행

데이터베이스, 미들웨어, 라우터등 서비스 등록

Sources/App/entrypoint.swift

@main 이 있는 파일

앱을 설정하고 실행하는 역할

configure.swift를 호출하여 앱을 구성

Sources/App/routes.swift

API 엔드포인트를 정의하는 파일

모든 라우트 설정이 이루어지는곳

HTTP 요청에 대한 핸들러를 정의

 

생각과 감정

간단하게 vapor를 알아보았습니다~ 물론 다만들어진건 아니구요 아직 조금씩 공부하면서 만드는중입니더~

아 그냥 파이어베이스나 슈퍼베이스 쓸걸 ㅋㅋ

 

https://github.com/jjwon2149/Metronome/tree/main/Server/MetronomeServer

 

Metronome/Server/MetronomeServer at main · jjwon2149/Metronome

합주를 위한 앱. Contribute to jjwon2149/Metronome development by creating an account on GitHub.

github.com

https://docs.vapor.codes/

 

Vapor Docs

Welcome Welcome to the Vapor Documentation! Vapor is a web framework for Swift, allowing you to write backends, web apps APIs and HTTP servers in Swift. Vapor is written in Swift, which is a modern, powerful and safe language providing a number of benefits

docs.vapor.codes

https://www.postgresql.org/

 

PostgreSQL

The world's most advanced open source database.

www.postgresql.org

 

 

 

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
✅ 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

ToDo
✅ Udemy 컴공선배 강의
✅ 운동

✅ Swift DelegatePattern

 

이번주만 탈락의 고배를 여러번 느꼈는데요,,하하 그러던중 오늘 한 영상을 보았습니다.

침착맨의 "대충 동기 부여하는 영상" 인데요, 조금 울적하던중 위로를 받았습니다 ^_^

궤도님이 한 말이 가장 마음에 남습니다.

"50%확률일때 하지않으면 다음 성공확률도 50%이다. 무조건 시도를 해봐야 다음 성공 확률이 올라간다는 의미입니다."

 

계속 도전하면서 다음 성공 확률을 조금씩 늘려가겠습니다 ^_^

 

나를 위로해준 최고 침착맨 고마워🙏

배운점

오늘은 간단히 회원가입을 할때 유효성 검사를 하기 위한 정규표현식을 같이 알아보았습니다.

    func isValidPassword() -> Bool {
        //비밀번호는 대,소문자,특수문자,숫자 and 8자이상
        let regularExpression = "^(?=.*[A-Z])(?=.*[a-z])(?=.*[\\d])(?=.*[~!@#\\$%\\^&\\*])[\\w~!@#\\$%\\^&\\*]{8,}$"
        let passwordValidation = NSPredicate.init(format: "SELF MATCHES %@", regularExpression)
        
        return passwordValidation.evaluate(with: self)
    }
    
    
    func isValidEmail() -> Bool {
        // @ 2글자
        let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
        let emailTest = NSPredicate.init(format: "SELF MATCHES %@", emailRegEx)
        
        return emailTest.evaluate(with: self)
    }

 

 

자주 쓰는 정규식

대문자만

"^[A-Z]*$"

 

소문자만

"^[a-z]*$"

 

숫자만

"^[0-9]*$"
"^[\\d]*$"

// 반대 (숫자 제외)
"^[\\D]*$"

 

알파벳 + 숫자

"^[0-9a-zA-Z]*$"
"^[\\w]*$"

// 반대 (알파벳, 숫자 제외)
"^[\\W]*$"

 

숫자, 알파벳, 특수문자 (~!@#$%^&*)

"^[0-9a-zA-Z~!@#\\$%\\^&\\*]*$"
"^[\\w~!@#\\$%\\^&\\*]*$"

 

최소 하나 이상 something

"(?=.*[something])"

 

최소 하나 이상의 대문자, 하나 이상의 소문자, 하나 이상의 숫자

"(?=.*[A-Z])(?=.*[a-z])(?=.*[\\d])"

 

최소 하나 이상의 대문자, 하나 이상의 소문자, 하나 이상의 숫자, 하나 이상의 특수문자(~!@#$%^&*), 그 외 불포함

"^(?=.*[A-Z])(?=.*[a-z])(?=.*[\\d])(?=.*[~!@#\\$%\\^&\\*])[\\w~!@#\\$%\\^&\\*]{8,}$"

 

생각과 감정

위로해준 침착맨에게 감사해🙏

 

앞으로의 계획선언

인스타그램 클론한후 기능 붙이기에 돌입해보겠습니다 ^_^ 빻쉬

 

'TIL' 카테고리의 다른 글

TIL_20231126  (0) 2023.11.26
TIL_20231120  (0) 2023.11.20
TIL_20231113  (0) 2023.11.13
TIL_20231107  (0) 2023.11.07
TIL_20231006  (0) 2023.10.06

흐 마지막으로 TIL을 작성한지가 언제지... Zzz ㅋㅌ 그동안 공부를 조금 소홀히 하긴 했지만 하루도 안한적은 옶습니다!!(주말 제외)

그.. 유데미의 엘런님 강의를 듣고 따라하고 다시보느라 TIL을 작성하는 시간이 없었네요!!
하지만~~ 이강의도 거의 끝나가니 패캠에서 새로운 강의를 구매해서 보는중입니다!!

앨런님의 강의는 UIkit 이지만  패캠 강의는 스유라는 사쉴 굉장히 익숙하지 않지만 UIkit보단 접근하기

편한것,,,, 같기도,,?? 아닌가~~

델리게이트 패턴에 대해서 한번 정리 하고자 왔습니다!~

(10/14에 밴드 공연을 해서 이번주와 다음주는 조금 바쁠 예정인것 같네요.. 그래도 짬내서 해야지...)

 

뭐라도 해야지.....

 

배운점

 UITableViewDataSource, UITableViewDelegate를 사용했지만 정확하게 이해를 하진 못하고 사용을 해왔습니다..

항상 아는 듯 모르는 듯한 Delegate Pattern을 공부 해보도록 하겠습니다..

 

Delegation

위임은 클래스 또는 구조체가 책임의 일부를 다른 타입의 인스턴스에 넘겨주거나 위임할 수 있도록 하는 디잔인 패턴입니다.

이 디자인 패턴은 위임된 기능을 제공하기 위해 준수하느느 타입이 보장되도록 위임된 책임을 캡슐화 하는 프로토콜을 정의하여 구현합니다.

예제를 보면서 이해하겠습니다!

protocol DiceGame {
    var dice: Dice { get }
    func play()
}
protocol DiceGameDelegate: AnyObject {
    func gameDidStart(_ game: DiceGame)
    func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
    func gameDidEnd(_ game: DiceGame)
}

DiceGame 프로토콜은 dice변수와 play라는 함수를 가집니다. 주사위와 관련된 모든 게임에서 채택될 수 있는 프로토콜 입니다.

 

프로토콜은 강력한 참조 순환을 방지하기 위해 위임자는 약한 참조로 선언됩니다.

//Example
weak var delegate: SecondViewControllerDelegate?

 

class SnakesAndLadders: DiceGame {
    let finalSquare = 25
    let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
    var square = 0
    var board: [Int]
    init() {
        board = Array(repeating: 0, count: finalSquare + 1)
        board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
        board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
    }
    weak var delegate: DiceGameDelegate?
    func play() {
        square = 0
        delegate?.gameDidStart(self)
        gameLoop: while square != finalSquare {
            let diceRoll = dice.roll()
            delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
            switch square + diceRoll {
            case finalSquare:
                break gameLoop
            case let newSquare where newSquare > finalSquare:
                continue gameLoop
            default:
                square += diceRoll
                square += board[square]
            }
        }
        delegate?.gameDidEnd(self)
    }
}

위의 코드는 Snake and Ladders의 게임 버전 입니다.

제가 이 보드게임은 처음이라... 코드만 확인 하겠슴다 ㅎㅎ

 

SnakesAndLadders의 클래스는 DiceGame의 프로토콜을 채택 합니다. 상속으로 표시됩니다.

weak var delegate: DiceGameDelegate?

위에서 말했듯이 강력한 참조 순환을 방지하기 위해 위임자는 약한 참조로 선언됩니다.

 

프로토콜을 준수하기 위해 dice프로퍼티와 play() 메서드를 제공합니다.

 

class DiceGameTracker: DiceGameDelegate {
    var numberOfTurns = 0
    func gameDidStart(_ game: DiceGame) {
        numberOfTurns = 0
        if game is SnakesAndLadders {
            print("Started a new game of Snakes and Ladders")
        }
        print("The game is using a \(game.dice.sides)-sided dice")
    }
    func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
        numberOfTurns += 1
        print("Rolled a \(diceRoll)")
    }
    func gameDidEnd(_ game: DiceGame) {
        print("The game lasted for \(numberOfTurns) turns")
    }
}

위의 예제는 DiceGameTracker클래스가  DIceGameDelegate 프로토콜을 채택합니다.

 

......계속 Document를 읽는데 무언가 확 와닿게 이해가 되지 않았습니다.. 

개발 블로그를 참고하여 이해를 도왔습니다 ㅎㅎ

 

 

Delegate pattern??

"객체가 자신의 책임을 다른 객체에게 위임 하는 디자인 패턴"

그럼 왜 "위임"을 하는거지??

 

UI의 Delegate Pattern

// Delegate Protocol
protocol UITableViewDelegate: AnyObject {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
}

// Delegating Object
class UITableView {

    weak var delegate: UITableViewDelegate?

    func didSelectedRowAt(indexPath: IndexPath) {
        delegate?.tableView(self, didSelectRowAt: indexPath)
    }
}

// Delegate Object
class ViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print(indexPath)
    }
}

UITableView의 내부 코드를 수정할 수 없기에 다른 객체에게 위임을 하는 것이다!!

셀이 탭되었을때 어떤 행동을 할지는 상황마다 다르기에 개발자가 코드를 작성한다. 하지만 숨겨진 TableView의 코드를

수정할 수 없기에 다른 객체에게 해당 코드를 작성한 뒤, TableView가 그 객체의 코드를 호출해줘야 한다.

이 때 테이블 뷰와 객체를 연결해주는 방식이 DelegatePattern이다!!

 

Delegate Pattern의 핵심은 두 객체를 연결하는 것, 두개의 객체가 효율적으로 소통하도록 도와주는 것이다!

 

 

 

생각과 감정

많이 쓰던 내용인 Delegate를 깊게 들어가보진 않았지만,, 이제야 깨닫네요 하하.. Document는 저랑 맞지 않는건지 아직 이해가 완벽히 되지 않는것인진 몰겠지만,, 한국인이 정리한 개발블로그 너무 감사드립니다... 으아ㅏ.... 덕분에 이해가 잘됐습니다!!

 

 

앞으로의 계획선언

앞으로의 계획 ㅋ 살아남아야한다....

 

 

 

 

 

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/protocols/

 

Documentation

 

docs.swift.org

https://velog.io/@nala/iOS-Delegate-%ED%8C%A8%ED%84%B4%EC%9D%84-%EC%9D%B4%ED%95%B4%ED%95%B4%EB%B3%B4%EC%9E%90

 

[iOS] Delegate 패턴을 이해해보자

Delegate v.(권한업무 등을) 위임하다

velog.io

 

'TIL' 카테고리의 다른 글

TIL_20231113  (0) 2023.11.13
TIL_20231107  (0) 2023.11.07
TIL_20230921  (0) 2023.09.21
TIL_20230918  (0) 2023.09.18
TIL_20230914  (0) 2023.09.14

안녕하십니까..ㅎㅎ 당차게 애플아카데미를 2번 지원했었지만 인터뷰를 해보지도 못하고 떨어졌군요~~
어제는 조금 매음이 아프고 세상이 미웠지만 다시일어났습니다 하하~~

뭐 어쩌겠습니까 다른걸 또 도전하면 되지않겠나요!! 그래서 11월달에 메쉬업에 지원을 하려 합니다~!

그전에 저의 내실을 많이 쌓아 둬야겠죠? 다른 분들에게 많은 도움이 되어야죠 ㅎㅎ (실상 도움을 많이 받는편)
어제 다하지 못한 후행클로저를 공부한후 "객체지향의 사실과 오해"를 조금 읽어보려 합니다~ 화이팅!!

 

배운점

후행클로저에 대해 알아보았습니다.

 

후행클로저

함수의 마지막 인수로 함수에 클로저 표현식을 전달해야하고 클로저 표현식이 긴 경우 후행 클로저를 사용한다고 합니다!

예제를 보면서 알아보았습니다.

func someFunctionThatTakesAClosure(closure: () -> Void) {
    // function body goes here
}

// Here's how you call this function without using a trailing closure:

someFunctionThatTakesAClosure(closure: {
    // closure's body goes here
})

// Here's how you call this function with a trailing closure instead:

someFunctionThatTakesAClosure() {
    // trailing closure's body goes here
}

위의 코드에서 someFunctionThatTakesAClosure 함수는 하나의 매개변수를 가지고 있습니다.

이 매개변수는 closure라는 이름의 클로저입니다.

someFunctionThatTakesAClosure함수를 호출할 때 closure 매개변수에 클로저를 전달합니다. 클로저는 중괄호로 묶여 있습니다.

 

someFunctionThatTakesAClosure함수를 호출할 때 후행 클로저를 사용합니다! 

후행클로저는 함수의 마지막 매개변수로 전달되기 때문에 매개변수 이름을 생략할 수 있습니다.

 

마지막 someFunctionThatTakesAClosure()의 부분을 보시면 코드를 더욱 간결하게 사용할 수 있습니다.

특히 함수의 마지막 매개변수가 클로저인 경우 후행클로저를 사용하면 코드가 더욱 깔끔해집니다!!

주의할 점은 후행 클로저는 함수의 마지막 매개변수로만 사용할 수 있다는 점입니다!

 

reversedNames = names.sorted() { $0 > $1 }

문자열 정렬 클로저는 메서드의 소괄호 밖에 작성될 수 있습니다!

 

reversedNames = names.sorted { $0 > $1 }

후행 클로저로 표현식이 함수와 메서드의 유일한 인수일 경우 함수를 호출할때 ()를 생략해도 좋습니다!

 

후행 클로저는 클로저가 길어서 한줄로 인라인으로 작성이 불가능 할 때 유용합니다.

let digitNames = [
    0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
    5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]

위의 예제는 0은 "Zero"등으로 숫자를 그에 해당하는 영어로 매핑하는 딕셔너리 입니다!

numbers 배열을 사용하여 후행 클로저로 map(_:)메서드로 클로저 표현식을 작성하여 String값의 배열을 생성 할 수 있습니다.

let strings = numbers.map { (number) -> String in
    var number = number
    var output = ""
    repeat {
        output = digitNames[number % 10]! + output
        number /= 10
    } while number > 0
    return output
}
// strings is inferred to be of type [String]
// its value is ["OneSix", "FiveEight", "FiveOneZero"]

클로저 몸체를 보시면,

number를 number로 초기화 하고있습니다.

output 변수를 ""로 초기화 하고있습니다.

repeat으로 number가 0이 될때까지 반복합니다.

반복문의 각 반복에서 클로저는 number의 나머지를 구하고, 해당하는 영어 이름을 output변수에 추가합니다. 

마지막으로 클로저는 output 변수의 값을 반환합니다.

따라서 map함수는 numbers 배열의 각 요소에 대해 위의 클로저를 실행하고, 각 요소에 대한 클로저의 결과를 배열 strings에 저장합니다.

( 딕셔너리의 서브 스크립트는 키가 존제하지 않는 경우에 값을 찾는걸을 실패하기 위해 옵셔널 값을 반환합니다! 따라서 digitNames뒤에 !를 붙여줍니다.)

 

 

함수가 여러개의 클로저를 가지고 있다면 첫번째 후행 클로저의 인수 라벨을 생략하고 남은 후행 클로저의 라벨을 표기합니다.

func loadPicture(from server: Server, completion: (Picture) -> Void, onFailure: () -> Void) {
    if let picture = download("photo.jpg", from: server) {
        completion(picture)
    } else {
        onFailure()
    }
}

completion과 onFailure의 두개의 클로저를 가지고 있는 모습입니다.

completion은 사진을 다운로드 한후 보여주는 완료처리이고,

onFailure는 오류를 표시하는 오류 처리기 입니다.

 

 

캡처값

클로저는 정의된 둘러싸인 컨텍스트에서 상수와 변수를 캡처(capture)할 수 있습니다? (캡처를 알아봐야 겠습니다,)

 

"개발자 소들의"님의 블로그에서 발췌한 내용입니다.

func doSomething() {
    var num: Int = 0
    print("num check #1 = \(num)")
    
    let closure = {
        print("num check #3 = \(num)")
    }
    
    num = 20
    print("num check #2 = \(num)")
    closure()
}

(클로저는 Value/Reference 타입에 관계없이 캡쳐하는 값들을 참조함!! 이것을Reference Capture라고 함)



클로저는 num을 캡처합니다! 즉 num이란 변수를 참조 한다고 합니다.

클로저를 실행하기 전에 num이란 값을 외부에서 변경하면 클로저 내부에서 사용하는 num의 값또한 변경됨!!

 

(다시 document로...)

Swift에서 값을 캡처할 수 있는 간단한 클로저 형태는 다른 분문 내에 작성하는 중첩함수 입니다. 

중첩함수는 바깥 함수의 어떠한 인수도 캡처할 수 있고, 바깥 함수 내에 정의된 상수와 변수를 캡처 할 수 있습니다.

(이해가 조금 어려우니 예제를 보면서 이해하겠습니다!)

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}

incrementer는 중첩함수로 makeIncrementer함수의 내부에 정의되어 있습니다.

incrementer()함수는 둘러싸인 컨텍스트에 runningTotal과 amount값을 캡처합니다. 이값을 캡처한 후 incrementer는 호출할때마다

amount로 runningTotal을 증가시키는 클로저로 makeIncrementer에 의해 반환됩니다.

makeIncrementer의 반환타입은 () -> Int 입니다. 이것은 함수를 반환한다는 의미입니다!


incrementer() 함수는 파라미터가 없으며 함수 본문 내에 runningTotal 과 amount 를 참조하고 있습니다. 둘러싸인 함수에 runningTotal 과 amount 대한 참조 (reference) 를 캡처하고 함수 내에서 사용합니다. 참조를 캡처하는 것은 makeIncrementer 호출이 종료될 때 runningTotal 과 amount 가 사라지지 않고 다음에 incrementer 함수가 호출될 때 runningTotal 을 사용할 수 있습니다.

 

func incrementer() -> Int {
    runningTotal += amount
    return runningTotal
}

단독으로 보면 incrementer 함수는 비정상적으로 보일 수 있습니다.

incrementer 함수는 파라미터가 없으며 함수 본문 내의 runningTotal과 amount를 참조하고 있습니다.

runningTotal과 amount에 대한 참조(reference)를 캡처하고 함수 내에서 사용합니다!
참조를 캡처하는 것은 makeIncrementer호출이 종료될 때 runningTotal과 amount가 사라지지 않고 다음에 incrementer함수가 호출될때 runningTotal을 사용할 수 있습니다!!

 

 

클로저는 참조 타입

참조하는 클로저는 캡처한 runningTotal을 계속 증가시킬수 있는 이유는 함수와 클로저가 참조타입(reference tpyes)이기 때문입니다!

 

뒷부분은 ARC를 공부한후 다시오도록!
- 엡! 삐~

 

 

생각과 감정

이전엔 졸업까지 별로 남지 않아 마음이 많이 조급했던것 같습니다! 흠... 다른분들에게 조언을 받고 여러 유튜브 영상을 보아도 천천히 자기페이스에 맞춰서 공부하는게 모쪼록 도움이 크다고 했지요.. 하하 저도 천천히 저의 페이스에 따라 나아가려 합니다!!

iOS개발자가 되는 그날까지 크흡... 화이또..!  

 

앞으로의 계획선언

ARC와 캡처, 탈출클로저에 대해 더욱 자세하게 알아봐야 겠습니다!!

 

 

 

 

https://babbab2.tistory.com/83

 

Swift) 클로저(Closure) 정복하기(3/3) - 클로저와 ARC

안녕하세요 :) 소들입니다! 이번 포스팅은 클로저 정복하기 마지막 편!!! 메모리나 ARC에 대한 사전 지식이 없으면 조금 이해하기 어려울 수 있으니, 메모리 관련 포스팅을 먼저 보고 오심을 추천

babbab2.tistory.com

 

'TIL' 카테고리의 다른 글

TIL_20230921  (0) 2023.09.21
TIL_20230918  (0) 2023.09.18
TIL_20230914  (0) 2023.09.14
TIL_20230912  (0) 2023.09.12
TIL_20230911  (0) 2023.09.11

정의 

구조체와 클래스의 정의 구문은 비슷합니다. struct키워드로 구조를 소개하고 키워드로 클래스를 소개합니다 class. 둘 다 한 쌍의 중괄호 안에 전체 정의를 배치합니다.

struct SomeStructure {
    // structure definition goes here
}
class SomeClass {
    // class definition goes here
}

 

EX)

struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

 

인스턴스를 생성하는 구문은 구조체와 클래스 모두 매우 유사합니다.

let someResolution = Resolution()
let someVideoMode = VideoMode()

 

 

모든 구조체에는 자동으로 생성된 멤버 단위 초기화가 있으며 이를 사용하여 새 구조체 인스턴스의 멤버 속성을 초기화할 수 있습니다. 새 인스턴스의 속성에 대한 초기 값은 이름으로 멤버별 이니셜라이저에 전달될 수 있습니다.

let vga = Resolution(width: 640, height: 480)

구조체와 달리 클래스 인스턴스는 기본 멤버 단위 초기화를 받지 않습니다. (초기화 initialization)

 

 

클래스는 참조 유형입니다.

값 유형과 달리 참조 유형은 변수나 상수에 할당되거나 함수에 전달될 때 복사되지 않습니다 . 복사본이 아니라 동일한 기존 인스턴스에 대한 참조가 사용됩니다.

let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

frameRate를 30으로 바꿈

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

 

 

 

 

 

 

 

 

import UIKit

struct Town{
    //프로퍼티
    let name = "Angelaland"
    var citizens = ["Angela", "Jack Bauer"]
    var resources = ["Gain" : 100, "Ore" :42, "Wool" : 75]
    
    //구조체 안의 리턴값 없는 메서드(구조체나 클래스 안에서의 정의)
    //독립실행형이고 어디선가 자유롭게 사용된다면 함수라고 정의 따라서 fortify는 메서드
    func fortify(){
        print ("Defences increased!")
    }
}


var myTown = Town() //Town개체를 myTown으로 사용

print(myTown.name) // 구조체의 name 출력
print("\(myTown.name) has \(myTown.resources["Gain"]!) bags of grain.")

myTown.citizens.append("Smith") //moTown의 citizens에 Smith추가
print(myTown.citizens.count) //citizens의 개수 출력

myTown.fortify()



//init사용
struct Town_initCase{
    //프로퍼티
    let name : String
    var citizens : [String]
    var resources : [String : Int]
    
    init(name : String, citizens : [String], resources : [String:Int]){
        self.name = name
        self.citizens = citizens
        self.resources = resources
    }
    
    //구조체 안의 리턴값 없는 메서드(구조체나 클래스 안에서의 정의)
    //독립실행형이고 어디선가 자유롭게 사용된다면 함수라고 정의 따라서 fortify는 메서드
    func fortify(){
        print ("Defences increased!")
    }
}

//init으로 Town_initCase 구조체 초기화
var anotherTown = Town_initCase(name: "Nameless Island", citizens: ["Tom"], resources: ["Coconuts" : 100])
anotherTown.citizens.append("Wilson")
print(anotherTown.citizens)

 

 

 

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/classesandstructures#Definition-Syntax

 

Documentation

 

docs.swift.org

 

'iOS > Swift문법' 카테고리의 다른 글

Swift 알고리즘 클럽  (0) 2023.06.30
Swift 기본문법) 특수문자 출력  (0) 2023.06.17
Swift 기본문법) 컬렉션 타입  (0) 2022.09.05
Swift 기본문법) 기본 데이터 타입  (1) 2022.08.31
Swift 기본문법) 상수와 변수  (1) 2022.08.30

 

특수한 문자열을 출력하려면 양끝에 #을 붙여주면된다.

import Foundation

print(#"!@#$%^&*(\'"<>?:;"#)
//!@#$%^&*(\'"<>?:; 그대로 출력됨.

 

 

 

 

 

간단하게 탭바를 사용하여 3개의 뷰를 만들었다.

1. RecentVideoView

: 이전 영상을 확인 할 수 있는 뷰 라즈베리 파이에서 찍은 영상을 파이어 베이스에 올려 앱에서 받을 것 이다.

 

2.RealTimeVideo

: 웹뷰를 사용하여 라즈베리파이에서 실시간 영상을 볼 수 있는 웹을 열어 그 사이트를 웹뷰로 띄울것 이다.

3.Setting

: 미정 (아마 폭력이 감지됐으때 알림 on/off 정도..??)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

임시로 웹뷰를 네이버로 띄워 놓았다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

RealTimeVideoViewController.swift

import UIKit
import WebKit

class RealTImeVideoViewController: UIViewController {

    @IBOutlet weak var webView: WKWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        //탭바 순서
        self.tabBarController?.selectedIndex = 1
        
        self.tabBarController?.tabBar.isHidden = false
        
        loadWebPage("http://www.naver.com")
    }
    

    func loadWebPage(_ url:String){
        let myUrl = URL(string: url)
        let myRequest = URLRequest(url:myUrl!)
        webView.load(myRequest)
    }

}

 open API 사용을 위한 공부 시작~~~ 하겠습니다~~~

개발하는 정대리님의 영상 보면서 공부했습니다.(항상 좋은강의 감사드립니다.)

https://www.youtube.com/watch?v=WR4_KenX0G8 

 

 

API호출은 Unsplash사이트

https://unsplash.com/ko

 

아름다운 무료 이미지 및 사진 | Unsplash

어떤 프로젝트를 위해서든 다운로드 및 사용할 수 있는 아름다운 무료 이미지 및 사진입니다. 어떤 로열티 프리 또는 스톡 사진보다 좋습니다.

unsplash.com

 

 

 

 

 

 

 

 

 

사이트에서 회원가입을 진행한 후 개발자/API에 들어갑니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Your apps를 클릭하여 앱을 만들어 주시고

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

이렇게 따라오시면 API키 발급이 됩니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

이렇게 API키가 만들어집니다.

Access Key를 활용하여 API를 호출합니다.

 

 

 

 

 

 

Unsplash의 Doucument에서

Search photos와 Search users를 사용하게됩니다.

 

 

 

 

 

 

 

 

API호출이 처음인 분들은 Postman을 사용하면 된다구 하시네요~

https://www.postman.com/

 

Postman API Platform | Sign Up for Free

Postman is an API platform for building and using APIs. Postman simplifies each step of the API lifecycle and streamlines collaboration so you can create better APIs—faster.

www.postman.com

 

 

 

GET에 https://api.unsplash.com/search/photos? 을 입력한 후,

 

client_id에 Unsplash의 Access키를 넣어주시고

 

query에 고양이 검색을 위한 cat를 입력후 Send를 하면 밑의 body에 결과값이 나옵니다. 

저 주소들은 Unsplash에 cat을 검색하면 나오는 이미지와 같습니다.

 

여기까지 API호출을 이용하여 데이터를 가져와 보았고 이제 Alamofire를 사용하여 API호출을 해봅시닷!!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'iOS' 카테고리의 다른 글

SwiftUI) previewLayout이 안돼요..  (0) 2023.12.21

+ Recent posts