728x90
๋ฐ˜์‘ํ˜•

๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๋˜ ์ค‘, ๋ฒ„ํŠผ ์•ˆ์— ์ด๋ฏธ์ง€๋ฅผ ๋„ฃ๊ณ  ์‹ถ์€๋ฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

์ด ๋ฌธ์ œ๋Š” ์ด์ „์— ํ•ด๊ฒฐํ•œ ์ ์ด ์žˆ์—ˆ๋Š”๋ฐ ์–ด๋–ป๊ฒŒ ํ–ˆ๋Š”์ง€ ๊ธฐ์–ต์ด ๋‚˜์งˆ ์•Š์•˜๋‹ค.

 

์•ž์œผ๋กœ ๋น„์Šทํ•œ ์ƒํ™ฉ์„ ๋งˆ์ฃผํ–ˆ์„ ๋•Œ ์ฐพ์•„๋ณด๊ธฐ ์œ„ํ•ด ์ •๋ฆฌํ•œ๋‹ค!!

 

์ด๋ฏธ์ง€ ํฌ๊ธฐ ์กฐ์ •ํ•˜๊ธฐ

์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ ์กฐ์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋จผ์ € extension์„ ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

import UIKit

extension UIImage {
    func resize(targetSize: CGSize) -> UIImage? {
        let rect = CGRect(origin: .zero, size: targetSize)
        UIGraphicsBeginImageContextWithOptions(targetSize, false, 0.0)
        self.draw(in: rect)
        let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return resizedImage
    }
}

์ด๋ ‡๊ฒŒ UIImage ์ต์Šคํ…์…˜์„ ๋งŒ๋“ค์–ด์ค€๋‹ค.

 

 

 

๊ทธ๋ฆฌ๊ณ , ์›ํ•˜๋Š” ๋ฒ„ํŠผ์„ ์„ธํŒ…ํ•˜๋Š” ๋ถ€๋ถ„์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

private let settingButton = UIButton().then {
    let imageSize = CGSize(width: 100, height: 100) // 1
    let resizedImage = UIImage(systemName: "gearshape")?.resize(targetSize: imageSize) // 2
    $0.imageView?.contentMode = .scaleToFill // 3
    $0.setImage(resizedImage, for: .normal) // 4
}

(๋‚˜๋Š” then์„ ์‚ฌ์šฉํ•˜์˜€๋‹ค.)

1. ๋จผ์ €, ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ CGSize๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ค์ •ํ•ด์ค€๋‹ค. ์œ„ ์ฝ”๋“œ์—์„œ๋Š” ๋„ˆ๋น„์™€ ๋†’์ด ๋ชจ๋‘ 100์œผ๋กœ ์„ค์ •๋˜์—ˆ๋‹ค.

2. ๊ทธ๋ฆฌ๊ณ , ํฌ๊ธฐ๋ฅผ ์กฐ์ •ํ•˜๊ณ ์ž ํ•˜๋Š” ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์™€ ์•„๊นŒ ๋งŒ๋“ค์–ด๋‘” extension์„ ์‚ฌ์šฉํ•ด์ค€๋‹ค.

3. ์ด์ œ ์ด๋ฏธ์ง€๋ทฐ์˜ contentMode๋ฅผ scaleToFill๋กœ ์„ค์ •ํ•ด ์ด๋ฏธ์ง€๊ฐ€ ๋ทฐ์— ๊ฐ€๋“ ์ฐจ๋„๋ก ํ•œ๋‹ค.

4. UIButton์— ์žˆ๋Š” setImageํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ์ด์ „์— ํฌ๊ธฐ๋ฅผ ์กฐ์ •ํ•œ ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์™€์ค€๋‹ค.

 

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด, UIButton ์•ˆ์— ์›ํ•˜๋Š” ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ , ๊ทธ ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ ๋งˆ์Œ๋Œ€๋กœ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

 

728x90
๋ฐ˜์‘ํ˜•
728x90
๋ฐ˜์‘ํ˜•

๊ฐœ๋ฐœ์„ ํ•˜๋˜ ์ค‘, ํ…Œ์ด๋ธ” ๋ทฐ ์•„๋ž˜ ์ž๊พธ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ๋ถ„์„ ์ด ์ƒ๊ธฐ๋Š” ๊ฑธ ํ™•์ธํ–ˆ๋‹ค.

 

limitTableView.separatorStyle = .none

 

๊ทธ๋Ÿด ๋•Œ๋Š” ์ด๋ ‡๊ฒŒ ๊ตฌ๋ถ„ ์Šคํƒ€์ผ์„ .none์œผ๋กœ ๋ฐ”๊ฟ”์ฃผ๋ฉด ๋œ๋‹ค.

 

 

์ด์ œ ์ด๋ ‡๊ฒŒ ๊ตฌ๋ถ„์„ ์ด ์‚ฌ๋ผ์ง„ ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค !

728x90
๋ฐ˜์‘ํ˜•
728x90
๋ฐ˜์‘ํ˜•
๋ฐ˜์‘ํ˜•

๋‹ค๋ฅธ ๋ธ”๋กœ๊ทธ๋“ค ๋ณด๋ฉด ์ง์ ‘ ๊ณต์‹๋ฌธ์„œ ๋ณด๋ฉด์„œ ๊ณต๋ถ€ํ•œ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•˜์‹œ๋˜๋ฐ,, ๋‚˜๋„ ๊ทธ๋ ‡๊ฒŒ ํ•˜๊ณ  ์‹ถ์€๋ฐ..

์‹œ๊ฐ„์ด ์ƒ๋‹นํžˆ ๋งŽ์ด ๊ฑธ๋ฆฌ๊ฒ ์ง€๋งŒ, ๋„์ „..ํ•ด๋ด์•ผ์ง€..

 

์ด์ „์— ์• ํ”Œ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•  ๋•Œ๋Š” ๊ทธ๋ƒฅ ๋ธ”๋กœ๊ทธ๋“ค ์ฐธ๊ณ ํ•ด์„œ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ , ๊ฐ€์ ธ์˜จ ์ฝ”๋“œ๋งŒ ๊ณต๋ถ€ํ•ด์„œ ๊ตฌํ˜„ํ–ˆ์—ˆ๋Š”๋ฐ

์‹œ๊ฐ„๋„ ์žˆ๊ฒ ๋‹ค, ์˜์š•๋„ ์žˆ๊ฒ ๋‹ค ํ•œ ๋ฒˆ ํ•ด๋ด์•ผ๊ฒ ๋‹ค.

 

๊ทธ๋‚˜์ €๋‚˜ ํ•œ๊ธ€ ๊ณต์‹๋ฌธ์„œ๋„ ์žˆ์œผ๋ฉด ๋„ˆ๋ฌด ์ข‹๊ฒ ๋‹ค.

ํ•œ๊ธ€ ๊ณต์‹๋ฌธ์„œ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ๋ณด๋‹ค ๋‚ด๊ฐ€ ์˜์–ด๋ฅผ ๋” ๊ณต๋ถ€ํ•ด์„œ ์ž˜ํ•˜๋Š”๊ฒŒ ๋น ๋ฅด๋ ค๋‚˜..?

 

 

์ผ๋‹จ ํ•ด๋ณด์ž๊ตฌ..

์•„๋ž˜ ๊ณต์‹๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•ด์„œ ๊ตฌํ˜„ํ•œ ์• ํ”Œ๋กœ๊ทธ์ธ์ด๋‹ค !

 

https://developer.apple.com/documentation/sign_in_with_apple/implementing_user_authentication_with_sign_in_with_apple

 

Implementing User Authentication with Sign in with Apple | Apple Developer Documentation

Provide a way for users of your app to set up an account and start using your services.

developer.apple.com

 

 

1. Sign in with Apple ์ถ”๊ฐ€ํ•˜๊ธฐ ๋ฐ Identifier ๋“ฑ๋กํ•˜๊ธฐ

Targets → Signing & Capabilities → +Capability ๋ฅผ ๋ˆŒ๋Ÿฌ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

 

(์ด ๋•Œ, Apple developer ๊ตฌ๋…์„ ํ•˜์ง€ ์•Š๋Š” ๊ฐœ๋ฐœ์ž๋ผ๋ฉด, ํ•ด๋‹น ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์—†๋‹ค..ใ… ใ… )

 

๋‹ค์Œ์€ ์• ํ”Œ ๊ฐœ๋ฐœ์ž ์‚ฌ์ดํŠธ์—์„œ identifier๋ฅผ ๋“ฑ๋กํ•ด์ค˜์•ผ ํ•œ๋‹ค.

