I am trying to show an Open Ad from Google Admob in my SwiftUI app. I am not too familiar with UIKit and stuff...
I am keep getting this error in my console: " Status bar could not be hidden for full screen ad. Ensure that your app is configured to allow full screen ads to control the status bar. For example, consider whether you need to set the childViewControllerForStatusBarHidden property on your ad's rootViewController."
How do I solve this?
// Extending Application to get RootView..
extension UIApplication {
func getRootViewController() -> UIViewController {
guard let scene = self.connectedScenes.first as? UIWindowScene else {
return .init()
}
guard let root = scene.windows.first?.rootViewController else {
return .init()
}
return root
}
}
final class OpenAd: NSObject, GADFullScreenContentDelegate {
var appOpenAd: GADAppOpenAd?
var loadTime = Date()
func currentDeviceOrientation() -> UIInterfaceOrientation {
let currentOrientation = UIDevice.current.orientation
switch currentOrientation {
case .unknown:
return .unknown
case .portrait:
return .portrait
case .portraitUpsideDown:
return .portraitUpsideDown
case .landscapeLeft:
return .landscapeLeft
case .landscapeRight:
return .landscapeRight
case .faceUp:
return .portrait
case .faceDown:
return .portrait
#unknown default:
return .unknown
}
}
func showAdForFirstLaunch() {
let request = GADRequest()
GADAppOpenAd.load(withAdUnitID: "ca-app-pub-3940256099942544/5662855259",
request: request,
orientation: UIInterfaceOrientation.portrait,
completionHandler: { (appOpenAdIn, _) in
self.appOpenAd = appOpenAdIn
self.appOpenAd?.fullScreenContentDelegate = self
self.loadTime = Date()
self.appOpenAd?.present(fromRootViewController: UIApplication.shared.getRootViewController())
})
}
func requestAppOpenAd() {
let request = GADRequest()
GADAppOpenAd.load(withAdUnitID: "ca-app-pub-3940256099942544/5662855259",
request: request,
orientation: UIInterfaceOrientation.portrait,
completionHandler: { (appOpenAdIn, _) in
self.appOpenAd = appOpenAdIn
self.appOpenAd?.fullScreenContentDelegate = self
self.loadTime = Date()
print("[OPEN AD] Ad is ready")
})
}
func tryToPresentAd() {
if let gOpenAd = self.appOpenAd, wasLoadTimeLessThanNHoursAgo(thresholdN: 4) {
gOpenAd.present(fromRootViewController: UIApplication.shared.getRootViewController())
} else {
self.requestAppOpenAd()
}
}
func wasLoadTimeLessThanNHoursAgo(thresholdN: Int) -> Bool {
let now = Date()
let timeIntervalBetweenNowAndLoadTime = now.timeIntervalSince(self.loadTime)
let secondsPerHour = 3600.0
let intervalInHours = timeIntervalBetweenNowAndLoadTime / secondsPerHour
return intervalInHours < Double(thresholdN)
}
func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
print("[OPEN AD] Failed: \(error)")
requestAppOpenAd()
}
func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
requestAppOpenAd()
print("[OPEN AD] Ad dismissed")
}
}
ViewModel:
searchButtonInDidTapSubject.withLatestFrom(retailIdSubject.map { text in return text}).flatMapLatest { [unowned self] retailId in
return service.searchRetailPayCashBack(retailId: retailId, createdAt: self.createdDates, sig: self.sig).materialize();
}.subscribe(onNext: { [weak self] event in
switch (event) {
case .next(_):
self?.checkResultSubject.onNext(true)
break;
case .error(let error):
self?.errorSubject.onNext(error as! ErrorResponse);
break;
default:
break;
}
}).disposed(by: disposeBag);
// here i need to convert retailId , currentTime and accessToken
after that i send request api
private var sig: String{
get {
let accessToken = self.keychain.get(Constants.accessToken)
// ???
let newAccessToken = String((accessToken?.substring(with: 11..<21))!)
let retailid = ???
let newSig = "\(newAccessToken)\(self.createdDates)\(retailid)"
let md5Base64 = newSig.base64Encoded()
let md5Data = self.MD5(md5Base64!)
return String(md5Data!)
}
}
retailId needs to be equal to retailid
Not sure what are you trying to achieve, but it sounds like the sig should be generated each time you tap the button, because the retailId can be different.
searchButtonInDidTapSubject
.withLatestFrom(retailIdSubject.map { text in return text })
.flatMapLatest { [unowned self] retailId in
return service
.searchRetailPayCashBack(
retailId: retailId,
createdAt: self.createdDates,
sig: self.sig(withRetailId: retailId, createdDate: self.createdDates)
)
.materialize()
}
.subscribe(onNext: { [weak self] event in
switch (event) {
case .next(_):
self?.checkResultSubject.onNext(true)
break;
case .error(let error):
self?.errorSubject.onNext(error as! ErrorResponse);
break;
default:
break;
}
})
.disposed(by: disposeBag)
func sig(withRetailId retailId: String, createdDate: String) -> String {
let accessToken = self.keychain.get(Constants.accessToken)
// ???
let newAccessToken = String((accessToken?.substring(with: 11..<21))!)
let newSig = "\(newAccessToken)\(createdDate)\(retailId)"
let md5Base64 = newSig.base64Encoded()
let md5Data = self.MD5(md5Base64!)
return String(md5Data!)
}
I have two requests. Each of them getting different data. I need to show an indicator when the all of two request is requesting. How i can do this?
this is my first request:
func productList(tableView:UITableView,spinner:UIActivityIndicatorView,index1:Int,index2:Int,index3:Int){
if product.count<=0{
alamoFireManager?.request(.GET, "http://mobile.unimax.kz/api/Default1",parameters: ["type1id":index1,"type2id":index2,"type3id":index3,"name":"","userid":1089])
.responseJSON { response in
guard response.result.error == nil else {
if let httpError = response.result.error {
switch(httpError.code){
case -1009:
let alert = UIAlertView(title: "Ошибка",message: "Нету интернета!!",delegate: nil,cancelButtonTitle: "OK")
alert.show()
break
default:
let alert = UIAlertView(title: "Ошибка",message: "Повторите попытку!!",delegate: nil,cancelButtonTitle: "OK")
alert.show()
break
}
} else { //no errors
let statusCode = (response.response?.statusCode)!
print(statusCode)
}
spinner.stopAnimating()
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
return
}
if let value = response.result.value {
// handle the results as JSON, without a bunch of nested if loops
let product = JSON(value)
for (_,subJson):(String, JSON) in product {
let img:NSData
if let src=subJson["sphoto"].string{
if src.containsString("jpg"){
let range = src.startIndex.advancedBy(2)..<src.endIndex
let substring = src[range]
var urlString = "http://admin.unimax.kz/\(substring)"
urlString = urlString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
if let dataFromURL=NSData(contentsOfURL: NSURL(string: urlString)!){
img=dataFromURL
}
else{
img=NSData(contentsOfURL: NSURL(string: "http://zhaksy-adam.kz/Images/domalak.png")!)!
}
}
else{
img=NSData(contentsOfURL: NSURL(string: "http://zhaksy-adam.kz/Images/domalak.png")!)!
}
}
else{
img=NSData(contentsOfURL: NSURL(string: "http://zhaksy-adam.kz/Images/domalak.png")!)!
}
//Do something you want
let id=subJson["id"].int!
let name=subJson["name"].string!
let price=subJson["price"].int!
let description=subJson["description"].rawString()
self.product.append(Product(id:id,title: name, img: UIImage(data: img), price: price,desc:description!))
}
spinner.stopAnimating()
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
tableView.reloadData()
}
}
}
else{
spinner.stopAnimating()
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
tableView.reloadData()
}
}
and this is my second request:
func makeGetFav(userID:Int,completionHandler: (responseObject:JSON) -> ()) {
alamoFireManager?.request(.GET, "http://mobile.unimax.kz/api/Klientapi/?authid=\(userID)")
.responseJSON {response in
guard response.result.error == nil else {
if let httpError = response.result.error {
switch(httpError.code){
case -1009:
let alert = UIAlertView(title: "Ошибка",message: "Нету интернета!!",delegate: nil,cancelButtonTitle: "OK")
alert.show()
break
default:
let alert = UIAlertView(title: "Ошибка",message: "Повторите попытку!!",delegate: nil,cancelButtonTitle: "OK")
alert.show()
break
}
} else { //no errors
let statusCode = (response.response?.statusCode)!
print(statusCode)
}
return
}
completionHandler(responseObject: JSON(response.result.value!))
}
}
func getFavs(userID:Int,tableView:UITableView,spinner:UIActivityIndicatorView){
getFavRequets(userID){(responseObject) in
if responseObject != nil{
self.favs.removeAll()
self.localDB.clearFav()
for (_,subJson):(String, JSON) in responseObject {
self.favs.append(FavModel(id: subJson["id"].int!, title: subJson["name"].string!, price: subJson["price"].int!))
}
spinner.stopAnimating()
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
tableView.reloadData()
}
}
}
there are i call it all:
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
indicator.startAnimating()
localDB.getUserInfo()
getRequests.productList(tableView, spinner: indicator, index1: catalog1Index, index2: catalog2Index, index3: catalog3Index)
if localDB.user.count>0{
getRequests.getFavs(localDB.user[0].id, tableView: tableView, spinner: indicator)
}
localDB.checkCart(tableView, tabCtrl: tabBarController!)
You can control it using a singleton to start and stop it according to the number of running requests:
class NetworkActivityIndicator: NSObject {
static let sharedInstance = NetworkActivityIndicator()
private override init() {
}
var count = 0 {
didSet {
self.updateIndicator()
}
}
private func updateIndicator() {
if count > 0 {
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
} else {
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
}
}
}
The you simply call NetworkActivityIndicator.sharedInstance.count += 1 just before the request and NetworkActivityIndicator.sharedInstance.count += 1 when you get the response
the easiest way is to add two variables to your class that indicate whether the associated request is complete, and then to create a function that stops the spinner only if both the variables indicate the calls are complete.
if you want to use the class for more than one ViewController then I suggest adding a struct-enum combo to organise the variables that are indicating which requests are currently underway.
eg
class GetRequests {
var productsLoaded = false
var favoritesLoaded = false
func stopSpinnerIfNeeded(spinner: UIActivityIndicatorView) {
if productsLoaded && favoritesLoaded {
spinner.stopAnimating()
spinner.hidden = true
}
}
func productList(tableView:UITableView,spinner:UIActivityIndicatorView,index1:Int,index2:Int,index3:Int){
defer {
productsLoaded = true
stopSpinnerIfNeeded(spinner)
}
if product.count<=0{
alamoFireManager?.request(.GET, "http://mobile.unimax.kz/api/Default1",parameters: ["type1id":index1,"type2id":index2,"type3id":index3,"name":"","userid":1089])
.responseJSON { response in
guard response.result.error == nil else {
if let httpError = response.result.error {
switch(httpError.code){
case -1009:
let alert = UIAlertView(title: "Ошибка",message: "Нету интернета!!",delegate: nil,cancelButtonTitle: "OK")
alert.show()
break
default:
let alert = UIAlertView(title: "Ошибка",message: "Повторите попытку!!",delegate: nil,cancelButtonTitle: "OK")
alert.show()
break
}
} else { //no errors
let statusCode = (response.response?.statusCode)!
print(statusCode)
}
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
return
}
if let value = response.result.value {
// handle the results as JSON, without a bunch of nested if loops
let product = JSON(value)
for (_,subJson):(String, JSON) in product {
let img:NSData
if let src=subJson["sphoto"].string{
if src.containsString("jpg"){
let range = src.startIndex.advancedBy(2)..<src.endIndex
let substring = src[range]
var urlString = "http://admin.unimax.kz/\(substring)"
urlString = urlString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
if let dataFromURL=NSData(contentsOfURL: NSURL(string: urlString)!){
img=dataFromURL
}
else{
img=NSData(contentsOfURL: NSURL(string: "http://zhaksy-adam.kz/Images/domalak.png")!)!
}
}
else{
img=NSData(contentsOfURL: NSURL(string: "http://zhaksy-adam.kz/Images/domalak.png")!)!
}
}
else{
img=NSData(contentsOfURL: NSURL(string: "http://zhaksy-adam.kz/Images/domalak.png")!)!
}
//Do something you want
let id=subJson["id"].int!
let name=subJson["name"].string!
let price=subJson["price"].int!
let description=subJson["description"].rawString()
self.product.append(Product(id:id,title: name, img: UIImage(data: img), price: price,desc:description!))
}
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
tableView.reloadData()
}
}
}
else{
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
tableView.reloadData()
}
}
func getFavs(userID:Int,tableView:UITableView,spinner:UIActivityIndicatorView){
getFavRequets(userID){(responseObject) in
if responseObject != nil{
self.favs.removeAll()
self.localDB.clearFav()
for (_,subJson):(String, JSON) in responseObject {
self.favs.append(FavModel(id: subJson["id"].int!, title: subJson["name"].string!, price: subJson["price"].int!))
}
favoritesLoaded = true
stopSpinnerIfNeeded(spinner)
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
tableView.reloadData()
}
}
}
A very fast way to do it:
// global var
var isIndicatorActive : Bool = false
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
indicator.startAnimating()
self.isIndicatorActive = true
In the line before each alamoFireManager?.request you call :
if isIndicatorActive == false {
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
indicator.startAnimating()
self.isIndicatorActive = true
}
And after each line spinner.stopAnimating() add:
self.isIndicatorActive = false
I suggest you to using single network activity indicator and keep a counter which keep track of network activity within the application. I know my answer is more in text, but looking at your code it seems you can implement the following.
0 indicates no activity indicator.
As soon as a new activity starts, increment the counter do a check if counter is greater than 0 then show the indicator.
Decrement the counter when the activity task finishes. On decrement keep a check if counter is 0 then set the indicator visibility to false.
PS: Don't forget to increment/decrement in sync blocks. You may use objc_sync_enter(..) and objc_sync_exit(..) methods for this.
Thx
thanks everybody for help. I solve it like this:
func makeReuest1(){
if localDB.user.count>0{
getRequests.getFavs(localDB.user[0].id)
}
makeRequest2()
}
func makeRequest2(){
getRequests.productList(tableView, spinner: indicator, index1: catalog1Index, index2: catalog2Index, index3: catalog3Index)
}
I'm simply trying to add a '!' when there is a new item and to have it removed once the tabbar is tapped. I'm at a loss of where to put the code in the following.
Essentially when there is a notification or a new conversation created, or an update to an existing conversation, I'd like a ! to pop up on the tabbar badge, and once the user taps said tabbar item the ! goes away.
func conversationViewController(viewController: ATLConversationViewController, didSendMessage message: LYRMessage) {
println("Message sent!")
}
func conversationViewController(viewController: ATLConversationViewController, didFailSendingMessage message: LYRMessage, error: NSError?) {
println("Message failed to sent with error: \(error)")
}
func conversationViewController(viewController: ATLConversationViewController, didSelectMessage message: LYRMessage) {
println("Message selected")
}
// MARK - ATLConversationViewControllerDataSource methods
func conversationViewController(conversationViewController: ATLConversationViewController, participantForIdentifier participantIdentifier: String) -> ATLParticipant? {
if (participantIdentifier == PFUser.currentUser()!.objectId!) {
return PFUser.currentUser()!
}
let user: PFUser? = UserManager.sharedManager.cachedUserForUserID(participantIdentifier)
if (user == nil) {
UserManager.sharedManager.queryAndCacheUsersWithIDs([participantIdentifier]) { (participants: NSArray?, error: NSError?) -> Void in
if (participants?.count > 0 && error == nil) {
//self.addressBarController.reloadView()
// TODO: Need a good way to refresh all the messages for the refreshed participants...
self.reloadCellsForMessagesSentByParticipantWithIdentifier(participantIdentifier)
} else {
println("Error querying for users: \(error)")
}
}
}
return user
}
func conversationViewController(conversationViewController: ATLConversationViewController, attributedStringForDisplayOfDate date: NSDate) -> NSAttributedString? {
let attributes: NSDictionary = [ NSFontAttributeName : UIFont.systemFontOfSize(14), NSForegroundColorAttributeName : UIColor.grayColor() ]
return NSAttributedString(string: self.dateFormatter.stringFromDate(date), attributes: attributes as? [String : AnyObject])
}
func conversationViewController(conversationViewController: ATLConversationViewController, attributedStringForDisplayOfRecipientStatus recipientStatus: [NSObject:AnyObject]) -> NSAttributedString? {
if (recipientStatus.count == 0) {
return nil
}
let mergedStatuses: NSMutableAttributedString = NSMutableAttributedString()
let recipientStatusDict = recipientStatus as NSDictionary
let allKeys = recipientStatusDict.allKeys as NSArray
allKeys.enumerateObjectsUsingBlock { participant, _, _ in
let participantAsString = participant as! String
if (participantAsString == self.layerClient.authenticatedUserID) {
return
}
let checkmark: String = "✔︎"
var textColor: UIColor = UIColor.lightGrayColor()
let status: LYRRecipientStatus! = LYRRecipientStatus(rawValue: recipientStatusDict[participantAsString]!.unsignedIntegerValue)
switch status! {
case .Sent:
textColor = UIColor.lightGrayColor()
case .Delivered:
textColor = UIColor.orangeColor()
case .Read:
textColor = UIColor.greenColor()
default:
textColor = UIColor.lightGrayColor()
}
let statusString: NSAttributedString = NSAttributedString(string: checkmark, attributes: [NSForegroundColorAttributeName: textColor])
mergedStatuses.appendAttributedString(statusString)
}
return mergedStatuses;
}
// MARK - ATLAddressBarViewController Delegate methods methods
// MARK - ATLParticipantTableViewController Delegate Methods
func participantTableViewController(participantTableViewController: ATLParticipantTableViewController, didSelectParticipant participant: ATLParticipant) {
println("participant: \(participant)")
self.addressBarController.selectParticipant(participant)
println("selectedParticipants: \(self.addressBarController.selectedParticipants)")
self.navigationController!.dismissViewControllerAnimated(true, completion: nil)
}
func participantTableViewController(participantTableViewController: ATLParticipantTableViewController, didSearchWithString searchText: String, completion: ((Set<NSObject>!) -> Void)?) {
UserManager.sharedManager.queryForUserWithName(searchText) { (participants, error) in
if (error == nil) {
if let callback = completion {
callback(NSSet(array: participants as! [AnyObject]) as Set<NSObject>)
}
} else {
println("Error search for participants: \(error)")
}
}
}
}�
Solved https://stackoverflow.com/a/29837976/2303865
Let's say that you have queried the number of messages into a variable called counts then to show the this count into the first tabBarItem.
var arrayOfTabBar = self.tabBarController?.tabBar.items as NSArray!
let tabItem = arrayOfTabBar.objectAtIndex(1) as! UITabBarItem
tabItem.badgeValue = counts
Is it possible to implement an in-app purchase within the SKScene? If so, how? I'm trying to use a SKSpriteNode as a 'buy' button with no luck. I'm not sure whether the code needs to go in the SKScene or the view controller. I've looked at loads of tutorials, but they all seem to be aimed at single view applications rather than in SpriteKit.
First, put this in your game scene line and make sure you have the framework 'StoreKit' imported
class GameScene: SKScene, SKPaymentTransactionObserver, SKProductsRequestDelegate {
Next, your going to want to put these lines in your didmovetoview. Keep in mind that after the "objects:" The string you put should be the in app purchase identifier you set up using iTunes connect.
// Set IAPS
if(SKPaymentQueue.canMakePayments()) {
println("IAP is enabled, loading")
var productID:NSSet = NSSet(objects: "Put IAP id here")
var request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as Set<NSObject>)
request.delegate = self
request.start()
} else {
println("please enable IAPS")
}
Outside of any other functions, but still within the game scene, insert these functions and variables
//In App Purchases
var list = [SKProduct]()
var p = SKProduct()
func buyProduct() {
println("buy " + p.productIdentifier)
var pay = SKPayment(product: p)
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().addPayment(pay as SKPayment)
}
func productsRequest(request: SKProductsRequest!, didReceiveResponse response: SKProductsResponse!) {
println("product request")
var myProduct = response.products
for product in myProduct {
println("product added")
println(product.productIdentifier)
println(product.localizedTitle)
println(product.localizedDescription)
println(product.price)
list.append(product as! SKProduct)
}
}
func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue!) {
println("transactions restored")
var purchasedItemIDS = []
for transaction in queue.transactions {
var t: SKPaymentTransaction = transaction as! SKPaymentTransaction
let prodID = t.payment.productIdentifier as String
switch prodID {
case "IAP id here":
//Right here is where you should put the function that you want to execute when your in app purchase is complete
default:
println("IAP not setup")
}
}
var alert = UIAlertView(title: "Thank You", message: "Your purchase(s) were restored. You may have to restart the app before banner ads are removed.", delegate: nil, cancelButtonTitle: "OK")
alert.show()
}
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
println("add paymnet")
for transaction:AnyObject in transactions {
var trans = transaction as! SKPaymentTransaction
println(trans.error)
switch trans.transactionState {
case .Purchased, .Restored:
println("buy, ok unlock iap here")
println(p.productIdentifier)
let prodID = p.productIdentifier as String
switch prodID {
case "IAP id here":
//Here you should put the function you want to execute when the purchase is complete
var alert = UIAlertView(title: "Thank You", message: "You may have to restart the app before the banner ads are removed.", delegate: nil, cancelButtonTitle: "OK")
alert.show()
default:
println("IAP not setup")
}
queue.finishTransaction(trans)
break;
case .Failed:
println("buy error")
queue.finishTransaction(trans)
break;
default:
println("default")
break;
}
}
}
func finishTransaction(trans:SKPaymentTransaction)
{
println("finish trans")
}
func paymentQueue(queue: SKPaymentQueue!, removedTransactions transactions: [AnyObject]!)
{
println("remove trans");
}
Next you must name the node you need to do the iAP
whateverYourNodeIs.name = "inAppPurchaseNode"
Finally, do this in the touchesBegan
let touch = touches.first as? UITouch
let positionInScene = touch!.locationInNode(self)
let touchedNode = self.nodeAtPoint(positionInScene)
if let name = touchedNode.name {
if name == "inAppPurchaseNode" {
for product in list {
var prodID = product.productIdentifier
if(prodID == "iAp id here") {
p = product
buyProduct() //This is one of the functions we added earlier
break;
}
}
}
}
You will also want this in your touches began to restore the purchases using a different node.
if let name = touchedNode.name {
if name == "restore" {
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
}
}