Programming/iOS2016.10.25 23:46

앱을 실행해면 전면 광고를 실행해서 보여주게 하였다. 


그러던 중 admob 으로 부터 메일 한통을 받았다.



경고를 받은 앱은 Android 앱이었지만 iOS에서도 동일한 방식으로 나오고 있었다. 


iOS도 수정이 필요한 상황이었다. 그래서 정책을 알아 보기로 했다. 여러 가지 정책이 있어서 모든 것을 다 소개할 수도 없고 내가 필요한 것은 기존 상태를 유지하면서 고칠 수 있는 방법이었다.


https://support.google.com/admob/answer/6201362?hl=ko&ref_topic=2745287


`예기치 않게 실행되는 삽입 광고` > `허용되지 않는 광고 구현의 예: 앱을 열 때 삽입 광고 실행` 


이 경우가 경고를 받았을 때의 경우이다. 


이걸 이제 어떻게 수정해야 할까..



이 방법대로 해보자~!!!


iOS Launch screen 이 나오고 메인 화면이 나오기 전에 광고가 떠야 한다.


그렇다면... AppDelegate 의 didFinishLaunchingWithOptions 에서 띄우면 될것 같아!


1. AppDelegate 에서 전면광고 띄우기


import GoogleMobileAds

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GADInterstitialDelegate {

    var window: UIWindow?
    var interstitial: GADInterstitial?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        interstitial = GADInterstitial(adUnitID: "********")

        let request = GADRequest()
        // Requests test ads on test devices.
        interstitial?.load(request)

        return true
    }

    func interstitialDidReceiveAd(_ ad: GADInterstitial!){
        guard let viewController = window?.rootViewController else { return }
        ad.present(fromRootViewController: viewController)
    }
}

앱 실행시 광고를 띄우는 코드는 모두 작성되었다. 이제 실행을 해보자~!



음...? 메인화면이 먼저 뜨고 전면광고가 뜬다. 위에 안 되는 경우가 바로 이런 경우이다.


이렇게 하면 정지를 받을게 뻔하다... 이건 아님...



전면광고를 로딩하는 시간 때문에 어쩔 수 없이 메인 화면이 노출되는 것 같다. 그러면... 만족스럽지 못하지만 자주 쓰는 방법으로 페이크 LaunchScreen 을 이용해 보자. 


Launch screen 과 같은 VC를 바로 띄워서 전면고를 보여주면...? 문제가.. 없겠는데?!! 그리고 전면광고를 닫을때 메인 화면으로 이동해주자.


2. Fake launch screen 을 이용하여 전면광고 띄우기


import GoogleMobileAds

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GADInterstitialDelegate {

    var window: UIWindow?
    var interstitial: GADInterstitial?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        if let viewController = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController() {
            window?.rootViewController = viewController
        }

        interstitial = GADInterstitial(adUnitID: "******")
        interstitial?.delegate = self

        let request = GADRequest()
        interstitial?.load(request)

        return true
    }

    func interstitialDidReceiveAd(_ ad: GADInterstitial!){
        guard let viewController = window?.rootViewController else { return }
        ad.present(fromRootViewController: viewController)
    }

    func interstitialWillDismissScreen(_ ad: GADInterstitial!) {
        if let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() {
            window?.rootViewController = viewController
        }
    }
}

이렇게 하면 완벽히 동작할 것이다. 이제 전면광고로 부터 자유로워 질 것이다.


실행을 해보자. 밑에 동영상을 확인해 보자.




우리가 원하는 동작이다!! 와우~!!


그런데 뭔가가... 좀... 부족하다..


전면 광고가 사라지는 애니메이션이 사라졌다. willDismiss에서 rootViewController를 변경해서 그런가 보다..


그렇다고 didDismiss 에서 rootViewController 를 변경하면 애니메이션이 끝나기 전까지 전면광고가 보인다. 이렇게 하면 다음과 같은 경우로 허용되지 않는다.



Launch screen에서 광고를 띄워야 하는 것은 유지되어야 하고 광고가 사라지는 애니메이션이 유지되면서 메인화면이 보여야 한다. 


일단 window의 rootViewController를 변경하면 애니메이션을 유지하지 못한다. rootViewController를 변경하는 방법을 제외한다.


그럼... Launch screen 을 VC 형태로 하는게 아니라 view 로 보여주는건 어떨까? view면 바로 추가하고 제거하면 되지 않을까?


음? 그럴듯하다. 한번 해보자. 생각하면 답이 안 나온다. 일단 삽질하다 보면 답이 나오겠지. 어서 해보자.


import GoogleMobileAds

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GADInterstitialDelegate {

    var window: UIWindow?
    var interstitial: GADInterstitial?
    var launchScreenView: UIView?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        if let view = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()?.view {
            launchScreenView = view
            view.translatesAutoresizingMaskIntoConstraints = false
            
            if let rootView = window?.rootViewController?.view {
                rootView.addSubview(view)
                
                var constraints = [NSLayoutConstraint]()
                constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[view]-0-|", options: [], metrics: nil, views: ["view": view])
                constraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[view]-0-|", options: [], metrics: nil, views: ["view": view])
                rootView.addConstraints(constraints)
            }
        }

        interstitial = GADInterstitial(adUnitID: "******")
        interstitial?.delegate = self

        let request = GADRequest()
        interstitial?.load(request)

        return true
    }

    func interstitialDidReceiveAd(_ ad: GADInterstitial!){
        guard let viewController = window?.rootViewController else { return }
        ad.present(fromRootViewController: viewController)
    }

    func interstitialWillDismissScreen(_ ad: GADInterstitial!) {
        launchScreenView?.removeFromSuperview()
    }
}


