Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS fixes and permission check #182

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
File renamed without changes.
File renamed without changes.
File renamed without changes.
55 changes: 55 additions & 0 deletions darwin/Classes/ContactViewIOS.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#if os(iOS)
import Flutter
import UIKit
import Contacts
import ContactsUI

@available(iOS 9.0, *)
@available(macOS, unavailable)
public class ContactViewIOS: NSObject, CNContactViewControllerDelegate, CNContactPickerDelegate {

private let flutterContactsPlugin: FlutterContactsPlugin
public var rootViewController: UIViewController

init(_ flutterContactsPlugin: FlutterContactsPlugin) {
self.flutterContactsPlugin = flutterContactsPlugin;
rootViewController = UIApplication.shared.delegate!.window!!.rootViewController!
}

public func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) {
if let result = flutterContactsPlugin.externalResult {
result(contact?.identifier)
flutterContactsPlugin.externalResult = nil
}
viewController.dismiss(animated: true, completion: nil)
}

public func contactPicker(_: CNContactPickerViewController, didSelect contact: CNContact) {
if let result = flutterContactsPlugin.externalResult {
result(contact.identifier)
flutterContactsPlugin.externalResult = nil
}
}

// public func contactPicker(_: CNContactPicker, didSelect contact: CNContact) {
// if let result = flutterContactsPlugin.externalResult {
// result(contact.identifier)
// flutterContactsPlugin.externalResult = nil
// }
// }

public func contactPickerDidCancel(_: CNContactPickerViewController) {
if let result = flutterContactsPlugin.externalResult {
result(nil)
flutterContactsPlugin.externalResult = nil
}
}

// public func contactPickerDidCancel(_: CNContactPicker) {
// if let result = flutterContactsPlugin.externalResult {
// result(nil)
// flutterContactsPlugin.externalResult = nil
// }
// }
}
#endif
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
#if os(macOS)
import Cocoa
import FlutterMacOS
#endif
#if os(iOS)
import Flutter
import UIKit
#endif
import Contacts
import ContactsUI
import Flutter
import UIKit

@available(iOS 9.0, *)
@available(iOS 9.0, macOS 10.11, *)
public enum FlutterContacts {
// Fetches contact(s).
static func selectInternal(
Expand Down Expand Up @@ -44,12 +50,12 @@ public enum FlutterContacts {
CNContactBirthdayKey,
CNContactDatesKey,
]
if #available(iOS 10, *) {
if #available(iOS 10, macOS 10.12, *) {
keys.append(CNContactPhoneticOrganizationNameKey)
}
// Notes need explicit entitlement from Apple starting with iOS13.
// https://stackoverflow.com/questions/57442114/ios-13-cncontacts-no-longer-working-to-retrieve-all-contacts
if #available(iOS 13, *), !includeNotesOnIos13AndAbove {} else {
if #available(iOS 13, macOS 10.11, *), !includeNotesOnIos13AndAbove {} else {
keys.append(CNContactNoteKey)
}
if externalIntent {
Expand Down Expand Up @@ -234,13 +240,13 @@ public enum FlutterContacts {
CNContactThumbnailImageDataKey,
CNContactImageDataKey,
]
if #available(iOS 10, *) { keys.append(CNContactPhoneticOrganizationNameKey) }
if #available(iOS 13, *), !includeNotesOnIos13AndAbove {} else {
if #available(iOS 10, macOS 10.12,*) { keys.append(CNContactPhoneticOrganizationNameKey) }
if #available(iOS 13, macOS 10.11, *), !includeNotesOnIos13AndAbove {} else {
keys.append(CNContactNoteKey)
}

let request = CNContactFetchRequest(keysToFetch: keys as! [CNKeyDescriptor])
if #available(iOS 10, *) { request.mutableObjects = true }
if #available(iOS 10, macOS 10.12, *) { request.mutableObjects = true }
request.predicate = CNContact.predicateForContacts(withIdentifiers: [id])
let store = CNContactStore()
var contacts: [CNContact] = []
Expand Down Expand Up @@ -407,7 +413,7 @@ public enum FlutterContacts {
(args["events"] as! [[String: Any]]).forEach {
Event(fromMap: $0).addTo(contact)
}
if #available(iOS 13, *), !includeNotesOnIos13AndAbove {} else {
if #available(iOS 13, macOS 10.11, *), !includeNotesOnIos13AndAbove {} else {
if let note = (args["notes"] as! [[String: Any]]).first {
Note(fromMap: note).addTo(contact)
}
Expand All @@ -418,32 +424,75 @@ public enum FlutterContacts {
}
}

