How to implement Interstitial iAds in Swift(Xcode 6.1) - swift

I am trying to figure out how to switch over from my banner view iAds to interstitial iAds in order to free up space for a tabbed controller. For some reason I am completely unable to find any resource for even getting started on these ads with swift.
Could anyone please give me some information on interstitial iAds with Swift and how I can implement them in a project.

Here is a relatively cleaner and easier to follow way to implement Interstitial Ads since this way doesn't require the use of NSNotificationCentre
import UIKit
import iAd
class ViewController: UIViewController, ADInterstitialAdDelegate {
var interstitialAd:ADInterstitialAd!
var interstitialAdView: UIView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
loadInterstitialAd()
}
func loadInterstitialAd() {
interstitialAd = ADInterstitialAd()
interstitialAd.delegate = self
}
func interstitialAdWillLoad(interstitialAd: ADInterstitialAd!) {
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
interstitialAdView = UIView()
interstitialAdView.frame = self.view.bounds
view.addSubview(interstitialAdView)
interstitialAd.presentInView(interstitialAdView)
UIViewController.prepareInterstitialAds()
}
func interstitialAdActionDidFinish(interstitialAd: ADInterstitialAd!) {
interstitialAdView.removeFromSuperview()
}
func interstitialAdActionShouldBegin(interstitialAd: ADInterstitialAd!, willLeaveApplication willLeave: Bool) -> Bool {
return true
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
interstitialAdView.removeFromSuperview()
}
And it may help if you put println("Function Name") in each function just to keep track of your Interstitial Ads process. If you have any questions and or a way to improve this block of code please leave a comment. Thank You

Here is how i use in my project
//adding iAd framework
import iAd
//conform iAd delegate
class ViewController: UIViewController,ADInterstitialAdDelegate
//create instance variable
var interstitial:ADInterstitialAd!
//default iAd interstitials does not provide close button so we need to create one manually
var placeHolderView:UIView!
var closeButton:UIButton!
override func viewDidLoad() {
super.viewDidLoad()
//iAD interstitial
NSNotificationCenter.defaultCenter().addObserver(self, selector: ("runAd:"), name:UIApplicationWillEnterForegroundNotification, object: nil)
}
//iAD interstitial
func runAd(notification:NSNotification){
var timer = NSTimer.scheduledTimerWithTimeInterval(3.0, target: self, selector: Selector("dislayiAdInterstitial"), userInfo: nil, repeats: false)
cycleInterstitial()
}
func cycleInterstitial(){
// Clean up the old interstitial...
// interstitial.delegate = nil;
// and create a new interstitial. We set the delegate so that we can be notified of when
interstitial = ADInterstitialAd()
interstitial.delegate = self;
}
func presentInterlude(){
// If the interstitial managed to load, then we'll present it now.
if (interstitial.loaded) {
placeHolderView = UIView(frame: self.view.frame)
self.view.addSubview(placeHolderView)
closeButton = UIButton(frame: CGRect(x: 270, y: 25, width: 25, height: 25))
closeButton.setBackgroundImage(UIImage(named: "error"), forState: UIControlState.Normal)
closeButton.addTarget(self, action: Selector("close"), forControlEvents: UIControlEvents.TouchDown)
self.view.addSubview(closeButton)
interstitial.presentInView(placeHolderView)
}
}
// iAd Delegate Mehtods
// When this method is invoked, the application should remove the view from the screen and tear it down.
// The content will be unloaded shortly after this method is called and no new content will be loaded in that view.
// This may occur either when the user dismisses the interstitial view via the dismiss button or
// if the content in the view has expired.
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!){
placeHolderView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitial = nil
cycleInterstitial()
}
func interstitialAdActionDidFinish(_interstitialAd: ADInterstitialAd!){
placeHolderView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitial = nil
println("called just before dismissing - action finished")
}
// This method will be invoked when an error has occurred attempting to get advertisement content.
// The ADError enum lists the possible error codes.
func interstitialAd(interstitialAd: ADInterstitialAd!,
didFailWithError error: NSError!){
cycleInterstitial()
}
//Load iAd interstitial
func dislayiAdInterstitial() {
//iAd interstitial
presentInterlude()
}
func close() {
placeHolderView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitial = nil
}

I know this is old - but I just came across
override func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject?) {
let destination = segue.destinationViewController
as UIViewController
destination.interstitialPresentationPolicy =
ADInterstitialPresentationPolicy.Automatic
}

If you add this to your app delegate, you'll avoid that 3 second delay suggested above.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
UIViewController.prepareInterstitialAds()
return true
}

Related

blank collection view in user login for the first time