๊ฐœ๋ฐœ์ž ์‚ฌ์ดํŠธ์— ๋กœ๊ทธ์ธ์„ ํ•˜๊ณ , Account ํ•ญ๋ชฉ์—์„œ, Identifiers๋ฅผ ๋ˆŒ๋Ÿฌ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŽ˜์ด์ง€์— ๋“ค์–ด๊ฐ„๋‹ค.

 

๋งŒ์•ฝ, ์ด ๊ณผ์ •์„ ์ง„ํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด.. ๋กœ๊ทธ์ธ์ด ๋˜์ง€ ์•Š๊ณ , ์ฝ˜์†”์— ์ถœ๋ ฅํ•ด๋„ ์ž๊พธ null ๊ฐ’์ด ๋‚˜์˜ค๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

(์ €๋„ ์•Œ๊ณ ์‹ถ์ง€ ์•Š์•˜์–ด์š”..)

 

ํŒŒ๋ž€์ƒ‰ + ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ์ค€๋‹ค.

 

๊ทธ๋Ÿผ ์ด๋Ÿฐ ์ฐฝ์ด ๋œจ๊ฒŒ ๋˜๋Š”๋ฐ, App ID์™€ ๊ด€๋ จํ•œ ๋‚ด์šฉ์ด๋ฏ€๋กœ ์ด ์ƒํƒœ์—์„œ continue๋ฅผ ํ•ด์ค€๋‹ค.

 

์—ฌ๊ธฐ์„œ๋„ ๊ณ„์† continue๋ฅผ ํ•ด์ค€๋‹ค.

 

 

๊ทธ๋Ÿผ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ™”๋ฉด์ด ๋ณด์ธ๋‹ค.

 

Description: ํŠน์ˆ˜๋ฌธ์ž๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ณ , Identifier์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ ์„ค๋ช…์„ ์ ์œผ๋ฉด ๋œ๋‹ค.

Bundle ID: App์˜ TARGETS → Signing & Capabilities์—์„œ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋‚ด์šฉ์„ ์ฑ„์šฐ๊ณ , Sign In with Apple์„ ์ฐพ์•„ ์ฒดํฌํ•ด์ค€๋‹ค.

๊ทธ๋ฆฌ๊ณ , Edit์„ ๋ˆŒ๋Ÿฌ ์ง€๊ธˆ ์ง„ํ–‰์ค‘์ธ ํ”„๋กœ์ ํŠธ์˜ app id๋ฅผ ์„ ํƒํ•ด์ฃผ์—ˆ๋‹ค.

 

์ด๋Œ€๋กœ next๋ฅผ ๋ˆ„๋ฅด๊ณ , register๊นŒ์ง€ ํ–ˆ๋Š”๋ฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŒ์—…์ด ๋ฐœ์ƒํ–ˆ๋‹ค.

 

์ฐพ์•„๋ณด๋‹ˆ, ์ด ์—๋Ÿฌ๋Š” Bundle ID ์•ž์— ํŒ€ ID(Apple์—์„œ ์ƒ์„ฑ๋œ)๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š์„ ๋•Œ ์ƒ๊ธด๋‹ค๊ณ  ํ•œ๋‹ค.

๊ทธ๋ƒฅ ์ €๊ธฐ ์žˆ๋Š” ์ฃผํ™ฉ์ƒ‰ ๋ถ€๋ถ„์„ ๋ฒˆ๋“ค์•„์ด๋”” ๊ฐ€์žฅ ์•ž์— ๋ถ™์—ฌ๋„ฃ๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

 

 

ํ›„ ์ด์ œ ์„ธํŒ…๋“ค์€ ๋ชจ๋‘ ๋๋‚ฌ๋‹ค !!

์• ํ”Œ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•  ์ฝ”๋“œ๋งŒ ์งœ๋ฉด ๋œ๋‹ค.

 

๊ณต์‹ ๋ฌธ์„œ์—์„œ ์ œ๊ณตํ•œ ์˜ˆ์ œ ์ฝ”๋“œ๋Š” ์Šคํ† ๋ฆฌ๋ณด๋“œ ๊ธฐ๋ฐ˜์ด์—ˆ๊ธฐ์— ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜๋Š” ์—†์—ˆ๋‹ค.

๋‚˜๋Š” UIKit์„ ์‚ฌ์šฉํ•˜๋˜, ์Šคํ† ๋ฆฌ๋ณด๋“œ๊ฐ€ ์•„๋‹Œ ์ฝ”๋“œ๋กœ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š”์ง€ ํ•ด๋ณด๊ฒ ๋‹ค !

 

 

 

2. AuthenticationServices import ํ•˜๊ธฐ

๋จผ์ € ์• ํ”Œ๋กœ๊ทธ์ธ ๊ตฌํ˜„์ด ํ•„์š”ํ•œ AuthenticationServices๋ฅผ ํŒŒ์ผ์— import ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

import AuthenticationServices

 

 

 

3. ๋ฒ„ํŠผ ๋งŒ๋“ค๊ธฐ

