터미널에서 vi Podfile을 통해 SDK 작성해주고 pod install 까지 완료합니다.
pod 'FBSDKLoginKit'
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>fb{APP-ID}</string> // APP-ID 앞에 fb 붙여 작성해야합니다
</array>
</dict>
</array>
<key>FacebookAppID</key>
<string>APP-ID</string> // APP-ID 작성
<key>FacebookDisplayName</key>
<string>APP-NAME</string> // 앱 생성할때 직접 만들어준 APP-NAME
<key>LSApplicationQueriesSchemes</key>
<array>
<string>fbapi</string>
<string>fbapi20130214</string>
<string>fbapi20130410</string>
<string>fbapi20130702</string>
<string>fbapi20131010</string>
<string>fbapi20131219</string>
<string>fbapi20140410</string>
<string>fbapi20140116</string>
<string>fbapi20150313</string>
<string>fbapi20150629</string>
<string>fbapi20160328</string>
<string>fbauth</string>
<string>fb-messenger-share-api</string>
<string>fbauth2</string>
<string>fbshareextension</string>
</array>
AppDelegate는 앱의 라이프사이클을 관리하는 부분으로 앱이 실행될 때마다 해주어야 하는 작업을 작성해줍니다. 네이티브로 페이스북 앱을 열기 위해서는 앱을 실행할 때마다 SDK를 초기화해 주어야 합니다.
import FBSDKCoreKit
...
// 앱이 실행된 후 호출되는 함수
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)
return true
}
iOS 13 이후부터는 SceneDelegate가 생기면서 사용하는 버전이 iOS 13 이후라면 아래 코드는 SceneDelegate에 작성해주고 그렇지않다면 AppDelegate에 작성해주어야 합니다. 저는 스토리보드를 사용하지 않고 코드로 프로젝트를 진행중이기 때문에 SceneDelegate가 없으므로 AppDelegate에 작성해 주었습니다.
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
ApplicationDelegate.shared.application(
app,
open: url,
sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String,
annotation: options[UIApplication.OpenURLOptionsKey.annotation]
)
}
SceneDelegate가 있다면 아래 코드를 SceneDelegate에 작성해주도록 합니다.
import FBSDKCoreKit
...
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else {
return
}
ApplicationDelegate.shared.application(
UIApplication.shared,
open: url,
sourceApplication: nil,
annotation: [UIApplication.OpenURLOptionsKey.annotation]
)
}
페이스북 버튼을 추가하는 방법엔 페이스북 자체에서 제공해주는 버튼을 상속받아와 사용하는 방법과 직접 버튼을 구현해 함수를 연결해주는 방법이 있습니다.
import FBSDKLoginKit
import SnapKit
...
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
initView()
}
private let facebookBtn = FBLoginButton()
private func initView() {
self.view.addSubview(self.facebookBtn)
self.facebook.snp.makeConstraint {(make) in
make.top.equalToSuperView().inset(150)
make.leading.trailing.equalToSuperView().inset(80)
}
}
}
import FBSDKLoginKit
import SnapKit
...
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
initView()
}
private let facebookBtn: UIButton = {
let btn = UIButton()
btn.backgroundColor = .blue
btn.setTitle("페이스북 로그인", for: UIControl.State.normal)
btn.setTitleColor(.white, for: UIControl.State.normal)
return btn
}()
private let facebookLogoutBtn: UIButton = {
let btn = UIButton()
btn.setTitle("페이스북 로그아웃", for: UIControl.State.normal)
btn.setTitleColor(.white, for: UIControl.State.normal)
return btn
}()
private func initView() {
self.view.addSubview(self.facebookBtn)
self.view.addSubview(self.facebookLogoutBtn)
self.facebook.snp.makeConstraint {(make) in
make.top.equalToSuperView().inset(150)
make.leading.trailing.equalToSuperView().inset(80)
}
self.facebookLogoutBtn.snp.makeConstraints {(make) in
make.top..equalToSuperView().inset(80)
make.trailing.equalToSuperView().inset(30)
}
self.facebookBtn.addTarget(self, action: #selector(self.facebookLogin(_:)), for: .touchUpInside)
self.facebookLogoutBtn.addTarget(self, action: #selector(self.facebookLogout(_:)), for: .touchUpInside)
}
}
extension ViewController {
@objc func facebookLogin(_ sender: Any) {
let loginManager = LoginManager()
loginManager.logIn(permissions: ["public_profile", "email"], from: self) {(result, error) in
guard error == nil else {
print(error!.localizedDescription)
return
}
guard let result = result, !result.isCancelled else {
print("User cancelled login")
return
}
GraphRequest.init(graphPath: "me", parameters: ["fields": "id, name, email, picture"])
.start(completion: {(connection, result, error) -> Void in
print("error: ", error ?? "No error")
guard let fb = result as? [String: AnyObject] else { return }
let token = fb["id"] as? String
let name = fb["name"] as? String
let email = fb["email"] as? String
var profile = ""
if let profileImg = fb["picture"] as? [String: AnyObject],
let data = profileImg["data"] as? [String: AnyObject] {
profile = data["url"] as? String ?? ""
}
print("token: ", token ?? "no token")
print("name: ", name ?? "no name")
print("email: ", email ?? "no email")
print("prfile_image: ", profile)
})
}
}
@objc func facebookLogout(_ sender: Any) {
let loginManager = LoginManager()
loginManager.logOut()
print("facebook logout")
}
}
이렇게만 하면 실제 로그인이 이루어지지는 않습니다. 그 이유는 현재 페이스북 로그인이 개발모드이기 때문입니다.
정상적으로 작동하기 위해서는 “라이브모드”로 전환해주어야 하는데, 라이브 모드 스위치를 켜주게 되면 개인정보처리방침 과 데이터 삭제 정보 를 제공해주어야 한다는 메시지가 뜹니다. 각 필드에 URL을 채워주면 라이브모드로 전환이 가능해집니다.
개인정보 처리방침 URL은 노션이나 블로그 게시글을 활용해 링크를 달아주어도 무관합니다!
사용자 데이터 삭제 부분은 사용자가 삭제를 원할때 서버에서 모든 정보를 지울 수 있는 방법을 마련해 놓으라는 것으로 데이터 삭제 콜백 URL과 데이터 삭제 안내 URL 중 선택할 수 있습니다. 이렇게 모두 완료하면 비로소 라이브모드를 켤수 있게 되고, 로그인도 가능해집니다.
[홈페이지 ‘내 어플리케이션’ 클릭] > [내 어플리케이션 추가하기 클릭] > [앱 이름 작성 후 저장]
[만들어 놓은 앱 선택] > [플랫폼 설정하기 클릭] > [iOS 플랫폼 등록 클릭] > [번들 ID 작성 후 저장]
[왼쪽 탭에서 카카오 로그인 클릭] > [활성화 설정 ON] > [활성화 버튼 클릭]
[왼쪽 탭에서 동의항목 클릭] > [원하는 항목 설정 버튼 클릭] > [필수/선택 동의 선택 후 동의목적 필수 작성, 카카오 계정으로 정보수집 후 제공 체크박스 선택]
터미널에서 vi Podfile을 통해 SDK 작성해주고 pod install 까지 완료합니다.
pod 'KakaoSDKCommon', '= 2.0.5'
pod 'KakaoSDKAuth', '= 2.0.5'
pod 'KakaoSDKUser', '= 2.0.5'
이렇게 iOS SDK를 설치하면 SDK에 필요한 외부라이브러리가 자동으로 설치됩니다. > Alamofire, DynamicCodable
<key>LSApplicationQueriesSchemes</key>
<array>
<!-- 카카오톡으로 로그인 -->
<string>kakaokompassauth</string>
<!-- 카카오링크 -->
<string>kakaolink</string>
</array>
앱 > 타겟 > info에서 URL Type + 버튼 클릭
kakao{네이티브 앱 키}
형식으로 등록import KakaoSDKCommon
...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
KakaoSDKCommon.initSDK(appKey: "NATIVE_APP_KEY")
...
}
여기서도 iOS13 이하 혹은 SceneDelegate가 기본이 아닐때 추가해주는 함수가 존재합니다.
iOS13 이하라면 아래 코드를 작성해줍니다.
import KakaoSDKAuth
...
class AppDelegate: UIResponder, UIWindowSceneDelegate {
...
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
if (AuthApi.isKakaoTalkLoginUrl(url)) {
return AuthController.handleOpenUrl(url: url)
}
return false
}
...
}
SceneDelegate가 존재한다면 SceneDelegate에 아래 코드를 추가합니다.
import KakaoSDKAuth
...
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
...
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url {
if (AuthApi.isKakaoTalkLoginUrl(url)) {
_ = AuthController.handleOpenUrl(url: url)
}
}
}
...
}
import KakaoSDKAuth
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
initView()
}
private let kakaoBtn: UIButton = {
let btn = UIButton()
btn.backgroundColor = .yellow
btn.setTitle("카카오톡 로그인", for: UIControl.State.normal)
btn.setTitleColor(.white, for: UIControl.State.normal)
return btn
}()
private let kakaoLogoutBtn: UIButton = {
let btn = UIButton()
btn.setTitle("카카오톡 로그아웃", for: UIControl.State.normal)
btn.setTitleColor(.white, for: UIControl.State.normal)
return btn
}()
private func initView() {
self.view.addSubview(self.kakaoBtn)
self.view.addSubview(self.kakaoLogoutBtn)
self.kakaoBtn.snp.makeConstraint {(make) in
make.top.equalToSuperView().inset(150)
make.leading.trailing.equalToSuperView().inset(80)
}
self.kakaoLogoutBtn.snp.makeConstraints {(make) in
make.top..equalToSuperView().inset(80)
make.trailing.equalToSuperView().inset(30)
}
self.kakaoBtn.addTarget(self, action: #selector(self.kakaoLogin(_:)), for: .touchUpInside)
self.kakaoLogoutBtn.addTarget(self, action: #selector(self.kakaoLogoutBtn(_:)), for: .touchUpInside)
}
}
extension ViewController {
@objc func kakaoLogin(_ sender: Any) {
AuthApi.shared.loginWithKakaoTalk {(oauthToken, error) in
if let error = error {
print(error)
} else {
print("login kakao")
_ = oauthToken
}
}
}
private func setUserInfo() {
UserApi.shared.me {(user, error) in
if let error = error {
print(error)
} else {
print("me() success")
_ = user
print("nickname: \(user?.kakaoAccount?.profile?.nickname ?? "no nickname")")
print("image: \(user?.kakaoAccount?.profile?.profileImageUrl)")
}
}
}
@objc func kakaoLogoutBtn(_ sender: Any) {
UserApi.shared.logout{(error) in
if let error = error {
print(error)
} else {
print("kakao logout success")
}
}
}
반드시 소문자로 작성
해야한다는 부분입니다.터미널에서 vi Podfile을 통해 SDK 작성해주고 pod install 까지 완료합니다.
pod 'naveridlogin-sdk-ios'
<key>LSApplicationQueriesSchemes</key>
<array>
<string>naversearchapp</string>
<string>naversearchthirdlogin</string>
</array>
import NaverThirdPartyLogin
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
NaverThirdPartyLoginConnection.getSharedInstance().isInAppOauthEnable = true
NaverThirdPartyLoginConnection.getSharedInstance().isNaverAppOauthEnable = true
let instance = NaverThirdPartyLoginConnection.getSharedInstance()
instance?.isNaverAppOauthEnable = true
instance?.isInAppOauthEnable = true
instance?.serviceUrlScheme = kServiceAppUrlScheme
instance?.consumerKey = kConsumerKey
instance?.consumerSecret = kConsumerSecret
instance?.appName = kServiceAppName
return true
}
}
위 설정을 위해서 NaverThirdPartyConstantForApp.h
파일로 이동해주세요.
폴더 경로는 아래와 같습니다.
그리고 해당 파일에서 값들을 지정해줍니다.
아래는 iOS 13 이하 버전 혹은 SceneDelegate가 기본이 아닌경우 AppDelegate에 추가할 코드입니다.
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
NaverThirdPartyLoginConnection.getSharedInstance()?.application(app, open: url, options: options)
return true
}
SceneDelegate가 기본이라면 SceneDelegate에 아래 코드를 추가해줍니다.
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
NaverThirdPartyLoginConnection
.getSharedInstance()?
.receiveAccessToken(URLContexts.first?.url)
}
import NaverThirdPartyLogin
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
initView()
}
private let naverBtn: UIButton = {
let btn = UIButton()
btn.backgroundColor = .yellow
btn.setTitle("네이버 로그인", for: UIControl.State.normal)
btn.setTitleColor(.white, for: UIControl.State.normal)
return btn
}()
private let naverLogoutBtn: UIButton = {
let btn = UIButton()
btn.setTitle("네이버 로그아웃", for: UIControl.State.normal)
btn.setTitleColor(.white, for: UIControl.State.normal)
return btn
}()
private func initView() {
self.view.addSubview(self.naverBtn)
self.view.addSubview(self.naverLogoutBtn)
self.naverBtn.snp.makeConstraint {(make) in
make.top.equalToSuperView().inset(150)
make.leading.trailing.equalToSuperView().inset(80)
}
self.naverLogoutBtn.snp.makeConstraints {(make) in
make.top..equalToSuperView().inset(80)
make.trailing.equalToSuperView().inset(30)
}
self.naverBtn.addTarget(self, action: #selector(self.naverLogin(_:)), for: .touchUpInside)
self.naverLogoutBtn.addTarget(self, action: #selector(self.naverLogoutBtn(_:)), for: .touchUpInside)
}
}
extension ViewController: NaverThirdPartyLoginConnectionDelegate {
@objc func naverLogin(_ sender: Any) {
naverConnection?.delegate = self
naverConnection?.requestThirdPartyLogin()
}
@objc func naverLogoutBtn(_ sender: Any) {
naverConnection?.requestDeleteToken()
}
func oauth20ConnectionDidFinishRequestACTokenWithAuthCode() {
print("Success Login")
self.getInfo()
}
func oauth20ConnectionDidFinishRequestACTokenWithRefreshToken() {
self.getInfo()
}
func oauth20ConnectionDidFinishDeleteToken() {
print("logout")
}
func oauth20Connection(_ oauthConnection: NaverThirdPartyLoginConnection!, didFailWithError error: Error!) {
print("error = \(error.localizedDescription)")
}
func getInfo() {
// 현재 토큰이 유효한지 확인 > default로 1시간
guard let isValidAccessToken = naverConnection?.isValidAccessTokenExpireTimeNow() else { return }
if !isValidAccessToken {
return
}
guard let tokenType = naverConnection?.tokenType else { return }
guard let accessToken = naverConnection?.accessToken else { return }
let urlStr = "https://openapi.naver.com/v1/nid/me"
let url = URL(string: urlStr)!
let authorization = "\(tokenType) \(accessToken)"
let req = AF.request(url, method: .get, parameters: nil,
encoding: JSONEncoding.default, headers: ["Authorization": authorization])
req.responseJSON {(response) in
print(response)
guard let result = response.value as? [String: AnyObject] else { return }
guard let object = result["response"] as? [String: AnyObject] else { return }
let name = object["name"] as? String
let id = object["id"] as? String
let image = object["profile_image"] as? String
print("name: ", name ?? "no name")
print("id: ", id ?? "no id")
print("image: \(image)")
}
}
}
읽어주셔서 감사합니다 :)