Using the new Instagram Basic Display API in Xcode using SwiftUI:

Tushar Gusain
15 min readMar 14, 2020

Learn about URLSession, using the new Instagram Basic Display API, and SwiftUI in this Swift 5 iOS Tutorial.

Version:-

Swift 5, iOS 13, Xcode 11

This is a post by Tushar Gusain, an Android/iOS developer.

Social networks are an indispensable part of our daily lives. Not only do we access social networks via websites like twitter.com, facebook.com or instagram.com, but we also find social features in apps, websites, blogs, video games, and more.

Integrating some social features into your apps can increase the virality of your app, they can help you identify and retain customers, and can add value to your app.

Before the update starting October 15, 2019, https://api.instagram.com/v1 was used to get the Instagram User Access Token, Permissions and Data from an Instagram user. After that the api was moved and is now owned by Facebook. You can find more information about the deprecation here: https://www.instagram.com/developer/ .

Keep reading to know how simple it is to integrate the new Instagram API in your app.

About the updated Instagram Api

Facebook provides two apis to integrate into your app to get the instagram features inside your app.The first one is Instagram Graph API ,which can allow the users of our app to access data in their Instagram Business and Instagram Creator accounts. It is categorised under the Business Apis provided by Facebook.

The second one (which we will be using in this tutorial) is the Instagram Basic Display API. It is categorised under the Consumer Api provided by Facebook , it’ll allow the users of our app to get basic profile information, photos, and videos from their Instagram accounts.This API is intended for non-Business and non-Creator Instagram users.

As we will be using a non-Business and non-Creator account in this tutorial, therefore we are using the Instagram Basic Display Api.

For more information on the Instagram Apis follow this link: https://developers.facebook.com/docs/instagram

Getting started

Before we start you should have your own Facebook developer account , you can create one here : Facebook Developer Account .

Step 1: Create a Facebook App

Go to developers.facebook.com, click My Apps, and create a new app. Once you have created the app and are in the App Dashboard, navigate to Settings > Basic, scroll to the bottom of page, and click Add Platform.

Choose iOS, add your iOS Bundle ID and Shared Secret, and save your changes.

To get your Bundle ID click on your project in Xcode, choose your Target, then click on General, you’ll find your Bundle ID inside Identity > Display Name.

For your Shared Secret copy your App Secret and paste it inside Shared Secret.

Step 2: Configure Instagram Basic Display

Click Products, locate the Instagram product, and click Set Up to add it to your app.

Click Basic Display, scroll to the bottom of the page, then click Create New App.

Now, inside Display Name field, enter the name of the Facebook app you just created.

Next in Valid OAuth Redirect URIs, enter your website’s URL or any other website’s URL. Normally this would be a dedicated URI that can capture redirect query string parameters, but for this tutorial you can use any website’s URL.

For example: https://www.google.com/

Copy the complete URL somewhere since you will need it in later steps to get authorization codes and access tokens.

In Deauthorize Callback URL, enter the copied URL again. Eventually you will have to change this to a URL that can handle deauthorization notifications, but for the purposes of this tutorial, you can re-use the above copied URL.

Do the same forData Deletion Request Callback URL.

Skip the App Review section for now since you will not be switching the app to Live Mode during the tutorial.

Step 3: Add an Instagram Test User

Navigate to Roles > Roles and scroll down to the Instagram Testers section. Click Add Instagram Testers and enter your Instagram account’s username and send the invitation.

Open a new web browser and go to www.instagram.com and sign into your Instagram account that you just invited. Navigate to (Profile Icon) > Edit Profile > Apps and Websites > Tester Invites and accept the invitation.

Note:- Do not add any profile in the Testers section, as we are not testing the facebook app here.

Your Instagram account is now eligible to be accessed by your Facebook app while it is in Development Mode.

Setting up your Xcode project

Step 1: Create an Xcode project

Open Xcode, click on New project, then select Single View App and click Next.

Now, Enter the name of your App in the Product Name field, then, select Swift as Language and SwiftUI as User Interface.

After that click on Next.

Step 2: Create the InstagramApi class

Now, create a new Swift file, name it InstagramApi, then inside the file declare a class InstagramApi, this class will be responsible for handling api requests. Getting the oauth token, fetching your feed, media etc.

class InstagramApi {  static let shared = InstagramApi()  private let instagramAppID = “{your-instagram-app-id}”  private let redirectURIURLEncoded = “https%3A%2F%2Fwww.hotcocoasoftware.com%2F"  private let redirectURI = “https://www.hotcocoasoftware.com/"  private let app_secret = “{your-app-secret}”  private let boundary = “boundary=\(NSUUID().uuidString)”  private init () {}}

