Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Project SF.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
9CCFDD2624B9753F00162B0F /* Main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CCFDD2524B9753F00162B0F /* Main.swift */; };
9CCFDD2824B9758A00162B0F /* TestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CCFDD2724B9758A00162B0F /* TestApp.swift */; };
9CD7F11E24B89C5000DDAD8C /* ProjectSFTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CD7F11D24B89C5000DDAD8C /* ProjectSFTests.swift */; };
9CD90D9724C99C4A00020288 /* FriendsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CD90D9624C99C4A00020288 /* FriendsManager.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -190,6 +191,7 @@
9CD7F11B24B89C5000DDAD8C /* Project SFTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Project SFTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
9CD7F11D24B89C5000DDAD8C /* ProjectSFTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectSFTests.swift; sourceTree = "<group>"; };
9CD7F11F24B89C5000DDAD8C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
9CD90D9624C99C4A00020288 /* FriendsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FriendsManager.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -504,6 +506,7 @@
9CCFDD1624B971D600162B0F /* CloudKit */,
30ABC14324C5684B0060825B /* User */,
30ABC14124C567DF0060825B /* Competitions */,
9CD90D9524C99C3C00020288 /* Friends */,
30ABC14424C568A10060825B /* Scoring */,
30ABC14224C568210060825B /* Invitations */,
9C6C74B424BBE56800C657B0 /* HealthKit */,
Expand Down Expand Up @@ -547,6 +550,14 @@
path = "Project SFTests";
sourceTree = "<group>";
};
9CD90D9524C99C3C00020288 /* Friends */ = {
isa = PBXGroup;
children = (
9CD90D9624C99C4A00020288 /* FriendsManager.swift */,
);
path = Friends;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -725,6 +736,7 @@
30251C1B24BFC1B10058D6D2 /* CompetitorCell.swift in Sources */,
30CCF30724BED9EA00103C1E /* ActivityOverview.swift in Sources */,
30D321BA24BB65D5009CD9D0 /* NotificationSettings.swift in Sources */,
9CD90D9724C99C4A00020288 /* FriendsManager.swift in Sources */,
30251C1E24BFE1D50058D6D2 /* PointsGraph.swift in Sources */,
30BFC8E724B8041C00DAC6D9 /* ActivityRingsView.swift in Sources */,
9C2445DC24BC714100D8F4FC /* Invitation.swift in Sources */,
Expand Down
74 changes: 0 additions & 74 deletions Project SF/Logic/Competitions/CompetitionsController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -351,80 +351,6 @@ class CompetitionsController {
}
}

// MARK: Friend Discovery (should be moved out of this class)

/// Requests permission from the user to discover their contacts.
/// - Parameter handler: The result handler. Not guaranteed to be executed on the main thread.
/// - Tag: requestDiscoveryPermission
func requestDiscoveryPermission(then handler: @escaping (Result<Bool, Error>) -> Void) {
container.requestApplicationPermission([.userDiscoverability]) { (status, error) in
if let error = error {
handler(.failure(error))
return
}
switch status {
case .granted:
handler(.success(true))
case .denied:
handler(.success(false))
default:
handler(.failure(CompetitionsControllerError.unknownError))
}
}
}

/// Asynchronously discovers the users friends. Fails if the adequate permissions have not been granted (you can request the required permission using [requestDiscoveryPermission](x-source-tag://requestDiscoveryPermission).
/// - Parameter handler: The result handler. Not guaranteed to be executed on the main thread.
func discoverFriends(then handler: @escaping (Result<[Friend], Error>) -> Void) {
container.status(forApplicationPermission: .userDiscoverability) { [weak container] status, error in
guard let container = container else { return }
if let error = error {
handler(.failure(error))
return
}
if case .granted = status {
container.discoverAllIdentities { identities, error in
if let error = error {
handler(.failure(error))
return
}
guard let identities = identities else {
handler(.failure(CompetitionsControllerError.unknownError))
return
}

let operation = CKFetchRecordsOperation(recordIDs: identities.compactMap { $0.userRecordID })
operation.qualityOfService = .userInitiated
operation.fetchRecordsCompletionBlock = { ckRecords, error in
if let error = error {
handler(.failure(error))
return
}
guard let ckRecords = ckRecords else {
handler(.failure(CompetitionsControllerError.unknownError))
return
}
let records = ckRecords
.map { UserRecord(record: $0.value) }

let friends = records
.map {
return Friend(name: $0.username ?? "",
profilePicture: URL(string: $0.profilePictureURL ?? ""),
recordID: $0.record.recordID)
}

handler(.success(friends))
}

container.sharedCloudDatabase.add(operation)
}
} else {
handler(.failure(CompetitionsControllerError.insufficientPermissions))
}
}
}

// MARK: Helper Methods

/// Utility method to create a zone with a randomised identifier.
Expand Down
104 changes: 104 additions & 0 deletions Project SF/Logic/Friends/FriendsManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//
// FriendsManager.swift
// Project SF
//
// Created by William Taylor on 23/7/20.
//

import Foundation
import CloudKit

class FriendsManager {

// MARK: Properties

private let container: CKContainer

// MARK: Init

init(container: CKContainer = .appDefault) {
self.container = container
}

// MARK: Methods

/// Requests permission from the user to discover their contacts.
/// - Parameter handler: The result handler. Not guaranteed to be executed on the main thread.
/// - Tag: requestDiscoveryPermission
func requestDiscoveryPermission(then handler: @escaping (Result<Bool, Error>) -> Void) {
container.requestApplicationPermission([.userDiscoverability]) { (status, error) in
if let error = error {
handler(.failure(error))
return
}
switch status {
case .granted:
handler(.success(true))
case .denied:
handler(.success(false))
default:
handler(.failure(FriendsManagerError.unknownError))
}
}
}

/// Asynchronously discovers the users friends. Fails if the adequate permissions have not been granted (you can request the required permission using [requestDiscoveryPermission](x-source-tag://requestDiscoveryPermission).
/// - Parameter handler: The result handler. Not guaranteed to be executed on the main thread.
func discoverFriends(then handler: @escaping (Result<[Friend], Error>) -> Void) {
container.status(forApplicationPermission: .userDiscoverability) { [weak container] status, error in
guard let container = container else { return }
if let error = error {
handler(.failure(error))
return
}
if case .granted = status {
container.discoverAllIdentities { identities, error in
if let error = error {
handler(.failure(error))
return
}
guard let identities = identities else {
handler(.failure(FriendsManagerError.unknownError))
return
}

let operation = CKFetchRecordsOperation(recordIDs: identities.compactMap { $0.userRecordID })
operation.qualityOfService = .userInitiated
operation.fetchRecordsCompletionBlock = { ckRecords, error in
if let error = error {
handler(.failure(error))
return
}
guard let ckRecords = ckRecords else {
handler(.failure(FriendsManagerError.unknownError))
return
}
let records = ckRecords
.map { UserRecord(record: $0.value) }

let friends = records
.map {
return Friend(name: $0.username ?? "",
profilePicture: URL(string: $0.profilePictureURL ?? ""),
recordID: $0.record.recordID)
}

handler(.success(friends))
}

container.sharedCloudDatabase.add(operation)
}
} else {
handler(.failure(FriendsManagerError.insufficientPermissions))
}
}
}

// MARK: FriendsManagerError

enum FriendsManagerError: Error {
case insufficientPermissions
case unknownError
}

}