diff --git a/FacebookSwift.xcodeproj/project.pbxproj b/FacebookSwift.xcodeproj/project.pbxproj index 3213cbe4..87268198 100644 --- a/FacebookSwift.xcodeproj/project.pbxproj +++ b/FacebookSwift.xcodeproj/project.pbxproj @@ -35,6 +35,7 @@ 816B75A71D07AEB70012AC43 /* GraphRequestDataAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 816B75A61D07AEB70012AC43 /* GraphRequestDataAttachment.swift */; }; 817021AE1D18DE1500ECE7AC /* UserProfile.PictureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 817021AD1D18DE1500ECE7AC /* UserProfile.PictureView.swift */; }; 817A0A0E1D07CE8A00FD423A /* GraphRequestResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 817A0A0D1D07CE8A00FD423A /* GraphRequestResult.swift */; }; + 819E34C61D495A9A000D33E8 /* UserProfile.FetchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81E3C0CB1D48196900C927ED /* UserProfile.FetchResult.swift */; }; 81AB8A871D36D43100066F63 /* FBSDKCoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AB8A3D1D36D41700066F63 /* FBSDKCoreKit.framework */; }; 81AB8A941D36D44C00066F63 /* FBSDKCoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AB8A3D1D36D41700066F63 /* FBSDKCoreKit.framework */; }; 81AB8A951D36D44C00066F63 /* FBSDKLoginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AB8A4B1D36D41700066F63 /* FBSDKLoginKit.framework */; }; @@ -429,6 +430,7 @@ 81D646871D2C8D7800690609 /* LikeControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LikeControl.swift; sourceTree = ""; }; 81D646881D2C8D7800690609 /* SendButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendButton.swift; sourceTree = ""; }; 81D646891D2C8D7800690609 /* ShareButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareButton.swift; sourceTree = ""; }; + 81E3C0CB1D48196900C927ED /* UserProfile.FetchResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserProfile.FetchResult.swift; sourceTree = ""; }; 81FC4B5F1D064F68003F3A46 /* LoginManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginManager.swift; sourceTree = ""; }; 81FC4B721D064F8B003F3A46 /* LoginDefaultAudience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginDefaultAudience.swift; sourceTree = ""; }; 81FC4B7A1D065059003F3A46 /* LoginBehavior.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginBehavior.swift; sourceTree = ""; }; @@ -906,6 +908,7 @@ isa = PBXGroup; children = ( 81FC4EF11D068564003F3A46 /* UserProfile.swift */, + 81E3C0CB1D48196900C927ED /* UserProfile.FetchResult.swift */, 817021AD1D18DE1500ECE7AC /* UserProfile.PictureView.swift */, ); path = UserProfile; @@ -1292,6 +1295,7 @@ 810192AE1D01305400B9E881 /* AppEvent.Builtin.swift in Sources */, 810192AF1D01305400B9E881 /* AppEvent.swift in Sources */, 810192B41D01305400B9E881 /* AccessToken.swift in Sources */, + 819E34C61D495A9A000D33E8 /* UserProfile.FetchResult.swift in Sources */, 81FC4CAB1D067490003F3A46 /* ReadPermission.swift in Sources */, 810192CA1D0139CF00B9E881 /* GraphRequestConnection.Delegate.swift in Sources */, 816B75A71D07AEB70012AC43 /* GraphRequestDataAttachment.swift in Sources */, diff --git a/Sources/Core/UserProfile/UserProfile.FetchResult.swift b/Sources/Core/UserProfile/UserProfile.FetchResult.swift new file mode 100644 index 00000000..34d15c79 --- /dev/null +++ b/Sources/Core/UserProfile/UserProfile.FetchResult.swift @@ -0,0 +1,47 @@ +// Copyright (c) 2016-present, Facebook, Inc. All rights reserved. +// +// You are hereby granted a non-exclusive, worldwide, royalty-free license to use, +// copy, modify, and distribute this software in source code or binary form for use +// in connection with the web services and APIs provided by Facebook. +// +// As with any software that integrates with the Facebook platform, your use of +// this software is subject to the Facebook Developer Principles and Policies +// [http://developers.facebook.com/policy/]. This copyright notice shall be +// included in all copies or substantial portions of the software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import FBSDKCoreKit + +extension UserProfile { + /** + Describes the result of a fetch of `UserProfile`. + */ + public enum FetchResult { + /// Profile was succesfully fetched. + case Success(UserProfile) + /// Profile fetch failed. + case Failed(ErrorType) + } +} + +extension UserProfile.FetchResult { + + internal init(sdkProfile: FBSDKProfile?, error: NSError?) { + if let error = error { + self = .Failed(error) + } else if let sdkProfile = sdkProfile { + let profile = UserProfile(sdkProfile: sdkProfile) + self = .Success(profile) + } else { + //FIXME: (nlutsenko) Use a good error type here. + let error = NSError(domain: "", code: 42, userInfo: nil) + self = .Failed(error) + } + } +} diff --git a/Sources/Core/UserProfile/UserProfile.swift b/Sources/Core/UserProfile/UserProfile.swift index b2f50129..71871763 100644 --- a/Sources/Core/UserProfile/UserProfile.swift +++ b/Sources/Core/UserProfile/UserProfile.swift @@ -85,6 +85,68 @@ public struct UserProfile { } } +//-------------------------------------- +// MARK: - Loading Profile +//-------------------------------------- + +extension UserProfile { + + /// Convenience alias for type of closure that is used as a completion for fetching `UserProfile`. + public typealias Completion = (FetchResult) -> Void + + /** + Fetches a user profile by userId. + + If the `current` profile is set, and it has the same `userId`, + calling method will reset the current profile with the newly fetched one. + + - parameter userId: Facebook user id of the profile to fetch. + - parameter completion: The closure to be executed once the profile is refreshed. + */ + public static func fetch(userId userId: String, completion: Completion) { + let request = GraphRequest(graphPath: userId, + parameters: ["fields" : "first_name,middle_name,last_name,name,link"], + httpMethod: .GET) + request.start { (httpResponse, result) in + switch result { + case .Success(let response): + let responseDictionary = response.dictionaryValue + + let profile = UserProfile(userId: userId, + firstName: responseDictionary?["first_name"] as? String, + middleName: responseDictionary?["middle_name"] as? String, + lastName: responseDictionary?["last_name"] as? String, + fullName: responseDictionary?["name"] as? String, + profileURL: (responseDictionary?["link"] as? String).flatMap({ NSURL(string: $0) }), + refreshDate: NSDate()) + + // Reset the current profile if userId matches + if AccessToken.current?.userId == userId { + UserProfile.current = profile + } + completion(.Success(profile)) + + case .Failed(let error): + completion(.Failed(error)) + } + } + } + + /** + Refreshes the existing user profile. + + If the `current` profile is set, and receiver has the same `userId`, + calling method will reset the current profile with the newly fetched one. + + - parameter completion: Optional closure to be executed once the profile is refreshed. Default: `nil`. + */ + public func refresh(completion: Completion? = nil) { + UserProfile.fetch(userId: userId) { result in + completion?(result) + } + } +} + //-------------------------------------- // MARK: - Current Profile //-------------------------------------- @@ -111,9 +173,12 @@ extension UserProfile { - parameter completion: The closure to be executed once the profile is loaded. */ - public static func loadCurrent(completion: ((UserProfile?, ErrorType?) -> Void)?) { - FBSDKProfile.loadCurrentProfileWithCompletion { (profile: FBSDKProfile?, error: NSError?) in - completion?(profile.map(UserProfile.init), error) + public static func loadCurrent(completion: Completion?) { + FBSDKProfile.loadCurrentProfileWithCompletion { (sdkProfile: FBSDKProfile?, error: NSError?) in + if let completion = completion { + let result = FetchResult(sdkProfile: sdkProfile, error: error) + completion(result) + } } }