D2GOClient

D2GOClient

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

Requirements

  • Xcode
  • Swift 3.2 Programming language.
  • Cocoapods

Interacting with Digital2Go Platform.

D2GOClient interacts with beacons and geofences related to campaigns and content created in the Digital2Go Cloud Platform. When a geofence or beacon is detected by your iOS app, it requires to ask the Digital2Go back-end for the ad related of such beacon or geofence to show up in the app.

In order to interact with the Digital2Go Cloud Platform, the user needs to register a user account and then login to the platform using that account.

After the user logins to the app using the Digital2Go Platform, the app can search for local offers triggered by geofences and beacons in proximity to the mobile device geolocation.

In this section we explain how accomplish and setup all these tasks in your app.

For more info, please visit http://www.digital2go.com/developer/ developer web site.

Installation and New App Setup

  1. Create a new project from Xcode->File->New->Project, iOS tab select your template, indicate your product name, organization name, organization identifier and select Swift programming language.

  2. Turn on push notifications, Select your app and under Capabilites turn on Push Notifications.

  3. Adding Amazon static libraries. Right-click under your app folder and create a New Group and rename it to AmazonServices.

  4. Copy the sdk folder into this new group.

  5. Close your project and from your command line inside your project folder run ruby pod init This will create create a Podfile, modify your this file and add the D2GOClient pod and its dependencies. D2GOClient is available through CocoaPods. To install it, simply add the following line to your Podfile:

Configure Info.plist

Adding your app D2GOclient SDK keys

  1. Open your Info.plist from in Xcode. Select your project’s Info.plist file, right-click and select Open As -> Source code.
  2. Insert the following SDK keys XML snippet into the body of your file just before the final element.

You required to create your account and create your own project to the Digital2Go CMS platform and all this info will be provided. For more info please, visit creating your Digital2Go app at http://www.digital2go.com/developer/.

Adding requesting always authorization keys

The D2GoClient SDK and your app requires always authorization for location services. Insert the the following xml snippet in the level as the secret keys of the previous section.

For more info visit(https://developer.apple.com/documentation/corelocation/choosing_the_authorization_level_for_location_services/requesting_always_authorization)

Connect App Delegate

class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:         [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    
        D2GOClient.sharedInstance.setupLogging()

        /* Push Notification permit */
        D2GOClient.sharedInstance.authorizePushNotifications()

        /*Subscribe to push notifications*/
        D2GOClient.sharedInstance.configurePushNotifications(pushNotificatioDelegate: self)

        /* Cognito pool */
        D2GOClient.sharedInstance.configureAWS(myDelegate: self)

        return AWSMobileClient.sharedInstance.didFinishLaunching(application, withOptions: launchOptions)
    }


    func application(_: UIApplication,
        didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

        let token = deviceToken.reduce("") { $0 + String(format: "%02X", $1) }
        D2GOClient.sharedInstance.setDeviceToken(deviceToken: token)
        D2GOClient.sharedInstance.pushNotificationsSubscribe(deviceToken: token)
    }

}

Creating a user account

In your SignUp view controller, add an AWS pool and user member. Register n user requires an username, password, phone number and an email, add UITextField var member for these fields.

import UIKit
import AWSCore
import AWSCognitoIdentityProvider

class SignupViewController: D2GOViewController, UITextFieldDelegate {
  var pool: AWSCognitoIdentityUserPool?
  var user: AWSCognitoIdentityUser?
  
   @IBOutlet weak var usernameField: UITextField!
   @IBOutlet weak var passwordField: UITextField!
   @IBOutlet weak var phoneField: UITextField!
   @IBOutlet weak var emailField: UITextField!
   @IBOutlet weak var imageView: UIImageView!
   @IBOutlet weak var signupButton: UIButton!
   
    override func viewDidLoad() {
        super.viewDidLoad()

        pool = AWSCognitoIdentityUserPool(forKey: "UserPool")
        ...
    }

}

