IMHABIB.COM - Dr.Virus's Technology Blog
Affiliate Marketing Internet Marketing Web Design
August 18, 2016
, , ,
Basic Bootstrap Template with CDN for Landing Pages

Basic Bootstrap Template with CDN

I started using Boot­strap for my land­ing pages and web­sites shortly after it was first released years ago. Boot­strap is an amaz­ing frame­work for devel­op­ing mobile first respon­sive web­sites.

Mobile first means that the frame­work is setup to work with mobile devices and then expand for desk­top browsers and tablets. Up until fairly recently, this was the exact oppo­site of how web devel­op­ers cre­ated web­sites. Web­sites were devel­oped to look great on a desk­top browser, but as mobile traf­fic started to grow many devel­op­ers would build their web­site so it would look good on mobile too. Now, we build for mobile know­ing the desk­top expe­ri­ence will look fine as well.

Boot­strap makes it extremely easy to build for both mobile and desk­top. When you use Boot­strap, your web­site is almost sure to look good on both.

This blog design uses the Boot­strap frame­work. I used it (v2.3.2) withFPTraf­fic and Skrayp (v3.2.0).

Need­less to say, I really like Boot­strap and I rec­om­mend it to every­one. Aside from being respon­sive and mak­ing your web­site or land­ing page look good with any browser, the frame­work also comes with all the key com­po­nents you are going to need with web devel­op­ment. Great look­ing but­tons, hero units, forms, tables, pro­gress bars, and nav­i­ga­tion menus can be coded up with very lit­tle work.

But wait, there’s more!


Because Boot­strap is so pop­u­lar, there are some great CDN sources you can use when includ­ing it on your web­site or land­ing page.

CDN, short for con­tent deliv­ery net­work, is an awe­some tool you can use to speed up the page load of your pages and decrease your over­all band­width use. Essen­tially, it’s a ver­sion of the file you want to use that is hosted by a CDN. Cloud­Flare is the major CDN net­work that I use in the exam­ple below.

Bootstrap CDN Template

One trick you can use to speed up the devel­op­ment of your land­ing pages (aside from using Boot­strap) is to setup a tem­plate that you can eas­ily copy and paste into a new file when you start devel­op­ment. My IDE (inte­grated devel­op­ment envi­ron­ment) of choice, Coda, has a cool fea­ture that lets you cre­ate “Clips” of code that you can then eas­ily access. My most used Clip is def­i­nitely my Boot­strap CDN tem­plate, shown below.

This tem­plate has every­thing you need for the basic Boot­strap tem­plate. The HTML, CSS, and JavaScript for Boot­strap is all included and hosted via the Cloud­Flare CDN so you don’t even have to worry about upload­ing any files to your server.

Once you have that basic tem­plate setup, all you need to do is add your con­tainer, rows, and the con­tent of your page.

Here is an exam­ple I made in about 15 min­utes.

Bootstrap Responsive Template Demo

Easy! Now, how well will it con­vert? We’ll see 🙂

If you are not using Boot­strap or CDNs on your land­ing pages then I highly rec­om­mend you give it a try. If you are not build­ing land­ing pages, then that is the first step! Learn how to build land­ing pages to help take your cam­paigns to the next level.

I will be doing another arti­cle soon show­ing how to setup a sur­vey land­ing page and pro­mote it on Plenty of Fish.

Let me know if you have any ques­tions. Let’s make some money!

Affiliate Marketing Internet Marketing SEO
August 18, 2016
, ,
Creating a Survey Landing Page

Create a Survey Landing Page

The sur­vey land­ing page has been around a long time. My first expe­ri­ence with it was with PPV traf­fic back in 2009. It has evolved quite a bit since then, but it is still a strat­egy used by many affil­i­ate mar­keters.

A sur­vey land­ing page is a sim­ple land­ing page that asks the vis­i­tor a few ques­tions and then presents them with the call to action. In most cases, the ques­tions and answers don’t even really mat­ter to the mar­keter. The goal is to get the user engaged with the land­ing page and poten­tially answer­ing yes to ques­tions to get their mind­set pos­i­tive towards say­ing yes to our call to action.

The most pop­u­lar use right now for the sur­vey land­ing page is prob­a­bly with adult and dat­ing traf­fic. I have been using it quite a bit for my dat­ing cam­paigns on Plenty of Fish.

Survey Landing Page Template

My friend David at Aff­Play­book posted a free tem­plate on his blog that I have been using ever since.

Survey Landing Page Template

You can pre­view the land­ing page here or down­load the code here. The code is the same as what David orig­i­nally pro­vided, but I did clean up the tab spac­ing a bit to make it eas­ier to read and edit.

You can see the tem­plate fea­tures a nice big head­line, a smaller head­line, a few para­graphs of info (includ­ing a count­down timer if you want to use it) and then the ques­tions that setup the final sec­tion with a nice big call to action.

Landing Page Call To Action

Of course, you’ll want to change all the text and images to match the offer you are pro­mot­ing.

Senior Soulmates Example

I started pro­mot­ing Senior Soul­mates on POF over a year ago and I have had some pretty good suc­cess with it. Once I started using a sur­vey lan­der my results were even bet­ter.

I have made many vari­a­tions of this land­ing page, but I thought it would be fun to share one of the ver­sions I was actively run­ning to give you an idea of the mod­i­fi­ca­tions you can do to the tem­plate above if you get cre­ative with it.

Senior Soulmates Survey Landing Page

To be clear, I am not rec­om­mend­ing you copy this entire land­ing page. Feel free to use it for inspi­ra­tion on how to mod­ify your land­ing page though.

It’s also worth not­ing that the tem­plate shown above is mobile respon­sive like the one I pro­vided in my Boot­strap exam­ple.

In con­clu­sion, if you are not test­ing sur­vey land­ing pages, you need to! Set one up and give it a try. Let me know if you have any ques­tions.

Let’s make some money!

Blogging SEO Wordpress
August 18, 2016
, , ,
7 Reasons You’re Not Getting Conversions

7 Reasons You're Not Getting Conversions

Are you ready to pull your hair out because despite every effort you have made, you are not get­ting any con­ver­sions?

Well, there is prob­a­bly a very log­i­cal rea­son why that is hap­pen­ing (or not hap­pen­ing in this case).

Here are 7 com­mon rea­sons why you’re not get­ting con­ver­sions and some changes you can make to improve your odds of see­ing a con­ver­sion come in with your next click!

Your Link is Broken

This should be obvi­ous, but if your link does not load the advertiser’s land­ing page, then you are not going to get a con­ver­sion.

One com­mon rea­son this may hap­pen is if you have messed up the link for­mat when copy­ing and past­ing it into your traf­fic source or when you added it to your land­ing page. I see this all the time with users who are using CPVLab or another 3rd party track­ing soft­ware as well.

If the land­ing page is not load­ing and you can not sort it out your­self, con­tact your Affil­i­ate Man­ager or send it to me and get help!

The Offer is Down

Another fairly obvi­ous issue, but one that is some­times hard to detect. If the offer you are attempt­ing to pro­mote has been taken down (either by the affil­i­ate net­work or the actual adver­tiser) then it’s def­i­nitely not going to con­vert.

Your affil­i­ate net­work should notify you if the offer has been taken down, but some­times the adver­tiser will take it down and not notify the affil­i­ate net­work. Either way, you can check this your­self by sim­ply going to your affil­i­ate link your­self. If it loads the cor­rect land­ing page then it’s likely still active.

If the offer does not allow traf­fic from your coun­try you may need touse a proxy or VPN to try to view your affil­i­ate link. If you’re look­ing for a VPN, I rec­om­mend and use Over­Play.

Pro Tip: If you’re run­ning or want to run a Peer­Fly offer you can sub­scribe to receive updates about that offer and get emails when­ever any­thing about that offer changes.PeerFly Offer Subscription

You Haven’t Sent Enough Traffic

When we setup an affil­i­ate cam­paign we have high hopes. We want to make a bunch of money so we hope to see a con­ver­sion right away. Most of the time, that is not going to hap­pen. Even when it does, it’s not always a good thing. I have run a bunch of cam­paigns over the years where I have seen a con­ver­sion come in right away at the begin­ning of the cam­paign and then not see another for hun­dreds of dol­lars worth of traf­fic. When you see good results right away you will typ­i­cally over­spend.

So, if you have sent 14 clicks to an offer and not seen a con­ver­sion, don’t get upset! Do not email your Affil­i­ate Man­ager say­ing that the offer must not con­vert. Give it time. I usu­ally rec­om­mend spend­ing 2-5x the pay­out of the offer on your ini­tial test.

My friend, JJ Alan, has made mil­lions of dol­lars with affil­i­ate mar­ket­ing over the past few years and he told me it all started with a Face­book cam­paign he setup and then went to the mall. He didn’t sit at his com­puter and refresh his stats every few min­utes. Because he was away from his com­puter he was able to get a good test in with­out killing the cam­paign before he even gave it a chance.

Setup your cam­paign and walk away from your com­puter. You may have a nice sur­prise when you come back!

You’re Not Sending The Right Traffic

I have learned this issue the hard way sev­eral times. Not every offer is going to con­vert with every traf­fic source. Let’s think about this log­i­cally. What is traf­fic and what is an offer? Traf­fic are peo­ple. An offer is a pro­duct or ser­vice. If you are try­ing to get a traf­fic source with a pri­mar­ily female demo­graphic to con­vert on a web­site that sells male enhance­ment pills, it’s prob­a­bly not going to go very well.

A more com­mon mis­take I see all the time is push­ing odd offers onPlenty of Fish. The rea­son 95% of the ads on Plenty of Fish are for dat­ing sites is because the peo­ple on POF are look­ing for a date!

Weak Landing Page

The Landing Page is Weak

I use the word weak because I do not believe any adver­tiser who has gone through the trou­ble of cre­at­ing a web­site for their pro­duct or ser­vice ever inten­tion­ally cre­ates a bad land­ing page and it’s likely that some­one is con­vert­ing on their land­ing page or it wouldn’t make it to the affil­i­ate net­work to begin with. But, that doesn’t mean it’s a strong land­ing page that is going to con­vert well.