@available(iOS 9.0, *)
public class SwiftFlutterContactsPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, CNContactViewControllerDelegate, CNContactPickerDelegate {
private let rootViewController: UIViewController
private var externalResult: FlutterResult?
@available(iOS 9.0, macOS 10.11, *)
public class FlutterContactsPlugin: NSObject, FlutterPlugin, FlutterStreamHandler {

#if os(iOS)
private var contactViewIOS: ContactViewIOS?
#endif
public var externalResult: FlutterResult?

public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(
name: "github.com/QuisApp/flutter_contacts",
binaryMessenger: registrar.messenger()
)
let eventChannel = FlutterEventChannel(
name: "github.com/QuisApp/flutter_contacts/events",
binaryMessenger: registrar.messenger()
)
let rootViewController = UIApplication.shared.delegate!.window!!.rootViewController!
let instance = SwiftFlutterContactsPlugin(rootViewController)
#if os(macOS)
let channel = FlutterMethodChannel(
name: "github.com/QuisApp/flutter_contacts",
binaryMessenger: registrar.messenger
)
let eventChannel = FlutterEventChannel(
name: "github.com/QuisApp/flutter_contacts/events",
binaryMessenger: registrar.messenger
)
let instance = FlutterContactsPlugin()
#endif
#if os(iOS)
let channel = FlutterMethodChannel(
name: "github.com/QuisApp/flutter_contacts",
binaryMessenger: registrar.messenger()
)
let eventChannel = FlutterEventChannel(
name: "github.com/QuisApp/flutter_contacts/events",
binaryMessenger: registrar.messenger()
)

//private let contactViewIOS: ContactViewIOS?
//contactViewIOS = ContactViewIOS(instance)

let instance = FlutterContactsPlugin()
instance.contactViewIOS = ContactViewIOS(instance)
#endif

registrar.addMethodCallDelegate(instance, channel: channel)
eventChannel.setStreamHandler(instance)
}

init(_ rootViewController: UIViewController) {
self.rootViewController = rootViewController
}
// #if os(iOS)
// @available(iOS 9.0, *)
// init(_ contactViewIOS: ContactViewIOS?) {
// self.contactViewIOS = contactViewIOS
// }
// #endif


public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "authorisationStatus":
DispatchQueue.global(qos: .userInteractive).async {
let status = CNContactStore.authorizationStatus(for: .contacts)
switch status {
case .notDetermined:
result("notDetermined");
case .restricted:
result("restricted");
case .denied:
result("denied");
case .authorized:
result("authorized");
case .limited:
result("limited");
default:
result("unknown");
}
}
case "requestPermission":
DispatchQueue.global(qos: .userInteractive).async {
CNContactStore().requestAccess(for: .contacts, completionHandler: { (granted, _) -> Void in
Expand Down Expand Up @@ -583,6 +632,14 @@ public class SwiftFlutterContactsPlugin: NSObject, FlutterPlugin, FlutterStreamH
}
}
case "openExternalViewOrEdit":
#if os(macOS)
result(FlutterError(
code: "macos.notImplemented",
message: "openExternalViewOrEdit is not implemented on MacOS",
details: "openExternalViewOrEdit is not implemented on MacOS"
))
#endif
#if os(iOS)
DispatchQueue.main.async {
let args = call.arguments as! [Any?]
let id = args[0] as! String
Expand All @@ -604,21 +661,39 @@ public class SwiftFlutterContactsPlugin: NSObject, FlutterPlugin, FlutterStreamH
target: self,
action: #selector(self.contactViewControllerDidCancel)
)
contactView.delegate = self
contactView.delegate = self.contactViewIOS!
// https://stackoverflow.com/a/39594589
let navigationController = UINavigationController(rootViewController: contactView)
self.rootViewController.present(navigationController, animated: true, completion: nil)
self.contactViewIOS!.rootViewController.present(navigationController, animated: true, completion: nil)
self.externalResult = result
}
}
#endif
case "openExternalPick":
#if os(macOS)
result(FlutterError(
code: "macos.notImplemented",
message: "openExternalPick is not implemented on MacOS",
details: "openExternalPick is not implemented on MacOS"
))
#endif
#if os(iOS)
DispatchQueue.main.async {
let contactPicker = CNContactPickerViewController()
contactPicker.delegate = self
self.rootViewController.present(contactPicker, animated: true, completion: nil)
contactPicker.delegate = self.contactViewIOS!
self.contactViewIOS!.rootViewController.present(contactPicker, animated: true, completion: nil)
self.externalResult = result
}
#endif
case "openExternalInsert":
#if os(macOS)
result(FlutterError(
code: "macos.notImplemented",
message: "openExternalInsert is not implemented on MacOS",
details: "openExternalInsert is not implemented on MacOS"
))
#endif
#if os(iOS)
DispatchQueue.main.async {
let contact = CNMutableContact()
let args = call.arguments as? [Any?]
Expand All @@ -636,12 +711,13 @@ public class SwiftFlutterContactsPlugin: NSObject, FlutterPlugin, FlutterStreamH
target: self,
action: #selector(self.contactViewControllerDidCancel)
)
contactView.delegate = self
contactView.delegate = self.contactViewIOS!
// https://stackoverflow.com/a/39594589
let navigationController = UINavigationController(rootViewController: contactView)
self.rootViewController.present(navigationController, animated: true, completion: nil)
self.contactViewIOS!.rootViewController.present(navigationController, animated: true, completion: nil)
self.externalResult = result
}
#endif
default:
result(FlutterMethodNotImplemented)
}
Expand All @@ -665,14 +741,7 @@ public class SwiftFlutterContactsPlugin: NSObject, FlutterPlugin, FlutterStreamH
return nil
}