Good day everyone,
I have root view controller set up as my HomeViewController, in this project i am using token based authentication ( which i am storing in user defaults ) and i am using token for all my API calls.
I have a check in my viewWillAppear method to check if there is access token present and then i make the api call in viewDidAppear to populate the collection view, and this works perfectly fine at all times except the first time.
If I log in for the first time it hits the viewWillAppear, viewDidAppear and then login screen pops up and once i authenticate the user and save it in the UserDefaults, dismiss the login screen and in the HomeViewController all i get is a spinner ( which means that viewDidAppear is also been called ) but if i close the app and open it again it all works fine.
What can i change in my code to make it work in the first time please and thank you!!
class HomeViewController: UIViewController {
// MARK: - Properties
let refreshControl = UIRefreshControl()
var publishedReportList: [ReportListDetail] = []
private let reportsCollectionView: UICollectionView = {
let viewLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: viewLayout)
collectionView.register(ReportsCollectionViewCell.self, forCellWithReuseIdentifier: ReportsCollectionViewCell.identifier)
collectionView.backgroundColor = .systemBackground
return collectionView
}()
// MARK: - Initialisation
override func viewDidLoad() {
super.viewDidLoad()
reportsCollectionView.delegate = self
reportsCollectionView.dataSource = self
print(publishedReportList.count)
refreshControl.tintColor = .blue
refreshControl.addTarget(self, action: #selector(pullToRefresh), for: .valueChanged)
reportsCollectionView.addSubview(refreshControl)
reportsCollectionView.alwaysBounceVertical = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// check auth status
handleNotAuthenticated()
}
override func viewDidAppear(_ animated: Bool) {
// call for reports
getReportUserLayout()
}
// MARK: - Handlers
#objc func pullToRefresh() {
// Code to refresh table view
getReportUserLayout()
}
fileprivate func getReportUserLayout() {
// publishedReportList.removeAll()
whySuchEmptyLabel.isHidden = true
spinner.show(in: view)
spinner.textLabel.text = "Loading Reports.."
DispatchQueue.main.async {
ReportsManager.shared.getReportData { [weak self] (listOfReports) in
guard let strongSelf = self else { return }
strongSelf.publishedReportList = listOfReports
if listOfReports.count == 0 {
strongSelf.whySuchEmptyLabel.isHidden = false
}
strongSelf.reportsCollectionView.reloadData()
strongSelf.spinner.dismiss()
strongSelf.refreshControl.endRefreshing()
}
}
}
private func handleNotAuthenticated() {
if UserDefaults.standard.string(forKey: "accessToken") == nil {
// show login view controller
let loginVC = LoginViewController()
loginVC.modalPresentationStyle = .overCurrentContext
present(loginVC, animated: false)
}
}
}
You are in the HomeViewController and presenting loginVC, so it will not trigger viewDidAppear or viewWillAppear because it is not disappeared from the app. You have to use closure or delegate or notification to communicate back to the HomeViewController. You can also use the Combine framework and save the state. Here is an example of using delegate.
// Add protocol
protocol ViewControllerDelegate {
func loggedIn()
}
class HomeViewController: UIViewController, ViewControllerDelegate {
private func handleNotAuthenticated() {
if UserDefaults.standard.string(forKey: "accessToken") == nil {
let loginVC = LoginViewController()
loginVC.modalPresentationStyle = .overCurrentContext
loginVC.viewDelegate = self
present(loginVC, animated: false)
}
}
}
class LoginViewController: UIViewController {
var viewDelegate: ViewControllerDelegate? = nil
func userLoggedIn() {
self.viewDelegate?.loggedIn()
}
}

About the callback of SKStoreReviewController.requestReview()