How can you tell if a land­ing page is weak? Well, a lot of times the only way is to send traf­fic to it and see how it con­verts.

Some pub­lish­ers will tell me they know a land­ing page is weak because the net­work con­ver­sion rate for the offer is low. That is not always true and is not some­thing I believe you should base your deci­sion whether or not to run an offer on. The biggest rea­son being you do not know what type of traf­fic an offer has got on the affil­i­ate net­work. For exam­ple, Peer­Fly gets a ton of PPV traf­fic so if the offer has a low con­ver­sion rate it’s pos­si­ble that pub­lish­ers are direct link­ing it with pop up PPV traf­fic that does not typ­i­cally have a high con­ver­sion rate.

You can also use your own judge­ment just by tak­ing a look at the land­ing page and see­ing if it has fac­tors that will con­vert well with your traf­fic. For exam­ple, I always look for a strong call to action.FPTraf­fic con­verts pretty well because although the land­ing page is some­what weak when it comes to show­ing off all the fea­tures, every sin­gle but­ton is a call to action to get the user to sign up and because of that, a high per­cent­age of peo­ple sign up.

Your Postback is Setup Wrong

This hap­pens every once in awhile and always makes me laugh. A pub­lisher at Peer­Fly will email me say­ing that they are not get­ting con­ver­sions, but they are sure that their traf­fic will con­vert with the offer. I will run their stats on Peer­Fly and see that they have actu­ally had many con­ver­sions and they’re show­ing up fine on the publisher’s account.

The rea­son the pub­lisher is not see­ing the con­ver­sions is because they are only check­ing their 3rd party track­ing soft­ware (Pros­per202, Voluum, CPVLab, etc) and their post­back is setup wrong.

Set­ting up your post­back on Peer­Fly is easy and all of our Affil­i­ate Man­agers are trained to be able to help you get that setup. I also have instruc­tions on how to setup your post­back with all the major track­ing soft­ware solu­tions here.

The Traffic Source Sucks

Unfor­tu­nately, some traf­fic sources just suck. I am not going to list any sources because sources change too. At one point 50onRed was con­vert­ing really well for me, but lately that has not been the case. The user base that traf­fic sources have come and go. 7Search is not a great con­verter, but lately I have had some solid cam­paigns going. How­ever, some traf­fic sources just suck all the time.

I rec­om­mend doing a Google search for any traf­fic source you are inter­ested in try­ing. There will be neg­a­tive com­ments about just about any source, but if the over­whelm­ing response is bad, it might be worth look­ing for some­thing else.

In con­clu­sion, do not get dis­cour­aged if your traf­fic is not con­vert­ing. It hap­pens to most affil­i­ate mar­keters every sin­gle day. Not every cam­paign is going to be a win­ner. Con­sider the options above, but keep try­ing! The win­ner might be a click away.

Let’s make some money!

Blogging Social Media Wordpress
August 18, 2016
, , , ,
How to Create Facebook Instant Articles For Your Blog

How to Create Facebook Instant Articles

If you have been on Face­book over the past few months, you have prob­a­bly noticed their new Instant Arti­cles fea­ture.

Face­book Instant Arti­cles are arti­cles that load within Face­book when they are clicked. Face­book says they load up to 10x faster than nor­mal arti­cles (links) and because of that they get more engage­ment.

I am always inter­ested in try­ing the lat­est trends and get­ting more engage­ment on my con­tent, so I decided to cre­ate Instant Arti­cles for this blog on my Face­book Page. The setup process is fairly easy, but there are a few things required so I decided to cre­ate a guide to help.

Here is a quick guide on how you can do it for your Word­Press blog too.

Instant Articles Example

Create Instant Articles For Your Blog

First, you will need to install the offi­cial Word­Press plugin for Instant Arti­cles cre­ated by Face­book.

Install Instant Articles WordPress Plugin

  1. Click Add New Plugin in your Word­Press Admin
  2. Search for Instant Arti­cles for WP
  3. Click on the Install but­ton for Instant Arti­cles for WP
  4. Acti­vate the Plugin

Click on Instant Arti­cles in your Word­Press Admin side­bar and con­fig­ure the Face­book Instant Arti­cles Set­tings.

Next, you will need to go to your Face­book Page for your blog (don’t have one? Cre­ate a Face­book Page), click Set­tings, and open up the Instant Arti­cles set­tings option.

Instant Article Settings

You should see the Instant Arti­cles Con­fig­u­ra­tion page shown above. Read through the Ini­tial Setup and then fol­low the direc­tions in the first two options of the Tools sec­tion to Claim Your URL and sub­mit your Pro­duc­tion RSS Feed.

Instant Articles Tools

Your RSS Feed URL is setup through the Word­Press plugin you installed in step 1. The URL by default is:

Once you have com­pleted the steps above, you can sub­mit your feed for review. Face­book man­u­ally reviews Instant Arti­cle RSS feeds and once approved, your blog will be setup for Instant Arti­cles!

If you have an iPhone or Android phone, you can install the Face­book Pages Man­ager app and actu­ally test out your Instant Arti­cles before they are approved. This is also the best place to test out a cus­tom style for your Instant Arti­cles.

Instant Article Style

Once Face­book has approved your RSS feed, you are able to pub­lish your links as Instant Arti­cles rather than tra­di­tional post links. You can do this under Instant Arti­cles, Pro­duc­tion in your Face­book PagePub­lish­ing Tools sec­tion.

I have not had Instant Arti­cles setup for this blog long enough yet to tell you whether or not I am see­ing more engage­ment using them, but I am opti­mistic I will.

Let me know if you have any ques­tions or issues set­ting up Instant Arti­cles for your own Word­Press blog.

Good luck!

Affiliate Marketing Social Media
August 18, 2016
Skyrocket Your Twitter Traffic with Twitter Cards

Twitter Cards

Ear­lier this month, I com­pletely revamped one of my viral media sites and one big change I made was set­ting up a Twit­ter Card. Since imple­ment­ing the Twit­ter Card for the site two weeks ago, I have seen a 444.92% increase in traf­fic from Twit­ter!

First, I want to explain what a Twit­ter Card is and then I will show you how you can setup your own Twit­ter Card for your web­site and increase your traf­fic from Twit­ter.

What is a Twitter Card?

I pub­lished an arti­cle about Twit­ter Cards back in 2013 shortly after they were released by Twit­ter. A Twit­ter Card is basi­cally a piece of media you can attach to your tweets when your web­site is tweeted about.

Here is a sam­ple Card of a link to my most recent post on my blog that I tweeted ear­lier yes­ter­day:

Twitter Card Example

This Twit­ter Card is called a Sum­mary Card with Large Image. You can see the big image, title, and descrip­tion of my link. The link is actu­ally removed from my tweet by Twit­ter and replaced with the Card.

The rea­son I am get­ting so much more traf­fic from Twit­ter after adding Twit­ter Cards to my sites is because the large image takes up way more space in the Twit­ter feed. Peo­ple who fol­low me are more likely to see the image and click it.

You can add your own Twit­ter Card to your web­site or blog by adding a few lines of code. These lines of code are called meta tags.

Setting up your Twitter Card

So far, I have setup Twit­ter Cards on my Word­Press blogs and, as I men­tioned above, on my viral media site. With the Word­Press blogs, I sim­ply installed the right plugin and did the Twit­ter Card ver­i­fi­ca­tion. With the viral media site, I had to man­u­ally install the meta tags. Here is how you can do it both with Word­Press and the meta tags.

Twitter Cards with Yoast SEO WP Plugin

I use the Yoast SEO Word­Press Plugin and I highly rec­om­mend that you do too (if you don’t already). In my opin­ion, this is the best SEO plugin avail­able for Word­Press. It has the fringe ben­e­fit of being setup for Twit­ter Cards by default as well.

If you have Yoast SEO installed, your blog is already ready for Twit­ter Cards. To acti­vate your Sum­mary Card with Large Image, all you have to do is val­i­date it using the Twit­ter Card Val­ida­tor.

Twitter Card Validator

Now, when you post a link to your blog on Twit­ter (or any­one else does), you will see the giant Card instead of just the link in the tweet!

Twitter Card Meta Tags

If you need to man­u­ally install Twit­ter Cards on your site, there are 6 dif­fer­ent meta tags that I rec­om­mend you add to the head of your web­site to cre­ate your Twit­ter Card.

  • Type
  • Title
  • Descrip­tion
  • Site
  • Image
  • Cre­ator

Here is the code so you can just copy and paste then replace the dummy text (Your Title, Your Descrip­tion, etc) with your own.

It’s that easy! Once you have added the meta tags, copy/paste the link to the page the tags are on into the Twit­ter Card Val­ida­tor and you’re all set.

Twit­ter Cards have made a huge dif­fer­ence in the traf­fic I am get­ting from Twit­ter. If you don’t have them setup yet, you need to do it now. It takes very lit­tle effort (as shown above) and can more than dou­ble your traf­fic.

Give it a try and let me know how much more traf­fic you get. Let’s make some money!

Blogging Wordpress
August 18, 2016
Time Management Tips for Bloggers

Blog­ging can be very tricky because you need to be able to gen­er­ate the right con­tent ideas and get every­thing pub­lished on-time. You have an enor­mous read­er­ship that is con­stantly look­ing for awe­some con­tent every few days. With the recent changes within Google, cer­tain fac­tors play an even more impor­tant role like fresh con­tent and fre­quency. How­ever, many blog­gers fail to meet the require­ments and much of this can be attrib­uted to poor time man­age­ment. If you’ve been blog­ging for years and need to find a way to keep your blog updated, then fol­low these sim­ple time man­age­ment tips.

These have been proven to work and you should go in order to get the full ben­e­fit. The key is to get used to imple­ment­ing these strate­gies into your blog­ging every time you sit down and write con­tent.


Setting Specific Goals

