Admob sdk v8.0+ rewarded ad in SwiftUI - ios

I am trying to make rewarded ad from Admob in my SwiftUI app but faced some problems. I am using official documentation that is written in obj-c and trying to make swift class from it.
Here what i have
final class Rewarded: NSObject, GADFullScreenContentDelegate {
let token = Bundle.main.object(forInfoDictionaryKey: "GADApplicationIdentifier") as? String
var rewardedAd = GADRewardedAd()
var rewardFunction: (() -> Void)? = nil
override init() {
super.init()
LoadRewarded()
}
func LoadRewarded() {
let req = GADRequest()
GADRewardedAd.load(withAdUnitID: token!, request: req, completionHandler: { gad, error in
print(error)
})
}
func showAd(rewardFunction: #escaping () -> Void){
let root = UIApplication.shared.windows.first?.rootViewController
do {
try self.rewardedAd.canPresent(fromRootViewController: root!)
self.rewardFunction = rewardFunction
self.rewardedAd.present(fromRootViewController: root!, userDidEarnRewardHandler: rewardFunction)
} catch let error {
print(error)
}
}
func rewardedAd(_ rewardedAd: GADRewardedAd, userDidEarn reward: GADAdReward) {
if let rf = rewardFunction {
rf()
}
}
func rewardedAdDidDismiss(_ rewardedAd: GADRewardedAd) {
self.rewardedAd = GADRewardedAd()
LoadRewarded()
}
}
In method showAd i have an exception nilerror from invocation of try self.rewardedAd.canPresent(fromRootViewController: root!) and have no idea what to do with that. I didn't find any tutorial that shows how to set up it with version of sdk 8+, can you please help me to figure out what is the problem.

Found a solution, Google has paper on migration to v8+ sdk. So, from https://medium.com/#michaelbarneyjr/how-to-integrate-admob-ads-in-swiftui-fbfd3d774c50 and https://developers.google.com/admob/ios/migration#swift_7 I made a working rewarded ad with version of sdk 8.5 in SwiftUI
final class Rewarded: NSObject, GADFullScreenContentDelegate {
var rewardedAd: GADRewardedAd?
var rewardFunction: (() -> Void)? = nil
override init() {
super.init()
LoadRewarded()
}
func LoadRewarded(){
let request = GADRequest()
GADRewardedAd.load(withAdUnitID: Bundle.main.object(forInfoDictionaryKey: "GADApplicationIdentifier") as! String,
request: request, completionHandler: { (ad, error) in
if let error = error {
print("Rewarded ad failed to load with error: \(error.localizedDescription)")
return
}
self.rewardedAd = ad
self.rewardedAd?.fullScreenContentDelegate = self
}
)
}
func showAd(rewardFunction: #escaping () -> Void){
let root = UIApplication.shared.windows.first?.rootViewController
if let ad = rewardedAd {
ad.present(fromRootViewController: root!,
userDidEarnRewardHandler: {
let reward = ad.adReward
rewardFunction()
}
)
} else {
print("Ad wasn't ready")
}
}
func rewardedAd(_ rewardedAd: GADRewardedAd, userDidEarn reward: GADAdReward) {
if let rf = rewardFunction {
rf()
}
}
}

