String Emoji를 Image로 변환하기(UIGraphicsImage)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


해야할 문제

프로젝트를 하는 도중 emoji를 imageView에 랜덤으로 받아줘야하는 테스크를 받았다.
여러 emoji를 스트링값으로 배열을 받아 랜덤 처리하는 방식은 생각해냈지만, 이를 이미지로 그려주는 함수를 애플에서 제공해주는지는 몰랐다.

  1. String타입의 이모지를 Image 타입으로 전환
  2. 버튼을 누르면 해당 이미지를 랜덤으로 ImageView에 보여주기

extension + string

func emojiToImg() -> UIImage? {
    let size = CGSize(width: 100, height: 100)  // 내가 원하는 이미지 사이즈
    UIGraphicsBeginImageContextWithOptions(size, false, 0)
    UIColor.clear.set()

    let rect = CGRect(origin: .zero, size: size)
    UIRectFill(CGRect(origin: .zero, size: size))
    (self as AnyObject).draw(in: rect, withAttributes: [.font: UIFont.systemFont(ofSize: 100)])
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return image
}

ViewController

class ViewController: UIViewController {
    var randomImgList = ["🐶", "🙊", "🍔", "🎨", "🎃", "👾", "🏓"]

    @IBAction func touchRandomBtn(_ sender: UIButton) {
    let randomIndex = Int(arc4random_uniform(UInt32(randomImgList.count)))
    imgView.image = "\(randomImgList[randomIndex])".emojiToImg()
  }
}

iOS 레이아웃이 깨졌을때 할 수 있는 방법?!

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


시작해보기

프로젝트를 진행하다보니 앱을 실행시킬때마다 엄청 지저분한 메시지들이 우두두두두 쏟아지는 것을 볼 수 있었다.

[LayoutConstraints] Unable to simultaneously satisfy constraints.
	Probably at least one of the constraints in the following list is one you don't want.
	Try this:
		(1) look at each constraint and try to figure out which you don't expect;
		(2) find the code that added the unwanted constraint or constraints and fix it.
(
    "<NSLayoutConstraint:0x60000012ba20 UIView:0x7feb9bc4e7c0.width == 335   (active)>",
    "<NSLayoutConstraint:0x60000012be30 H:[UIView:0x7feb9bc4e7c0]-(20)-|   (active, names: '|':UITableViewCellContentView:0x7feb9bc1e4e0 )>",
    "<NSLayoutConstraint:0x60000012bd90 H:|-(20)-[UIView:0x7feb9bc4e7c0]   (active, names: '|':UITableViewCellContentView:0x7feb9bc1e4e0 )>",
    "<NSLayoutConstraint:0x60000016c690 'fittingSizeHTarget' UITableViewCellContentView:0x7feb9bc1e4e0.width == 414   (active)>"
)

Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x60000012ba20 UIView:0x7feb9bc4e7c0.width == 335   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.

말 그대로 레이아웃이 꺠졌다는 의미이다.
막상 앱을 실행시켰을때는 에러도 없고, 눈에 보이는 레이아웃에는 문제가 없어보이지만 실제로는 고쳐야할 요소가 있다는 의미이다.

레이아웃을 쉽게 고치는 방법에 대해서 알아봅시다.

1. view Hierarchy 를 캡쳐

여기서 맨 마지막 아이콘을 누르게 되면 스토리보드의 화면이 아래와 같이 변한다.

보다 정교하게 UI를 분석할 수 있으며, 아래 네비게이터를 보면 계층구조와 제약조건을 볼 수 있게 된다.
만약 여기서 중복되어 적용된 레이아웃이 있다면 오른쪽에 보라색으로 느낌표가 뜰 것이다.

2. breakPoint 사용

xib나 storyboard같은 UI Builder 사용하여 만들면, 레이아웃이 중복적용되어있거나 빠져있는 경우에 경고를 날려준다. 하지만, 코드로 구성할 때는 이를 알 수 없고 아까 위에서처럼 단순히 메시지만 던져주게 된다.

이때 중점적으로 볼 메시지는 아래와 같다.

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.