func setupAppleLoginButton() {
    // ASAuthorizationAppleIDButton์„ ์‚ฌ์šฉํ•ด ์• ํ”Œ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ ์ƒ์„ฑ
    let appleLoginButton = ASAuthorizationAppleIDButton(type: .signIn, style: .whiteOutline)
    appleLoginButton.translatesAutoresizingMaskIntoConstraints = false
    appleLoginButton.addTarget(self, action: #selector(handleAuthorizationAppleIDButtonPress), for: .touchUpInside)

    // ๋ฒ„ํŠผ์„ ํ™”๋ฉด์— ์ถ”๊ฐ€
    self.view.addSubview(appleLoginButton)

    // ์˜คํ† ๋ ˆ์ด์•„์›ƒ ์„ค์ •
    NSLayoutConstraint.activate([
        appleLoginButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
        appleLoginButton.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
        appleLoginButton.heightAnchor.constraint(equalToConstant: 50),
        appleLoginButton.widthAnchor.constraint(equalToConstant: 300)
    ])
}

 

์ด๋ ‡๊ฒŒ ์˜คํ† ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ๋ฒ„ํŠผ์˜ ํฌ๊ธฐ์™€ ์œ„์น˜๋ฅผ ์žก์•„์ฃผ์—ˆ๋‹ค.

์• ํ”Œ์€ ์• ํ”Œ๋กœ๊ทธ์ธ API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์•Œ์•„์„œ ๋ฒ„ํŠผ์„ ๋””์ž์ธํ•ด์ฃผ๊ณ , default๋กœ ๋””์ž์ธ๋˜์–ด ์žˆ๋Š” ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์ƒ‰์ƒ, ๋””์ž์ธ์ด ์ด๋ฏธ ์ง€์ •๋˜์–ด ์žˆ์–ด ์ƒํ™ฉ์— ๋งž๊ฒŒ ์ ์ ˆํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋ผ๊ณ  HIG์—๋„ ๊ฐ€์ด๋“œ๋ผ์ธ์„ ์ œ์‹œํ•œ๋‹ค.

 

๋‚˜๋Š” ์ผ๋‹จ SignIn ํƒ€์ž…์œผ๋กœ ์•„์›ƒ๋ผ์ธ์ด ํฐ์ƒ‰์ธ ๋ฒ„ํŠผ์„ ๋งŒ๋“ค์—ˆ๋‹ค. 

ASAuthorizationAppleIDButton์ด๋ผ๋Š” ํ•จ์ˆ˜๋งŒ ๊ฐ€์ ธ์˜ค๋ฉด ๋ฐ”๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค !!

 

์ด์ œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ, ํ˜ธ์ถœํ•  ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์ค˜์•ผํ•œ๋‹ค.

 

 

 

 

3. ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์• ํ”Œ๋กœ๊ทธ์ธ ๊ตฌํ˜„ํ•˜๊ธฐ

๋‚˜๋Š” ์ผ๋‹จ ๊ณต์‹๋ฌธ์„œ์— ์žˆ๋Š” handleAuthorizationAppleIDButtonPress ํ•จ์ˆ˜๋ฅผ ๊ทธ๋Œ€๋กœ ์—ฐ๊ฒฐํ•ด์ฃผ์—ˆ๋‹ค.

// apple id๋กœ ๊ถŒํ•œ ์š”์ฒญํ•˜๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜
@objc func handleAuthorizationAppleIDButtonPress() {
    // ์• ํ”Œ ID Provider ์ƒ์„ฑ (์• ํ”Œ๋กœ๊ทธ์ธ ์š”์ฒญ์„ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ASAuthorizationAppleIDProvider๊ฐ์ฒด์ž„)
    let appleIDProvider = ASAuthorizationAppleIDProvider()
    // ์• ํ”Œ ๋กœ๊ทธ์ธ์— ํ•„์š”ํ•œ request ๊ฐ์ฒด ์ƒ์„ฑํ•˜๊ธฐ
    let request = appleIDProvider.createRequest()
    // ์‚ฌ์šฉ์ž์—๊ฒŒ ์–ป๊ณ ์ž ํ•˜๋Š” ์ •๋ณด ์ง€์ •ํ•˜๊ธฐ (์ด๋ฆ„, ๋ฉ”์ผ)
    request.requestedScopes = [.fullName, .email]

    // ์• ํ”Œ๋กœ๊ทธ์ธ ์ ˆ์ฐจ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ์ด๋‹ค.
    let authorizationController = ASAuthorizationController(authorizationRequests: [request])
    // ๋กœ๊ทธ์ธ ๊ณผ์ •์˜ ์„ฑ๊ณต/์‹คํŒจ ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.
    authorizationController.delegate = self
    // ์ธ์ฆ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์–ด๋–ค ํ™”๋ฉด์— ํ‘œ์‹œ๋ ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๋ถ€๋ถ„. ๋ณดํ†ต ํ˜„์žฌ ํ™”๋ฉด์„ ์ง€์ •ํ•œ๋‹ค.
    authorizationController.presentationContextProvider = self
    // ์• ํ”Œ๋กœ๊ทธ์ธ request ์‹œ์ž‘ !
    authorizationController.performRequests()
}

 

์ด๋ ‡๊ฒŒ ์กฐ๊ธˆ์€ ๋ณต์žกํ•ด๋ณด์ด์ง€๋งŒ, ์šฐ๋ฆฐ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๊ด€๋ฆฌํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋งŒ๋“ค์–ด ์—ฐ๊ฒฐํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ์• ํ”Œ๋กœ๊ทธ์ธ ๊ณผ์ •์ด ์ง„ํ–‰๋œ๋‹ค.

 

๋‚˜๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์• ํ”Œ๋กœ๊ทธ์ธ์„ ์™„๋ฃŒํ•˜๋ฉด, ๋กœ๊ทธ์ธ ํ•œ ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„๊ณผ ์ด๋ฉ”์ผ์„ ์„œ๋ฒ„์— ๋„˜๊ฒจ์ค˜์•ผ ํ•˜๊ณ , ์•ฑ ๋‚ด์— ์ €์žฅํ•ด์•ผ ํ•œ๋‹ค.

๊ทธ ์ „์—, ๋ฐ›์€ ์ •๋ณด๋ฅผ ์ž˜ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฝ˜์†”์— ์ถœ๋ ฅํ•ด๋ณด์ž !

 

 

 

 

4. ๋กœ๊ทธ์ธ ์ •๋ณด ์ฝ˜์†”์— ์ถœ๋ ฅํ•˜๊ธฐ

๊ณต์‹๋ฌธ์„œ์—์„œ๋Š” ์„ฑ๊ณตํ–ˆ์„ ๋•Œ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ํ‚ค์ฒด์ธ์— ์ €์žฅํ•˜๋ผ๊ณ  ํ•œ๋‹ค.

์ด๊ฒŒ ๋ฌด์Šจ๋ง์ธ์ง€ ์ดํ•ด๊ฐ€ ์ž˜ ์•ˆ๋ผ์„œ ์ง€ํ”ผํ‹ฐํ•œํ…Œ ๋ฌผ์–ด๋ดค๋‹ค.

 

์ข€ ๊ธธ๊ธด ํ•˜์ง€๋งŒ,, ์ดํ•ด๋Š” ๋๋‹ค.

ํ•„์ˆ˜๋Š” ์•„๋‹ˆ์ง€๋งŒ ๊ฐœ์ธ์ •๋ณด๋Š” ์†Œ์ค‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์—.. ํ‚ค์ฒด์ธ์— ์ €์žฅํ•ด ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ ์•ฑ์„ ๋‹ค์‹œ ์‚ญ์ œํ•˜๊ณ  ์žฌ์„ค์น˜ํ•˜๊ฑฐ๋‚˜ ์•ฑ์„ ์ข…๋ฃŒํ•œ ๋’ค ๋‹ค์‹œ ๋“ค์–ด์™”์„ ๋•Œ ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์„ ์—†์•จ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

 

์ง€๊ธˆ๊นŒ์ง€ ์• ํ”Œ๋กœ๊ทธ์ธ ๊ทธ์ € ์ฝ”๋“œ๋งŒ ๋„ฃ์–ด์„œ ๊ตฌํ˜„ํ•ด์™”๋Š”๋ฐ ์ด๋ ‡๊ฒŒ ๊ณต๋ถ€ํ•ด๋ณด๋‹ˆ,, ๋” ํŒŒ์•ผํ•  ๊ธธ์ด ์žˆ๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

 

ํ‚ค์ฒด์ธ ์ €์žฅ๊นŒ์ง€ ํ•˜๋Š” ๊ณผ์ •์„ ๊ฒฝํ—˜ํ•ด๋ณด๊ณ  ์‹ถ๊ธฐ๋„ ํ•˜์ง€๋งŒ, ๋„ˆ๋ฌด ๋ณต์žกํ• ๊นŒ ๊ฒ์ด ๋‚˜๊ธฐ๋„ ํ•˜๊ณ , ์ง€๊ธˆ ๋กœ๊ทธ์ธ๋ณด๋‹จ ์ฃผ๊ธฐ๋Šฅ ๊ตฌํ˜„์ด ๋” ์šฐ์„ ์ด๊ธฐ์—..! ์ผ๋‹จ ๋‚œ userDefaults์— ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์„ ์„ ํƒํ•˜๊ฒ ๋‹ค.

 

์กฐ๊ธˆ ๋” ์šฉ๊ฐํ•ด์ ธ์„œ ๋Œ์•„์˜ฌ๊ฒŒ..

 

 

 

๋‹ค์‹œ ๋Œ์•„์™€์„œ, ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ์ฝ˜์†”์— ์ถœ๋ ฅํ•ด๋ณด์ž.

๊ทธ ์ „์—, ํ•œ ๋ฒˆ ๋กœ๊ทธ์ธํ•˜๋ฉด ์ถœ๋ ฅํ•  ๋•Œ userIdentifier๋งŒ ๋ณด์—ฌ์ง€๊ธฐ ๋•Œ๋ฌธ์—, ๋ฐ›์•„์˜ด๊ณผ ๋™์‹œ์— UserDefaults์— ์ €์žฅํ•  ๊ฒƒ์ด๋‹ค.

 

// didCompleteWithAuthorization = ์• ํ”Œ๋กœ๊ทธ์ธ ํ›„ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜
// ์ด ํ•จ์ˆ˜๋Š” ์• ํ”Œ๋กœ๊ทธ์ธ ์ ˆ์ฐจ๊ฐ€ ์™„๋ฃŒ๋œ ๋’ค ํ˜ธ์ถœ๋œ๋‹ค.
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
    switch authorization.credential {
    
    // case1: ์‚ฌ์šฉ์ž๊ฐ€ apple ID๋กœ ๋กœ๊ทธ์ธํ–ˆ์„ ๋•Œ ๋ฐ˜ํ™˜๋˜๋Š” ์ •๋ณด๋ฅผ ์ฒ˜๋ฆฌ
    case let appleIDCredential as ASAuthorizationAppleIDCredential:

        // Create an account in your system.
        let userIdentifier = appleIDCredential.user
        let fullName = appleIDCredential.fullName
        let email = appleIDCredential.email

        print("useridentifier: \(userIdentifier)")
        print("fullName: \(String(describing: fullName))")
        print("email: \(email ?? "failed")")

        // UserDefaults์— ์ด๋ฆ„๊ณผ ์ด๋ฉ”์ผ ์ €์žฅ
        let defaults = UserDefaults.standard

        // Optional fullName ์ฒ˜๋ฆฌ
        if let givenName = fullName?.givenName, let familyName = fullName?.familyName {
            let completeName = "\(givenName) \(familyName)"
            defaults.set(completeName, forKey: "fullName")
            print("Saved fullName: \(completeName)")
        } else {
            defaults.set("", forKey: "fullName") // ์ด๋ฆ„์ด ์—†์„ ๊ฒฝ์šฐ ๋นˆ ๋ฌธ์ž์—ด๋กœ ์ €์žฅ
            print("Saved fullName as empty string")
        }

        if let email = email {
            defaults.set(email, forKey: "email")
            print("Saved email: \(email)")
        } else {
            defaults.set("", forKey: "email") // ์ด๋ฉ”์ผ์ด ์—†์„ ๊ฒฝ์šฐ ๋นˆ ๋ฌธ์ž์—ด๋กœ ์ €์žฅ
            print("Saved email as empty string")
        }

	// case2 : iCloud ํ‚ค์ฒด์ธ์— ์ €์žฅ๋œ ๊ฒƒ์„ ์‚ฌ์šฉํ•ด ๋กœ๊ทธ์ธํ•˜๋Š” ๋ฐฉ์‹ (์—ฌ๊ธฐ์„  ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ)	
    case let passwordCredential as ASPasswordCredential:

        // Sign in using an existing iCloud Keychain credential.
        let username = passwordCredential.user
        let password = passwordCredential.password

        print("passwordCredential = \(username)")
        print("passwordCredential = \(password)")

    default:
        break
    }
}

 

