Nikola Brežnjak blog - Tackling software development with a dose of humor
  • Home
  • Daily Thoughts
  • Ionic
  • Stack Overflow
  • Books
  • About me
Home
Daily Thoughts
Ionic
Stack Overflow
Books
About me
  • Home
  • Daily Thoughts
  • Ionic
  • Stack Overflow
  • Books
  • About me
Nikola Brežnjak blog - Tackling software development with a dose of humor
iOS

How to create a native iOS app that can read Search Ads Attribution API information

TL;DR

In this post, I’ll show you how easy it is to create a native iOS app that can read Search Ads Attribution API information.

You can check out the full working code on Github.

What is Search Ads App Attribution?

From Apple’s documentation:

Search Ads App Attribution enables developers to track and attribute app downloads that originate from Search Ads campaigns. With Search Ads App Attribution, iOS developers have the ability to accurately measure the lifetime value of newly acquired users and the effectiveness of their advertising campaigns.

Steps to recreate this yourself

  • Create a new blank project in Xcode
  • Add a label on the Main.storyboard:

  • Create an Interface Builder Outlet for this label and name it searchAdsInfoLabel. For those not familiar with iOS development, this is done by pressing Ctrl and dragging the label into the ViewController.swift file.

  • Once done, you should see this in your ViewController.swift file:
import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var searchAdsInfoLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
  • Next, import iAd to your project:

  • import iAd in your ViewController.swift file: import iAd.

Add the following function to the ViewController.swift file:

func getSearchAdsInfo() {
    ADClient.shared().requestAttributionDetails({ (attributionDetails, error) in
        if error == nil {
            for (type, adDictionary) in attributionDetails! {
                print(type);
                print(adDictionary);

                let attribution = adDictionary as? Dictionary<AnyHashable, Any>;

                let iadAdgroupId = attribution?["iad-adgroup-id"] as? String
                let iadAdgroupName = attribution?["iad-adgroup-name"] as? String
                let iadAttribution = attribution?["iad-attribution"] as? String
                let iadCampaignId = attribution?["iad-campaign-id"] as? String
                let iadCampaignName = attribution?["iad-campaign-name"] as? String
                let iadClickDate = attribution?["iad-click-date"] as? String
                let iadConversionDate = attribution?["iad-conversion-date"] as? String
                let iadCreativeId = attribution?["iad-creative-id"] as? String
                let iadCreativeName = attribution?["iad-creative-name"] as? String
                let iadKeyword = attribution?["iad-keyword"] as? String
                let iadLineitemId = attribution?["iad-lineitem-id"] as? String
                let iadLineitemName = attribution?["iad-lineitem-name"] as? String
                let iadOrgName = attribution?["iad-org-name"] as? String

                self.searchAdsInfoLabel.text = "iad-adgroup-id: \(iadAdgroupId ?? "")\niad-adgroup-name: \(iadAdgroupName ?? "")\niad-attribution: \(iadAttribution ?? "")\niad-campaign-id: \(iadCampaignId ?? "")\niad-campaign-name: \(iadCampaignName ?? "")\niad-click-date: \(iadClickDate ?? "")\niad-conversion-date: \(iadConversionDate ?? "")\niad-creative-id: \(iadCreativeId ?? "")\niad-creative-name: \(iadCreativeName ?? "")\niad-keyword: \(iadKeyword ?? "")\niad-lineitem-id: \(iadLineitemId ?? "")\niad-lineitem-name: \(iadLineitemName ?? "")\niad-org-name: \(iadOrgName ?? "")"
            }
        }
    })
}

Call this function in the viewDidLoad() method.

The full code listing, of the ViewController.swift file should now look like this:

import UIKit
import iAd