위 아이콘을 클릭해 브레이크포인트를 만들어봅시다. 우리가 만들 브레이크 포인트는 symbolic breakpoint입니다.

+버튼을 눌러 symbolic breakpoint를 누르고 심볼 부분에 UIViewAlertForUnsatisfiableConstraints를 작성해줍니다.

이후 앱이 깨지는 부분으로 돌아가 고쳐주면 됩니다 :)

3. WTF Auto Layout

마지막으로는 WTF Auto Layout입니당.

왓더..ㅍ..이 아닌 Why The Failure의 약자로 여기로 들어가보면 바로 실행해볼 수 있다.

위에서 받은 메시지 중 아래를 통해 실행해본다.

"<NSLayoutConstraint:0x60000012ba20 UIView:0x7feb9bc4e7c0.width == 335   (active)>",
"<NSLayoutConstraint:0x60000012be30 H:[UIView:0x7feb9bc4e7c0]-(20)-|   (active, names: '|':UITableViewCellContentView:0x7feb9bc1e4e0 )>",
"<NSLayoutConstraint:0x60000012bd90 H:|-(20)-[UIView:0x7feb9bc4e7c0]   (active, names: '|':UITableViewCellContentView:0x7feb9bc1e4e0 )>",
"<NSLayoutConstraint:0x60000016c690 'fittingSizeHTarget' UITableViewCellContentView:0x7feb9bc1e4e0.width == 414   (active)>"

위 메시지를 고대~로 복사붙이기 해준다.

그러고 GO! 버튼을 눌러주면 내 레이아웃의 문제가 무엇이었는지를 바로 알려준다.

iOS tableviewCell에 광고 넣어보기(Adfit)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


iOS tableviewCell에 광고 넣어보

이번 프로젝트를 하면서 앱 안에 광고를 넣는 작업을 하게 되었는데, 사용한 방법은 아래와 같습니다.

  1. tableViewCell을 이용
  2. 광고는 카카오에서 제공하는 Adfit을 사용

보여지는 화면 자체를 테이블뷰로 구성하였고 따라서 앱 하단에 들어가는 배너광고 또한 테이블 뷰 셀에 넣는 방식을 사용하였습니다 :)
정확히는 테이블 뷰 셀 위에 uiView를 놓고 그 위에 addSubview를 하는 방식입니다.

ViewController

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCell(withIdentifier: "AdFit") as! AdfitTableViewCell
            let adCellView = cell.setAD(rootVC: self, frame: cell.bounds)
            adCellView.delegate = self
            cell.addSubview(adCellView)

            return cell
}

TableViewCell

func setAD(rootVC: UIViewController, frame: CGRect) -> AdFitBannerAdView {
    let adView = AdFitBannerAdView(clientId: "", adUnitSize: "")
    adView.frame = CGRect(x: 0, y: 0, width: contentView.bounds.width, height: )
    adView.rootViewController = rootVC
    adView.loadAd()

    return adView
}

그런데 잠깐…

혹시 앱을 실행시켰는데 광고가 제대로 된 사이즈로 나오지 않았다면, 셀에 제대로 제약을 걸어주었는지 확인합시다.
uiView를 올리고 해당 뷰에 높이 설정을 제대로 해주었다면 사실 문제없이 광고가 정 사이즈로 나오겠지만, uiView를 띄우지 않고 addSubview를 했다면 셀높이에 대한 제약 조건을 걸어주어야 합니다 :)

iOS 카카오 ADFit 연동해보기

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


너무나 오랜만에 블로그를 작성하네요. 반성합니다.
이번에 사이드 프로젝트를 진행하면서 앱 안에 광고를 넣으려면서 카카오에서 제공해주는 Adfit을 사용해보았는데..
생각만큼 참고할만한 자료가 많이 없어서 고생고생하다가 고생기를 정리해보려고 합니다.

우선 참고하며 진행했던 자료는 요것입니다 » adfit-ios-sdk

시작해보기

지원 환경

  1. 최신 버전의 Xcode (Xcode 12.0 / Swift 5.3)
  2. iOS Deployment Target: iOS 12.0 이상