Handling Sign Up click button. We requires to call pool!.signUp method

@IBAction func signupPressed(_ sender: AnyObject) {
        var attributes = [AWSCognitoIdentityUserAttributeType]()
        let phone = AWSCognitoIdentityUserAttributeType()

        phone?.name = "phone_number"
        phone?.value = phoneField.text

        let email = AWSCognitoIdentityUserAttributeType()
        email?.name = "email"
        email?.value = emailField.text

        if email?.value != "" {
            attributes.append(email!)
        }
        if phone?.value != "" {
            attributes.append(phone!)
        }
   

        pool!.signUp(usernameField.text!, password: passwordField.text!, userAttributes: attributes, validationData: nil).continue({ task in
            // needs to be async so we can ALWAYS return nil for AWSTask
            DispatchQueue.main.async {
                if task.error != nil { // some sort of error
                    //Show message error.
                } else {
                    if let taskResult = task.result {
                        let response: AWSCognitoIdentityUserPoolSignUpResponse = taskResult 
                        self.user = response.user

                        if response.userConfirmed != NSNumber(value: AWSCognitoIdentityUserStatus.confirmed.rawValue) { // not confirmed
                            // setup to send sentTo thru segue
                            self.sentTo = response.codeDeliveryDetails?.destination
                            self.performSegue(withIdentifier: "confirmSignup", sender: sender)
                        } else {
                            self.navigationController?.popToRootViewController(animated: true)
                        }
                    }
                }
            }
            return nil
        })
    }

Confirm user account

A confirmation code will be sent to your email. Before login, the user requires to confirm the account just created. After your sign up, you can call the confirmation account view controller when you can let user type the confirmation code and tap the confirm account button. You required to call user?.confirmSignUp in Confirm button click handle method as shown in the below code.

class ConfirmSignupViewController: UIViewController  {
    var pool: AWSCognitoIdentityUserPool?
    var user: AWSCognitoIdentityUser?
 
    @IBOutlet weak var confirmButton: SpecialEffectsUIButton!
    @IBOutlet weak var usernameField: UITextField!
    @IBOutlet weak var confirmationCodeField: UITextField!
 
    override func viewDidLoad() {
        super.viewDidLoad()
        pool = AWSCognitoIdentityUserPool(forKey: "UserPool")
    }
}
@IBAction func confirm(_: AnyObject) {
        user?.confirmSignUp(confirmationCodeField.text!).continue({ task in
            // needs to be async so we can ALWAYS return nil for AWSTask
            DispatchQueue.main.async {

                if task.error != nil { // A error ocurred
                    //Show error message
                } else { // confirmed back to login
                   
                    //Go to the main screen
                }
            }
            return nil
        })
    }

After calling the user?.confirmSignUp if no error occurs, you can go to the main screen or login screen.

User Login

If a user is already signed up.

In your Login view controller, you required to implement the AWSCognitoIdentityPasswordAuthentication to login with username and password. You will get notified in didCompleteStepWithError whether the login success or fail.

import UIKit
import AWSCore
import AWSCognitoIdentityProvider
import AWSMobileHubHelper
import FBSDKLoginKit

import D2GOClient
class LoginViewController: UIViewController, AWSCognitoIdentityPasswordAuthentication {
    /*Using username and password login*/
    @IBOutlet weak var usernameField: UITextField!
    @IBOutlet weak var passwordField: UITextField!
    @IBOutlet weak var loginButton: UIButton!
    var passwordAuthenticationCompletion: AWSTaskCompletionSource<AnyObject>?
    
    /*Using your Facebook account to login*/
     @IBOutlet weak var facebookButton: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        /*For Facebook Login button*/
        AWSFacebookSignInProvider.sharedInstance().setPermissions(["public_profile", "email", "user_friends"])
        