If the review popup initiated from a view controller shows up, there isn't a way to switch the window focus back to the view controller when the popup is dismissed due to lack of callback function of SKStoreReviewController.requestReview().
I would like to make a call to becomeFirstResponder() when the review popup is dismissed. Any idea?
Is there a way to extend the SKStoreReviewController and add a callback somehow?
Warning this will probably break at some point.
Step 1: add this code to your didFinishLaunchingWithOptions
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let windowClass: AnyClass = UIWindow.self
let originalSelector: Selector = #selector(setter: UIWindow.windowLevel)
let swizzledSelector: Selector = #selector(UIWindow.setWindowLevel_startMonitor(_:))
let originalMethod = class_getInstanceMethod(windowClass, originalSelector)
let swizzledMethod = class_getInstanceMethod(windowClass, swizzledSelector)
let didAddMethod = class_addMethod(windowClass, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!))
if didAddMethod {
class_replaceMethod(windowClass, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!))
} else {
method_exchangeImplementations(originalMethod!, swizzledMethod!)
}
return true
}
Step 2: add this class
class MonitorObject: NSObject {
weak var owner: UIWindow?
init(_ owner: UIWindow?) {
super.init()
self.owner = owner
NotificationCenter.default.post(name: UIWindow.didBecomeVisibleNotification, object: self)
}
deinit {
NotificationCenter.default.post(name: UIWindow.didBecomeHiddenNotification, object: self)
}
}
Step 3: Add this UIWindow extension
private var monitorObjectKey = "monitorKey"
private var partialDescForStoreReviewWindow = "SKStore"
extension UIWindow {
// MARK: - Method Swizzling
#objc func setWindowLevel_startMonitor(_ level: Int) {
setWindowLevel_startMonitor(level)
if description.contains(partialDescForStoreReviewWindow) {
let monObj = MonitorObject(self)
objc_setAssociatedObject(self, &monitorObjectKey, monObj, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
Step 4: add this to ViewDidLoad of your controller where you want this
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
NotificationCenter.default.addObserver(self, selector: #selector(windowDidBecomeHiddenNotification(_:)), name: UIWindow.didBecomeHiddenNotification, object: nil)
}
Step 5: add the callback for the notification and check that the associated object is a match
#objc func windowDidBecomeHiddenNotification(_ notification: Notification?) {
if notification?.object is MonitorObject {
print("hello")
}
}
Now when a review dialog is closed the notification is triggered and 'print("hello") will be called.
Sometimes iOS app is losing the responder chain, like in the above example of showing StoreKit prompt. What we can do is to detect such events in UIApplication.sendAction and reactivate the first responder chain via becomeFirstResponder. UIKit will reestablish the first responder chain and we can resend the same event.
class MyApplication: UIApplication {
func reactivateResponderChainWhenFirstResponderEventWasNotHandled() {
becomeFirstResponder()
}
override func sendAction(_ action: Selector, to target: Any?, from sender: Any?, for event: UIEvent?) -> Bool {
let wasHandled = super.sendAction(action, to: target, from: sender, for: event)
if wasHandled == false, target == nil {
reactivateResponderChainWhenFirstResponderEventWasNotHandled()
return super.sendAction(action, to: target, from: sender, for: event)
}
return wasHandled
}
}
This works for me on iOS 13 and does not require any private API access.

Swift admob not showing even after interstitialWillPresentScreen

I don't know why but even after printing Admop interstitialWillPresentScreen,
ad is not showing.
func interstitialWillPresentScreen(_ ad: GADInterstitial) {
print("Admop interstitialWillPresentScreen")
}
appDelegate
GADMobileAds.configure(withApplicationID: "ca-app-pub-3940256099942544/4411468910")
ViewController
override func viewDidLoad() {
super.viewDidLoad()
interstitial = GADInterstitial(adUnitID: "ca-app-pub- 3940256099942544/4411468910")
interstitial.delegate=self
let request = GADRequest()
interstitial.load(request)
}
func didSelectRowAt(indexPath:IndexPath){
<-- I want to show ad when this function is called back from table view.
if interstitial.isReady {
print("Ad showing")
interstitial.present(fromRootViewController: self)
} else {
print("Ad wasn't ready")
}
but I think the problem is that...
I don't have view from main.storyboard sth...
I generate all view from code in viewDidLayoutSubviews method.
I guess...this is the reason...???
I don't find any error in code and because it comes to interstitialWillPresentScreen method...

How to add a tap gesture to multiple UIViewControllers

I'd like to print a message when an user taps twice on the remote of the Apple TV. I got this to work inside a single UIViewController, but I would like to reuse my code so that this can work in multiple views.
The code 'works' because the app runs without any problems. But the message is never displayed in the console. I'm using Swift 3 with the latest Xcode 8.3.3. What could be the problem?
The code of a UIViewController:
override func viewDidLoad() {
super.viewDidLoad()
_ = TapHandler(controller: self)
}
The code of the TapHandler class
class TapHandler {
private var view : UIView?
required init(controller : UIViewController) {
self.view = controller.view
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.message))
tapGesture.numberOfTapsRequired = 2
self.view!.addGestureRecognizer(tapGesture)
self.view!.isUserInteractionEnabled = true
}
#objc func message() {
print("Hey there!")
}
}
Your TapHandler just getting released. Try This:
var tapHandler:TapHandler? = nil
override func viewDidLoad() {
super.viewDidLoad()
tapHandler = TapHandler(controller: self)
}
I have tested the code and is working.

Interstitial iAd delayed after pressing button