Hav­ing speci­fic goals out­lined every time you sit down and write con­tent, you’ll be able to cut out all the things that don’t mat­ter. By now, you should know what’s work­ing well and what con­tent doesn’t pro­duce any results. For exam­ple, I con­sider the first few months of blog­ging a trial and error run as you’re essen­tially try­ing to fig­ure out what res­onates well with your read­ers. Here’s some­thing you should con­sider…

If your blog­ging goal is to teach peo­ple how to build links and increase SEO rank­ings, then you should spend less time wor­ry­ing about con­tent reflect­ing,, etc. Hav­ing a clear cut vision will help you decide where you should focus your efforts.

To imple­ment this strat­egy is NOT dif­fi­cult because you need to write down your goals and stick to them going for­ward. Make sure they are embed­ded into your mind until they come nat­u­rally to you.

Planning Posts

When­ever I’m writ­ing con­tent, I always make use of the time in between when I pause to think. For exam­ple, when writ­ing this con­tent, I paused twice for around 5–10 min­utes and started skim­ming the Inter­net. While skim­ming, I was able to find “2” more con­tent ideas for my upcom­ing posts. This way, I’m get­ting a list of ideas ready so I don’t need to worry about research later on. The key is to man­age your time so you can spend more time writ­ing and less time on research.

Additional Research

Some­times, the con­tent you’re writ­ing will require addi­tional research, espe­cially when you’re writ­ing sta­tis­tic based con­tent. Sta­tis­tic based con­tent involves prov­ing what you’ve writ­ten by gath­er­ing data online. Here’s a few exam­ples of data dri­ven con­tent…

  • Best time to post in Twit­ter
  • Best time to send email mes­sages
  • What con­tent is shared most on social plat­forms?

When you need to ded­i­cate time to research, I’ve found the fol­low­ing works very well…

First, doing a quick search in Google will provide you with more than enough data. Next, it’s always good to have the research ready before you start writ­ing so you don’t have to pause in between for lengthy peri­ods. This means ded­i­cat­ing 30–45 min­utes before start­ing to gather the URL’s of the web­sites where charts and sta­tis­tics can be found. Now, when you write con­tent, you know where to go to find what you’re look­ing for.

Write Straight Through

Other than the small pauses you take in between, which is obvi­ously optional, it’s impor­tant once you’re wired in to keep focused. Don’t write for 10 min­utes and then plan to take a break because you’ll lose your train of thought. This means a job that should have taken 45 min­utes takes 90 min­utes because of the unnec­es­sary breaks in between. The point I’m try­ing to make is that when you have to write con­tent, make sure you are wired in from the begin­ning to the end.

Here’s some­thing to keep in mind when writ­ing con­tent…

First, the sooner you can get your con­tent pub­lished, the quicker you can have it pub­lished and start writ­ing other con­tent. This means your read­ers get fresh new con­tent, which increases loy­alty. Next, writ­ing straight through is a great way to learn the dis­ci­pline needed to be an effec­tive blog­ger. Blog­gers know when they have to write con­tent and when they have mis­cel­la­neous time for every­thing else.

To help stream­line your con­tent writ­ing, you can move to a place with­out dis­trac­tions and NO other devices. Turn off your mobile phone and close all other browser win­dows until you get your job done. The funny thing is when I imple­mented the NO dis­trac­tion habit into my con­tent writ­ing, I was able to fit in 1 more post daily, increas­ing my pub­lish­ing from 2 to 3.

Give it a try and see what hap­pens!

Get Someone Else To Edit

As a reg­u­lar blog­ger, I don’t have time to proof read my work so I’ve hired some­one to get the work done for me. Imag­ine writ­ing “3” posts daily and then spend­ing time edit­ing your work. Per­son­ally, it takes me more time to edit than it does to write con­tent. Find­ing some­one to edit your work is a great way for you to stick to writ­ing con­tent and avoid­ing the detailed work of edit­ing. How do you find some­one?

It’s going to take some time to find the right per­son for the job, how­ever, once you do, you’ll save enor­mous time going for­ward. To find the most cost effec­tive per­son, I rec­om­mend look­ing on the fol­low­ing web­sites…


You can find peo­ple all over the world with a solid under­stand­ing of the Eng­lish lan­guage. Some peo­ple work at an hourly rate of $8/hr and can edit 2 arti­cles within that time.

Outsourcing Does Help (Optional)

I know many peo­ple are begin­ning to out­source con­tent, which is NOT a bad idea, depend­ing on your sched­ule. If you’re try­ing to man­age your time between sev­eral projects, then it might NOT be a bad idea to get some­one cre­ative to write con­tent for you. How­ever, you have to do a quick trial and error run before hir­ing them so you know they can do the job. Here are a few cool tips to help you find the right per­son for the job…

  • Make sure they are rel­e­vant to your niche
  • Ask for sam­ples of their work to make sure they have the knowl­edge
  • Nego­ti­ate a good rate because con­tent writ­ing can become expen­sive depend­ing on fre­quency
  • Start with a hand­ful of peo­ple and nar­row down to 1–2 who can get the job done with the qual­ity you’re look­ing for.
August 18, 2016
How Much Money Do You Need For Your SEO Campaign?

I wanted to put this quick post together to out­line a few very impor­tant fac­tors in SEO from an invest­ment stand­point. First, you have to under­stand there are two dif­fer­ent types of SEO cam­paigns you can uti­lize for your web­site. You have the option of FREE link build­ing and then mak­ing use of some paid mar­ket­ing meth­ods that are very use­ful. I’ve always tried the FREE approach because it was cost effec­tive but found that to get the results impor­tant to you, you’ll need to invest money into your cam­paign.

I will like to go over a few quick fac­tors that answer the ques­tion of how much money an effec­tive SEO cam­paign really costs.


My Evaluation

Per­son­ally, I’ve paid close to $600.00–800.00/mo over the last 2 months build­ing links, writ­ing con­tent, tools, etc. It’s impor­tant to note that all my link build­ing is “white-hat” and I don’t uti­lize any “black-hat” tech­niques that Google frowns on. For those of you NOT aware of “black-hat” link build­ing, it cor­re­spond to any strate­gies that Google would find manip­u­la­tive and against Google pol­icy. So, you’re prob­a­bly won­der­ing where I invested most of this money when opti­miz­ing my web­sites. Here’s a quick break­down of the costs and what you should be look­ing to spend going for­ward.

My Websites

It’s impor­tant to note that SEO really starts the sec­ond you pur­chase a domain and host­ing. With­out both of those ele­ments, you won’t be able to estab­lish an online pres­ence. Cur­rently, I have “5” web­sites and the total cost to have them online is roughly $60.00/mo.

This num­ber obvi­ously fluc­tu­ates as I am always adding web­sites to my PBN and for per­sonal use. For you, the num­ber will be dif­fer­ent because you have your own web­sites, either 1, or sev­eral.

The Tools

Cur­rently, I’m using sev­eral tools to track my pro­gress like and keeps track of the DA and PA rel­e­vant to my web­sites and oth­ers I’m think­ing of build­ing links from. is used for keep­ing track of my key­word rank­ings and if they’ve increased or decreased over sev­eral months. Obvi­ously, my objec­tive is to see if my SEO efforts are boost­ing my rank­ings in the SERP’s. Next,

For my web­sites, I’m using speci­fic plug-ins to help pro­tect my web­site. I use as an LSI tool for key­word research when writ­ing con­tent and Vault­Press to backup my web­sites just in case the unfor­tu­nate hap­pens where I lose my Word­Press files.

Here is a quick break­down of the costs…

  • – $5.00
  • – $10.00
  • – $10.00
  • – $99.00

It’s impor­tant to note that these are the tools I find use­ful and you might dis­agree so won’t invest in the same tools. Either way, using these tools has helped stream­line my link build­ing research.

Writ­ing Con­tent

I make use of FREE and paid arti­cle direc­to­ries that all require unique con­tent. Before con­tin­u­ing, it’s impor­tant to note…

I don’t post dupli­cate con­tent and each sub­mis­sion is made once. I invest a lot of money in writ­ing unique, high qual­ity con­tent. Not to men­tion, I write close to 10,000/words a week pro­duc­ing con­tent for my web­sites, guest blog­ging, etc.

I have an awe­some team of con­tent writ­ers who I ONLY use for link build­ing. Guest blog­ging and per­sonal web­site con­tent is pro­duced by me. My team writes close to 50 arti­cles a month for link build­ing, all at dif­fer­ent rates. With every­thing included, I spend around $350.00/mo on con­tent writ­ing.

Here are some of the places I hire con­tent writ­ers…


Authority Link Building

Over the years, I’ve learned a lot about link build­ing and how to effec­tively use it to boost rank­ings. I use a com­bi­na­tion of PBN’s, link direc­to­ries, and arti­cle direc­to­ries through­out the month. It’s impor­tant to men­tion that some are FREE, how­ever, sev­eral of them do cost money, which makes com­plete sense. These direc­to­ries and arti­cle web­sites have a solid rep­u­ta­tion and are mod­er­ated reg­u­larly. I spend close to $200/mo on build­ing links with them, and it aver­ages around $9.99 a sub­mis­sion.

Many of my links are recur­ring as I try and change key­words around and mix things up. In the end, these web­sites are all “white-listed” and have high author­ity within Google. I check all of their…

  • Page Ranks
  • Domain Author­ity
  • Page Author­ity
  • Inter­nal Links
  • Exter­nal Links
  • Trust Flow
  • Cita­tion Flow
  • Link Diver­sity

In the end, I have a solid link pro­file point­ing to all of my web­sites from high author­ity web­sites that dom­i­nate Google already. I’ll invest the money, but I have a solid link pro­file from web­sites that actu­ally provide value in the end. Next, you have to invest this money because alter­na­tive arti­cle direc­to­ries are poorly mod­er­ated. With the recent Google updates, they’ll actu­ally harm your web­sites instead of pro­vid­ing long term ben­e­fits.