์œ„ ์ฝ”๋“œ๋Š” ๊ณต์‹๋ฌธ์„œ์—์„œ ๊ฐ€์ ธ์™€์„œ ์ถœ๋ ฅํ•˜๊ณ , UserDefaults์— ์ €์žฅํ•˜๋Š” ์ฝ”๋“œ๋งŒ ์ถ”๊ฐ€ํ–ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์ฝ”๋“œ๋ฅผ ์งœ๊ณ , ๋นŒ๋“œ๋ฅผ ํ•ด๋ณด์ž !

 

์ด๋ฉ”์ผ ๊ฐ€๋ฆฌ๊ธฐ๋ฅผ ํ•˜๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜์˜จ๋‹ค.

 

์• ํ”Œ ๋กœ๊ทธ์ธ์˜ "์ด๋ฉ”์ผ ๊ฐ€๋ฆฌ๊ธฐ" ๊ธฐ๋Šฅ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ž์‹ ์˜ ์‹ค์ œ ์ด๋ฉ”์ผ ์ฃผ์†Œ๋ฅผ ๊ณต์œ ํ•˜์ง€ ์•Š๊ณ ๋„ ์• ํ”Œ ID๋ฅผ ํ†ตํ•ด ์„œ๋น„์Šค์— ๋กœ๊ทธ์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

๊ทธ๋ ‡๊ธฐ์— ์‚ฌ์šฉ์ž๊ฐ€ '์ด๋ฉ”์ผ ๊ฐ€๋ฆฌ๊ธฐ'๋กœ ๋กœ๊ทธ์ธ์„ ํ•˜๋ฉด, ์• ํ”Œ์€ ์‚ฌ์šฉ์ž๋ฅผ ๋Œ€์‹ ํ•ด ๋ฌด์ž‘์œ„๋กœ ์ƒ์„ฑ๋œ ํ”„๋ก์‹œ ์ด๋ฉ”์ผ ์ฃผ์†Œ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

user@privaterelay.appleid.com ๊ณผ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ๋ฉ”์ผ์ด ์ƒ์„ฑ๋œ๋‹ค.

 

์™œ ์•ฑ ๋‚ด์—์„œ ์ตœ์ดˆ ๋กœ๊ทธ์ธ ๋•Œ๋งŒ ์‚ฌ์šฉ์ž ์ด๋ฆ„๊ณผ ์ด๋ฉ”์ผ์ด ๋‚˜์˜ฌ๊นŒ?

์•ž์—์„œ ๋งํ–ˆ๋“ฏ, ๋ฐ›์•„์˜ด๊ณผ ๋™์‹œ์— UserDefault์— ์ €์žฅํ–ˆ๋˜ ์ด์œ ๋Š” ์ตœ์ดˆ ๋กœ๊ทธ์ธ๋•Œ๋งŒ ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„๊ณผ ์ด๋ฉ”์ผ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์™œ ๊ทธ๋Ÿฐ์ง€ ๊ถ๊ธˆํ•ด์„œ ์ฐพ์•„๋ดค๋‹ค.

 

๋ณด์•ˆ์— ์ฒ ์ €ํ•œ ์• ํ”Œ์€ ์• ํ”Œ ๋กœ๊ทธ์ธ์„ ํ†ตํ•ด ์ด๋ฆ„๊ณผ ์ด๋ฉ”์ผ์„ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์€ ์ฒ˜์Œ ๋กœ๊ทธ์ธํ•  ๋•Œ์—๋งŒ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•œ๋‹ค.

์ด๋Š” ์• ํ”Œ์ด ์‚ฌ์šฉ์ž์˜ ๊ฐœ์ธ ์ •๋ณด๋ฅผ ์ตœ์†Œํ•œ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ์‹์„ ์ฑ„ํƒํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋ผ๊ณ  ํ•œ๋‹ค.

  • ์ฒ˜์Œ ๋กœ๊ทธ์ธ ์‹œ ์ •๋ณด ์ œ๊ณต: ์• ํ”Œ ๋กœ๊ทธ์ธ ์ ˆ์ฐจ์—์„œ ์ฒ˜์Œ ๋กœ๊ทธ์ธ์„ ํ•  ๋•Œ, ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„๊ณผ ์ด๋ฉ”์ผ์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค. ์ด๋•Œ ์‚ฌ์šฉ์ž๊ฐ€ ์• ํ”Œ ID๋ฅผ ํ†ตํ•ด ์„œ๋น„์Šค์— ์ฒ˜์Œ ์—ฐ๊ฒฐ๋  ๋•Œ๋งŒ ์ด๋ฆ„๊ณผ ์ด๋ฉ”์ผ์ด ์• ํ”Œ์—์„œ ์ „๋‹ฌ๋œ๋‹ค.
  • ๋‹ค์Œ ๋กœ๊ทธ์ธ ์‹œ ์ด๋ฆ„๊ณผ ์ด๋ฉ”์ผ ์ œ๊ณต ๋ถˆ๊ฐ€: ์ดํ›„ ๋™์ผํ•œ ์• ํ”Œ ID๋กœ ๋กœ๊ทธ์ธํ•  ๋•Œ๋Š” ์• ํ”Œ์—์„œ ์ œ๊ณตํ•˜๋Š” userIdentifier (์• ํ”Œ ID ๊ณ ์œ  ์‹๋ณ„์ž)๋งŒ ์ „๋‹ฌ๋œ๋‹ค. ์ด๋ฆ„์ด๋‚˜ ์ด๋ฉ”์ผ ์ฃผ์†Œ๋Š” ๋‹ค์‹œ ์ œ๊ณต๋˜์ง€ ์•Š๋Š”๋‹ค. ์ด๋Š” ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ ๋ณดํ˜ธ ๋ฐ ํ”„๋ผ์ด๋ฒ„์‹œ ๊ฐ•ํ™”๋ฅผ ์œ„ํ•œ ์• ํ”Œ์˜ ์ •์ฑ…์ด๋‹ค.

ํ•˜์ง€๋งŒ, userIdentifier๋Š” ๊ณ„์† ๋ฐ›์•„์˜ด์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์ด๋Š” ์• ํ”Œ ID ๊ณ ์œ  ์‹๋ณ„์ž๋กœ, ์ด๋ฅผ ์‚ฌ์šฉํ•ด ์‚ฌ์šฉ์ž๋ฅผ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•จ์ด๋ผ๊ณ  ํ•œ๋‹ค.

 

์–ด์ฉ์ง€ ์•„๋ฌด๋ฆฌ ๋กœ๊ทธ์ธํ•ด๋„ null์ด ๋‚˜์˜ค๋”๋ผ๋‹ˆ..!

์ด๊ฑธ ๋ชจ๋ฅด๋ฉด ๊ตฌํ˜„์ด ์ž˜ ์•ˆ๋œ์ค„ ์•Œ๊ณ  ์‚ฝ์งˆ์„ ๊ฝค๋‚˜ ํ•  ๊ฒƒ ๊ฐ™๋‹ค !!

 

 

์ด์ œ ์• ํ”Œ๋กœ๊ทธ์ธ์€ ๋๋‚ฌ๋‹ค !!

 

๋‹ค์Œ์— ๋‹ค์‹œ ํ™”๋ฉด๋“ค์„ ๋ช‡๊ฐœ ๋งŒ๋“ค๊ณ  ๋‚˜์„œ, ์•ฑ ๋‚ด ์ ‘๊ทผ ํ•  ๋•Œ๋งˆ๋‹ค ๋กœ๊ทธ์ธ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•ด ์ ์ ˆํ•œ ํ™”๋ฉด์œผ๋กœ ์ด๋™์‹œํ‚ค๋Š” ์ž๋™๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•˜๊ณ  ์ •๋ฆฌํ•ด๋ด์•ผ๊ฒ ๋‹ค !

 

 

ํ›„ ํž˜๋“ค์—ˆ๋‹ค.

 

728x90
๋ฐ˜์‘ํ˜•
728x90
๋ฐ˜์‘ํ˜•

์ง€๋‚œ ๊ธ€์— ์ด์–ด ์ด๋ฒˆ์—๋Š” DeviceActivity๋ฅผ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ๊ธ€์ด๋‹ค.