AdFitSDK는 Swift로 개발되어있으며, swift 기반의 프로젝트에서 AdFitSDK를 사용하시려면 반드시 최신 버전의 Xcode를 사용해주어야 합니다.

1. 광고단위 ID(Client ID) 발급받기

실제 광고를 수신하고 수익을 창출하기 위해서는, 먼저 AdFit 플랫폼에서 자신의 앱을 매체로 등록하고 광고단위 ID(Client ID)를 발급받아야 합니다.<br. 아래의 웹 사이트에서 앱 등록 / 광고단위 ID 발급 단계를 진행할 수 있습니다.

광고단위 ID 발급받기

앱 등록과 광고단위 ID 발급이 완료된 후, 다음 단계의 안내에 따라 AdFit SDK를 설치한다.

2. SDK 설치하기

pod 'AdFitSDK' > pod install

3. 프로젝트 설정

ATS(App Transport Security)처리

iOS 9부터 ATS(App Transport Security) 기능이 기본적으로 활성화 되어 있기 때문에 암호화된 HTTPS 방식의 통신만 허용됩니다. AdFit SDK는 ATS 활성화 상태에서도 정상적으로 동작하도록 구현되어 있으나, 광고를 통해 노출되는 광고주 페이지는 HTTPS 방식을 지원하지 않을 수도 있기 때문 에 아래의 사항을 앱 프로젝트의 Info.plist 파일에 적용하여 주어야 한다.

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

ATT(App Tracking Transparency) framework 적용

iOS14 타겟팅된 앱이 IDFA 식별자를 얻기 위해서는 ATT Framework를 반드시 적용해야 한다.

<key> NSUserTrackingUsageDescription </key>
<string> 맞춤형 광고 제공을 위해 사용자의 데이터가 사용됩니다. </string>

이는 앱이 사용자 또는 장치를 추적하기 위해 데이터 권한을 요청하는 이유를 사용자에게 알리는 메세지입니다.

이후 ATT 아래 코드를 App delegate에 적용시켜줍니다.

이는 광고 요청하기 전에 사용자로 부터 개인정보 보호에 관한 권한을 요청하는 것으로
앱이 설치되고 한번만 호출하면 되며, 아래 코드는 사용자가 권한에 대한 응답 후에는 더 이상 사용자에게 권한을 묻지 않습니다.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  self.adFit()
}

extension AppDelegate {
    func adFit() {
        if #available(iOS 14, *) {
          ATTrackingManager.requestTrackingAuthorization(completionHandler: { status in
            // 권한 요청이 완료된 다음, 광고를 요청해 주세요.
//             loadAd()
          })
        }
    }
}

4. 광고 요청하기

import UIKit
import AdFitSDK

class MyViewController: UIViewController {
  override func viewDidLoad() {
      super.viewDidLoad()
      setAdfit()
  }

  func setAdfit() {
      let adView = AdFitBannerAdView(clientId: "", adUnitSize: "")
      adView.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: 100)
      adView.rootViewController = self
      adView.addSubview(adView)

      adView.loadAd()
      adView.delegate = self
  }
}

5. delegate 메서드 구현

func adViewDidClickAd(_ bannerAdView: AdFitBannerAdView) {
    print("adDidClicked")
}

func adViewDidReceiveAd(_ bannerAdView: AdFitBannerAdView) {
    print("adDidReceived")
}

func adViewDidFailToReceiveAd(_ bannerAdView: AdFitBannerAdView, error: Error) {
    print("adDidFailReceived - error :\(error.localizedDescription)")
}

하면 위 setAdfit함수에 적여있는 adView.delegate = self에 의해 광고가 잘 전달되었는지, 에러가 왜 발생하는지를 알 수 있게 됩니다.

발생했던 에러

The connection to service on pid 0 named com.apple.commcenter.coretelephony.xpc was invalidated from this process.

xcrun simctl spawn booted log config –mode “level:off” –subsystem com.apple.CoreTelephony

터미널에 위와 같이 실행해서 처리하였다.