I found this code. I think it can help you:
import SwiftUI
import GoogleMobileAds
import UIKit
final class Rewarded: NSObject, GADRewardedAdDelegate{
var rewardedAd:GADRewardedAd = GADRewardedAd(adUnitID: rewardID)
var rewardFunction: (() -> Void)? = nil
override init() {
super.init()
LoadRewarded()
}
func LoadRewarded(){
let req = GADRequest()
self.rewardedAd.load(req)
}
func showAd(rewardFunction: #escaping () -> Void){
if self.rewardedAd.isReady{
self.rewardFunction = rewardFunction
let root = UIApplication.shared.windows.first?.rootViewController
self.rewardedAd.present(fromRootViewController: root!, delegate: self)
}
else{
print("Not Ready")
}
}
func rewardedAd(_ rewardedAd: GADRewardedAd, userDidEarn reward: GADAdReward) {
if let rf = rewardFunction {
rf()
}
}
func rewardedAdDidDismiss(_ rewardedAd: GADRewardedAd) {
self.rewardedAd = GADRewardedAd(adUnitID: rewardID)
LoadRewarded()
}
}
struct ContentView:View{
var rewardAd:Rewarded
init(){
self.rewardAd = Rewarded()
}
var body : some View{
Button(action: {
self.rewardAd.showAd(rewardFunction: {
print("Give Reward")
}
}){
Text("My Button")
}
}
}
Here is the link where I find the snippet: https://medium.com/#michaelbarneyjr/how-to-integrate-admob-ads-in-swiftui-fbfd3d774c50

Related

Unable to implement Apple Pay in SwiftUI

I'm trying to implement Apple Pay in my SwiftUI app and I'm stuck at showing the button.
I have done that by using UIViewRepresentable
import SwiftUI
import UIKit
import PassKit
import Foundation
struct ApplePayButton: UIViewRepresentable {
func makeCoordinator() -> Coordinator {
Coordinator()
}
func updateUIView(_ uiView: PKPaymentButton, context: Context) {
}
func makeUIView(context: Context) -> PKPaymentButton {
let paymentButton = PKPaymentButton(paymentButtonType: .plain, paymentButtonStyle: .black)
return paymentButton
}
class Coordinator: NSObject, PKPaymentAuthorizationViewControllerDelegate {
func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
//
}
func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, handler completion: #escaping (PKPaymentAuthorizationResult) -> Void) {
print("did authorize payment")
}
func paymentAuthorizationViewControllerWillAuthorizePayment(_ controller: PKPaymentAuthorizationViewController) {
print("Will authorize payment")
}
}
}
class ApplePayManager: NSObject {
let currencyCode: String
let countryCode: String
let merchantID: String
let paymentNetworks: [PKPaymentNetwork]
let items: [PKPaymentSummaryItem]
init(items: [PKPaymentSummaryItem],
currencyCode: String = "EUR",
countryCode: String = "AT",
merchantID: String = "c.c.c",
paymentNetworks: [PKPaymentNetwork] = [PKPaymentNetwork.masterCard, PKPaymentNetwork.visa]) {
self.items = items
self.currencyCode = currencyCode
self.countryCode = countryCode
self.merchantID = merchantID
self.paymentNetworks = paymentNetworks
}
func paymentViewController() -> PKPaymentAuthorizationViewController? {
if PKPaymentAuthorizationViewController.canMakePayments(usingNetworks: paymentNetworks) {
let request = PKPaymentRequest()
request.currencyCode = self.currencyCode
request.countryCode = self.countryCode
request.supportedNetworks = paymentNetworks
request.merchantIdentifier = self.merchantID
request.paymentSummaryItems = items
request.merchantCapabilities = [.capabilityCredit, .capabilityDebit]
return PKPaymentAuthorizationViewController(paymentRequest: request)
}
return nil
}
}
I do not want to use PKPaymentAuthorizationController because I want to use the native button.
When I click at the button I get this error:
[General] Payment request is invalid: Error Domain=PKPassKitErrorDomain Code=1 "Invalid in-app payment request" UserInfo={NSLocalizedDescription=Invalid in-app payment request, NSUnderlyingError=0x600003aeebb0 {Error Domain=PKPassKitErrorDomain Code=1 "PKPaymentRequest must contain an NSArray property 'paymentSummaryItems' of at least 1 valid objects of class PKPaymentSummaryItem" UserInfo={NSLocalizedDescription=PKPaymentRequest must contain an NSArray property 'paymentSummaryItems' of at least 1 valid objects of class PKPaymentSummaryItem}}}
View:
struct PaymentView: View {
#Environment(\.presentationMode) private var presentationMode
#ObservedObject var requestViewModel: RequestViewModel
var applePayManager = ApplePayManager(items: [
PKPaymentSummaryItem(label: "Some Product", amount: 9.99)
])
var body: some View {
NavigationView {
VStack {
Text("By paying you agree to give the package to transporter.")
// requestViewModel.respondToRequest(status: button.status)
ApplePayButton()
.frame(width: 228, height: 40, alignment: .center)
.onTapGesture {
applePayManager.paymentViewController()
}
}
.navigationBarTitle("Payment")
.navigationBarItems(trailing: Button(action: {
presentationMode.wrappedValue.dismiss()
}) {
Text("Done")
})
}
}
}
What am I doing wrong here?
Just in case someone else is struggling like me: Here is the full code.
import Foundation
import PassKit
class PaymentHandler: NSObject, ObservableObject {
func startPayment(paymentSummaryItems: [PKPaymentSummaryItem]) {
// Create our payment request
let paymentRequest = PKPaymentRequest()
paymentRequest.paymentSummaryItems = paymentSummaryItems
paymentRequest.merchantIdentifier = "merchant.de.xxx"
paymentRequest.merchantCapabilities = .capability3DS
paymentRequest.countryCode = "AT"
paymentRequest.currencyCode = "EUR"
paymentRequest.requiredShippingContactFields = [.phoneNumber, .emailAddress]
paymentRequest.supportedNetworks = [.masterCard, .visa]
// Display our payment request
let paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest)
paymentController.delegate = self
paymentController.present(completion: { (presented: Bool) in })
}
}
/**
PKPaymentAuthorizationControllerDelegate conformance.
*/
extension PaymentHandler: PKPaymentAuthorizationControllerDelegate {
func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, completion: #escaping (PKPaymentAuthorizationStatus) -> Void) {
completion(.success)
print("paymentAuthorizationController completion(.success)")
}
func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) {
print("DidFinish")
}
func paymentAuthorizationControllerWillAuthorizePayment(_ controller: PKPaymentAuthorizationController) {
print("WillAuthorizePayment")
}
}
struct PaymentButton: UIViewRepresentable {
func updateUIView(_ uiView: PKPaymentButton, context: Context) { }
func makeUIView(context: Context) -> PKPaymentButton {
return PKPaymentButton(paymentButtonType: .plain, paymentButtonStyle: .automatic)
}
}
Use it in the view:
PaymentButton()
.frame(width: 228, height: 40, alignment: .center)
.onTapGesture {
paymentHandler.startPayment(paymentSummaryItems: paymentSummaryItems)
}