๋‹ค์Œ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์˜€๋‹ค !

https://developer.apple.com/documentation/DeviceActivity

 

DeviceActivity | Apple Developer Documentation

Monitor device activity with your app extension while maintaining user privacy.

developer.apple.com

 

์ด๋ฒˆ ๊ธ€์€ ๋‚ด๊ฐ€ ์ง„ํ–‰ ์ค‘์ธ ํ”„๋กœ์ ํŠธ์—์„œ ์ƒ๋‹นํžˆ ์ค‘์š”ํ•œ ์ฃผ๊ธฐ๋Šฅ์— ๊ผญ ํ•„์š”ํ•œ framework์ด๋ฏ€๋กœ, ์ฐจ๊ทผ์ฐจ๊ทผ ๋”ฐ๋ผํ•˜๋ฉด์„œ ํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

๊ทธ ์ „์—, ์ด framework๋Š” ์•ฑ ํ™•์žฅ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด ์‚ฌ์šฉ์ž์˜ ํ”„๋ผ์ด๋ฒ„์‹œ๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ, ๊ธฐ๊ธฐ ํ™œ๋™์„ ๋ชจ๋‹ˆํ„ฐ๋ง ํ•œ๋‹ค.

 

์ด์ „ ๊ธ€๊ณผ ์กฐ๊ธˆ ๊ฒน์น˜๋Š” ๋ถ€๋ถ„์ด ์—†์ž–์•„ ์žˆ์ง€๋งŒ,

1. ํŠน์ • ์‹œ๊ฐ„๋™์•ˆ ํ™œ๋™์„ ๋ชจ๋‹ˆํ„ฐ๋ง ํ•  ์ˆ˜ ์žˆ๋‹ค.

2. ํ™œ๋™์ด ๋ฏธ๋ฆฌ ์„ค์ •๋œ ํ•œ๊ณ„์— ๋„๋‹ฌํ•  ๋•Œ ๊ฒฝ๊ณ ๋ฅผ ์ค„ ์ˆ˜ ์žˆ๋‹ค.

3. ์•ฑ๊ณผ ์›น์‚ฌ์ดํŠธ์—์„œ ์‚ฌ์šฉ๋œ ์‹œ๊ฐ„์„ ๋ชจ๋‹ˆํ„ฐ๋ง ํ•œ๋‹ค.

 

์ด์ œ ๊ฐ๊ฐ์˜ Topic์„ ํ•˜๋‚˜์”ฉ ๋‹ค๋ค„๋ณด์ž !

 


 

Manage Activities

  • struct DeviceActivityEvent
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜, ์นดํ…Œ๊ณ ๋ฆฌ ๋˜๋Š” ์›น์‚ฌ์ดํŠธ ํ™œ๋™์„ ๋‚˜ํƒ€๋‚ด๋Š” ์ด๋ฒคํŠธ
  • struct DeviceActivityName
    • ํ™œ๋™์˜ ๊ณ ์œ ํ•œ ์ด๋ฆ„์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
  • struct DeviceActivitySchedule
    • ๊ธฐ๊ธฐ ํ™œ๋™์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•  ๋•Œ์˜ ์ผ์ • ๊ธฐ๋ฐ˜ ์Šค์ผ€์ค„์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
  • struct DeviceActivityCenter
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ™•์žฅ์ด ์ผ์ •์— ๋”ฐ๋ผ ๊ธฐ๊ธฐ ํ™œ๋™ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ํด๋ž˜์Šค

 

Monitor Activity

  • class DeviceActivityMonitor
    • ์ผ์ •์— ๋”ฐ๋ผ ๊ธฐ๊ธฐ ํ™œ๋™์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๋Š” ๊ฐ์ฒด

 

Errors

  • enum MonitoringError
    • ํ™œ๋™ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ์‹œ์ž‘ํ•  ๋•Œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ค๋ฅ˜๋“ค์„ ๋‚˜์—ดํ•˜๋Š” ์—ด๊ฑฐํ˜•

 

Classes

  • class DeviceActivityAuthorization
    • ๊ธฐ๊ธฐ ํ™œ๋™ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ์œ„ํ•œ ๊ถŒํ•œ ๋ถ€์—ฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํด๋ž˜์Šค์ด๋‹ค.

 

Protocols

  • protocol DeviceActivityAuthorizing
    • ๊ธฐ๊ธฐ ํ™œ๋™ ๋ชจ๋‹ˆํ„ฐ๋ง์— ๋Œ€ํ•œ ๊ถŒํ•œ ๋ถ€์—ฌ๋ฅผ ์ •์˜ํ•˜๋Š” ํ”„๋กœํ† ์ฝœ์ด๋‹ค.
  • protocol DeviceActivityReportExtension
    • ๊ธฐ๊ธฐ ํ™œ๋™ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ณ ํ•˜๋Š” ์•ฑ extension์ด๋‹ค.
  • protocol DeviceActivityReportScene
    • ์‚ฌ์šฉ์ž ์ •์˜ ๊ธฐ๊ธฐ ํ™œ๋™ report scene์„ ์ •์˜ํ•˜๋Š” ํ”„๋กœํ† ์ฝœ์ด๋‹ค.

 

Structures

  • struct DeviceActivityData
    • ํŠน์ • DeviceActivityData.Device์—์„œ DeviceActivityData.User์˜ ํ™œ๋™์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
  • struct DeviceActivityFilter
    • ๋ณด๊ณ ์„œ์— ํฌํ•จํ•  ๊ธฐ๊ธฐ ํ™œ๋™ ๋ฐ์ดํ„ฐ๋ฅผ ํ•„ํ„ฐ๋งํ•˜๋Š” ํƒ€์ž….
  • struct DeviceActivityReport
    • ์‚ฌ์šฉ์ž์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜, ์นดํ…Œ๊ณ ๋ฆฌ ๋ฐ ์›น ๋„๋ฉ”์ธ ํ™œ๋™์„ ํ”„๋ผ์ด๋ฒ„์‹œ๋ฅผ ๋ณดํ˜ธํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ณด๊ณ ํ•˜๋Š” view์ด๋‹ค.
  • struct DeviceActivityReportBuilder
    • ํ•˜๋‚˜ ์ด์ƒ์˜ DeviceActivityReportScenes์„ ๊ฒฐํ•ฉํ•˜์—ฌ ๋‹จ์ผ ์žฅ๋ฉด์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒฐ๊ณผ ๋นŒ๋”.
  • struct DeviceActivityResults
    • ํ•„ํ„ฐ๋ง๋œ ๊ธฐ๊ธฐ ํ™œ๋™ ๊ฒฐ๊ณผ์˜ ๋น„๋™๊ธฐ sequence.

 

 

ํ”„๋กœ์ ํŠธ์— ๋ฐ˜์˜ํ•˜๊ธฐ

์ด์ œ xcode์— ํ”„๋กœ์ ํŠธ๋ฅผ ํŒŒ๊ณ , ํ•ด๋‹น api๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๋Š” ๊ณผ์ •์„ ์ •๋ฆฌํ•˜๋ ค ํ•œ๋‹ค.

 

๋จผ์ €, ๊ด€๋ จ๋œ extension์„ ์„ค์น˜ํ•ด์ค€๋‹ค.

 

ํƒ€๊นƒ์—์„œ ํ•˜๋‹จ์˜ + ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ add target์„ ํ•ด์ค€๋‹ค.

 

 

๊ทธ๋Ÿผ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŒ์—…์ด ๋œจ๋Š”๋ฐ, ์ด ๋•Œ DeviceActivity Monitor Extension๊ณผ Device Activity Report Extension์„ ๊ฐ๊ฐ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

 

์ด๋ ‡๊ฒŒ ๋‘ ๊ฐœ์˜ extension์„ ๋ชจ๋‘ ์ถ”๊ฐ€ํ–ˆ๋‹ค๋ฉด, ์ด๋ ‡๊ฒŒ xcode์— ์ถ”๊ฐ€๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.



 

์ด์ œ extension์„ ์ถ”๊ฐ€ํ•˜์˜€์œผ๋ฉด, ๋‚˜์™€ ๊ฐ™์€ ๊ฒฝ์šฐ, ๋Œ€๋ถ€๋ถ„์˜ ์ฝ”๋“œ๋ฅผ UIKit์œผ๋กœ ์งค ์˜ˆ์ •์ด๊ธฐ์— SwiftUI ๊ธฐ๋ฐ˜์˜ api์™€ UIKit์„ ์„œ๋กœ ์—ฐ๊ฒฐํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