        /*Adding Facebook button to view controller*/
        btnLogin = UIButton(frame: CGRect(x: 0, y: 0, width: 180, height: 60)) 
        btnLogin?.setImage(UIImage(named: "yourfacebookbuttonimage.png"), for: UIControlState.normal)
        btnLogin?.addTarget(self, action: #selector(LoginViewController.handleFacebookLogin), for: UIControlEvents.touchUpInside)
        view.addSubview(btnLogin!)
        
       
    }
    
   
    /*Handling Facebook button touch*/
    func handleFacebookLogin() {
   
        handleLoginWithSignInProvider(AWSFacebookSignInProvider.sharedInstance())
    }
    
    
    func handleLoginWithSignInProvider(_ signInProvider: AWSSignInProvider) {
        AWSIdentityManager.defaultIdentityManager().loginWithSign(signInProvider, completionHandler: { (result: Any?, error: Error?) -> Void in
            // If no error reported by SignInProvider, discard the sign-in view controller.
            if error == nil {
                DispatchQueue.main.async(execute: {
                  
                   /*Login with Facebook success, get Facebook profile data and store int the D*/
                    D2GOClient.sharedInstance.getFacebookProfileData()
                
                    DispatchQueue.main.async {
                        /*Go to the main screen*/
                    }
                })
            }
        })
    }
       
     /*Login with username and password*/  
    @IBAction func loginButtonPressed(_: AnyObject) {
        passwordAuthenticationCompletion?.setResult(AWSCognitoIdentityPasswordAuthenticationDetails(username: usernameField.text!, password: passwordField.text!))
    }
    
    /*AWSCognitoIdentityPasswordAuthentication protocol*/
    public func didCompleteStepWithError(_ error: Error?) {
        DispatchQueue.main.async(execute: {
            if let theError = error {
               //Show message error
            } else { 
              //Authenticated, go to the main screen
            }
        })
    }
    
     /*AWSCognitoIdentityPasswordAuthentication protocol*/
     public func getDetails(_ authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput,  passwordAuthenticationCompletionSource: AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails>) {
        passwordAuthenticationCompletion = passwordAuthenticationCompletionSource as? AWSTaskCompletionSource<AnyObject>

        DispatchQueue.main.async(execute: {
            
            if self.usernameText == nil && self.usernameField != nil {
                // i can use this to pre-populate the username field for convenience
                self.usernameText = authenticationInput.lastKnownUsername
            }
        })
    }
    
    
  }

Main Screen

If you have logged in, you are ready to interact with geofences, beacons, get push notifications and call Digital2Go APIs for content. A token is required to call the APIs and ARN and ARN topic to subscribe to for push notification s.

Getting authorise token, sns topic and ios arn

In your main view controller or search offer view controller, we required to get the Digital2Go token to interact with Digital2Go endpoints. sns and ios_arn to get push notifications.

class D2GViewController: UIViewController, CLLocationManagerDelegate, UITableViewDelegate, UITableViewDataSource, UITextViewDelegate {


  func loginPost(_ monitoredEntity: MonitoredEntity?) {
        let d2gAPIRequest = D2GAPIRequest() 
        var data = [
            "username": K.D2GO_API_LOGIN_USERNAME(),
            "password": K.D2GO_API_LOGIN_PASSWORD(),
            "device_id": D2GOClient.sharedInstance.uuid,
        ]

        // Benchmarking responses
         d2gAPIRequest.post(K.D2GO_API_LOGIN_URL, data: data, callback: { err, response, body in
              if err == nil {
                var strDisplayData = "Response Code: \(response!.statusCode)"
                self.printResponseDetails(data as [String: NSObject], response: response!, body: body!)

              if response?.statusCode == 200 {
                /*Save token*/
                    let dictReturnedVals = body as! NSDictionary
                    if let success = dictReturnedVals["success"], success as! Bool {
                    
                        let data = dictReturnedVals["data"] as! NSDictionary
                        
                        let accessToken = data["token"] as! String
                        let refreshToken = data["refresh"] as! String
                       

                        /********************************
                        *Subscribe to push notifications *
                        ********************************/
                        
                        let topicARN = data["sns"] as! String
                        let arn = data["ios_arn"] as! String
                         let singleton = D2GOClient.sharedInstance
                        DispatchQueue.main.async {
                            if let token = singleton.getDeviceToken() {
                                singleton.setARN(arn: arn)
                                singleton.setTopicARN(topicARN: topicARN)
                                singleton.pushNotificationsSubscribe(deviceToken: token)
                            }
                        }
                        
                        /*Save your token in secure way.*/
                 } else if response?.statusCode == 401 || response?.statusCode == 422 {
                   
                    /*Display the error message*/
                    }
                } else {
                    /*Display the error message*/
                }
            } else {
              /*Display the error message*/ 
            }
        })
    }

}

