Takahiro Octopress Blog

-1から始める情弱プログラミング

FacebookのGraph API V3から分離されたuser_genderについて見てみる

はじめに

最近、いろいろと個人情報の話が取り沙汰されていますが、Facebookが Graph API v3 を発表しましたね!
Facebookログインを利用して会員登録の簡略化をするなんて、今や当たり前の時代に、個人情報の保護強化のために情報取得のハードルが少々引き上げられました。

また、これまでFacebookログインを利用していたサービスはFacebookの再審査を受けることが義務化されました。
今回新たに審査が必要となる項目も追加され、Facebook社のより一層の個人情報強化の意志を感じさせますね。
因みに、新規にパーミションが必要となった項目は、

  • user_link
  • user_age_range
  • user_gender

の3つになります。
本記事では、中でも user_gender に焦点を当てて見ていきたいと思います。

user_genderとは

フィールド名から分かる通り、値としては、ユーザの 性別 を取得するものになります。
これは、新たにパーミッションが必要になった項目ということですが、
もちろんこれまでも取得できる項目でした。

どうやって取得していたかと言うと、Facebook上でユーザの基本情報としてまとめられていた public_profilegender として含まれていました。
v3.0での変更でこれが独立したということになります。

パーミッション画面での変更

続いて、public_profileuser_gender を利用した場合でそれぞれユーザに表示するパーミッション画面はどのように変わるのでしょうか?
実際に見ていきましょう。

まずは、これまでの public_profile を利用した場合の画面は
public_profileのみ指定した場合のパーミッション画面

続いて、 user_gender を指定した場合の画面は
user_genderも指定した場合のパーミッション画面

となります。
今後どうなるかはわからないものの、今のところは public_profile の中に gender が含まれており、プラスで user_gender も指定できるという状況になっています。

パーミッション指定の処理

参考までに今回試したソースコードを記載しておきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// FacebookManager.swift
import Foundation
import PromiseKit
import FacebookCore
import FacebookLogin

public enum Gender: Int {
    case male
    case female
}

public struct FacebookUserProfile: Codable {
    public var id: String
    public var email: String?
    public var gender: Gender

    private enum CodingKeys: String, CodingKey {
        case id
        case email
        case gender
    }

    public init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try values.decode(String.self, forKey: .id)
        self.email = try values.decodeIfPresent(String.self, forKey: .email)
        let genderString = try values.decode(String.self, forKey: .gender)
        if genderString == "male" {
            self.gender = .male
        } else {
            self.gender = .female
        }
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encodeIfPresent(email, forKey: .email)

        if gender == .male {
            try container.encode("male", forKey: .gender)
        } else {
            try container.encode("female", forKey: .gender)
        }
    }
}

public final class FacebookManager {

    public static let shared = FacebookManager()
    private init() {
    }

    enum FacebookError: Error {
        case cancel
        case apiError
        case decodeError
    }

    public enum FacebookPermission: String {
        case email = "email"
        case publicProfile = "public_profile"
        case userGender = "user_gender"       // 新規パーミッションを指定
    }

    public func application(_ application: UIApplication, didFinishLaunchingWithOptions: [UIApplicationLaunchOptionsKey: Any]?) {

        SDKApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: didFinishLaunchingWithOptions)
    }

    public func application(_ app: UIApplication,
                            open url: URL,
                            options: [UIApplicationOpenURLOptionsKey: Any] = [:]) -> Bool {

        return SDKApplicationDelegate.shared.application(app, open: url, options: options)
    }

    public func isLoggedIn() -> Bool {
        return AccessToken.current != nil
    }

    public func login(_ viewController: UIViewController,
                      permissions: [FacebookPermission] = [.email, .publicProfile, .userGender]) -> Promise<String> {

        if isLoggedIn() {
            logout()
        }

        let promise = Promise<String> { fulfill, reject in
            var readPermissions = [ReadPermission]()
            permissions.forEach {
                readPermissions.append(ReadPermission.custom($0.rawValue))
            }

            LoginManager().logIn(readPermissions: readPermissions,
                                 viewController: viewController,
                                 completion: { result in

                                    switch result {
                                    case .success(_, _, let accessToken):
                                        _ = fulfill(accessToken.authenticationToken)
                                    case .cancelled:
                                        let error = FacebookError.cancel
                                        _ = reject(error)
                                    case .failed(let error):
                                        _ = reject(error)
                                    }
            })
        }

        return promise
    }

    public func logout() {
        LoginManager().logOut()
    }
}

extension JSONDecoder {
    // Any型をdecode可能なメソッドを追加
    public func decode<T: Decodable>(_ type: T.Type,
                                     withJSONObject object: Any,
                                     options opt: JSONSerialization.WritingOptions = []) throws -> T {

        let data = try JSONSerialization.data(withJSONObject: object, options: opt)
        return try decode(T.self, from: data)
    }
}

まとめ

さて如何でしたでしょうか?
今後移行期間がどうなるのか、イマイチ見えない状況ではありますが、取り急ぎ8/1までにFacebookの再審査を受けて大事に備えておくべきでしょう。
といったところで本日はここまで。

参考

Comments