์ฆ‰, ๋‘ ๊ฐœ์˜ framework๋ฅผ ํ˜ผ์šฉํ•ด์„œ ์“ธ ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์„ ์„ธํŒ… ํ•ด์•ผ ํ•œ๋‹ค.

 

 

UIKit๊ณผ SwiftUI ํ˜ผ์šฉํ•˜๊ธฐ

UIHostingController๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

ํ•˜์ง€๋งŒ, ์•„๋ฌด๋ฆฌ ์‚ฌ์šฉํ•ด์„œ ์—ฐ๊ฒฐ์„ ํ•ด๋„, ์ž๊พธ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” TotalActivityView๊ฐ€ scope์— ์—†๋‹ค๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ๋ณด์˜€๋‹ค.

๊ณ„์† ์„œ์น˜ํ•˜๊ณ , ์‚ฝ์งˆ์„ ํ•˜๋‹ค ๋ณด๋‹ˆ,, ๋‚ด๊ฐ€ extension๋“ค์„ add target์œผ๋กœ ํ•ด์คฌ์œผ๋ฏ€๋กœ,, ์–˜๋„ค๋“ค์„ ๋ชจ๋‘ ํƒ€๊นƒ์— ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ํ–ˆ๋‹ค.

 

์ง€๋‚œ ํ”„๋กœ์ ํŠธ ๋‹น์‹œ์—๋„ ์ด๊ฒƒ๋•Œ๋ฌธ์— ์‚ฝ์งˆ์„ ๊ฝค ํ–ˆ์—ˆ๋Š”๋ฐ.. ์•„์ฐจ์ฐจ ์ด๊ฑธ ๋˜ ์žŠ์—ˆ๋‹ค๋‹ˆ..

 

์˜ค๋ž˜ ์‚ฝ์งˆ ์•ˆํ•˜๊ณ  ์ง€๊ธˆ์ด๋ผ๋„ ์•Œ์•˜์œผ๋‹ˆ ๋‹คํ–‰์ด๋‹ค.

๋จผ์ €, viewController์˜ ์˜ค๋ฅธ์ชฝ inspector์—์„œ target์— ActivityReport๋ฅผ ์ฒดํฌํ•ด์ฃผ์—ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ , TotalActivityView์—์„œ๋„ ๋ฉ”์ธ ํ”„๋กœ์ ํŠธ ํƒ€๊นƒ์„ ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ๋‹ค.

 

 

๊ทธ๋žฌ๋”๋‹ˆ, ์ด์ œ TotalActivityView๋ฅผ ์ž˜ ์ฐพ์•„ ๋นŒ๋“œ๊ฐ€ ์„ฑ๊ณตํ–ˆ๋‹ค.

๋งŒ์•ฝ ์•ˆ๋œ๋‹ค๋ฉด, ์ƒ๋‹จ์˜ Product -> Clean Build Folder๋ฅผ ๊ผญ ํ•˜๊ธธ!!

 

์•„์ฐจ์ฐจ ๊ทธ๋Ÿฐ๋ฐ ํ™”๋ฉด์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋ณด์ด์ง€ ์•Š์•˜๋Š”๋ฐ, ๊ทธ ์ด์œ ๊ฐ€ SceneDelegate.swift ํŒŒ์ผ์„ ์ˆ˜์ •ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค..

ํ˜„์žฌ๋Š” StoryBoard๋กœ ์—ฐ๊ฒฐ๋˜๋„๋ก ๋ผ ์žˆ์–ด ์ด๊ฑธ ๋ฐ”๊ฟ”์ค˜์•ผ ํ•œ๋‹ค.

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }

    let window = UIWindow(windowScene: windowScene)

    let viewController = MainViewController()

    window.rootViewController = viewController

    window.makeKeyAndVisible()

    self.window = window
}

 

์ด๋ ‡๊ฒŒ ํ•˜๊ณ  ์‹คํ–‰ํ•˜๋‹ˆ ํ™”๋ฉด์ด ์ž˜ ์—ฐ๊ฒฐ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค !

 

์ง€๊ธˆ์€ ์ž๋™์œผ๋กœ ์ œ๊ณต๋˜๋Š” view๋งŒ ์—ฐ๊ฒฐํ–ˆ๋Š”๋ฐ, ๊ทธ๋ ‡๊ธฐ์— ์‹ค์ œ ๊ธฐ๊ธฐ์—์„œ ์‚ฌ์šฉํ•œ ์•ฑ์— ๋Œ€ํ•œ ์‹œ๊ฐ„๋“ค์ด ๋‚˜์˜ค๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ, ๋‚ด๊ฐ€ ํ•ด๋‹น VC๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋„ฃ์€ argument ๊ฐ’์ด ๊ทธ๋Œ€๋กœ ๋ณด์—ฌ์ง€๊ณ ๋งŒ ์žˆ์—ˆ๋‹ค.

 

์œ„ ์‹คํ–‰ ๊ฒฐ๊ณผ๋Š” ๊ธฐ์กด์˜ extension์—์„œ ์ค€ ์ฝ”๋“œ๋ฅผ rootView๋กœ ์—ฐ๊ฒฐํ•ด ์‹คํ–‰ํ•œ ๊ฒฐ๊ณผ์ด๋‹ค.

 

์ด์ œ, ActivityReport๋ฅผ ์ง์ ‘ ์—ฐ๊ฒฐํ•ด์„œ ๋ณด์—ฌ์ค˜์•ผ ํ•œ๋‹ค !!

 

 

 

๊ทธ ์ „์—, ์Šคํฌ๋ฆฐํƒ€์ž„์œผ๋กœ ๊ธฐ๊ธฐ์— ์žˆ๋Š” ์ „์ฒด ์•ฑ์„ ๊ฐ€์ ธ์™€ ์ œํ•œ์‹œ์ผœ๋ณด์ž !

 

์†”์งํžˆ ๋งํ•˜๋ฉด,, ์—ฌ๊ธฐ์„œ ์‚ฝ์งˆ์„ ์ง„์งœ ์™•๋งŽ์ด ํ–ˆ๋‹ค.

๊ฒฐ๊ตญ ์ ‘๊ทผ ๊ถŒํ•œ์„ ๋จผ์ € ํ—ˆ์šฉํ•ด์ค˜์•ผํ–ˆ๋‹ค๋Š”๊ฑธ ๋’ค๋Šฆ๊ฒŒ ๊นจ๋‹ฌ์•˜๋‹ค..

 

1. ์Šคํฌ๋ฆฐํƒ€์ž„ ์ ‘๊ทผ ๊ถŒํ•œ ์š”์ฒญํ•˜๊ธฐ

๋จผ์ € ์ ‘๊ทผ ๊ถŒํ•œ์„ ํ—ˆ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” family controls๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด๋ ‡๊ฒŒ Family Controls๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

์•„์ฐจ์ฐจ Family Controls์˜ ์ผ๋ถ€๋งŒ ์ฝ๊ณ  ๋‚ด ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์“ฐ์ง€ ์•Š์„๊ฑฐ๋ผ๊ณ  ์ž๋ถ€ํ–ˆ๋Š”๋ฐ..ใ…‹ใ…Žใ…‹ใ…Ž

 

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        
    guard let windowScene = (scene as? UIWindowScene) else { return }

    // SwiftUI ๋ทฐ๋ฅผ ํ˜ธ์ŠคํŒ…ํ•˜๋Š” UIHostingController๋ฅผ ์ƒ์„ฑ - ์ฆ‰ SwiftUI ์—ฐ๊ฒฐํ•˜๊ธฐ
    let totalActivityView = TotalActivityView(totalActivity: "hello")
    let hostingController = UIHostingController(rootView: totalActivityView)

    // UIKit์˜ UIWindow ์„ค์ •
    window = UIWindow(windowScene: windowScene)
    window?.rootViewController = hostingController
    window?.makeKeyAndVisible()

    // ์Šคํฌ๋ฆฐํƒ€์ž„ ๊ถŒํ•œ ๊ถŒํ•œ ์š”์ฒญ
    Task {
        let center = AuthorizationCenter.shared
        do {
            try await center.requestAuthorization(for: .individual)
        } catch {
            print("Fail: \(error)")
        }
    }
}

 

์ด๋ ‡๊ฒŒ ๊ถŒํ•œ ์š”์ฒญ์„ ์—ฐ๊ฒฐํ•˜๋ฉด ๋œ๋‹ค !!

012

 

2. ๊ธฐ๊ธฐ์˜ ์•ฑ ๋ฆฌ์ŠคํŠธ ๋ณด์—ฌ์ฃผ๊ธฐ

์ด์ œ ๊ธฐ๊ธฐ์— ์žˆ๋Š” ๋ชจ๋“  ์•ฑ์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณด์—ฌ์ค˜์•ผ ํ•œ๋‹ค.