Vonage OpenTok iOS Swift: Subscribers count is zero in case of Publisher

I have just download Multi-party example and install in 3 different devices with a same session token. (Media Mode: Routed)
Also, I have created 1 publisher and 2 subscribers generate token and added in code.
I am getting a stream/video of a publisher in all subscriber devices screen.
But in publisher, I am not getting a stream/video and audio of subscribers ?
class ViewController: UIViewController {
#IBOutlet var endCallButton: UIButton!
#IBOutlet var swapCameraButton: UIButton!
#IBOutlet var muteMicButton: UIButton!
#IBOutlet var userName: UILabel!
#IBOutlet var collectionView: UICollectionView!
var subscribers: [IndexPath: OTSubscriber] = [:]
lazy var session: OTSession = {
return OTSession(
apiKey: kApiKey,
sessionId: kSessionId,
delegate: self
)!
}()
lazy var publisher: OTPublisher = {
let settings = OTPublisherSettings()
settings.name = UIDevice.current.name
return OTPublisher(delegate: self, settings: settings)!
}()
var error: OTError?
override func viewDidLoad() {
super.viewDidLoad()
self.session.connect(withToken: kToken, error: &error)
self.userName.text = UIDevice.current.name
}
override func viewDidAppear(_ animated: Bool) {
guard let layout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
return
}
layout.itemSize = CGSize(width: self.collectionView.bounds.size.width / 2,
height: self.collectionView.bounds.size.height / 2)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func swapCameraAction(_ sender: AnyObject) {
let previousPosition = self.publisher.cameraPosition
self.publisher.cameraPosition = previousPosition == .front ? .back : .front
}
#IBAction func muteMicAction(_ sender: AnyObject) {
self.publisher.publishAudio = !publisher.publishAudio
let buttonImage: UIImage = {
if !self.publisher.publishAudio {
return #imageLiteral(resourceName: "mic_muted-24")
} else {
return #imageLiteral(resourceName: "mic-24")
}
}()
self.muteMicButton.setImage(buttonImage, for: .normal)
}
#IBAction func endCallAction(_ sender: AnyObject) {
self.session.disconnect(&error)
}
}
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate {
func reloadCollectionView() {
print("self.subscribers.count: \(self.subscribers.count)")
self.collectionView.isHidden = self.subscribers.count == 0
self.collectionView.reloadData()
}
func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return self.subscribers.count
}
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "subscriberCell",
for: indexPath
) as! SubscriberCollectionCell
cell.subscriber = self.subscribers[indexPath]
return cell
}
}
// MARK: - OpenTok Methods
extension ViewController {
func doPublish() {
self.swapCameraButton.isEnabled = true
self.muteMicButton.isEnabled = true
self.endCallButton.isEnabled = true
if let pubView = self.publisher.view {
let publisherDimensions = CGSize(width: view.bounds.size.width / 4,
height: view.bounds.size.height / 6)
pubView.frame = CGRect(origin: CGPoint(x:collectionView.bounds.size.width - publisherDimensions.width,
y:collectionView.bounds.size.height - publisherDimensions.height + collectionView.frame.origin.y),
size: publisherDimensions)
view.addSubview(pubView)
}
self.session.publish(self.publisher, error: &error)
}
func doSubscribe(to stream: OTStream) {
if let subscriber = OTSubscriber(stream: stream, delegate: self) {
let indexPath = IndexPath(item: self.subscribers.count, section: 0)
self.subscribers[indexPath] = subscriber
self.session.subscribe(subscriber, error: &error)
self.reloadCollectionView()
}
}
func findSubscriber(byStreamId id: String) -> (IndexPath, OTSubscriber)? {
for (_, entry) in self.subscribers.enumerated() {
if let stream = entry.value.stream, stream.streamId == id {
return (entry.key, entry.value)
}
}
return nil
}
func findSubscriberCell(byStreamId id: String) -> SubscriberCollectionCell? {
for cell in collectionView.visibleCells {
if let subscriberCell = cell as? SubscriberCollectionCell,
let subscriberOfCell = subscriberCell.subscriber,
(subscriberOfCell.stream?.streamId ?? "") == id {
return subscriberCell
}
}
return nil
}
}
// MARK: - OTSession delegate callbacks
extension ViewController: OTSessionDelegate {
func sessionDidConnect(_ session: OTSession) {
print("Session connected")
doPublish()
}
func sessionDidDisconnect(_ session: OTSession) {
print("Session disconnected")
self.subscribers.removeAll()
reloadCollectionView()
}
func session(_ session: OTSession, streamCreated stream: OTStream) {
print("Session streamCreated: \(stream.streamId)")
if self.subscribers.count == 4 {
print("Sorry this sample only supports up to 4 self.subscribers :)")
return
}
self.doSubscribe(to: stream)
}
func session(_ session: OTSession, streamDestroyed stream: OTStream) {
print("Session streamDestroyed: \(stream.streamId)")
guard let (index, subscriber) = self.findSubscriber(byStreamId: stream.streamId) else {
return
}
subscriber.view?.removeFromSuperview()
self.subscribers.removeValue(forKey: index)
self.reloadCollectionView()
}
func session(_ session: OTSession, didFailWithError error: OTError) {
print("session Failed to connect: \(error.localizedDescription)")
}
}
// MARK: - OTPublisher delegate callbacks
extension ViewController: OTPublisherDelegate {
func publisher(_ publisher: OTPublisherKit, streamCreated stream: OTStream) {
print("Publisher streamCreated")
//TESTING
self.reloadCollectionView()
}
func publisher(_ publisher: OTPublisherKit, streamDestroyed stream: OTStream) {
print("Publisher streamDestroyed")
}
func publisher(_ publisher: OTPublisherKit, didFailWithError error: OTError) {
print("Publisher failed: \(error.localizedDescription)")
}
}
// MARK: - OTSubscriber delegate callbacks
extension ViewController: OTSubscriberDelegate {
func subscriberDidConnect(toStream subscriberKit: OTSubscriberKit) {
print("Subscriber connected")
self.reloadCollectionView()
}
func subscriber(_ subscriber: OTSubscriberKit, didFailWithError error: OTError) {
print("Subscriber failed: \(error.localizedDescription)")
}
func subscriberVideoDataReceived(_ subscriber: OTSubscriber) {
print("Subscriber subscriberVideoDataReceived")
}
}
Even I tried to console log subscribers count in publisher,
I am getting 0.
What I am missing, I need subscriber videos and count ??