public func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) {
if let result = externalResult {
result(contact?.identifier)
externalResult = nil
}
viewController.dismiss(animated: true, completion: nil)
}

#if os(iOS)
@objc func contactViewControllerDidCancel() {
if let result = externalResult {
let viewController: UIViewController? = UIApplication.shared.delegate?.window??.rootViewController
Expand All @@ -681,18 +750,7 @@ public class SwiftFlutterContactsPlugin: NSObject, FlutterPlugin, FlutterStreamH
externalResult = nil
}
}
#endif

public func contactPicker(_: CNContactPickerViewController, didSelect contact: CNContact) {
if let result = externalResult {
result(contact.identifier)
externalResult = nil
}
}

public func contactPickerDidCancel(_: CNContactPickerViewController) {
if let result = externalResult {
result(nil)
externalResult = nil
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ struct Address {
case CNLabelOther:
label = "other"
default:
if #available(iOS 13, *), a.label == CNLabelSchool {
if #available(iOS 13, macOS 15, *), a.label == CNLabelSchool {
label = "school"
} else {
label = "custom"
Expand All @@ -59,7 +59,7 @@ struct Address {
postalCode = a.value.postalCode
country = a.value.country
isoCountry = a.value.isoCountryCode
if #available(iOS 13, *) {
if #available(iOS 13, macOS 15, *) {
subAdminArea = a.value.subAdministrativeArea
subLocality = a.value.subLocality
}
Expand Down Expand Up @@ -126,7 +126,7 @@ struct Address {
case "home":
labelInv = CNLabelHome
case "school":
if #available(iOS 13, *) {
if #available(iOS 13, macOS 15, *) {
labelInv = CNLabelSchool
} else {
labelInv = "school"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ struct Email {
case CNLabelOther:
label = "other"
default:
if #available(iOS 13, *), e.label == CNLabelSchool {
if #available(iOS 13, macOS 15, *), e.label == CNLabelSchool {
label = "school"
} else {
label = "custom"
Expand All @@ -52,7 +52,7 @@ struct Email {
case "iCloud":
labelInv = CNLabelEmailiCloud
case "school":
if #available(iOS 13, *) {
if #available(iOS 13, macOS 15, *) {
labelInv = CNLabelSchool
} else {
labelInv = "school"
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ struct Phone {
case CNLabelOther:
label = "other"
default:
if #available(iOS 13, *), p.label == CNLabelSchool {
if #available(iOS 13, macOS 15, *), p.label == CNLabelSchool {
label = "school"
} else {
label = "custom"
Expand Down Expand Up @@ -82,7 +82,7 @@ struct Phone {
case "pager":
labelInv = CNLabelPhoneNumberPager
case "school":
if #available(iOS 13, *) {
if #available(iOS 13, macOS 15, *) {
labelInv = CNLabelSchool
} else {
labelInv = "school"
Expand Down
Loading