Calling the POST /apps/login.json it returns the following json result. You require the token for future APIs requests.

Use the sns and ios_arn to subscribe to the push notifications.

Please see Use this function along with the example data to retrieve a token / Authorise against API using Username and Password for more details. https://api.digital2go.com/swagger2/#!/Authorization/post_apps_login_json

Getting list of geofences based upon user location

Getting the list of geofences around the user location requires the latitude, longitude and the radius of such location to be sent as a parameter to the Digital2Go API.

More API Info please visit(https://api.digital2go.com/swagger2/#!/Geofences/get_geofences_inRange_json)

func getListOfGeofencesREST(_ userLocation: CLLocationCoordinate2D) {
  
        let d2gAPIRequest = D2GAPIRequest()

        /*Set saved token to a new request.*/
        d2gAPIRequest.accessToken = D2GOClient.sharedInstance.accessToken
        let params = [
            "lat": String(userLocation.latitude), 
            "lng": String(userLocation.longitude), 
            "radius": "\(D2GOClient.sharedInstance.geofenceRadius)", 
        ]
     
        d2gAPIRequest.get(K.D2GO_API_IMPRESSIONS_GEOFENCE_LIST_REST_URL, params: params, callback: { err, response, body in
            if err == nil {
                let dictReturnedVals = body as! NSDictionary

                if response?.statusCode == 200 {
                    //Save your geofences
                    if let success = dictReturnedVals["success"], success as! Bool {
                        if let data = dictReturnedVals["data"] as? NSArray {
                       
                            var arrPolygons: [[CLLocationCoordinate2D]] = [] // Array of Polygons
                            // iterate through the data items for the returned geofences to monitor
                            var geofenceID: Int = 0
                            self.arrGeofences.removeAll() // empty out the geofences array
                            var geofenceCount = 0

                            for dataItem in data {
                                if let di = dataItem as? NSDictionary {
                                    if let coverageType = di["coverage_type"], let campaignID = di["campaign_id"] {
                                         if coverageType as! String == "radius" { // handle radius type geofences
                                            if let coverage = di["coverage"] as? NSDictionary {
                                                let strLat = coverage["lat"] as! String
                                                let latDouble = Double(strLat)
                                                let numCP0 = CLLocationDegrees(latDouble!)
                                                let strLng = coverage["lng"] as! String
                                                let lngDouble = Double(strLng)
                                                let numCP1 = CLLocationDegrees(lngDouble!)

                                                let coord: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: numCP0, longitude: numCP1)
                                                let strRadius = coverage["radius"] as! String
                                                /*Convert geofence radius to meter units*/
                                                let radius = Double(strRadius)! * K.D2GO_MILES_TO_METERS_FACTOR
                                                let pointsCircle = MKCircle(center: coord, radius: radius)
                                                let geofenceIdString = "G\(geofenceID)"
                                                
                                                /*Create a geonce instance*/
                                                let geofence = Geofence(coordinate: pointsCircle.coordinate, radius: pointsCircle.radius, identifier: geofenceIdString, note: geofenceIdString, eventType: EventType.onBoth, originalGeofenceType: GeofenceType.radius, campaignID: campaignID as! Int)
                                                geofenceID += 1
                                      
                                                /*Store geofences into an array*/
                                                self.arrGeofences.append(geofence)
                   

                                                geofenceCount += 1
                                            } else {
                                               /*Not a dictionary*/
                                            }

                                        } else if coverageType as! String == "polygon" { // handle polygon type geofences

                                            if let coverage = di["coverage"] as? String {
                                                var arrPolygonCoords: [CLLocationCoordinate2D] = [] // Array of points in current polygon

                                                let jsonBody = coverage.parseJSONString
                                                if let jsonBodyarr = jsonBody as? NSMutableArray {
                                                   
                                                   /*Fetch geofence polygon points*/
                                                    for coveragePoint in jsonBodyarr {
                                                        if let cp = coveragePoint as? NSMutableArray {
                                                            let trimmedCP0 = (cp[0] as! String).trimmedString
                                                            let trimmedCP1 = (cp[1] as! String).trimmedString
                                                            let numCP0 = CLLocationDegrees(trimmedCP0)
                                                            let numCP1 = CLLocationDegrees(trimmedCP1)
                                                            let coord: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: numCP0!, longitude: numCP1!)
                                                            arrPolygonCoords.append(coord)
                                                        }
                                                    }
                                                    let poinstRect: MKMapRect = self.getRectForPoints(arrPolygonCoords)
                                                    let pointsCircle: MKCircle = self.getCircleFromRect(poinstRect)
                                                    let geofenceIdString = "G\(geofenceID)"
                                                    let geofence = Geofence(coordinate: pointsCircle.coordinate, radius: pointsCircle.radius, identifier: geofenceIdString, note: geofenceIdString, eventType: EventType.onBoth, originalGeofenceType: GeofenceType.polygon, campaignID: campaignID as! Int)
                                                    geofenceID += 1
                                     
                                                    self.arrGeofences.append(geofence)
                                                    arrPolygons.append(arrPolygonCoords)
                                                    geofenceCount += 1
                                                } 
                                            }
                                        } 
                                    } 
                                } 
                            } 
                           self.startMonitoringGeofences(self.arrGeofences)
                        } 
                    } 
                 }
               
              }
               
    func startMonitoringGeofences(_ geofences: [Geofence]) {
        for geofence in geofences {
            startMonitoringGeofence(geofence)
        }
    }

    func startMonitoringGeofence(_ geofence: Geofence) {
        if !CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
            D2GOClient.sharedInstance.showSimpleAlertWithTitle("Error", message: "Geofencing is not supported on this device!", viewController: self)
            return
        }
        if CLLocationManager.authorizationStatus() != .authorizedAlways {
            D2GOClient.sharedInstance.showSimpleAlertWithTitle("Warning", message: "Your geofences are saved but will only be activated once you grant D2GO permission to access the device location.", viewController: self)
        }
        let region = regionWithGeofence(geofence)
        locationManager.startMonitoring(for: region)
    }

    func regionWithGeofence(_ geofence: Geofence) -> CLCircularRegion {
        let region = CLCircularRegion(center: geofence.coordinate, radius: geofence.radius, identifier: geofence.identifier)
        region.notifyOnEntry = (geofence.eventType == .onEntry) || (geofence.eventType == .onBoth)
        region.notifyOnExit = (geofence.eventType == .onExit) || (geofence.eventType == .onBoth)
        return region
    }

    func getCircleFromRect(_ rect: MKMapRect) -> MKCircle {
        return MKCircle(mapRect: rect)
    }

    func getRectForPoints(_ points: [CLLocationCoordinate2D]) -> MKMapRect {
        var zoomRect: MKMapRect = MKMapRectNull
        //        for annotation in mapView.annotations {
        for point in points {
            let annotationPoint = MKMapPointForCoordinate(point) // (annotation.coordinate)
            let pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.1, 0.1)
            zoomRect = MKMapRectUnion(zoomRect, pointRect)
        }
      
        return zoomRect
    }
     