How to unit test RxCocoa BehaviorRelay

I am starting out with unit testing RxSwift Driver. And I am having issues testing a Driver.
This is the code structure of my ViewModel:
import Foundation
import RxSwift
import RxCocoa
class LoginViewViewModel {
private let loginService: LoginService
private let _loading = BehaviorRelay<Bool>(value: false)
private let _loginResponse = BehaviorRelay<LoginResponse?>(value: nil)
private let _phoneMessage = BehaviorRelay<String>(value: "")
private let _pinMessage = BehaviorRelay<String>(value: "")
private let _enableButton = BehaviorRelay<Bool>(value: false)
var loginResponse: Driver<LoginResponse?> { return _loginResponse.asDriver() }
var loading: Driver<Bool> { return _loading.asDriver() }
var phoneMessage: Driver<String> { return _phoneMessage.asDriver() }
var pinMessage: Driver<String> { return _pinMessage.asDriver() }
var enableButton: Driver<Bool> { return _enableButton.asDriver() }
private let phone = BehaviorRelay<String>(value: "")
private let pin = BehaviorRelay<String>(value: "")
private let disposeBag = DisposeBag()
init(phone: Driver<String>, pin: Driver<String>, buttonTapped: Driver<Void>, loginService: LoginService) {
self.loginService = loginService
phone
.throttle(0.5)
.distinctUntilChanged()
.drive(onNext: { [weak self] (phone) in
self?.phone.accept(phone)
self?.validateFields()
}).disposed(by: disposeBag)
pin
.throttle(0.5)
.distinctUntilChanged()
.drive(onNext: { [weak self] (pin) in
self?.pin.accept(pin)
self?.validateFields()
}).disposed(by: disposeBag)
buttonTapped
.drive(onNext: { [weak self] () in
self?.loginUser(phone: self!.phone.value, pin: self!.pin.value)
}).disposed(by: disposeBag)
}
private func validateFields() {
guard phone.value.count > 0 else {
return
}
_enableButton.accept(false)
guard pin.value.count > 0 else {
return
}
_enableButton.accept(true)
_phoneMessage.accept("")
_pinMessage.accept("")
}
private func loginUser(phone: String, pin: String) {
_loading.accept(true)
_phoneMessage.accept("")
_pinMessage.accept("")
loginService.loginUser(phone: phone, pin: pin) { [weak self] (response, error) in
self?._loading.accept(false)
if let error = error {
if error.message! == "Invalid credentials" {
self?._phoneMessage.accept("Invalid Phone Number")
self?._pinMessage.accept("Invalid Pin Provided")
}
} else {
response?.saveUserInfo()
self?._loginResponse.accept(response)
}
}
}
}
And my UnitTest looks like this:
class LoginViewViewModelTest: XCTestCase {
private class MockLoginService: LoginService {
func loginUser(phone: String, pin: String, completion: #escaping LoginService.LoginDataCompletion) {
guard phone == "+17045674568", pin == "1234" else {
let loginresponse = LoginResponse(message: "Login Successfully", status: true, status_code: 200, data: LoginData(access_token: "adadksdewffjfwe", token_type: "bearer", expires_in: 3600, expiry_time: "today", user: User(id: "1dsldsdsjkj", name: "RandomGuy", phone: "12345", pin_set: true, custom_email: false, email: "somerandom#email.com")))
completion(loginresponse, nil)
return
}
let akuError = AKUError(status: false, message: "Invalid Credential.", status_code: "404")
completion(nil, akuError)
}
}
var viewModel: LoginViewViewModel!
var scheduler: SchedulerType!
var phone: BehaviorRelay<String>!
var pin: BehaviorRelay<String>!
var buttonClicked: BehaviorRelay<Void>!
override func setUp() {
super.setUp()
phone = BehaviorRelay<String>(value: "")
pin = BehaviorRelay<String>(value: "")
buttonClicked = BehaviorRelay<Void>(value: ())
let loginService = MockLoginService()
viewModel = LoginViewViewModel(phone: phone.asDriver(), pin: pin.asDriver(), buttonTapped: buttonClicked.asDriver(), loginService: loginService)
scheduler = ConcurrentDispatchQueueScheduler(qos: .default)
}
override func tearDown() {
super.tearDown()
}
func testLoginButtonClicked_Loading() {
let loadingObservable = viewModel.loading.asObservable().subscribeOn(scheduler)
phone.accept("12345")
pin.accept("12345")
buttonClicked.accept(())
let loadingState = try! loadingObservable.skip(0).toBlocking().first()!
XCTAssertNotNil(loadingState)
XCTAssertEqual(loadingState, true)
}
}
My question:
I am trying to track the state of the loading driver variable. But, it's always false. Even after writing a debugger for checking the states, it only prints out one value and, it's always false.
I decided to add a break point to the code, and I noticed
let loadingState = try! loadingObservable.skip(0).toBlocking().first()! only gets called once the function is done executing.
Is there a way to test for the loading state?
Is it necessary to test for the loading state?
Thanks.
I believe the problem is that RxBlocking only deals with the first event that is emitted. You need to look at a series of events. Look into using RxTest instead. Here is a unit test using RxTest that passes with the view model you created:
class LoginLoadingTests: XCTestCase {
var scheduler: TestScheduler!
var result: TestableObserver<Bool>!
var bag: DisposeBag!
override func setUp() {
super.setUp()
scheduler = TestScheduler(initialClock: 0)
result = scheduler.createObserver(Bool.self)
bag = DisposeBag()
}
func testLoading() {
let loginService = MockLoginService { phone, pin, response in
self.scheduler.scheduleAt(20, action: { response(nil, RxError.unknown) })
}
let tap = scheduler.createHotObservable([.next(10, ())])
let viewModel = LoginViewViewModel(phone: Driver.just("9876543210"), pin: Driver.just("1234"), buttonTapped: tap.asDriver(onErrorJustReturn: ()), loginService: loginService)
viewModel.loading
.drive(result)
.disposed(by: bag)
scheduler.start()
XCTAssertEqual(result.events, [
.next(0, false),
.next(10, true),
.next(20, false)
])
}
}
struct MockLoginService: LoginService {
init(loginUser: #escaping (_ phone: String, _ pin: String, _ response: #escaping (LoginResponse?, Error?) -> Void) -> Void) {
_loginUser = loginUser
}
func loginUser(phone: String, pin: String, response: #escaping (LoginResponse?, Error?) -> ()) {
_loginUser(phone, pin, response)
}
let _loginUser: (_ phone: String, _ pin: String, _ response: #escaping (LoginResponse?, Error?) -> Void) -> Void
}

Swift CMMotionActivityManager not displaying correct authorization Status

I am trying to create a very simple step counter and am following Kamil Wysocki's article at https://brightinventions.pl/blog/coremotion-pedometer-swift/. However, my .isActivityAvailable() is returning False preventing any of my other functions from initiating. Also, my CMMotionActivityManager.authorizationStatus() call is returning 3 (denied). I cannot for the life of me figure out why this is happening.
I have added Motion Usage Description to my info.plist with a description and have enabled authorization on my iPhone simulator. I have also tried simulating the "City walk" mode in the debug menu, which doesn't help. My code is below.
import UIKit
import CoreMotion
import CoreLocation
class ViewController: UIViewController {
#IBOutlet weak var startButton: UIButton!
#IBOutlet weak var activityTypeLabel: UILabel!
#IBOutlet weak var stepsCountLabel: UILabel!
private let activityManager = CMMotionActivityManager()
private let pedometer = CMPedometer()
private var shouldStartUpdating: Bool = false
private var startDate: Date? = nil
override func viewDidLoad() {
super.viewDidLoad()
startButton.addTarget(self, action: #selector(didTapStartButton), for: .touchUpInside)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let startDate = startDate else { return }
updateStepsCountLabelUsing(startDate: startDate)
}
#objc private func didTapStartButton() {
shouldStartUpdating = !shouldStartUpdating
shouldStartUpdating ? (onStart()) : (onStop())
}
}
extension ViewController {
private func onStart() {
startButton.setTitle("Stop", for: .normal)
startDate = Date()
checkAuthorizationStatus()
startUpdating()
}
private func onStop() {
startButton.setTitle("Start", for: .normal)
startDate = nil
stopUpdating()
}
private func startUpdating() {
if CMMotionActivityManager.isActivityAvailable() {
startTrackingActivityType()
} else {
activityTypeLabel.text = "Not available"
}
if CMPedometer.isStepCountingAvailable() {
startCountingSteps()
} else {
stepsCountLabel.text = "Not available"
}
}
private func checkAuthorizationStatus() {
switch CMMotionActivityManager.authorizationStatus() {
case CMAuthorizationStatus.denied:
onStop()
activityTypeLabel.text = "Not available"
stepsCountLabel.text = "Not available"
default:break
}
}
private func stopUpdating() {
activityManager.stopActivityUpdates()
pedometer.stopUpdates()
pedometer.stopEventUpdates()
}
private func on(error: Error) {
//handle error
}
private func updateStepsCountLabelUsing(startDate: Date) {
pedometer.queryPedometerData(from: startDate, to: Date()) {
[weak self] pedometerData, error in
if let error = error {
self?.on(error: error)
} else if let pedometerData = pedometerData {
DispatchQueue.main.async {
self?.stepsCountLabel.text = String(describing: pedometerData.numberOfSteps)
}
}
}
}
private func startTrackingActivityType() {
activityManager.startActivityUpdates(to: OperationQueue.main) {
[weak self] (activity: CMMotionActivity?) in
guard let activity = activity else { return }
DispatchQueue.main.async {
if activity.walking {
self?.activityTypeLabel.text = "Walking"
} else if activity.stationary {
self?.activityTypeLabel.text = "Stationary"
} else if activity.running {
self?.activityTypeLabel.text = "Running"
} else if activity.automotive {
self?.activityTypeLabel.text = "Automotive"
}
}
}
}
private func startCountingSteps() {
pedometer.startUpdates(from: Date()) {
[weak self] pedometerData, error in
guard let pedometerData = pedometerData, error == nil else { return }
DispatchQueue.main.async {
self?.stepsCountLabel.text = pedometerData.numberOfSteps.stringValue
}
}
}
}
Here is my viewController upon running and clicking start
Big thanks in advance for anyone who can offer any knowledge!!!
Use this code to check authorization status :
let manager = CMMotionActivityManager()
let today = Date()
manager.queryActivityStarting(from: today, to: today, to: OperationQueue.main, withHandler: { (activities: [CMMotionActivity]?, error: Error?) -> () in
if error != nil {
let errorCode = (error! as NSError).code
if errorCode == Int(CMErrorMotionActivityNotAuthorized.rawValue) {
print("NotAuthorized")
}
} else {
print("Authorized")
//Start Tracking Activity
}
manager.stopActivityUpdates()
})
Hope this will work for you.

AWSMobileHubHelper add User pool as AWSIdentityProvider, how to handle `func login(completionHandler: (AnyObject, NSError) -> Void)`?

I want to add AWS User pool as a AWSIdentityProvider to my iOS Application,
But there is little guide for the AWSMobileHubHelper.
So I've try it by my self.
In my LoginViewController I treat AWS User pool as other provider like this:
final
class LoginViewController: UIViewController {
#IBOutlet weak var usernameTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var loginButton: UIButton!
var passwordAuthenticationCompletion: AWSTaskCompletionSource!
var completionHandler: ((AnyObject, NSError) -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
AWSFacebookSignInProvider.sharedInstance().setPermissions(["public_profile"]);
AWSGoogleSignInProvider.sharedInstance().setScopes(["profile", "openid"])
AWSGoogleSignInProvider.sharedInstance().setViewControllerForGoogleSignIn(self)
}
#IBAction private func facebookLogin() {
handleLogin(signInProvider: AWSFacebookSignInProvider.sharedInstance())
}
#IBAction private func googleLogin() {
handleLogin(signInProvider: AWSGoogleSignInProvider.sharedInstance())
}
#IBAction private func myLogin() {
handleLogin(signInProvider: LoginProvider.sharedInstance())
}
private func handleLogin(signInProvider signInProvider: AWSSignInProvider) {
title = "Loging ..."
AWSIdentityManager.defaultIdentityManager().loginWithSignInProvider(signInProvider) { (result, error) in
switch error {
case let error? where error.domain != "success":
print("Login failed.")
default:
print("Login succeed.")
}
}
}
}
My LoginProvider code:
final
class LoginProvider: NSObject {
static func sharedInstance() -> LoginProvider {
return _sharedInstance
}
static private let _sharedInstance = LoginProvider()
lazy var pool: AWSCognitoIdentityUserPool = {
let serviceConfiguration = AWSServiceConfiguration(region: .USEast1, credentialsProvider: nil)
let poolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: "clientId", clientSecret: "clientSecret", poolId: "poolId")
AWSCognitoIdentityUserPool.registerCognitoIdentityUserPoolWithConfiguration(serviceConfiguration, userPoolConfiguration: poolConfiguration, forKey: "UserPool")
let result = AWSCognitoIdentityUserPool(forKey: "UserPool")
result.delegate = self
return result
}()
}
extension LoginProvider: AWSCognitoIdentityInteractiveAuthenticationDelegate {
func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
return loginViewController
}
func startMultiFactorAuthentication() -> AWSCognitoIdentityMultiFactorAuthentication {
fatalError("Identity MultiFactor Authentication Not Supportted!")
}
private var loginViewController: LoginViewController {
return LoginViewController.sharedInstance
}
}
extension LoginProvider: AWSIdentityProvider {
var identityProviderName: String {
return pool.identityProviderName
}
func token() -> AWSTask {
return pool.token()
}
}
extension LoginProvider: AWSSignInProvider {
var loggedIn: Bool {
#objc(isLoggedIn) get {
return currentUser?.signedIn ?? false
}
}
var imageURL: NSURL? {
return nil
}
var userName: String? {
return currentUser?.username
}
func login(completionHandler: (AnyObject, NSError) -> Void) {
loginViewController.completionHandler = completionHandler
loginViewController.doLogin()
}
func logout() {
currentUser?.signOut()
}
func reloadSession() {
currentUser?.getSession()
}
func interceptApplication(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
return true
}
func interceptApplication(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
return false
}
private var currentUser: AWSCognitoIdentityUser? {
return pool.currentUser()
}
}
I've extended my LoginViewController to support the process:
extension LoginViewController: AWSCognitoIdentityPasswordAuthentication {
func doLogin() {
pool.getUser(usernameTextField.text!).getSession(usernameTextField.text!, password: passwordTextField.text!, validationData: nil).continueWithBlock { task -> AnyObject? in
print("pool.getUser().getSession")
print("task.result:", task.result)
print("task.error:", task.error)
if let session = task.result as? AWSCognitoIdentityUserSession {
print("session.idToken:", session.idToken?.tokenString)
print("session.accessToken:", session.accessToken?.tokenString)
print("session.refreshToken:", session.refreshToken?.tokenString)
print("session.expirationTime:", session.expirationTime)
}
switch (task.result, task.error) {
case let (_, error?):
self.completionHandler?("", error)
self.completionHandler = nil
case let (result?, _):
self.completionHandler?(result, NSError(domain: "success", code: -1, userInfo: nil))
self.completionHandler = nil
default: break
}
return nil
}
}
func getPasswordAuthenticationDetails(authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource) {
self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource
}
func didCompletePasswordAuthenticationStepWithError(error: NSError?) {
switch error {
case let error?:
Queue.Main.execute {
self.completionHandler?("", error)
self.completionHandler = nil
}
default: break
}
}
private var pool: AWSCognitoIdentityUserPool! {
return LoginProvider.sharedInstance().pool
}
}
extension LoginViewController {
#nonobjc static let navigationController = UIStoryboard(name: "Login", bundle: nil).instantiateInitialViewController() as! UINavigationController
static var sharedInstance: LoginViewController {
return navigationController.viewControllers[0] as! LoginViewController
}
}
Now I can login to my user pool, but I can't integrate it with federated id pool, It think I've give a wrong result when use completionHandler.
In LoginViewController's func doLogin() , when i get the getSession()'s result which is AWSCognitoIdentityUserSession, How can I convert it to other result that completionHandler needed?

Resources