참고한 stackoverflow

iOS Firebase Push Notification(FCM)연동해보기

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


제 블로그에서는 단순히 파이어베이스에서 fcm 연동하는 부분만 정리하였습니다.
애플 개발자 홈페이지에서 인증서를 발급받는 부분은 정리되어있지 않습니다 :)

ios 파이어베이스 연동하기

파이어베이스 콘솔 > [settings] > [클라우드 메시징]

APNs 파일을 업로드 하기위해 애플에서 발급받은 p8 파일을 같이 업로드 해줍니다.
뿐만 아니라 애플에서 발급받은 키ID와 팀ID를 입력합니다.

그리고 이제 xcode로 돌아옵니다.

Xcode > target > Push Notifications 추가

xcode로 돌아와 프로젝트의 target으로 들어가 + 버튼을 누르고 Push Notifications을 추가해줍니다.
그리고 Podfile에 아래를 추가해줍니다.

pod 'Firebase/Core'
pod 'Firebase/Analytics'
pod 'Firebase/Messaging'

이후 pod install 하는 것은 잊지마시구용!

App Delegate

pod install이 끝나면 app delegate로 돌아옵니다.
그리고 아래 코드를 추가해줍니다.

(import Firebase와 import UserNotifications 꼭 해주세요!)

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    FirebaseApp.configure()
    UNUserNotificationCenter.current().delegate = self
    Messaging.messaging().delegate = self

    let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
    UNUserNotificationCenter
      .current()
      .requestAuthorization(
        options: authOptions,completionHandler: { (_, _) in }
      )
    application.registerForRemoteNotifications()
    return true

위 코드는 푸시 권한과 파이어베이스 기타 셋팅입니다.

extension AppDelegate : MessagingDelegate {
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        print("파이어베이스 토큰: \(String(describing: fcmToken))")
    }
}

extension AppDelegate : UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter,willPresent notification: UNNotification,withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.alert, .badge, .sound])
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter,didReceive response: UNNotificationResponse,withCompletionHandler completionHandler: @escaping () -> Void) {
        completionHandler()
    }
}

그리고 파이어베이스 메시지 전송의 토큰을 받을 수 있는 MessagingDelegate를 extension으로 분리해줍니다.
뿐만 아니라, UNUserNotificationCenterDelegate를 통해 파이어베이스 노티를 수신받습니다.

파이어베이스 토큰값은 하나의 기기 특정 상대에게만 보낼 수 있는 기기의 고유한 값입니다.

GoogleServiceInfo-plist 추가하기

그리고 한가지 더 추가할 점! > xcode에 GoogleServiceInfo.plist 파일을 넣어주어야 합니다.
위 파일은 파이어베이스에서 처음 앱을 등록했을 때 받았을 수도 있고, 그렇지 않다면 아래 경로로 들어가 저장해주면 된다.

파이어베이스 콘솔창에서 [프로젝트 설정] > [일반]에 들어가 아래로 스크롤 하면 plist를 저장받을 수 있습니다.
저장한 파일은 xcode내 supporting files안에 넣어주면 됩니다.

그럼 이제 백그라운드 및 포어그라운드에서 알람메시지가 잘 작동하는지 다시 파이어베이스 홈페이지로 고고합시당.

push 확인해보기

파이어베이스 콘솔창을 한참 내리다 보면 Cloud Messaging이 있습니다.

클릭 후 들어가 새 알림 을 클릭해주고, 푸시할 내용을 적어주면 테스트 메시지 전송 버튼이 활성화될 것입니다.

그러면 아래와 같이 FCM 등록 토큰 추가 라는 화면이 보일 것이다.

이 토큰은 어디서 구할 수 있냐!? 다시 xcode로 들어가보자.
위 과정을 모두 처리한 다음 시뮬레이터를 돌려보면 xcode 로그창에 이상한 메시지가 떠있을것이다.

이때 받은 토큰을 파이어베이스 FCM 등록 토큰 추가에 넣어주면 됩니다.

이후 테스트 버튼을 눌러서 제대로 푸시가 오는지 확인해보세요!