class ViewController: UIViewController {
    @IBOutlet weak var searchAdsInfoLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        getSearchAdsInfo();
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func getSearchAdsInfo() {
        ADClient.shared().requestAttributionDetails({ (attributionDetails, error) in
            if error == nil {
                for (type, adDictionary) in attributionDetails! {
                    print(type);
                    print(adDictionary);

                    let attribution = adDictionary as? Dictionary<AnyHashable, Any>;

                    let iadAdgroupId = attribution?["iad-adgroup-id"] as? String
                    let iadAdgroupName = attribution?["iad-adgroup-name"] as? String
                    let iadAttribution = attribution?["iad-attribution"] as? String
                    let iadCampaignId = attribution?["iad-campaign-id"] as? String
                    let iadCampaignName = attribution?["iad-campaign-name"] as? String
                    let iadClickDate = attribution?["iad-click-date"] as? String
                    let iadConversionDate = attribution?["iad-conversion-date"] as? String
                    let iadCreativeId = attribution?["iad-creative-id"] as? String
                    let iadCreativeName = attribution?["iad-creative-name"] as? String
                    let iadKeyword = attribution?["iad-keyword"] as? String
                    let iadLineitemId = attribution?["iad-lineitem-id"] as? String
                    let iadLineitemName = attribution?["iad-lineitem-name"] as? String
                    let iadOrgName = attribution?["iad-org-name"] as? String

                    self.searchAdsInfoLabel.text = "iad-adgroup-id: \(iadAdgroupId ?? "")\niad-adgroup-name: \(iadAdgroupName ?? "")\niad-attribution: \(iadAttribution ?? "")\niad-campaign-id: \(iadCampaignId ?? "")\niad-campaign-name: \(iadCampaignName ?? "")\niad-click-date: \(iadClickDate ?? "")\niad-conversion-date: \(iadConversionDate ?? "")\niad-creative-id: \(iadCreativeId ?? "")\niad-creative-name: \(iadCreativeName ?? "")\niad-keyword: \(iadKeyword ?? "")\niad-lineitem-id: \(iadLineitemId ?? "")\niad-lineitem-name: \(iadLineitemName ?? "")\niad-org-name: \(iadOrgName ?? "")"
                }
            }
        })
    }
}

After you run the project (make sure you run it on your device, as you won’t get any data back on the simulator) you should get some stub data that Apple returns:

Conclusion

If you’re utilizing Apple’s Search Ads, then I hope this helped you see how easy it is to fetch Search Ads App Attribution information in your app.

Of course, once you get the data, you should send this to your server for saving and further analysis.

How to create a #native #iOS app that can read Search Ads Attribution API information #SearchAdsAttribution https://t.co/6HDQBtzmM7

— Nikola Brežnjak (@HitmanHR) September 10, 2017

Programming

Code Complete 2 – Steve McConnell – Pseudocode Programming Process

I just love Steve McConnell’s classic book Code Complete 2, and I recommend it to everyone in the Software ‘world’ who’s willing to progress and sharpen his skills.

Other blog posts in this series:

  • Part 1 (Chapters 1 – 4): Laying the Foundation
  • Chapter 5: Design in Construction
  • Chapter 6: Working Classes
  • Chapter 7: High-Quality Routines
  • Chapter 8: Defensive programming

The benefits you can expect from using the pseudocode:

  • makes reviews easier – you can review detailed designs without examining the source code
  • supports the idea of iterative refinement
  • makes changes easier – a few lines of pseudocode are easier to change than a page of code
  • it’s easier to maintain than other forms of design documentation

Steps in building a routine

  1. Design the routine
  2. Code the routine
  3. Check the code
  4. Clean up loose ends
  5. Repeat as needed

1. Design the routine

  • Check the prerequisites to see that the job of the routine is well defined and fits cleanly into the overall design
  • Define the problem the routine will solve
  • Name the routine – good routine names are one sign of a superior program and they’re not easy to come up with
  • Decide how to test the routine
  • Research functionality available in the standard libraries – the single biggest way to improve both the quality of your code and your productivity is to reuse good code
  • Think about error handling – think about all the things that could possibly go wrong in the routine
  • Think about efficiency – in a vast majority of systems, efficiency isn’t critical. Design your routine so that it will meet its resource and speed goals
  • Research the algorithms and data types – if functionality isn’t available in the available libraries, it might still be described in an algorithms book
  • Write the pseudocode – you might not have much in writing after you finish the preceding steps. The main purpose of the steps is to establish a mental orientation that’s useful when you actually write the routine
  • Think about the data
  • Check the pseudocode – once you’ve written the pseudocode and designed the data, take a minute to review the pseudocode you’ve written. Back away from it, and think about how you would explain it to someone else
  • Try a few ideas in pseudocode, and keep the best (iterate)