후후 코드는 완벽하다.


이제 실행 동영상을 보자.





완벽하게 내가 원했던 동작이다.


축하~ 축하!!



여러분의 광고 수익에 조금이나마 도움이 되었으면 좋겠다.





신고
Posted by 초프(초보 프로그래머)
Programming2016.06.17 12:06

Google I/O 에서 Firebase 에 대한 발표 후 Firebase에 대한 언급이 많아지고 있네요.


테스트를 해보니 채팅을 구현하기 정말 쉬울 것 같아서 한번 만들어 보겠습니다.


채팅 구현에 여러가지 기능을 넣을 수 있겠지만 텍스트 대화와 푸시없이 구현하도록 하겠습니다.


사용하는 Firebase의 기능은 Realtime database, Authentication 입니다.



Firebase 프로젝트 설정


https://console.firebase.google.com 접속

`새로만들기` 클릭


프로젝트 `이름`과 `국가/지역` 선택



iOS 앱에 Firebase 추가



`iOS 번들 ID` 입력 (실제 iOS  bundle id 와 같아야 합니다.)



`앱 추가` 버튼을 클릭하면 `GoogleService-info.plist` 파일을 다운로드 받게 됩니다. 나중에 xcode(appcode) project 생성 후에 추가해야 하니 보관해두세요.


이후 나오는 `계속`, `완료` 버튼을 눌러 `iOS 앱에 Firebase 추가` 과정을 끝냅니다.


앱이 추가되었습니다.




회원 인증 설정


어떤 채팅이든 유저를 구분할 수 있는 로그인이 필요하죠.


Firebase에서는 Auth를 이용해 쉽게 회원기능(회원가입/ 로그인)을 구현할 수 있습니다.


Authentication (https://firebase.google.com/docs/auth/)


Authentication 에서는 이메일 인증부터 구글, 페이스북등 다양한 소셜 로그인 기능과 익명 기능이 제공됩니다.

우리 프로젝트는 익명 채팅으로 구현할거에요. 


Anonymous Authentication (https://firebase.google.com/docs/auth/ios/anonymous-auth)


콘솔 메뉴에서 Auth 를 선택합니다.




로그인 방법 설정을 클릭합니다.



  1. Anonymous 선택
  2. 사용 설정 켜기
  3. 저장



사용 설정 완료~!!!




Firebase에서 프로젝트 설정은 끝났습니다. 



iOS 프로젝트 설정


Xcode의 프로젝트 생성은 많이 해보셨을테니 저는 Appcode로 하겠습니다. ㅋ


(사용하는 Appcode의 버전은 2016.2 EAP 입니다.)


앱코드(Appcode) 실행

New project




iOS > Application > Single View Application 선택 후 Next

`Product Name`, `Organization Name`, `Organization Identifier` 입력

언어는 Objective-C, Swift 둘중 편한걸 선택하세요. 저는  Swift입니다

설정이 끝났다면 Finish




Appcode는 cocoapods이 통합되어 있어서 IDE안에서 cocoapods을 사용 가능합니다.

cocoapods podfile 생성



podfile 작성 후 pod install (cocoapods 에서 확인하세요)

platform :ios, '9.0'
use_frameworks!

target 'Shouting' do
  pod 'Firebase/Auth'
  pod 'Firebase/Database'
end


AppDelegate.swift


import UIKit
import Firebase

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        FIRApp.configure()
        return true
    }
}

Firebase console 에서 받은 GoogleService-Info.plist 를 Shouting 프로젝트에 추가합니다.



빌드하고 실행하면 오류 없이 정상 실행됩니다.


다음은 iOS 앱에서 채팅을 구현해 볼까요?

신고
Posted by 초프(초보 프로그래머)
Programming/Android2012.08.31 12:30

인앱빌링을 사용하여 아이템을 판매하고 있을 경우

아이템을 재구매하면 다음과 같은 문제가 생길 수 있습니다.

'이미 구입한 항목임'


이 문제는 purchase type 을 잘못 선택한 경우 입니다.

managed per user account - 계정당 하나의 아이템 구매 가능

unmanaged - 관리하지 않아 갯수 제한 없음 / 소모성 아이템

2개의 타입이 존재 합니다.


앱에서 재구매가 되면 안 되는데 구매가 가능하게 되었거나

재구매 가능해야 하는데 unmanaged가 아닐 경우

이 2가지 경우에 문제가 발생합니다.


항상 purchase type을 확인하도록 합시다.

참고로 확인은 developer console 에서 가능합니다.


http://developer.android.com/guide/google/play/billing/billing_admin.html#billing-purchase-type

신고
Posted by 초프(초보 프로그래머)
Programming/Android2011.01.09 16:32
디버그용 키

위치 : C:\Documents and Settings\Administrator\.android\

명령 : keytool -list -alias androiddebugkey -keystore debug.keystore -storepass android -keypass android


릴리즈용 키

위치 : 인증서가 있는 위치

명령 : keytool -list -keystore 인증서이름 -storepass 인증서암호


키 생성 사이트 : http://code.google.com/android/maps-api-signup.html


계속 방법을 잊어서 기록했음;;


이제 제발 잊지 말자 ㅜ.ㅜ

신고
Posted by 초프(초보 프로그래머)