Roughly, from the costs I’ve listed above, you can see they aver­age roughly around $600.00–800.00/mo. How­ever, the end result has been amaz­ing because “5” key­words are rank­ing on Google home­page with “4” of them on the 2nd.

Wrapping It Up…

Going for­ward, with the recent Google changes, you’ll have to make sure rel­e­vance and high author­ity web­sites are where you focus your link build­ing efforts. Unfor­tu­nately, many of these web­sites are paid but for a very good rea­son. They are highly mod­er­ated with their own SEO cam­paign, which helps their author­ity in the long run. This just adds more value to your link that is placed on their page. You can try FREE link build­ing direc­to­ries, etc, how­ever, with expe­ri­ence, I’ve achieved much more spend­ing money on effec­tive link build­ing strate­gies.

Apple iOS Programming
August 17, 2016
, ,
UIActivityViewController Tutorial: Sharing Data

A lot of devel­op­ers want to be able to share their app data via email, Mes­sages, or Air­Drop. Shar­ing is a con­ve­nient way for users to send data to each other or between devices – and it may even net you some new cus­tomers!

Luck­ily, since iOS6 Apple has pro­vided the handy but much over­lookedUIActivityViewController class which pro­vides a clean inter­face for shar­ing and manip­u­lat­ing data inside your app. You’re going to learn every­thing you need to know in this UIAc­tiv­i­tyView­Con­troller tuto­rial!

To setup shar­ing inside your app you just have to con­fig­ure a few keys inside your appli­ca­tions Info.plist file and han­dle a few sys­tem call­backs inside your appli­ca­tions AppDel­e­gate. Once setup, iOS can open your app with the URL point­ing to the data to import or export.

Ready for some beer shar­ing fun? Read on.

Getting Started

Start by down­load­ing the starter project for this tuto­rial, which is a mod­i­fied ver­sion of the Beer Tracker app used in a pre­vi­ous tuto­rial. Build and run (Pro­duct \ Run or ⌘R) the project in Xcode; you’ll see the fol­low­ing:

UIActivityViewController tutorial

Well, that’s no good, there are no beers to share. Lets get started so you can start shar­ing won­der­ful beers with every­one.

UTIs and your Plist

The first thing you need to do is set up your Info.plist to let iOS know your app can han­dle Beer Tracker Doc­u­ments. To do this you need to reg­is­ter your app as being able to han­dle cer­tain Uni­form Type Iden­ti­fiers, or UTIs, export­ing any UTIs that are not already known by the sys­tem.

In sum­mary, UTIs are unique iden­ti­fiers that rep­re­sent doc­u­ments. There are UTIs already built-in to iOS for han­dling com­mon doc­u­ment types such as public.jpeg or public.html.

You’re going to reg­is­ter your app to han­dle doc­u­ments with the com.raywenderlich.BeerTracker.btkr UTI rep­re­sent­ing the descrip­tion of a beer. You’ll tell iOS infor­ma­tion about the UTI such as what file name exten­sion it uses, what mime-type it’s encoded as when shar­ing and finally the file’s icon.

So let’s see it in action! Open Info.plist, and add the fol­low­ing entries under the Information Property Listkey:

UIActivityViewController tutorial

You can read up on what each of these value’s mean in Apple’s UTI guide, but here are the impor­tant things to note:

  • The Document types entry defines what UTIs your app sup­ports – in your case, thecom.raywenderlich.BeerTracker.btkr UTI, as an Owner/Editor.
  • The Document types is also where you set the names of the icons iOS should use when dis­play­ing your file type. You’ll need to make sure you have an icon for each of the sizes listed in the plist.
  • The Exported Type UTIs entry gives some infor­ma­tion about com.raywenderlich.BeerTracker.btkr, since it isn’t a pub­lic UTI. Here you define files end­ing in .btkr, or files that have a mime type ofapplication/beertracker can be han­dled by your app.

Believe it or not, by set­ting these keys, you have informed iOS to start send­ing your app files that end with the.btkr exten­sion. You can test this out by email­ing your­self a copy of this sam­ple beer file. Please make sure you unzip the file before email­ing it to your­self oth­er­wise both the file exten­sion UTI and mime type will be wrong.

UIActivityViewController tutorial

You can tap on the attach­ment and it will prompt you to open the beer in the Beer Tracker app. Select­ing Beer Tracker will open the app, how­ever it won’t load the data from the sam­ple file because you haven’t imple­mented the code for that yet.

Now that you are a UTI wiz­ard, lets sprin­kle some magic.

Note: If you don’t see “Copy to Beer Tracker” in the UIActivityViewController dis­played after tap­ping the attached file in your email, you may need to edit the order of sup­ported appli­ca­tions by scrolling to the end of the list, select­ing More, and mov­ings “Copy to Beer Tracker” to the top of the list.

Importing App Data

Before you can han­dle open­ing data from the file, you’ll need some code that can work with the file passed. Open Beer.swift, and replace the importDataFromURL(_:) method with the fol­low­ing:

static func importDataFromURL(url: NSURL) {
  // 1
  guard let dictionary = NSDictionary(contentsOfURL: url),
    beerInfo = dictionary as? [String: AnyObject],
    name = beerInfo[Keys.Name.rawValue] as? String,
    rating = beerInfo[Keys.Rating.rawValue] as? NSNumber else {
  // 2
  let beer = Beer(name: name, note: beerInfo[Keys.Note.rawValue] as? String, rating: rating)
  // 3
  if let base64 = beerInfo[Keys.ImagePath.rawValue] as? String,
    imageData = NSData(base64EncodedString: base64, options: .IgnoreUnknownCharacters),
    image = UIImage(data: imageData) {
  // 4
  // 5
  do {
    try NSFileManager.defaultManager().removeItemAtURL(url)
  } catch {
    print("Failed to remove item from Inbox")

Here’s a step-by-step expla­na­tion of the above code:

  1. Ver­ify the app can read the con­tents of the URL pro­vided to it. Once ver­i­fied make sure the data is of a type the appli­ca­tion expects and has the min­i­mum data set required to import a beer.
  2. Cre­ate a new Beer object with data from the URL.
  3. If image infor­ma­tion exists in the beer data, process the image and per­sist to disk.
  4. Add the new beer to your app’s data
  5. Finally, delete the doc­u­ment that was saved to your app’s sand­box when iOS opened it.
Note: If you don’t delete these files as they come in, your app will con­tinue to grow in size with no way for the user to clear the app’s data – except… delete your app!

Next, when an exter­nal app wants to send your app a file, it does so via the application(_:openURL:options:)method. Open AppDelegate.swift, and add the fol­low­ing code under­neath theapplication(_:didFinishLaunchingWithOptions:) method:

// MARK: - Handle File Sharing
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
  // 1
  guard url.pathExtension == "btkr" else { return false }
  // 2
  // 3
  guard let navigationController = window?.rootViewController as? UINavigationController,
    beerTableViewController = navigationController.viewControllers.first as? BeersTableViewController else {
      return true
  // 4
  return true

Here’s a step-by-step expla­na­tion of the above:

  1. Ver­ify the URL’s exten­sion is btkr since your app only sup­ports files with that exten­sion.
  2. Use the static method on the Beer object you added above to import the data into your app.
  3. Ver­ify the root view con­troller is an instance of a UINavigationController and that its first view con­troller is an instance of BeersTableViewController.
  4. Reload BeersTableViewController‘s table view to show the newly imported beer, then return true to inform iOS that your app suc­cess­fully processed the pro­vided beer infor­ma­tion.
Note: The method still returns true even if the view con­troller hier­ar­chy was not setup cor­rectly. This is done because your app has in fact processed the pro­vided beer infor­ma­tion in step 2 above.

Build and run your app. If all works well you should be able to open the email attach­ment and see the beer imported into your app as shown below.

UIActivityViewController tutorial

Exporting App Data

So far in this UIAc­tiv­i­tyView­Con­troller tuto­rial you’ve added func­tion­al­ity to your app that han­dles import­ing data from other apps, how­ever what if you wish to share your app’s data? You’re in luck here, as Apple has made export­ing data almost as nice as a lime infused cerveza on a hot beach – maybe.

Your app will need code to han­dle brew­ing export­ing your favorite beers. Open Beer.swift and replace the exist­ing exportToFileURL() def­i­n­i­tion with the fol­low­ing:

func exportToFileURL() -> NSURL? {
  // 1
  var contents: [String: AnyObject] = [Keys.Name.rawValue: name, Keys.Rating.rawValue: rating]
  // 2
  if let image = beerImage() {
    let data = UIImageJPEGRepresentation(image, 1)
    contents[Keys.ImagePath.rawValue] = data?.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
  // 3
  if let note = note {
    contents[Keys.Note.rawValue] = note
  // 4
  guard let path = NSFileManager.defaultManager()
     .URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first else {
        return nil
  // 5
  let saveFileURL = path.URLByAppendingPathComponent("/\(name).btkr")
  (contents as NSDictionary).writeToURL(saveFileURL, atomically: true)
  return saveFileURL

Here’s a step-by-step expla­na­tion of the above code:

  1. Cre­ate a Dictionary to hold your basic beer infor­ma­tion.
  2. If you have an image, encode it to a base64 string to save in the dic­tio­nary. This makes it very easy to share your photo and data all in the same file.
  3. If you have notes, save those as well.
  4. Ver­ify your app can access its Doc­u­ments direc­tory with­out error.
  5. Finally, per­sist the dic­tio­nary data to your Doc­u­ments direc­tory, and return the URL to the newly cre­ated file.

Now that you can export Beer data to a file, you’re going to need an easy way to share it. OpenBeerDetailViewController.swift, and replace the imple­men­ta­tion of the share(_:) method with the fol­low­ing:

@IBAction func share(sender: AnyObject) {
  guard let detailBeer = detailBeer,
    url = detailBeer.exportToFileURL() else {
  let activityViewController = UIActivityViewController(
    activityItems: ["Check out this beer I liked using Beer Tracker.", url],
    applicationActivities: nil)
  presentViewController(activityViewController, animated: true, completion: nil)

Here you’ve added a check to ver­ify you have a detail beer, and can retrieve the URL from theexportToFileURL() method. Next, you cre­ate an instance of a UIActivityViewController pass­ing in a mes­sage to be used, if pos­si­ble and the URL to your app’s data file. UIActivityViewController will do all of the heavy lift­ing for you. Since you defined all of the required infor­ma­tion in your Info.plist file, it’ll know just what to do with it. Finally you present the UIActivityViewController to the user.

Note: UIActivityViewController has been around since iOS6, how­ever it is a very use­ful and some­times under appre­ci­ated class. The mes­sage you pass to UIActivityViewController will be used by appli­ca­tions such as Mail and Mes­sages, adding this to the body of your mes­sage.

Build and run your app, open a beer, and try to share. You’ll notice that you see a few options avail­able to you as shown below:

UIActivityViewController tutorial

Per­haps one the best options is the abil­ity to Air­Drop. If you have two devices – or bet­ter yet, a friend with the Beer Tracker app installed – you can test Air­Drop­ping beers!

UIActivityViewController tutorial

Where To Go From Here?

You can down­load the fin­ished project from this UIAc­tiv­i­tyView­Con­troller tuto­rial here.

Now that you know how iOS imports and exports app data, you’re ready to take this knowl­edge and start shar­ing your favorite beers or any other data of your choice.

In the mean­time, if you have any ques­tions or com­ments about this tuto­rial or Shar­ing Data in gen­eral, please join the forum dis­cus­sion below!

Apple MacOSX Programming
August 17, 2016
Drag and Drop Tutorial for macOS

Ever since the inven­tion of the Mac, drag and drop has been a part of the user inter­face. The quin­tes­sen­tial exam­ple is in Finder, where you can drag files around to orga­nize things or drop them in the trash.

The fun doesn’t stop there.

You can drag your lat­est sun­set panorama from Pho­tos and drop it in Mes­sages, or pull a file from Down­loads on the dock and drop it right in an email. You get the point, right? It’s cool and an inte­gral part of the macOS expe­ri­ence.

Drag and drop has come a long way from its begin­nings and now you can drag almost any­thing any­where. Try it and you might be pleas­antly sur­prised by the actions and types sup­ported by your favorite apps.

In this drag and drop tuto­rial for macOS, you’ll dis­cover how to add sup­port to your own apps, so users can get the full Mac expe­ri­ence with your app.

Along the way, you’ll learn how to:

  • Imple­ment core drag and drop actions on NSView sub­classes
  • Accept data dropped from other appli­ca­tions
  • Provide data to be dragged into other views of your app
  • Cre­ate cus­tom dragged types

Getting Started

This project uses Swift 3 and requires, at a min­i­mum, Xcode 8 beta 6. Down­load the starter project, open it in Xcode and build and run it.


Meet the Project App

Many kids enjoy play­ing with stick­ers and mak­ing cool col­lages with them, so you’re going to build an app that fea­tures this expe­ri­ence. You can drag images onto a sur­face and then you can kick things up a few notches by adding sparkles and uni­corns to the view.

After all, who doesn’t like uni­corns and sparkles? :]

To keep you focused on the objec­tive — build­ing out sup­port for drag­ging and drop­ping — the starter project comes com­plete with the views you’ll require. All you need to do is learn about the mechan­ics of drag and drop.

There are three parts to the project win­dow:

  • The sticker view where you’ll drag images and other things
  • Two smaller views that you’ll turn into dif­fer­ent drag­ging sources

Take a look at the project.

You’ll edit four speci­fic files as you work through this tuto­rial, and they’re in two places: Drag­ging Des­ti­na­tionsand Drag­ging Sources:

In Drag­ging Des­ti­na­tions:

  • StickerBoardViewController.swift: the main view con­troller
  • DestinationView.swift: view that sits on top of the upper sec­tion of the win­dow — it will be the recip­i­ent of your drag actions


In Drag­ging Sources:

  • ImageSourceView.swift: bot­tom view with the uni­corn image that you’ll turn into a drag­ging source
  • AppActionSourceView.swift: view that has the label Sparkles — you’ll turn this into another type of drag­ging source

There are some other files in the Draw­ing and Other Stuff groups that provide helper meth­ods and are essen­tial to the project app, but you won’t need to give them any of your time. Go ahead and explore if you’d like to see how this thing is built!

Pasteboards and Dragging Sessions

Drag and drop involves a source and a des­ti­na­tion.

You drag an item from a source, which needs to imple­ment the NSDraggingSource pro­to­col. Then you drop it into a des­ti­na­tion, which must imple­ment the NSDraggingDestination pro­to­col in order to accept or reject the items received. NSPasteboard is the class that facil­i­tates the exchange of data.

The whole process is known as a drag­ging ses­sion:

dragging session macroscopic view

When you drag some­thing with your mouse, e.g., a file, the fol­low­ing hap­pens:

  1. A drag­ging ses­sion kicks off when you ini­ti­ate a drag.
  2. Some data bits — often an image and URL — are cho­sen to rep­re­sent the infor­ma­tion placed on the drag­ging paste­board.
  3. You drop that image on a des­ti­na­tion, which chooses to reject or accept it and take some action — for instance, move the file to a new folder.
  4. The drag­ging ses­sion con­cludes.

That’s pretty much the gist of it. It’s a pretty sim­ple con­cept!

First up is cre­at­ing a drag­ging des­ti­na­tion for receiv­ing images from Finder or any other app.

Creating a Dragging Destination

A drag­ging des­ti­na­tion is a view or win­dow that accepts types of data from the drag­ging paste­board. You cre­ate a drag­ging des­ti­na­tion by adopt­ing NSDraggingDestination.

This dia­gram shows the anatomy of a drag­ging ses­sion from the point of view of a drag­ging des­ti­na­tion.
dragging session

There are a few steps involved in cre­at­ing the des­ti­na­tion:

  1. When build­ing the view, you have to declare the types that it should receive from any drag­ging ses­sion.
  2. When a drag­ging image enters the view, you need to imple­ment logic to allow the view to decide whether to use the data as well as let the drag­ging ses­sion know its deci­sion.
  3. When the drag­ging image lands on the view, you use data from the drag­ging paste­board to show it on your view.

Time to get down with some code­ness!

Select DestinationView.swift in the project nav­i­ga­tor and locate the fol­low­ing method:

func setup() {

Replace it with this:

var acceptableTypes: Set<String> { return [NSURLPboardType] }
func setup() {
  register(forDraggedTypes: Array(acceptableTypes))

This code defines a set with the sup­ported types. In this case, URLs. Then, it calls register(forDraggedTypes:)to accept drags that con­tain those types.

Add the fol­low­ing code into DestinationView to ana­lyze the drag­ging ses­sion data:

let filteringOptions = [NSPasteboardURLReadingContentsConformToTypesKey:NSImage.imageTypes()]
func shouldAllowDrag(_ draggingInfo: NSDraggingInfo) -> Bool {
  var canAccept = false
  let pasteBoard = draggingInfo.draggingPasteboard()
  if pasteBoard.canReadObject(forClasses: [NSURL.self], options: filteringOptions) {
    canAccept = true
  return canAccept

You’ve done a few things in here:

  1. Cre­ated a dic­tio­nary to define the desired URL types (images).
  2. Got a ref­er­ence to the drag­ging paste­board from the drag­ging ses­sion info.
  3. Asked paste­board if it has any URLs and whether those URLs are ref­er­ences to images. If it has images, it accepts the drag. Oth­er­wise, it rejects it.

NSDraggingInfo is a pro­to­col that declares meth­ods to sup­ply infor­ma­tion about the drag­ging ses­sion. You don’t cre­ate them, nor do you store them between events. The sys­tem cre­ates the nec­es­sary objects dur­ing the drag­ging ses­sion.

You can use this infor­ma­tion to provide feed­back to the drag­ging ses­sion when the app receives the image.

NSView con­forms to NSDraggingDestination, so you need to over­ride the draggingEntered(_:) method by adding this code inside the DestinationView class imple­men­ta­tion:

var isReceivingDrag = false {
  didSet {
    needsDisplay = true
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
  let allow = shouldAllowDrag(sender)
  isReceivingDrag = allow
  return allow ? .copy : NSDragOperation()

This is what the code above does:

  1. Cre­ates a prop­erty named isReceivingDrag to track when the drag­ging ses­sion is inside the view and has data that you want. It trig­gers a redraw on the view each time it is set.
  2. Over­rides the draggingEntered(_:) , and decides if it accepts the drag oper­a­tion.

In sec­tion two, the method needs to return an NSDragOperation. You have prob­a­bly noticed how the mouse pointer changes depend­ing on the keys you hold down or the des­ti­na­tion of a drag.

For exam­ple, if you hold down Option dur­ing a Finder drag, the pointer gains a green + sym­bol to show you a file copy is about to hap­pen. This value is how you con­trol those pointer changes.

In this con­fig, if the drag­ging paste­board has an image then it returns .copy to show the user that you’re about to copy the image. Oth­er­wise it returns NSDragOperation() if it doesn’t accept the dragged items.

Handling an Exit

What enters the view may also exit, so the app needs to react when a drag­ging ses­sion has exited your view with­out a drop. Add the fol­low­ing code:

override func draggingExited(_ sender: NSDraggingInfo?) {
  isReceivingDrag = false

You’ve over­rid­den draggingExited(_:) and set the isReceivingDrag vari­able to false.

Tell the User What’s Happening

You’re almost done with the first stretch of cod­ing! Users love to see a visual cue when some­thing is hap­pen­ing in the back­ground, so the next thing you’ll add is a lit­tle draw­ing code to keep your user in the loop.

Still in DestinationView.swift, find draw(:_) and replace it with this.

override func draw(_ dirtyRect: NSRect) {
  if isReceivingDrag {
    let path = NSBezierPath(rect:bounds)
    path.lineWidth = Appearance.lineWidth

This code draws a sys­tem-col­ored bor­der when a valid drag enters the view. Aside from look­ing sharp, it makes your app con­sis­tent with the rest of the sys­tem by pro­vid­ing a visual when it accepts a dragged item.

Note: Want to know more about cus­tom draw­ing? Check out our Core Graph­ics on macOS Tuto­rial.

Build and run then try drag­ging an image file from Finder to Stick­er­Drag. If you don’t have an image handy, usesample.jpg inside the project folder.


You can see that the cur­sor picks up a + sym­bol when inside the view and that the view draws a bor­der around it.

When you exit the view, the bor­der and + dis­ap­pears; absolutely noth­ing hap­pens when you drag any­thing but an image file.

Wrap up the Drag

Now, on to the final step for this sec­tion: You have to accept the drag, process the data and let the drag­ging ses­sion know that this has occurred.

Append the DestinationView class imple­men­ta­tion with the fol­low­ing:

override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool {
  let allow = shouldAllowDrag(sender)
  return allow

The sys­tem calls the above method when you release the mouse inside the view; it’s the last chance to reject or accept the drag. Return­ing false will reject it, caus­ing the drag image to slide back to its orig­i­na­tion. Return­ingtrue means the view accepts the image. When accepted, the sys­tem removes the drag image and invokes the next method in the pro­to­col sequence: performDragOperation(_:).

Add this method to DestinationView:

override func performDragOperation(_ draggingInfo: NSDraggingInfo) -> Bool {
  isReceivingDrag = false
  let pasteBoard = draggingInfo.draggingPasteboard()
  let point = convert(draggingInfo.draggingLocation(), from: nil)
  if let urls = pasteBoard.readObjects(forClasses: [NSURL.self], options:filteringOptions) as? [URL], urls.count > 0 {
    delegate?.processImageURLs(urls, center: point)
    return true
  return false

Here’s what you’re doing in there:

  1. Reset isReceivingDrag flag to false.
  2. Con­vert the win­dow-based coor­di­nate to a view-rel­a­tive coor­di­nate.
  3. Hand off any image URLs to the del­e­gate for pro­cess­ing, and return true — else you reject the drag oper­a­tion return­ing false.

Note: Feel­ing extra heroic? If you were to make an ani­mated drop sequence, performDragOperation(:_)would be the best place to start the ani­ma­tion.

Con­grat­u­la­tions! You’ve just fin­ished the first sec­tion and have done all the work DestinationView needs to receive a drag.

Use DestinationView’s Data

Next up you’ll use the data that DestinationView pro­vides in its del­e­gate.

Open StickerBoardViewController.swift and intro­duce your­self to the class that is the del­e­gate ofDestinationView.

To use it prop­erly, you need to imple­ment the DestinationViewDelegate method that places the images on the tar­get layer. Find processImage(_:center:) and replace it with this.

func processImage(_ image: NSImage, center: NSPoint) {
  invitationLabel.isHidden = true
  let constrainedSize = image.aspectFitSizeForMaxDimension(Appearance.maxStickerDimension)
  let subview = NSImageView(frame:NSRect(x: center.x - constrainedSize.width/2, y: center.y - 
constrainedSize.height/2, width: constrainedSize.width, height: constrainedSize.height))
  subview.image = image
  let maxrotation = CGFloat(arc4random_uniform(Appearance.maxRotation)) - Appearance.rotationOffset
  subview.frameCenterRotation = maxrotation

This code does the fol­low­ing tricks:

  1. It hides the Drag Images Here label.
  2. It fig­ures out the max­i­mum size for the dropped image while hold­ing the aspect ratio con­stant.
  3. It con­structs a sub­view with that size, cen­ters it on the drop point and adds it to the view hier­ar­chy.
  4. It ran­domly rotates the view a lit­tle bit for a bit of funk­i­ness.

With all that in place, you’re ready to imple­ment the method so it deals with the image URLs that get dragged into the view.
Replace processImageURLs(_:center:) method with this:

func processImageURLs(_ urls: [URL], center: NSPoint) {
  for (index,url) in urls.enumerated() {
    if let image = NSImage(contentsOf:url) {
      var newCenter = center
      if index > 0 {
        newCenter = center.addRandomNoise(Appearance.randomNoise)
      processImage(image, center:newCenter)

What you’re doing here is:

  1. Cre­at­ing an image with the con­tents from the URLs.
  2. If there is more than one image, this off­sets the images’ cen­ters a bit to cre­ate a lay­ered, ran­dom­ized effect.
  3. Pass the image and cen­ter point to the pre­vi­ous method so it can add the image to the view.

Now build and run then drag an image file (or sev­eral) to the app win­dow. Drop it!


Look at that board of images just wait­ing to be made fear­lessly fan­ci­ful.

You’re at about the halfway point and have already explored how to make any view a drag­ging des­ti­na­tion and how to com­pel it to accept a stan­dard drag­ging type — in this case, an image URL.

Intermission: let's all go to the lobby and get ourselves some drinks. And snacks. And new iMacs

Creating a Dragging Source

You’ve played around with the receiv­ing end, but how about the giv­ing end?

In this sec­tion, you’ll learn how to super­charge your app with the abil­ity to be the source by let­ting those uni­corns and sparkles break free and bring glee to the users’ images in the right cir­cum­stances.

All drag­ging sources must con­form to the NSDraggingSource pro­to­col. This MVP (most valu­able player) takes the task of plac­ing data (or a promise for that data) for one or more types on the drag­ging paste­board. It also sup­plies a drag­ging image to rep­re­sent the data.

When the image finally lands on its tar­get, the des­ti­na­tion unar­chives the data from the paste­board. Alter­na­tively, the drag­ging source can ful­fil the promise of pro­vid­ing the data.

You’ll need to sup­ply the data of two dif­fer­ent types: a stan­dard Cocoa type (an image) and cus­tom type that you cre­ate.

Supplying a Standard Dragging Type

The drag­ging source will be ImageSourceView — the class of the view that has the uni­corn. Your objec­tive is sim­ple: get that uni­corn onto your col­lage.

The class needs to adopt the nec­es­sary pro­to­cols NSDraggingSource and NSPasteboardItemDataProvider, so open ImageSourceView.swift and add the fol­low­ing exten­sions:

// MARK: - NSDraggingSource
extension ImageSourceView: NSDraggingSource {
  func draggingSession(_ session: NSDraggingSession, sourceOperationMaskFor context: NSDraggingContext) -> NSDragOperation {
    return .generic
// MARK: - NSDraggingSource
extension ImageSourceView: NSPasteboardItemDataProvider {
  func pasteboard(_ pasteboard: NSPasteboard?, item: NSPasteboardItem, provideDataForType type: String) {
    //TODO: Return image data
  1. This method is required by NSDraggingSource. It tells the drag­ging ses­sion what sort of oper­a­tion you’re attempt­ing when the user drags from the view. In this case it’s a generic oper­a­tion.
  2. This imple­ments the manda­tory NSPasteboardItemDataProvider method. More on this soon — for now it’s just a stub.

Start a Dragging Session

In a real world project, the best moment to ini­ti­ate a drag­ging ses­sion depends on your UI.

With the project app, this par­tic­u­lar view you’re work­ing in exists for the sole pur­pose of drag­ging, so you’ll start the drag on mouseDown(with:).

In other cases, it may be appro­pri­ate to start in the mouseDragged(with:) event.

Add this method inside the ImageSourceView class imple­men­ta­tion:

override func mouseDown(with theEvent: NSEvent) {
  let pasteboardItem = NSPasteboardItem()
  pasteboardItem.setDataProvider(self, forTypes: [kUTTypeTIFF])
  let draggingItem = NSDraggingItem(pasteboardWriter: pasteboardItem)
  draggingItem.setDraggingFrame(self.bounds, contents:snapshot())
  beginDraggingSession(with: [draggingItem], event: theEvent, source: self)

Things get rolling when the sys­tem calls mouseDown(with:) when you click on a view. The base imple­men­ta­tion does noth­ing, elim­i­nat­ing the need to call super. The code in the imple­men­ta­tion does all of this:

  1. Cre­ates an NSPasteboardItem and sets this class as its data provider. A NSPasteboardItem is the box that car­ries the info about the item being dragged. The NSPasteboardItemDataProvider pro­vides data upon request. In this case you’ll sup­ply TIFF data, which is the stan­dard way to carry images around in Cocoa.
  2. Cre­ates a NSDraggingItem and assigns the paste­board item to it. A drag­ging item exists to provide the drag image and carry one paste­board item, but you don’t keep a ref­er­ence to the item because of its lim­ited lifes­pan. If needed, the drag­ging ses­sion will recre­ate this object. snapshot() is one of the helper meth­ods men­tioned ear­lier. It cre­ates an NSImage of an NSView.
  3. Starts the drag­ging ses­sion. Here you trig­ger the drag­ging image to start fol­low­ing your mouse until you drop it.

Build and run. Try to drag the uni­corn onto the top view.


An image of the view fol­lows your mouse, but it slides back on mouse up because DestinationView doesn’t accept TIFF data.

Take the TIFF

In order to accept this data, you need to:

  1. Update the reg­is­tered types in setup() to accept TIFF data
  2. Update shouldAllowDrag() to accept the TIFF type
  3. Update performDragOperation(_:) to take the image data from the paste­board

Open DestinationView.swift.

Replace the fol­low­ing line:

var acceptableTypes: Set<String> { return [NSURLPboardType] }

With this:

var nonURLTypes: Set<String>  { return [String(kUTTypeTIFF)] }
var acceptableTypes: Set<String> { return nonURLTypes.union([NSURLPboardType]) }

You’ve just reg­is­tered the TIFF type like you did for URLs and cre­ated a sub­set to use next.

Next, go to shouldAllowDrag(:_), and add find the return canAccept method. Enter the fol­low­ing just above thereturn state­ment:

else if let types = pasteBoard.types, nonURLTypes.intersection(types).count > 0 {
  canAccept = true

Here you’re check­ing if the nonURLTypes set con­tains any of the types received from the paste­board, and if that’s the case, accepts the drag oper­a­tion. Since you added a TIFF type to that set, the view accepts TIFF data from the paste­board.

Unarchive the Image Data

Lastly, update performDragOperation(_:) to unar­chive the image data from the paste­board. This bit is really easy.

Cocoa wants you to use paste­boards and pro­vides an NSImage ini­tial­izer that takes NSPasteboard as a para­me­ter. You’ll find more of these con­ve­nience meth­ods in Cocoa when you start explor­ing drag and drop more.

Locate performDragOperation(_:), and add the fol­low­ing code at the end, just above the return sen­tence return false:

else if let image = NSImage(pasteboard: pasteBoard) {
  delegate?.processImage(image, center: point)
  return true

This extracts an image from the paste­board and passes it to the del­e­gate for pro­cess­ing.

Build and run, and then drag that uni­corn onto the sticker view.


You’ll notice that now you get a green + on your cur­sor.

The des­ti­na­tion view accepts the image data, but the image still slides back when you drop. Hmmm. What’s miss­ing here?

Show me the Image Data!

You need to get the drag­ging source to sup­ply the image data — in other words: ful­fil its promise.

Open ImageSourceView.swift and replace the con­tents of pasteboard(_:item:provideDataForType:) with this:

if let pasteboard = pasteboard, type == String(kUTTypeTIFF), let image = NSImage(named:"unicorn") {
  let finalImage = image.tintedImageWithColor(NSColor.randomColor())
  let tiffdata = finalImage.tiffRepresentation
  pasteboard.setData(tiffdata, forType:type)

In this method, the fol­low­ing things are hap­pen­ing:

  1. If the desired data type is kUTTypeTIFF, you load an image named uni­corn.
  2. Use one of the sup­plied helpers to tint the image with a ran­dom color. After all, col­or­ful uni­corns are more fes­tive than a smat­ter­ing of all-black uni­corns. :]
  3. Trans­form the image into TIFF data and place it on the paste­board.

Build and run, and drag the uni­corn onto the sticker view. It’ll drop and place a col­ored uni­corn on the view. Great!



Dragging Custom Types

Uni­corns are pretty fab­u­lous, but what good are they with­out mag­i­cal sparkles? Strangely, there’s no stan­dardCocoa data type for sparkles. I bet you know what comes next. :]


Note: In the last sec­tion you sup­plied a stan­dard data type. You can explore the types for stan­dard data in the API ref­er­ence.

In this sec­tion you’ll invent your own data type.

These are the tasks on your to-do list:

  1. Cre­ate a new drag­ging source with your cus­tom type.
  2. Update the drag­ging des­ti­na­tion to rec­og­nize that type.
  3. Update the view con­troller to react to that type.

Create the Dragging Source

Open AppActionSourceView.swift. It’s mostly empty except for this impor­tant def­i­n­i­tion:

enum SparkleDrag {
  static let type = "com.razeware.StickerDrag.AppAction"
  static let action = "make sparkles"

This defines your cus­tom drag­ging type and action iden­ti­fier.

Drag­ging source types must be Uni­form Type Iden­ti­fiers. These are reverse-coded name paths that describe a data type.

For exam­ple, if you print out the value of kUTTypeTIFF you’ll see that it is the string public.tiff.

To avoid a col­li­sion with an exist­ing type, you can define the iden­ti­fier like this: bundle iden­ti­fier + AppAction. It is an arbi­trary value, but you keep it under the pri­vate name­space of the appli­ca­tion to min­i­mize the risk of using an exist­ing name.

If you attempt to con­struct a NSPasteboardItem with a type that isn’t UTI, the oper­a­tion will fail.

Now you need to make AppActionSourceView adopt NSDraggingSource. Open AppActionSourceView.swift and add the fol­low­ing exten­sion:

// MARK: - NSDraggingSource
extension AppActionSourceView: NSDraggingSource {
  func draggingSession(_ session: NSDraggingSession, sourceOperationMaskFor
    context: NSDraggingContext) -> NSDragOperation {
    switch(context) {
    case .outsideApplication:
      return NSDragOperation()
    case .withinApplication:
      return .generic

This code block dif­fers from ImageSourceView because you’ll place pri­vate data on the paste­board that has no mean­ing out­side the app. That’s why you’re using the context para­me­ter to return a NSDragOperation() when the mouse is dragged out­side your appli­ca­tion.

You’re already famil­iar with the next step. You need to over­ride the mouseDown(with:) event to start a drag­ging ses­sion with a paste­board item.

Add the fol­low­ing code into the AppActionSourceView class imple­men­ta­tion:

override func mouseDown(with theEvent: NSEvent) {
  let pasteboardItem = NSPasteboardItem()
  pasteboardItem.setString(SparkleDrag.action, forType: SparkleDrag.type)
  let draggingItem = NSDraggingItem(pasteboardWriter: pasteboardItem)
  draggingItem.setDraggingFrame(self.bounds, contents:snapshot())
  beginDraggingSession(with: [draggingItem], event: theEvent, source: self)

What did you do in there?

You con­structed a paste­board item and placed the data directly inside it for your cus­tom type. In this case, the data is a cus­tom action iden­ti­fier that the receiv­ing view may use to make a deci­sion.

You can see how this dif­fers from ImageSourceView in one way. Instead of defer­ring data gen­er­a­tion to the point when the view accepts the drop with the NSPasteboardItemDataProvider pro­to­col, the dragged data goes directly to the paste­board.

Why would you use the NSPasteboardItemDataProvider pro­to­col? Because you want things to move as fast as pos­si­ble when you start the drag ses­sion in mouseDown(with:).

If the data you’re mov­ing takes too long to con­struct on the paste­board, it’ll jam up the main thread and frus­trate users with a per­cep­ti­ble delay when they start drag­ging.

In this case, you place a small string on the paste­board so that it can do it right away.

Accept the New Type

Next, you have to let the des­ti­na­tion view accept this new type. By now, you already know how to do it.

Open DestinationView.swift and add SparkleDrag.type to the reg­is­tered types. Replace the fol­low­ing line:

var nonURLTypes: Set<String>  { return [String(kUTTypeTIFF)] }

With this:

var nonURLTypes: Set<String>  { return [String(kUTTypeTIFF),SparkleDrag.type] }

Now Sparkle­Drags are accept­able!

performDragOperation(:_) needs a new else-if clause, so add this code at the end of the method just beforereturn false:

else if let types = pasteBoard.types, types.contains(SparkleDrag.type),
  let action = pasteBoard.string(forType: SparkleDrag.type) {
  delegate?.processAction(action, center:point)
  return true

This addi­tion extracts the string from the paste­board. If it cor­re­sponds to your cus­tom type, you pass the action back to the del­e­gate.

You’re almost done, you just need to update StickerBoardViewController to deal with the action instruc­tion.

Handle the Action Instruction

Open StickerBoardViewController.swift and replace processAction(_:center:) with this:

func processAction(_ action: String, center: NSPoint) {
  if action == SparkleDrag.action  {
    invitationLabel.isHidden = true
    if let image = NSImage(named:"star") {
      for _ in 1..<Appearance.numStars {
        let maxSize:CGFloat = Appearance.maxStarSize
        let sizeChange = CGFloat(arc4random_uniform(Appearance.randonStarSizeChange))
        let finalSize = maxSize - sizeChange
        let newCenter = center.addRandomNoise(Appearance.randomNoiseStar)
        let imageFrame = NSRect(x: newCenter.x, y: newCenter.y, width: finalSize , height: finalSize)
        let imageView = NSImageView(frame:imageFrame)
        let newImage = image.tintedImageWithColor(NSColor.randomColor())
        imageView.image = newImage

The above code does the fol­low­ing:

  1. Only responds to the known sparkle action
  2. Loads a star image from the bundle
  3. Makes some copies of the star image and… 
    1. Gen­er­ates some ran­dom num­bers to alter the star posi­tion.
    2. Cre­ates an NSImageView and sets its frame.
    3. Gives the image a ran­dom color — unless you’re going for a David Bowie trib­ute, black stars are a bit gothic.
    4. Places the image on the view.

Build and run. Now you can drag from the sparkles view onto the sticker view to add a spray of stars to your view.


Where to go From Here?

Con­grat­u­la­tions, you cre­ated a cus­tom drag and drop inter­face in your very own app!

You can use the Save Image To Desk­top but­ton to save your image as a JPG with the name Stick­er­Drag. Maybe take it a step fur­ther and tweet it to the team @rwenderlich.

Here’s the source code for the the com­pleted project.

This drag and drop tuto­rial for macOS cov­ered the basics of the Cocoa drag and drop mech­a­nism, includ­ing:

  • Cre­at­ing a drag­ging des­ti­na­tion and accept­ing sev­eral dif­fer­ent types of data
  • Using the drag­ging ses­sion life­cy­cle to provide user feed­back of the drag oper­a­tion
  • Decod­ing infor­ma­tion from the paste­board
  • Cre­at­ing a drag­ging source and pro­vid­ing deferred data
  • Cre­at­ing a drag­ging source that pro­vides a cus­tom data type

Now you have the knowl­edge and expe­ri­ence needed to sup­port drag and drop in any macOS app.

There’s cer­tainly more to learn.

You could study up on how to apply effects, such as chang­ing the drag­ging image dur­ing the drag or imple­ment­ing an ani­mated drop tran­si­tion, or work­ing with promised files — Pho­tos is one appli­ca­tion that places promised data on the drag­ging paste­board.

Another inter­est­ing topic is how to use drag and drop with NSTableView and NSOutlineView, which work in slightly dif­fer­ent ways. Learn about it from the fol­low­ing resources:

Apple iOS
August 17, 2016
, , ,
TestFlight Tutorial: iOS Beta Testing

Test­Flight Beta Test­ing is an Apple pro­duct that makes it easy to invite users to test your iOS, watchOS and tvOS apps before you release them into the App Store. This Test­Flight tuto­rial will walk you through inte­grat­ing Test­Flight into your own apps.

This is one of those rare tuto­ri­als where you don’t have to code — just fol­low through the steps in this tuto­rial and you’ll be up and run­ning with Test­Flight in no time! :]

Getting Started

Don’t have an app right now but still want to pro­ceed with this tuto­rial? No prob­lem! Down­load our beloved Flappy Felipe project and use that as you fol­low along. Make sure you change the bundle ID of the app to your own unique ID; for exam­ple, com.yourname.FlappyFelipe.

This tuto­rial assumes that your app is set up for pro­vi­sion­ing, and has an app ID cre­ated in both the devel­oper por­tal and on iTunes Con­nect.

This setup is out­side the scope of this tuto­rial, but you should be able to use auto­matic pro­vi­sion­ing as described in Set­ting up Xcode to auto­mat­i­cally man­age your pro­vi­sion­ing pro­files, and cre­ate a record in iTunes by fol­low­ing the instruc­tions in Cre­ate an iTunes Con­nect Record for Test­Flight Beta Test­ing in the iTunes Con­nect Devel­oper Guide.

Submitting your Build to iTunes Connect

Open up your project in Xcode, make sure you have a cor­rect Bundle Iden­ti­fier, that your Team ID is set, and that you’ve cho­sen the auto­matic Dis­tri­b­u­tion Cer­tifi­cate Code Sign­ing Iden­tity:

AA- Code Signing

Choose Product\Archive from the top tool­bar:

2- Product Archive

Once Xcode fin­ishes archiv­ing your project, click the shiny blue Upload to App Store… but­ton:

3- Archive

Now you need to choose your devel­op­ment team:

4- Choose Team

Finally, click Upload:

5- Submit

Wait for a few min­utes as your build uploads. Grab a cof­fee, per­haps, or if you have a slow inter­net con­nec­tion, go grab a bite. Or two. Or three. :]


Once you’re done, you should receive a suc­cess mes­sage like the fol­low­ing:


That’s all the work required for Xcode. Your beta build is now avail­able on iTunes Con­nect, and that’s where you’ll be doing the rest of the work to set up Test­Flight.

Create and Enable Testers to Beta Test Apps

Your build is ready for test­ing, but who’s going to test it?

Apple defines two types of testers for Test Flight:

    • Inter­nal Tester: This is an iTunes Con­nect user that has an Admin, Tech­ni­cal, App Man­ager, Devel­oper, or Mar­keter role with access to your app. This is usu­ally a team mem­ber or a client you’re devel­op­ing an app for. You can add up to 25 inter­nal testers.
Note: Apple are in the process of dep­re­cat­ing the Tech­ni­cal role in favour of the App Man­ager role. At the time of writ­ing both roles are avail­able. If you have exist­ing users with the Tech­ni­cal role you should start migrat­ing them over to the App Man­ager role. New users should be cre­ated with the App Man­ager role.
  • Exter­nal Tester: This is any user out­side of your team that wants to test your app. An exter­nal tester has no access to your iTunes Con­nect account in any way, and can only down­load and install the app. You can add up to 2000 exter­nal testers.

Before your exter­nal devel­op­ers can test your app, you have to sub­mit your app to be reviewed by Apple, just as you would with a nor­mal App Store sub­mis­sion. These reviews tend to go faster than nor­mal app reviews (although don’t count on it), and once it’s approved you can let exter­nal devel­op­ers test your app. Inter­nal testers, on the other hand, are instan­ta­neously able to test new builds.

You’ll learn more about exter­nal testers later, but for now, let’s focus on inter­nal testers.

To add an inter­nal tester, head to the Users and Roles sec­tion in iTunes Con­nect:

10 - Users and roles

On the Users and Roles screen, click the + but­ton to add a new user:

11- Users

Fill in your new user info and click Next:

15- Roles

You’ll need to use a unique email address for your new user. If you don’t have a sec­ond email account, you can usu­ally append +whateveryouwant to the first part of your email address, and the email will still get deliv­ered to you. For exam­ple

Now you need to assign roles for the user. In most cases, you’ll want to choose App Man­ager. You can read more about the priv­i­leges for each role and choose the appro­pri­ate one for your user.

Note: The num­ber of dif­fer­ent roles and per­mis­sions can be a bit daunt­ing! When it comes to Test Flight, use this handy table to help you.
AdminApp Man­agerDevel­operMar­keterSales
Can be an Inter­nal Tester
Can Upload a Build
Can Sub­mit a Build for Exter­nal Test­ing

Once that’s done, click Next:

16- Roles

Choose the type of noti­fi­ca­tions you want your new testers to receive, then click Save:

17- notifications

Your user is now cre­ated, but as the mes­sage indi­cates, that user first needs to ver­ify his or her email address before the account will show in iTunes Con­nect.

Cre­at­ing a new inter­nal beta tester is only the first part of the process. The remain­ing step is to invite this par­tic­u­lar tester to test your lat­est build.

It’s time to enable your app for test­ing — so the tester actu­ally has some­thing to test! :]

Starting Beta Testing

To start beta test­ing of your app, go to the My Apps sec­tion on the iTunes Con­nect home page and click on your app:

20- connect

Select the Activ­ity tab and you’ll find your lat­est build. Make sure it is no longer marked as pro­cess­ing. If it is, go make another cup of cof­fee and come back later. :]


Next, click on the Test­Flight tab, then Inter­nal Test­ing in the left hand menu. Click Select Ver­sion to Test and choose the ver­sion you just uploaded. Finally, click Start Test­ing and, in the con­fir­ma­tion pop-up, Start Test­ingagain.

Start Testing
All selected testers will now receive an email that lets them down­load and install this build from the Test­Flight app. There are detailed instruc­tions on how to do that in the last sec­tion of this tuto­rial.

That takes care of inter­nal testers, but what about exter­nal testers?

That’s just as easy! First, go to the Test Infor­ma­tion tab and fill in your Feed­back Email, Mar­ket­ing URL and Pri­vacy Pol­icy URL. You can choose to add a License Agree­ment at this stage as well if you wish, but it is not nec­es­sary.

30- TestInformation

Next, go to the Exter­nal Test­ing tab, click the + but­ton and select Add New Testers:

32- External

Add the email addresses of any exter­nal users you want to add. Once you’re fin­ished, click Add to add these testers to your account. All exter­nal testers will count toward your 2000 exter­nal tester limit:


Click Save.

You now need to select a build (again) for your exter­nal testers, and put that build through the Beta App Review.

Note: Why do you need to select the build again? Well you may want your inter­nal and exter­nal testers to be test­ing dif­fer­ent builds. For exam­ple, your exter­nal testers may be test­ing your next release can­di­date, but your inter­nal testers are test­ing your mas­ter build. By mak­ing you select a build for inter­nal and exter­nal testers sep­a­rately iTunes Con­nect allows this kind of sep­a­ra­tion.

Click Add Build To Test, select your build, and then click Next.


Fill in all the fields. Remem­ber – the more infor­ma­tion you give Apple the eas­ier it is for them to review your app! Finally, hit Sub­mit.


Your app will be added to the review queue! :]

Note: In my own expe­ri­ence, your first beta app review can take up to 48 hours to be approved. Sub­se­quent beta app reviews are usu­ally much faster.


Once the app has passed Beta App Review you will receive an email with con­fir­ma­tion that your app can now be used by exter­nal testers.

Note: A build is only valid for 60 days. If you want your testers to use the app beyond that, you’ll have to upload a new build before the expi­ra­tion date.

Head back to the Exter­nal Test­ing sec­tion for your app in iTunes Con­nect, select the build and hit Save. A dialog will pop up con­firm­ing that you are about to notify peo­ple. Click Start Test­ing.


Your exter­nal testers will then receive an invi­ta­tion email sim­i­lar to the one received by your inter­nal testers as described above.

Note: What hap­pens if invites go miss­ing? Cur­rently there is no easy way to re-send invite emails if they go miss­ing. For inter­nal testers the eas­i­est approach is prob­a­bly to upload a new build (as it doesn’t have to go through Beta App Review). For exter­nal testers you can either remove the user and then re-add the user as a tester, or remove your app from exter­nal test­ing and then quickly re-add it. This sec­ond approach will re-send an email invite to all your exter­nal testers so be care­ful! :]

That shows the developer’s per­spec­tive of app test­ing, but what does it look like from the tester’s per­spec­tive?

Testing an App

As an inter­nal tester, you need to link your Apple ID to iTunes Con­nect (exter­nal testers can skip to theTest­Flight App sec­tion below). By now, you should have received an email from iTunes Con­nect that looks like this:

21- Email

Click on acti­vate your account and fol­low the sup­plied steps. Once your account is ready for test­ing, get your iOS device and go to the Set­tings app. Scroll down to iTunes & App Store:

19- Settings

Log in with the account you just ver­i­fied a min­ute ago. If you’re already logged in with another account, log out first:

20- Log in

TestFlight App

Go to the App Store, and search for the Test­Flight app:

21- Search

Down­load the Test­Flight app and launch it.

Note: If you’re required to log in, sim­ply use the same cre­den­tials you used to ver­ify your account.

Inter­nal testers will auto­mat­i­cally receive an email when new ver­sions of the app are uploaded to iTunes Con­nect. Exter­nal testers will receive a sim­i­lar email after the app has been through Beta App Review and the build has been pushed out to exter­nal testers by an Admin or App Man­ager in iTunes Con­nect:

24- Invite Email

Open this email on your test­ing device, then tap Start Test­ing. This will launch Test­Flight and show you the app you need to test. A tester must tap Start Test­ing on the device they’ll be test­ing on; oth­er­wise the app won’t be avail­able for down­load by the tester. Tap Accept, then Install, and wait for the app to down­load:

25- Accept

The app will be down­loaded and appear on your home screen!

That was the hard­est part of being a tester. From now on, when­ever a new ver­sion of this app is avail­able, you’ll see a noti­fi­ca­tion from Test­Flight. All you need to do is update your app and run the lat­est ver­sion.

Page 1 of 44
1 2 3 44