Now, copy and paste the above code inside your InstagramApi class.

Replace {your-instagram-app-id} with the instagram app id in your facebook app.

(Note:- Do not confuse instagram app id with facebook app id, your instagram app id is given inside Products > Instagram> Basic Display > Instagram App ID )

Then , replace {your-app-secret} with your Instagram app secret.

Here, I have used https://www.hotcocoasoftware.com/ as the redirect URI , you can use any URI you want for this tutorial.

For example, if you use https://www.google.com/ as redirectURI your then change the redirectURIURLEncoded’s value to “https%3A%2F%2Fwww.google.com%2F".

Now, declare the below enums inside InstagramApi class :

private enum BaseURL: String {  case displayApi = “https://api.instagram.com/"  case graphApi = “https://graph.instagram.com/"}private enum Method: String {  case authorize = “oauth/authorize”  case access_token = “oauth/access_token”}

Step 3: Create a model file

Now, create a new swift file , name it InstagramResponse. Inside the file declare the below structs:

struct InstagramTestUser: Codable {  var access_token: String  var user_id: Int}struct InstagramUser: Codable {  var id: String  var username: String}struct Feed: Codable {  var data: [MediaData]  var paging: PagingData}struct MediaData: Codable {  var id: String  var caption: String?}struct PagingData: Codable {  var cursors: CursorData  var next: String}struct CursorData: Codable {  var before: String  var after: String}struct InstagramMedia: Codable {  var id: String  var media_type: MediaType  var media_url: String  var username: String  var timestamp: String}enum MediaType: String, Codable {  case IMAGE  case VIDEO  case CAROUSEL_ALBUM}

Step 4: Setting up the content view

(If you want to use the UIStoryboard instead of swiftUI you can follow this post :- https://medium.com/@tushargusain40/using-the-new-instagram-basic-display-api-using-swift-5-ios-10e94292e280?sk=49f9eb63531bddd457519aec99b7c526 )

Inside your ContentView class , add the following code above var body :

@State var instagramApi = InstagramApi.shared@State var signedIn = false@State var instagramImage = UIImage(imageLiteralResourceName: 
“instagram_background”)

Then, inside var body: some View, add the following code :

NavigationView {

NavigationView {  ZStack {    Image(uiImage: instagramImage)      .resizable()      .aspectRatio(contentMode: .fill)      .edgesIgnoringSafeArea(Edge.Set.all)    VStack{      Button(action: {        //get instagram user data      }) {        Image(“Instagram_icon”)          .renderingMode(.original)          .resizable()          .scaledToFill()          .frame(width: 100, height: 100)      }      Button(action: {        //get instagram media      }){        Text(“Fetch Media to background”)          .font(.headline)          .padding()      }    }  }}

After this, Download image from google and name it “instagram_background”, this will serve as the background image for your app.

Also, download an instagram icon from google and name it “Instagram_icon”.

This will create a button which will be used to authorize and sign in the user,

Below it will be a button Fetch Media to background which will set your background with the latest image you posted with your instagram account.

It will look like this in the preview:

Now , you are all set to call the Instagram Api.

Getting your Access Token

Get Authorization

We need to authenticate the Test User, to do this we have to call the below url :

https://api.instagram.com/oauth/authorize?app_id={app-id}&redirect_uri={redirect-uri-urlencoded}&scope=user_profile,user_media&response_type=code

This will provide you with an authorization window, showing you the test user id that you added in the instagram app.

Go inside your InstagramApi class, create a function authorizeApp and paste the following code:

func authorizeApp(completion: @escaping (_ url: URL?) -> Void ) {  let urlString = “\(BaseURL.displayApi.rawValue)\. (Method.authorize.rawValue)?app_id=\(instagramAppID)&redirect_uri=\ (redirectURIURLEncoded)&scope=user_profile,user_media&response_type=code”  let request = URLRequest(url: URL(string: urlString)!)  let session = URLSession.shared  let task = session.dataTask(with: request, completionHandler: {  data, response, error in    if let response = response {      print(response)      completion(response.url)    }  })  task.resume()}

This function will call the above URL for authorization.

Now, go to the ContentView class and add the following two variables :

@State var presentAuth = false@State var testUserData = InstagramTestUser(access_token: “”, user_id: 0)

After this create a new Swift UI View file, name it WebView. Then, add the following variables to the class:

import SwiftUIimport WebKitstruct WebView: UIViewRepresentable {  //MARK:- Member variables  @Binding var presentAuth: Bool  @Binding var testUserData: InstagramTestUser  @Binding var instagramApi: InstagramApi

Add the UIViewRepresentable delegate methods:

//MARK:- UIViewRepresentable Delegate Methodsfunc makeCoordinator() -> WebView.Coordinator {  return Coordinator(parent: self)}func makeUIView(context: UIViewRepresentableContext<WebView>) -> WKWebView {  // return a webView object}func updateUIView(_ webView: WKWebView, context: UIViewRepresentableContext<WebView>) {  //authorize the app}

Initialize a Coordinator class inside WebView , and add the following code :

//MARK:- Coordinator classclass Coordinator: NSObject, WKNavigationDelegate {  var parent: WebView  init(parent: WebView) {  self.parent = parent  }  func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {  // get instagram test user token and id  }
}

Now, initialize and return a new WKWebView from the makeUIView function:

func makeUIView(context: UIViewRepresentableContext<WebView>) -> WKWebView {  let webView = WKWebView()  webView.navigationDelegate = context.coordinator  return webView}

Call the InstagramApi’s authorizeApp function from updateUIView:

func updateUIView(_ webView: WKWebView, context: UIViewRepresentableContext<WebView>) {  instagramApi.authorizeApp { (url) in    DispatchQueue.main.async {      webView.load(URLRequest(url: url!))    }  }}

Now go inside your ContentView class and inside the var body, add the following code as the Button action present the authorization window when the instagram icon is clicked:

Image(uiImage: instagramImage)  .resizable()  .aspectRatio(contentMode: .fill)  .edgesIgnoringSafeArea(Edge.Set.all)VStack{  Button(action: {    if self.testUserData.user_id == 0 {    self.presentAuth.toggle()    } else {      // get the signed in user details    }  }) {    Image(“Instagram_icon”)      .renderingMode(.original)      .resizable()      .scaledToFill()      .frame(width: 100, height: 100)}

At the end of your NavigationView’s body add a sheet and paste the following code:

.sheet(isPresented: self.$presentAuth) {  WebView(presentAuth: self.$presentAuth, testUserData:   self.$testUserData, instagramApi: self.$instagramApi)}

Now, run the app , clicking the instagram icon will present one of the following sheet as shown in the images below:

Enter your username & password(or click on the continue button if you got the second screen) to authorise the test user and grant your app access to your profile data. Upon success, the page will redirect you to the redirect URI you included in the previous step and append an Authorization Code. For example:

https://hotcocoasoftware.com/auth/?code=AQDp3TtBQQ...#_

Note:- #_ has been appended to the end of the redirect URI, but it is not part of the code itself.

Exchange the Code For a Token

Inside your InstagramApi class create a new function getTokenFromCallbackURL, and add the following code to it :

private func getTokenFromCallbackURL(request: URLRequest) -> String? {  let requestURLString = (request.url?.absoluteString)! as String  if requestURLString.starts(with: “\(redirectURI)?code=”) {    print(“Response uri:”,requestURLString)    if let range = requestURLString.range(of: “\(redirectURI)?code=”) {      return String(requestURLString[range.upperBound…].dropLast(2))    }  }  return nil}

Add another private function getFormBody which will return a form-data made by the key value pairs. Add the following code to it :

private func getFormBody(_ parameters: [[String : String]], _ boundary: String) -> Data {  var body = “”  let error: NSError? = nil  for param in parameters {    let paramName = param[“name”]!    body += “ — \(boundary)\r\n”    body += “Content-Disposition:form-data; name=\”\(paramName)\””    if let filename = param[“fileName”] {      let contentType = param[“content-type”]!      var fileContent: String = “”      do { 
fileContent = try String(contentsOfFile: filename, encoding: String.Encoding.utf8)
}catch {
print(error) } if (error != nil) { print(error!) } body += “; filename=\”\(filename)\”\r\n” body += “Content-Type: \(contentType)\r\n\r\n” body += fileContent } else if let paramValue = param[“value”] { body += “\r\n\r\n\(paramValue)” } } return body.data(using: .utf8)!}

To get the test user token and id we need to call the post method of the api whose curl representation is given below :

curl -X POST \https://api.instagram.com/oauth/access_token \-F app_id={app-id} \-F app_secret={app-secret} \-F grant_type=authorization_code \-F redirect_uri={redirect-uri} \-F code={code}

Upon success, the API will return a JSON encoded object containing an Instagram User Access Token and your Instagram test user’s ID:

{“access_token”: “IGQVJ…”,“user_id”: 17841405793187218}

Now, inside your InstagramApi class create a new function getTestUserIDAndToken, and add the following code inside :

func getTestUserIDAndToken(request: URLRequest, completion: @escaping (InstagramTestUser) -> Void){  guard let authToken = getTokenFromCallbackURL(request: request)   else {    return  }  let headers = [    “content-type”: “multipart/form-data; boundary=\(boundary)”  ]  let parameters = [    [      “name”: “app_id”,      “value”: instagramAppID    ],    [      “name”: “app_secret”,      “value”: app_secret    ],    [      “name”: “grant_type”,      “value”: “authorization_code”    ],    [      “name”: “redirect_uri”,      “value”: redirectURI    ],    [      “name”: “code”,      “value”: authToken    ]  ]  var request = URLRequest(url: URL(string:   BaseURL.displayApi.rawValue + Method.access_token.rawValue)!)  let postData = getFormBody(parameters, boundary)  request.httpMethod = “POST”  request.allHTTPHeaderFields = headers  request.httpBody = postData  let session = URLSession.shared  let dataTask = session.dataTask(with: request, completionHandler: {(data, response, error) in    if (error != nil) {      print(error!)    } else {      do { let jsonData = try JSONDecoder().decode(InstagramTestUser.self, from: data!)        print(jsonData)        completion(jsonData)      } catch let error as NSError {        print(error)      }    }  })  dataTask.resume()}

Now, inside your WebView.Coordinator class’ method decidePolicyFor navigationAction, add the following lines of code to call the getTestUserIDAndToken method :

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {  let request = navigationAction.request  self.parent.instagramApi.getTestUserIDAndToken(request: request) { (instagramTestUser) in  self.parent.testUserData = instagramTestUser  self.parent.presentAuth = false  }  decisionHandler(WKNavigationActionPolicy.allow)}

This will get the Test user’s ID and Token and save it in the binding variable testUserData.

Get the user’s feed

Query the User node

Now, you need to get the instagram user’s(who granted your app permission) id and username to get it’s feed.

We can get this data by the GET method of the InstagramApi given below :

“https://graph.instagram.com/{user-id}?fields=id,username&access_token={access-token}”

Upon success, the API will respond with your Instagram user’s ID and username:

{  “id”: “17841405793187218”,  “username”: “username_xyz”}

Now, inside your InstagramApi class create a function getInstagramUser, and add the following code to it :

func getInstagramUser(testUserData: InstagramTestUser, completion: @escaping (InstagramUser) -> Void) {  let urlString = “\(BaseURL.graphApi.rawValue)\(testUserData.user_id)?fields=id,username&access_token=\(testUserData.access_token)”  let request = URLRequest(url: URL(string: urlString)!)  let session = URLSession.shared  let dataTask = session.dataTask(with: request, completionHandler: {(data, response, error) in    if (error != nil) {      print(error!)    } else {      let httpResponse = response as? HTTPURLResponse      print(httpResponse!)    }    do { let jsonData = try JSONDecoder().decode(InstagramUser.self, from: data!)      completion(jsonData)    }catch let error as NSError {      print(error)    }  })  dataTask.resume()}

Now go into your ContentView class and add a state variable instagramUser :

@State var instagramUser: InstagramUser? = nil

After this add the following lines of code inside the instagram icon Button’s action part :

Button(action: {  if self.testUserData.user_id == 0 {    self.presentAuth.toggle()  } else {    self.instagramApi.getInstagramUser(testUserData: self.testUserData) { (user) in      self.instagramUser = user      self.signedIn.toggle()    }  }}) {  Image(“Instagram_icon”)    .renderingMode(.original)    .resizable()    .scaledToFill()    .frame(width: 100, height: 100)}

After this add an action sheet at the end your NavigationView’s body :

}.sheet(isPresented: self.$presentAuth) {  WebView(presentAuth: self.$presentAuth, testUserData:   self.$testUserData, instagramApi: self.$instagramApi)}.actionSheet(isPresented: self.$signedIn) {  let actionSheet = ActionSheet(title: Text(“Signed in:”), message: Text(“with account: @\(self.instagramUser!.username)”),buttons:   [.default(Text(“OK”))])  return actionSheet}

This will show an action sheet on clicking the instagram icon Button once you have authorized the App. Inside the Actionsheet , it will show the username of your instagram account as shown in the image below.

Get the User’s Media

Now you have the ID and the username of the Instagram user’s account, by knowing the user’s ID you can get the user’s details by calling this GET method og the Instagram Api:

“https://graph.instagram.com//me/media?fields={fields}&access_token={access-token}”

We will use “id,caption” as {fields} in this url parameter.

It will give the following response :

{  “data”: [    {       “id”: “17895695668004550”,        “caption”: “”     },     {       “id”: “17899305451014820”,        “caption”: “”      },      {        “id”: “17896450804038745”,        “caption”: “”      },      {         “id”: “17881042411086627”,         “caption”: “”      }    ],  “paging”: {    “cursors”: {    “after”: “MTAxN…”,    “before”: “NDMyN…”    },    “next”: “https://graph.faceb..."  }}

Now, got to your InstagramApi class and create a new function getMediaData and add the following lines of code inside it :

private func getMediaData(testUserData: InstagramTestUser, completion: @escaping (Feed) -> Void) {  let urlString = “\(BaseURL.graphApi.rawValue)me/media? fields=id,caption&access_token=\(testUserData.access_token)”  let request = URLRequest(url: URL(string: urlString)!)  let session = URLSession.shared  let task = session.dataTask(with: request, completionHandler: { data, response, error in    if let response = response {      print(response)    }    do { let jsonData = try JSONDecoder().decode(Feed.self, from: data!)      print(jsonData)      completion(jsonData)    } catch let error as NSError {      print(error)    }  })  task.resume()}

This will give you the whole array of your instagram account’s feed starting from the most recent one.

To fetch a single post, we will use the id of one of the post from the feed.

To get the data of a single post , we will use this GET method of the InstagramApi :

“https://graph.instagram.com/{media-id}?fields={fields}&access_token={access-token}”

For this we will replace the {fields} by “id,media_type,media_url,username,timestamp”

It will give the following response :

{  “id”: “17895695668004550”,  “media_type”: “IMAGE”,  “media_url”: “https://fb-s-b-a.akamaihd.net/...",  “username”: “jayposiris”,  “timestamp”: “2017–08–31T18:10:00+0000”}

Now, create a new function getMedia inside your InstagramApi class, and add the following code inside it :

func getMedia(testUserData: InstagramTestUser, completion: @escaping (InstagramMedia) -> Void) {  getMediaData(testUserData: testUserData) { (mediaFeed) in    let urlString = “\(BaseURL.graphApi.rawValue + mediaFeed.data[0].id)?fields=id,media_type,media_url,username,timestamp&access_token=\(testUserData.access_token)”    let request = URLRequest(url: URL(string: urlString)!)    let session = URLSession.shared    let task = session.dataTask(with: request, completionHandler: { data, response, error in      if let response = response {        print(response)      }      do { let jsonData = try JSONDecoder().decode(InstagramMedia.self, from: data!)        print(jsonData)         completion(jsonData)      } catch let error as NSError {         print(error)      }    })    task.resume()  }}

You can use this feed data to show all of the posts’ data as a List in a TableView, and once the user clicks one of the post items in your list , show them the detailed post.It’s all up to you.

Here, we will be showing the first post that is the post at index 0 in the MediaFeed.data array(which is the latest post in your instagram account).

Now, inside the fetch media text Button’s action closure , call the getMedia method to get the latest post made by the Instagram account you are using.Add the following lines of code inside it.

Button(action: {  if self.instagramUser != nil {    self.instagramApi.getMedia(testUserData: self.testUserData) { (media) in      if media.media_type != MediaType.VIDEO {        let media_url = media.media_url        self.instagramApi.fetchImage(urlString: media_url, completion: { (fetchedImage) in          if let imageData = fetchedImage {            self.instagramImage = UIImage(data: imageData)!          } else {            print(“Didn’t fetched the data”)          }        })        print(media_url)      } else {        print(“Fetched media is a video”)      }    }  } else {      print(“Not signed in”)  }}){ Text(“Fetch Media to background”)   .font(.headline)
.padding()
}

Now, create a function fetchImage inside your InstagramApi class, this function will fetch an image from the given url. Inside this functions body add the following lines of code :

func fetchImage(urlString: String, completion: @escaping (Data?) -> Void) {  let request = URLRequest(url: URL(string: urlString)!)  let session = URLSession.shared  let task = session.dataTask(with: request, completionHandler: { data, response, error in    if let response = response {      print(response)    }    completion(data)  })  task.resume()}

Now, on clicking the Fetch Image to background you will see you latest posted Image on the background of your app, as shown in the preview below :

Congratulations!!! , you fetched your instagram account’s feed from the Instagram Api provided by Facebook.

And with that my friend, you are done. Great job!

You can find the whole code for this app here at : https://github.com/tushar40/InstaApp-iOS

I hope you enjoyed this tutorial. Feel free to post your questions and comments on the forums, I can’t wait for what you guys come up with for your next great app! ;)

This is a post by Tushar Gusain, an Android/iOS developer.

--

--