import UIKit
import SwiftUI
import FamilyControls

struct AppListView: View {

    // ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด @State ์‚ฌ์šฉํ•˜๊ธฐ
    // FamilyActivitySelection()์€ ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•œ ๊ฐ€์กฑ ํ™œ๋™ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ์ฒด.
    @State var selection = FamilyActivitySelection()

    // isPresented๋Š” familyActivityPicker๊ฐ€ ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๋Š”์ง€ ํ†ต์ œํ•จ
    @State var isPresented = false

    var body: some View {
        
        // VStack์€ ์ˆ˜์ง์œผ๋กœ ๊ทธ๋ฆฌ๊ธฐ
        VStack {
            Button {
                // ๋ฒ„ํŠผ์ด ํด๋ฆญ๋˜๋ฉด isPresented ์ƒํƒœ๋ฅผ true๋กœ ์„ค์ •
                isPresented = true
            } label: {
                // ๋ฒ„ํŠผ์˜ ๋ ˆ์ด๋ธ” ์ง€์ •ํ•˜๊ธฐ
                Text("show app list")
            }
        }
        // .familyActivityPicker๋Š” family activity๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” Picker ํ™”๋ฉด์— ๋ณด์—ฌ์ฃผ๊ธฐ
        // isPresented๊ฐ€ true์ผ ๋•Œ Picker๊ฐ€ ํ‘œ์‹œ.
        // selection์€ ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•œ ํ™œ๋™ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•จ.
        .familyActivityPicker(isPresented: $isPresented, selection: $selection)
    }
}

์ด๋ ‡๊ฒŒ ์ฝ”๋“œ๋ฅผ ์งœ๋ฉด ๋˜๋Š”๋ฐ, ๋‚˜๋Š” SwftUI๋ฅผ ์ „ํ˜€ ์•Œ์ง€ ๋ชปํ•ด์„œ ์ฃผ์„์œผ๋กœ ์ •๋ฆฌ๋ฅผ ์ข€ ํ•ด๋’€๋‹ค.

 

๋‚˜๋Š” ์ƒˆ๋กœ์šด ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์„œ ๋ฆฌ์ŠคํŠธ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ถ€๋ถ„์„ ๊ตฌํ˜„ํ–ˆ๋Š”๋ฐ, ์ด ๋•Œ ๋นŒ๋“œ๋Š” ์‹คํŒจํ•˜๊ณ , ์‹คํŒจ ๋ฉ”์‹œ์ง€๊ฐ€ ์ž๊พธ ๋ณด์—ฌ์กŒ๋‹ค ์‚ฌ๋ผ์กŒ๋‹ค.

์‹ค์ˆ˜๋„ ๋ฐ˜๋ณต๋˜๋ฉด ์‹ค๋ ฅ์ธ๋ฐ ๋˜ ํƒ€๊นƒ ์„ค์ • ๋ฌธ์ œ์˜€๋‹ค.

 

์ง€๊ธˆ familyCotrol์„ ์‚ฌ์šฉํ•˜๋ ค ํ•˜๊ธฐ ๋•Œ๋ฌธ์— target์— activity report๋ฅผ ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋นŒ๋“œํ•˜๋‹ˆ, ์ด๋ ‡๊ฒŒ ์ž˜ ๋‚˜์˜ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

01

 

์ด์ œ ๋ฆฌ์ŠคํŠธ์—์„œ ์„ ํƒํ•œ ์•ฑ๋“ค์„ ์ฝ˜์†”์— ์ถœ๋ ฅํ•ด๋ณด๊ณ , ์„ ํƒํ•œ ์•ฑ๋“ค์˜ ์‚ฌ์šฉ์„ ์ œํ•œํ•ด๋ณด์ž.

 

 

3. ์„ ํƒํ•œ ์•ฑ ์‚ฌ์šฉ ์ œํ•œํ•˜๊ธฐ

๋จผ์ € ์„ ํƒํ•œ ์•ฑ๋“ค์„ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋ชจ๋ธ์„ ๋งŒ๋“ค์–ด์ค€๋‹ค.

import SwiftUI
import FamilyControls
import ManagedSettings

// ObservableObject ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•œ Model ํด๋ž˜์Šค ์ •์˜
class Model: ObservableObject {
    
    // ์ด ํด๋ž˜์Šค์˜ ๊ณต์œ  ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ (์‹ฑ๊ธ€ํ†ค ํŒจํ„ด)
    static var shared = Model()
    
    // ManagedSettingsStore์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์„ค์ •์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•จ
    let store = ManagedSettingsStore()
    
    // ์‚ฌ์šฉ์ž๊ฐ€ ์ œํ•œํ•˜๋ ค๋Š” ์•ฑ ๋ฐ ์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒ์„ ์ €์žฅํ•˜๋Š” ๋ณ€์ˆ˜, ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค UI ์—…๋ฐ์ดํŠธ
    @Published var selectedtoLimit: FamilyActivitySelection
    
    // ์ดˆ๊ธฐํ™” ๋ฉ”์„œ๋“œ: selectedtoLimit์„ ์ƒˆ๋กœ์šด FamilyActivitySelection ๊ฐ์ฒด๋กœ ์ดˆ๊ธฐํ™”
    init() {
        selectedtoLimit = FamilyActivitySelection()
    }
    
    // ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•œ ์•ฑ๊ณผ ์นดํ…Œ๊ณ ๋ฆฌ์— ๋Œ€ํ•ด ์ฐจ๋‹จ ์ œํ•œ์„ ์„ค์ •ํ•˜๋Š” ํ•จ์ˆ˜
    func setShieldRestrictions() {
        // ์‚ฌ์šฉ์ž๊ฐ€ ์ œํ•œํ•  ์•ฑ๊ณผ ์นดํ…Œ๊ณ ๋ฆฌ ์„ ํƒ์„ ๊ฐ€์ ธ์˜ด
        let applications = Model.shared.selectedtoLimit
        
        // ์•ฑ ์ฐจ๋‹จ์„ ์„ค์ •, ์„ ํƒ๋œ ์•ฑ์ด ์—†์œผ๋ฉด nil๋กœ ์„ค์ •
        store.shield.applications = applications.applicationTokens.isEmpty ? nil : applications.applicationTokens
        
        // ์•ฑ ์นดํ…Œ๊ณ ๋ฆฌ ์ฐจ๋‹จ ์„ค์ •, ์„ ํƒ๋œ ์นดํ…Œ๊ณ ๋ฆฌ๊ฐ€ ์—†์œผ๋ฉด nil๋กœ ์„ค์ •
        store.shield.applicationCategories = applications.categoryTokens.isEmpty
        ? nil
        : ShieldSettings.ActivityCategoryPolicy.specific(applications.categoryTokens)
    }
}

 

 

์ด์ œ ๋ชจ๋ธ์„ ์ถ”๊ฐ€ํ–ˆ์œผ๋‹ˆ, ํ™”๋ฉด์„ ๊ทธ๋ฆฌ๋Š” ๋ถ€๋ถ„๋„ ๋ชจ๋ธ์— ๋‹ด๊ณ  ๋ชจ๋ธ ๋‹จ์œ„๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์กฐ๊ธˆ ์ˆ˜์ •ํ•ด์ค€๋‹ค.

struct AppListView: View {
    
    // ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด @State ์‚ฌ์šฉํ•˜๊ธฐ
    // FamilyActivitySelection()์€ ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•œ ๊ฐ€์กฑ ํ™œ๋™ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ์ฒด.
    @State var selection = FamilyActivitySelection()
    
    // isPresented๋Š” familyActivityPicker๊ฐ€ ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๋Š”์ง€ ํ†ต์ œํ•จ
    @State var isPresented = false
    @EnvironmentObject var model: Model
    
    var body: some View {
        
        // VStack์€ ์ˆ˜์ง์œผ๋กœ ๊ทธ๋ฆฌ๊ธฐ
        VStack {
            Button {
                // ๋ฒ„ํŠผ์ด ํด๋ฆญ๋˜๋ฉด isPresented ์ƒํƒœ๋ฅผ true๋กœ ์„ค์ •
                isPresented = true
            } label: {
                // ๋ฒ„ํŠผ์˜ ๋ ˆ์ด๋ธ” ์ง€์ •ํ•˜๊ธฐ
                Text("show app list")
                // .familyActivityPicker๋Š” family activity๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” Picker ํ™”๋ฉด์— ๋ณด์—ฌ์ฃผ๊ธฐ
                // isPresented๊ฐ€ true์ผ ๋•Œ Picker๊ฐ€ ํ‘œ์‹œ.
                // selection์€ ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•œ ํ™œ๋™ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋ธ์— ์ €์žฅํ•จ.
            }.familyActivityPicker(isPresented: $isPresented, selection: $model.selectedtoLimit)
        }
        // ์ดํ›„ ์„ ํƒํ•œ ์•ฑ๋“ค์— ๋Œ€ํ•œ shieldRestriction์ด ์‹คํ–‰๋œ๋‹ค.
        .onChange(of: model.selectedtoLimit, {
            Model.shared.setShieldRestrictions()
        })
    }
}

 

 