2. Code the routine

  • Start with pseudocode
  • Write the routine declaration
    • Turn the original header comment into a programming-language code comment above declaration
  • Write the first and last statements, and turn the pseudocode into high-level comments
    /* Header comment about what routine does */
    Status ReportErrorMessage(
        ErrorCode errorToReport
    ) {
        //set the default status to "fail"

        //if the error code is valid, display the error message 
        //and declare success

        //if the error code isn't valid, notify the user that an internal 
        //error has been detected

        //return status information
    }
  • Fill in the code below each comment
/* Header comment about what routine does */
Status ReportErrorMessage(
    ErrorCode errorToReport
) {
    //set the default status to "fail"
    Status errorMessageStatus = Status_Failure;

    //if the error code is valid, display the error message 
    //and declare success
    if ( errorMessage.ValidCode() ) {
        DisplayMessage( errorMessage.Text() );
        errorMessageStatus = Status_Success;
    }

    //if the error code isn't valid, notify the user that an internal 
    //error has been detected
    else {
        DisplayMessage( "Internal Error: Invalid error code in ReportErrorMessage()" );
    }

    //return status information
    return errorMessageStatus;
}

Each comment should normally expand to about 2 to 10 lines of code.

3. Check the code

  • Mentally check the routine for errors

A working routine isn’t enough. If you don’t know why it works, study it, discuss it, and experiment with alternative designs until you do.

Compile the routine after reviewing it. It might seem inefficient to wait this long to compile. You’ll benefit in several ways, however, by not compiling until late in the process. After the first compile, you step up the pressure: “I’ll get it right with just one more compile.” The “Just One More Compile” syndrome leads to hasty, error-prone changes that take more time in the long run.

  • Step through the code in the debugger
  • Test the code

4. Clean up leftovers

  • Check the routine’s interface. Make sure that all input and output data is accounted for and that all parameters are used
  • Check for general design quality. Make sure the routine does one thing and does it well, that it’s loosely coupled to other routines, and that it’s designed defensively
  • Check the routine’s variables (inaccurate variable names, unused objects, undeclared variables, and so on)
  • Check routine’s statements and logic
  • Remove redundant comments

Alternatives to the PPP

  • Test-first development – popular development style in which test cases are written before writing any code
  • Refactoring – development approach in which you improve code through a series of semantic preserving transformation
  • Design by contract – development approach in which each routine is considered to have preconditions and postconditions
  • Hacking? – some programmers try to hack their way toward working code rather than using a systematic approach like the PPP. If you’ve ever found that you’ve coded yourself into a corner in a routine and have to start over, that’s an indication that the PPP might work better

Recent posts

  • Discipline is also a talent
  • Play for the fun of it
  • The importance of failing
  • A fresh start
  • Perseverance

Categories

  • Android (3)
  • Books (114)
    • Programming (22)
  • CodeProject (35)
  • Daily Thoughts (77)
  • Go (3)
  • iOS (5)
  • JavaScript (127)
    • Angular (4)
    • Angular 2 (3)
    • Ionic (61)
    • Ionic2 (2)
    • Ionic3 (8)
    • MEAN (3)
    • NodeJS (27)
    • Phaser (1)
    • React (1)
    • Three.js (1)
    • Vue.js (2)
  • Leadership (1)
  • Meetups (8)
  • Miscellaneou$ (77)
    • Breaking News (8)
    • CodeSchool (2)
    • Hacker Games (3)
    • Pluralsight (7)
    • Projects (2)
    • Sublime Text (2)
  • PHP (6)
  • Quick tips (40)
  • Servers (8)
    • Heroku (1)
    • Linux (3)
  • Stack Overflow (81)
  • Unity3D (9)
  • Windows (8)
    • C# (2)
    • WPF (3)
  • Wordpress (2)

"There's no short-term solution for a long-term result." ~ Greg Plitt

"Everything around you that you call life was made up by people that were no smarter than you." ~ S. Jobs

"Hard work beats talent when talent doesn't work hard." ~ Tim Notke

© since 2016 - Nikola Brežnjak