Regarding Guideline 1.2 - Safety - User Generated Content

To build on @dahiya_boy excellent answer. This is the way I did it in my app and I got approved.

  1. A method for filtering objectionable material from being posted to the app

You don't need to filter the content before it's posted. For eg if someone was posting a nude pic you don't need AI to figure out what's inside the pic before it's posted. There are too many different things that can and cannot be considered objectionable that the AI would get wrong. What Apple wants you to do is filter everything from a blocked user. So for example:

Your app has 2 tabs, on the first tab users can view photos from other users, and on the second tab users can view messages from other users. In both tabs the photos and messages would be considered user generated content.

If userA is in tab1 and see's something that they feel is offensive from userB then userA can Block userB. Once that block mechanism is in place userA will no longer see any photos from userB in tab1 nor will they see any messages from userB in tab2.

In that situation userB's photos and messages are filtered so that userA cannot see them. That is what Apple is looking for (I literally just got approved because I used that method). They want it so that a user who blocks someone will no longer be able to see anything objectionable from the the user they blocked. I’m not sure if this has to be one specific post (the objectionable one) from the other user or all the posts from the other user. What I did to play it safe was block all the posts. My code example below outlines how I did it.

For this example with these 2 tabs this block mechanism needs to be in place for both tabs so that that if userA blocks userB's messages, then userB's photos will also be unavailable to userA.

Another thing I added was so that once userA blocked userB, then userB cannot see any photos or messages from userA, I'm not sure if that's mandatory but that's the way I did it. It's basically a 2 way street but that might not be important.

  1. The ability to block abusive users from the service

A Block button that once pressed lets one user block another user so that the first user will no longer see anything from the second user. Basically what I described above.

  1. A mechanism to report offensive content and timely responses to concerns

A Report button. On anything that is user generated content such as a post being a video, photo, or a message, there needs to be a button to report it. You have to let the user know that it'll get reviewed within in 24 hrs, it'll get removed within that timeframe if inappropriate, and you will take some action against the user that posted it (Apple wants you to ban them from the app but that’s ultimately up to you).

The reviewer I had was very meticulous. Initially the button just said "Report". Ocne you pressed it an alert appeared that said "Is this Inappropriate" with a Yes button and a Cancel button.

I got 2 rejections that said something along the lines of "this doesn't state that they are reporting anything objectionable" and "this doesn't say it will be removed within 24 hrs".

I had to change it so that the report button said "Report Inappropriate" and after pressing it the alert title and message said (title)"Report Inappropriate" and (message) Is this post inappropriate? We will review this report within 24 hrs and if deemed inappropriate the post will be removed within that timeframe. We will also take actions against it's author".

  1. Published contact information so users can easily reach you.

You don't need to have a contact us button inside your action sheet.

As long as anywhere inside the app there is a way for users to contact you that is enough. In Settings I have a cell that says "Contact Us" and inside the contact us view controller I have the company address and email address.

These are the 4 methods I used to get my app passed.

On a side note the best way to implement the block feature is to use a blockedUsers ref and if a user adds someone to it then that person's userId will be inside that ref. Whenever the user who initiated the block is in a tab that is pulling data such as messages or posts, you need to do a check inside the blocked ref and filter out any posts from anyone they blocked.

For eg.

@-blockedUsers
     |
     @----userA_userID
             |
             @----userB_userId: true

Using the filtering method below no posts from userB will ever show up inside userA's Posts tab.

let currentUserID = Auth.auth().currentUser!.uid

Database.database().reference().child("posts")
  .queryOrderedByKey()
  .queryLimited(toLast: 10)
  .observeSingleEvent(of: .value) { (snapshot) in

     if !snapshot.exists() { return }

     guard let firstChild = snapshot.children.allObjects.first as? DataSnapshot else { return }

     for child in snapshot.children.allObjects as! [DataSnapshot] {

         if let postDict = child.value as? [String: Any] {

            let post = Post(dict: postDict)
            let userBsUserID = post.userId // UserB is really every user who created a post. I just used UserB for demo purposes. A more accurate term would be “otherUsersID” or “posterID”

            let blockedUsersRef = Database.database().reference().child("blockedUsers").child(currentUserID)
            blockedUsersRef.observeSingleEvent(of: .value) { (snapshot) in

                // if the currentUser isn't inside this blockedUsersRef then neither is userB
                if !snapshot.exists() {

                     self.dataSource.append(post)
                     self.collectionView.reloadData()

                     // *** OR now use this same method to check this currentUserID against userB to make sure it isn't in their blockedUsersRef either before adding their post to the datasource ***
                     return
                }

                // if the currentUserID does exist then check if userB is inside their blockedUsersRef
                for child in snapshot.children {
                    let snap = child as! DataSnapshot
                
                    if snap.key == userBsUserID {
                        // userB IS inside the currentUserId's blockedUsersRef so DON't ADD their post to the datasource
                        return
                    }
                }

                // userB ISN'T inside the currentUser's blockedUsersRef so ADD the post to the datasource
                self.dataSource.append(post)
                self.collectionView.reloadData()

                // *** OR now use this same method to check this currentUserID against userB to make sure it isn't in their blockedUsersRef either before adding their post to the datasource ***
            })
         }
     }
})

If you wanted to filter against the other user you would just have to do this same thing again in reverse before adding the post to the datasource.

It might seem like a lot of steps but when the reviewer tested against it I passed.


By referencing apple doc

  1. A method for filtering objectionable material from being posted to the app

Like instagram, you can add a button Don't show me this post again based on this you can filter the data for that user.

  1. A mechanism to report offensive content and timely responses to concerns

Add report option with every post so that if someone report against that post than you can alert other users as its a sensitive post, are you sure wanted to see this. Or if something harassment then remove that post from your app.

sample image of instagram.

enter image description here

  1. The ability to block abusive users from the service

If someone continuously violating your app privacy & protocols then suspend the account temporarily.

  1. Published contact information so users can easily reach you.

Add contact page where users can report against some post or user or any query if they have.

Sample image for points 1,3,4

enter image description here


These are the basics protocols that every social app follows so you can take reference from any app.