๊ทธ๋ฆฌ๊ณ , ์Šคํฌ๋ฆฐํƒ€์ž„์— ๋Œ€ํ•œ ๊ถŒํ•œ ์š”์ฒญ์„ AppDelegate๋กœ ๋„˜๊ฒจ์ค€๋‹ค.

// AppDelegate.swift

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
           
   guard let windowScene = (scene as? UIWindowScene) else { return }

   // `Model.shared`์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑ
   let model = Model.shared

   // SwiftUI์˜ AppListView๋ฅผ ์„ค์ •
   let contentView = AppListView().environmentObject(model)

   // ์ƒˆ๋กœ์šด UIWindow๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ดˆ๊ธฐ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์„ค์ €
   let window = UIWindow(windowScene: windowScene)
   window.rootViewController = UIHostingController(rootView: contentView)
   self.window = window
   window.makeKeyAndVisible()
}

 

 

๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ, SceneDelegate๋ฅผ ์ˆ˜์ •ํ•ด์ค€๋‹ค. ์—ฌ๊ธฐ์— ๋ชจ๋ธ์„ ๋‹ด๋„๋ก ์ˆ˜์ •ํ•˜๊ณ , ๊ถŒํ•œ ํ—ˆ์šฉ ๋ถ€๋ถ„์„ ์œ„์—์„œ ํ•œ ๊ฒƒ ์ฒ˜๋Ÿผ AppDelegate๋กœ ๋„˜๊ฒจ์คฌ๋‹ค.

// SceneDelegate.swift

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
           
   guard let windowScene = (scene as? UIWindowScene) else { return }

   // `Model.shared`์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑ
   let model = Model.shared

   // SwiftUI์˜ AppListView๋ฅผ ์„ค์ •
   let contentView = AppListView().environmentObject(model)

   // ์ƒˆ๋กœ์šด UIWindow๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ดˆ๊ธฐ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์„ค์ €
   let window = UIWindow(windowScene: windowScene)
   window.rootViewController = UIHostingController(rootView: contentView)
   self.window = window
   window.makeKeyAndVisible()
}

 

์ด๋ ‡๊ฒŒ ํ•˜๊ณ , ์•ฑ์„ ์‹คํ–‰ํ•ด๋ณด์ž !

 

 

์‹คํ–‰ ๊ฒฐ๊ณผ

 

 

 

์•„๋‹ˆ ์ด๋ ‡๊ฒŒ ๊ฐ„๋‹จํ•œ ๊ธฐ์ˆ ๊ฒ€์ฆ์„ ๊ทธ๋•Œ๋Š” ์™œ๊ทธ๋ ‡๊ฒŒ ์‚ฝ์งˆํ•˜๊ณ  ํž˜๋“ค์–ดํ–ˆ์„๊นŒ..

์‹œ๊ฐ„์ด ์ด‰๋ฐ•ํ•˜๋‹ค๋Š” ์ด์œ ๋กœ reference๋ถ€ํ„ฐ ์ฐพ๊ณ , ๋ƒ…๋‹ค ์‹คํ–‰ํ•ด๋ณด๊ณ  ๋„˜์–ด๊ฐ”๋˜ ๊ณผ๊ฑฐ์˜ ๋‚˜.. ๋ฐ˜์„ฑํ•˜๋ผ..

 

์ด์ œ ์• ํ”Œ ๊ณต์‹๋ฌธ์„œ ๊ตฌ์กฐ๋„ ์–ด๋Š์ •๋„ ๊ฐ์ด ์žกํ˜”๊ณ , ์•„์ง ๊ทธ๊ฑธ ๊ธฐ๋ฐ˜์œผ๋กœ ์ฝ”๋“œ๋Š” ๋ชป์งœ๊ฒ ์ง€๋งŒ,, ๋ญ,, ์•ž์œผ๋กœ ์ฐจ๊ทผ์ฐจ๊ทผ ํ•ด๋ด์•ผ๊ฒ ๋‹ค !!

 

๋‹ค์Œ ๊ธ€์—์„œ๋Š” ์ด์ œ ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ์˜ ์ฃผ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด๋ด์•ผ์ง€ !

 

 

 

 

์ฐธ๊ณ ์ž๋ฃŒ

https://hongz-developer.tistory.com/246

 

728x90
๋ฐ˜์‘ํ˜•
728x90
๋ฐ˜์‘ํ˜•

 

 

์ฒ˜์Œ ๋ฐฐํฌ๋ฅผ ์ค€๋น„ํ•ด๋ณด๋Š” ๋‚˜๋Š”,,,

๋””๋ฒ„๊น… ๋ชจ๋“œ์™€ ๋ฆด๋ฆฌ์ฆˆ ๋ชจ๋“œ๊ฐ€ ๋”ฐ๋กœ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋ชฐ๋ž๋‹ค.

 

์ˆœํƒ„ํ•˜๊ฒŒ test flight๊นŒ์ง€ ๋“ฑ๋กํ•˜๊ณ , ์„ค๋ ˆ๋Š” ๋งˆ์Œ์œผ๋กœ ์•ฑ์„ ์„ค์น˜ํ•ด ์‹คํ–‰ํ•ด๋ดค๋Š”๋ฐ

์›ฌ๊ฑธ,,,

 

๊ฐ€์žฅ ์ค‘์š”ํ•œ ํƒญ๋ฐ”๊ฐ€ ํ™”๋ฉด์— ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค.

 

์•„ใ…... ์–ด์ฉŒ์ง€..

 

๋ฐฐํฌ ๊ฒฝํ—˜์ด ์žˆ๋Š” ์นœ๊ตฌ์—๊ฒŒ ๋ฌผ์–ด๋ดค๋‹ค.

๐Ÿคฏ : ํ…Œํ”Œ์— ๋“ฑ๋กํ–ˆ๋Š”๋ฐ ๊ฐœ๋ฐœํ• ๋•Œ๋ž‘ ๋‹ค๋ฅด๋ฉด ์–ด๋–กํ•ด?

๐Ÿ˜ : ๋ญ˜ ์–ด๋–กํ•ด ๋‹ค์‹œ ์ˆ˜์ •ํ•ด์•ผ์ง€

๐Ÿ™ : (์•„์˜ค) ์–ด๋–ป๊ฒŒ ๊ทธ๊ฑธ ์žฌํ˜„ํ•˜๋Š”๋ฐ,,

๐Ÿค” : release ๋ชจ๋“œ๋กœ ๋นŒ๋“œํ•˜๋Š”๊ฑฐ ์žˆ์„๊ฑธ? flutter์—์„œ๋Š” flutter run —releaseํ•˜๋ฉด ๋˜๋˜๋ฐ ๋„ˆ๋„ค๋„ ์žˆ์„๊ฑธ?

 

์ด๋ ‡๊ฒŒ ํ• ๊ฒŒ ๋œ release ๋ชจ๋“œ ๋นŒ๋“œ..

 

์–ด๋–ป๊ฒŒ ํ•˜๋ƒ๋ฉด, xcode์˜ ์ƒ๋‹จ์— ์žˆ๋Š” Product ์—์„œ Scheme > Edit Scheme์„ ํ•˜๊ณ , 

 

 

 

Build Configuration์„ Debug -> Release๋กœ ๋ฐ”๊ฟ”์ฃผ๋ฉด ๋œ๋‹ค !

 

๋‚ด ๊ฒฝ์šฐ, ์ด๋ ‡๊ฒŒ ํ•ด๋„ ์—ฌ์ „ํžˆ ํƒญ๋ฐ”๊ฐ€ ์•ˆ๋ณด์˜€์ง€๋งŒ,,

์ด๊ฑด ๋ฌด์Šจ ๋ฌธ์ œ์ธ์ง€ ๋” ์ฐพ์•„๋ด์•ผ ํ•  ๋“ฏ ํ•˜๋‹ค.

 

์–ด์จŒ๋“  ๋ฐฐํฌ๋œ ๋ฒ„์ „์œผ๋กœ ๋นŒ๋“œํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ์ด๋ ‡๊ฒŒ Build Configuration์„ ๋ฐ”๊ฟ”์ฃผ๋ฉด ๋œ๋‹ค !

728x90
๋ฐ˜์‘ํ˜•

+ Recent posts