Monitoring geofences

After you call startMonitoring method as shown in the previous section code locationManager.startMonitoring(for: region), you are done to get notified when a user enter/exit into geofence region. In your view controller you required to implement the protocol CLLocationManagerDelegate. This protocol tells your when you entered into a region or exit. (More info https://developer.apple.com/documentation/corelocation/cllocationmanagerdelegate?language=objc)

class D2GViewController: UIViewController, CLLocationManagerDelegate
{
   ...
    func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion)
    {
        if type(of: region) == CLBeaconRegion.self {
            /*Ignore beacons*/
        } else {
          /*Get geofence content from the Digital2Go end-point*/
        }
    }

    func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion)
    {
        if type(of: region) == CLBeaconRegion.self {
            /*Ignore beacons*/
        } else {
          /*Get geofence content from the Digital2Go end-point*/
        }
    }
    ...
}

Monitoring beacons

Monitoring Digital2Go beacons requires D2GO_BEACON_UUID_FOR_D2GO key and BEACON_MAIN_REGION_IDENTIFIER region identifier provided by Digital2Go when creating a new app from the platform. Using this key required to create the region to monitor beaconRegion = CLBeaconRegion(proximityUUID: uuid!, identifier: K.BEACON_MAIN_REGION_IDENTIFIER) and then call `self.locationManager.startRangingBeacons(in: self.beaconRegion)’ as shown in the below code.

After setting up the monitorBeaconRegions, when a new beacon is detected you will get notified throught the method didRangeBeacons as part of CLLocationManagerDelegate protocol.

class D2GViewController: UIViewController, CLLocationManagerDelegate
{
    private func monitorBeaconRegions() {
        let uuid = UUID(uuidString: K.D2GO_BEACON_UUID_FOR_D2GO())

        if beaconRegion != nil {
            locationManager.stopMonitoring(for: beaconRegion)
            devicesManager.stopDevicesDiscovery()
        } else {
            beaconRegion = CLBeaconRegion(proximityUUID: uuid!, identifier: K.BEACON_MAIN_REGION_IDENTIFIER)
        }

        devicesManager.startDevicesDiscovery(withInterval: 3)
        DispatchQueue.main.asyncAfter(deadline: .now() + 3.1) {
            self.beaconRegion.notifyEntryStateOnDisplay = true
            self.locationManager.startMonitoring(for: self.beaconRegion)
            self.locationManager.startRangingBeacons(in: self.beaconRegion)
        }
    }

    func locationManager(_: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in _: CLBeaconRegion) {
        for beacon in beacons {
    
                ...
              /* Verify whether the beacon is already ranged in you want to skip*/
                ...
    
             /*Get beacon content for each of the beacons array*/
            let beaconDevice = BeaconDevice()
            beaconDevice.initWithBeaconAndConfig(beacon, config: nil, device: nil)
            beaconDevice.timeDetected = Date()
         
            let monitoredEntity: MonitoredEntity = beaconDevice
            arrBeaconsAndGeofencesFound.append(monitoredEntity) /*Local tracking of  monired beacons*/
            submitRequest(monitoredEntity) /*Get beacon content*/
       }    
    }
}

Geting beacons and geofences content from the Digital2Go platform

To get the geofence or beacon content you can use d2gAPIRequest.postJSON method as show in the next code. Call postJSON method after you have setup all the parameters required by the API.

More Info https://api.digital2go.com/swagger2/#!/Impressions/post_impressions_beacon_json https://api.digital2go.com/swagger2/#!/Impressions/post_impressions_geofence_json

func submitRequest(_ monitoredEntity: MonitoredEntity) {

        let d2gAPIRequest = D2GAPIRequest()
        d2gAPIRequest.accessToken = D2GOClient.sharedInstance.accessToken
    
        // Device Info Array
        var deviceInfo: [String: NSObject] = [:]
        if referenceUserLocation != nil {
            deviceInfo["altitude"] = referenceUserLocation.altitude as NSObject?
            deviceInfo["horizontal_accuracy"] = referenceUserLocation.horizontalAccuracy as NSObject?
            deviceInfo["vertical_accuracy"] = referenceUserLocation.verticalAccuracy as NSObject?
            deviceInfo["speed"] = referenceUserLocation.speed as NSObject?
            deviceInfo["course"] = referenceUserLocation.course as NSObject?
        }
        if let addr = D2GOClient.sharedInstance.getWiFiAddress() {
            deviceInfo["ip_address4"] = addr as NSObject?
        } else {
            print("No WiFi address")
        }

        if let ipAddress6 = D2GOClient.sharedInstance.getIPAddress6() {
            deviceInfo["ip_address6"] = ipAddress6 as NSObject?
        } else {
            print("No WiFi address6")
        }
      
        deviceInfo["battery_level"] = UIDevice.current.batteryLevel as NSObject?
        deviceInfo["device_os"] = UIDevice.current.systemName as NSObject?
        deviceInfo["device_version"] = UIDevice.current.systemVersion as NSObject?
        deviceInfo["device_model"] = UIDevice.current.model as NSObject?

        if let iPhoneBluetoothRssi = D2GOClient.sharedInstance.getPhoneBluetoothRSSI() {
            deviceInfo["device_rssi"] = iPhoneBluetoothRssi as NSObject?
        } else {
            print("No Bluetooth enabled")
        }

        if type(of: monitoredEntity) == BeaconDevice.self {
            if let tmpBeaconDevice = monitoredEntity as? BeaconDevice {
                let beacon = tmpBeaconDevice.beacon
                let idStr = beacon?.minor.stringValue
                if let nearbyDevice = self.dictNearbyDevice[idStr!] {
                    deviceInfo["brssi"] = nearbyDevice.rssi
                    deviceInfo["btx_power"] = nearbyDevice.transmissionPower.rawValue as NSObject
                }
            }
        } else {
            print("No beacon entity")
        }

        var demog: [String: NSObject] = [
            "age": NSNumber(value: 23 as Int32),
            "gender": "m" as NSObject,
            "city": "Ypsilanti" as NSObject,
        ]

        let prefs = UserDefaults.standard
        if let city = prefs.string(forKey: "d2gCity"), let gender = prefs.string(forKey: "d2gGender"), let yob = prefs.string(forKey: "d2gYOB") {
            let date = Date()
            let calendar = Calendar.current
            let components = (calendar as NSCalendar).components([.day, .month, .year], from: date)
            let currentYear = components.year

            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "MM-dd-yyyy"
            dateFormatter.dateStyle = DateFormatter.Style.medium
            if let dateObj = dateFormatter.date(from: yob) {
               
                let components1 = (calendar as NSCalendar).components([.day, .month, .year], from: dateObj)
                let year1 = components1.year
                demog["age"] = (currentYear! - year1!) as NSObject?
            } 
            demog["gender"] = gender as NSObject?
            demog["city"] = city as NSObject?
        }

        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss"

        let currentTimeStamp = Date().iso8601 

        var data: [String: NSObject] = [
            "device_id": D2GOClient.sharedInstance.uuid as NSObject, 
            "os": "ios" as NSObject,
            "bluetooth_enabled": NSNumber(value: 1 as Int32),
            "app_name": K.D2GO_BEACON_APP_NAME as NSObject, 
           "lat": (locationManager.location?.coordinate.latitude)! as NSObject, 
            "lng": (locationManager.location?.coordinate.longitude)! as NSObject, 
            "timestamp": currentTimeStamp as NSObject,
            "demographics": demog as NSObject,
            "deviceinfo": deviceInfo as NSObject,
        ]

        // if monitoredEntity is BeaconDevice
        if type(of: monitoredEntity) == BeaconDevice.self {
            if let tmpBeaconDevice = monitoredEntity as? BeaconDevice {
                let beacon = tmpBeaconDevice.beacon
                data["major"] = beacon?.major 
                data["minor_dec"] = beacon?.minor 
                data["proximity"] = getProximityStringFromProximityEnum(tmpBeaconDevice.beacon!.proximity) as NSObject?
                data["type"] = "beacon" as NSObject?
                data["uuid"] = K.D2GO_BEACON_UUID_FOR_D2GO() as NSObject?
               
            }
        } else if type(of: monitoredEntity) == Geofence.self {

            if let tmpGeofence = monitoredEntity as? Geofence { 
                data["type"] = "geofence" as NSObject?
                data["campaign_id"] = tmpGeofence.campaignID as NSObject? 
                if tmpGeofence.eventType == EventType.onEntry {
                    data["proximity"] = GeofenceActivity.enter.rawValue as NSObject?
                } else {
                    data["proximity"] = GeofenceActivity.exit.rawValue as NSObject?
                }
              
            }
        } else {
            // if neither a beacon device nor a geofence then something went wrong
            print("Neither a BeaconDevice nor a Geofence - Something went wrong")
            return
        }

        var sTargetURL = K.D2GO_API_IMPRESSIONS_BEACON_POST_URL  /*It's a beacon*/
        if monitoredEntity is Geofence {
            sTargetURL = K.D2GO_API_IMPRESSIONS_GEOFENCE_POST_URL  /*Geofence content*/
        }
        
        d2gAPIRequest.postJSON(sTargetURL, data: data, callback: { err, response, body in
            if err == nil {
                var strDisplayData = "Response Code: \(response!.statusCode)"
                let dictReturnedVals = body as! NSDictionary
                 if response?.statusCode == 200 {
 
                    if D2GOClient.sharedInstance.isATEnabled() == false {
                        D2GOClient.sharedInstance.printAndLog(strToLog: "<**> Limited ad tracking on. Do not proceed with ads")
                        return
                    }
                   if let success = dictReturnedVals["success"], success as! Bool {
                        if let data = dictReturnedVals["data"] as? NSDictionary {
                            if let media_cdn = data["media_cdn"] as? String {
                                /*Fetch your ad JSON content here*/
                                }
                            }
                        }
                    }
                    
                }
            }
        }
    }

Tracking buttons and image touch events

An advertisement content can have up to two buttons(primary and secondary). The touch buttons are configurable from Digital2Go CMS. They can be set an action like opening an url, the action can be making a phone call, sending an e-mail, opening a map and playing a video. Same actions can also apply by touching ad image.

You can track all these event actions by calling the Send an interaction for Campaign Content. https://api.digital2go.com/swagger2/#!/Interactions/post_interactions_register_json

func interactionPost(action: String) {
        let d2gAPIRequest = D2GAPIRequest()
        d2gAPIRequest.accessToken = D2GOClient.sharedInstance.accessToken
        let data: [String: NSObject] = [
            "campaign_id": 1 as! NSObject,
            "impression_id": 1 as! NSObject,
            "device_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" as NSObject,
            "action": action as NSObject,
            "timestamp": "2016-08-22 00:00:01" as NSObject,
        ]
        
        d2gAPIRequest.postJSON(K.D2GO_API_INTERACTION_REGISTER_URL, data: data, callback: { err, response, body in
            if err == nil {
                if response?.statusCode == 500 {
                    print("Refresh your token")
                } else if response?.statusCode == 200 {
                    /*Request success*/
                } 
            } 
        })
    }

Author

gelacio@digital2go.com, gelaciomar@gmail.com

License

D2GOClient is available under the MIT license. See the LICENSE file for more info.