I'm trying to setup iAd after clicking cancel button on JSSAlert. I have in JSSAlert function that set alpha for full view 0.7.. And in view controller I have function for iAd and set back alpha to 1.0...
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Vyhodnotenie testu"
self.showAlert()
}
func showAlert() {
func callback(){}
if numberOfPoints > 49 {
let customIcon = UIImage(named: "smile")
let alertview = JSSAlertView().show(self, title: "Gratulujeme! Uspeli ste.", text: "Dokončili ste test s počtom bodov \(numberOfPoints + 1) z \(maximumNumberOfPoints)!", buttonText: "OK!", color: UIColorFromHex(0x22c411, alpha: 1), iconImage: customIcon)
alertview.setTextTheme(.Light)
alertview.addAction(myCancelCallback)
self.navigationController?.navigationBar.alpha = 0.7
} else {
let customIcon = UIImage(named: "sad")
let alertview = JSSAlertView().show(self, title: "Ľutujeme! Neuspeli ste.", text: "Dokončili ste test s počtom bodov \(numberOfPoints + 1) z \(maximumNumberOfPoints)!", buttonText: "OK!", color: UIColorFromHex(0xd20606, alpha: 1), iconImage: customIcon)
alertview.addAction(myCancelCallback)
alertview.setTextTheme(.Light)
self.navigationController?.navigationBar.alpha = 0.7
}
}
func myCancelCallback() {
self.navigationController?.navigationBar.alpha = 1.0
self.interstitialPresentationPolicy = ADInterstitialPresentationPolicy.Automatic
}
func interstitialAdWillLoad(interstitialAd: ADInterstitialAd!) {
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
interstitialAdView = UIView()
interstitialAdView.frame = self.view.bounds
view.addSubview(interstitialAdView)
interstitialAd.presentInView(interstitialAdView)
UIViewController.prepareInterstitialAds()
}
func interstitialAdActionDidFinish(var interstitialAd: ADInterstitialAd!) {
interstitialAd = nil
interstitialAdView.removeFromSuperview()
}
func interstitialAdActionShouldBegin(interstitialAd: ADInterstitialAd!, willLeaveApplication willLeave: Bool) -> Bool {
return true
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
}
func interstitialAdDidUnload(var interstitialAd: ADInterstitialAd!) {
interstitialAd = nil
interstitialAdView.removeFromSuperview()
}
Alpha back to 1.0 in function myCancelCallback is working but iAd is delayed.. What can cause that delay? Or how can I deal with it?
I want to show iAd immediately after pressing OK!.
Video how it's working:
https://www.youtube.com/watch?v=r6LKN-cjaz8&feature=youtu.be
Update:
iAd App Network Shutdown As of December 31, 2016, the iAd App Network
is no longer available. If you'd like to promote your apps, you can
advertise using Search Ads, Apple News, or third party networks and
advertising sellers.
Reference: https://developer.apple.com/support/iad/
Here is what your gonna do, you have to create the interstitial including close button programmatically , i just made you a sample :
Add row in info.plist : View controller-based status bar appearance -> NO
in AppDelegate didFinishLaunchingWithOptions method add the following line to insure that status bar will be not hidden :
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.None)
Here is a full sample view controller of how your gonna add the interstitial programmatically , but you only need to write animation when the interstitial is showing instead of just using addSubView. you can use animate transform translation you will find a lot of samples about that.
import UIKit
import iAd
class ViewController: UIViewController, ADInterstitialAdDelegate {
var interstitialAd:ADInterstitialAd!
var interstitialAdView: UIView = UIView()
var closeButton:UIButton!
override func viewDidLoad() {
super.viewDidLoad()
NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "loadInterstitialAd", userInfo: nil, repeats: false)
}
func loadInterstitialAd() {
interstitialAd = ADInterstitialAd()
interstitialAd.delegate = self
}
func interstitialAdWillLoad(interstitialAd: ADInterstitialAd!) {
print("interstitialAdWillLoad")
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.Fade)
print("interstitialAdDidLoad")
UIApplication.sharedApplication().statusBarHidden = true
interstitialAdView = UIView()
interstitialAdView.frame = self.view.bounds
self.navigationController?.navigationBar.addSubview(interstitialAdView)
closeButton = UIButton(frame: CGRect(x: 15, y: 15, width: 20, height: 20))
//add a cross shaped graphics into your project to use as close button
closeButton.setBackgroundImage(UIImage(named: "close"), forState: UIControlState.Normal)
closeButton.addTarget(self, action: Selector("close"), forControlEvents: UIControlEvents.TouchDown)
self.navigationController?.navigationBar.addSubview(closeButton)
interstitialAd.presentInView(interstitialAdView)
UIViewController.prepareInterstitialAds()
}
func close() {
interstitialAdView.removeFromSuperview()
closeButton.removeFromSuperview()
interstitialAd = nil
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.Fade)
}
func interstitialAdActionDidFinish(interstitialAd: ADInterstitialAd!) {
print("interstitialAdActionDidFinish")
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.Fade)
}
func interstitialAdActionShouldBegin(interstitialAd: ADInterstitialAd!, willLeaveApplication willLeave: Bool) -> Bool {
return true
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
print("didFailWithError")
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
print("interstitialAdDidUnload")
close()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
}
Update: It might be you just have to call : UIViewController.prepareInterstitialAds() in viewDidLoad of your class to download the content of the interstitial so whenever you ask to present it will be ready and then might be no delay.