trying to get Facebook connect working from a swift project.
Have been trying to follow along the following youtube video: https://www.youtube.com/watch?v=I6rTmfLp9aY
which unfortunately for me is in German.
so this is what I have so far:
I have my Facebook app with IOS enabled enabled and I planted my bundleID there.
Downloaded latest iOS framework and added to project
to the AppDelegate file I added:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FBSDKApplicationDelegate.sharedInstance()
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
FBSDKAppEvents.activateApp()
}
and this is the ViewController file
import UIKit
import FBSDKCoreKit
import FBSDKLoginKit
import FBSDKShareKit
class ViewController: UIViewController, FBSDKAppInviteDialogDelegate, FBSDKLoginButtonDelegate{
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if (FBSDKAccessToken.current() != nil)
{
let content = FBSDKAppInviteContent()
content.appLinkURL = NSURL(string: "{Facebook link to app}") as URL!
FBSDKAppInviteDialog.show(from: self, with: content, delegate: self)
}
else
{
let loginView : FBSDKLoginButton = FBSDKLoginButton()
self.view.addSubview(loginView)
loginView.center = CGPoint(x: self.view.frame.midX, y: self.view.frame.midY + 100)
loginView.readPermissions = ["public_profile", "email"]
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func appInviteDialog (_ appInviteDialog: FBSDKAppInviteDialog!, didCompleteWithResults results: [AnyHashable : Any]!)
{
}
func appInviteDialog (_ appInviteDialog: FBSDKAppInviteDialog!, didFailWithError error: Error!) {
print("Error took place in appInviteDialog \(error)")
}
func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!) {
if ((error) != nil)
{
//process error
}
else if result.isCancelled {
//handle cancelation
}
else {
let content = FBSDKAppInviteContent()
content.appLinkURL = NSURL(string: "{Facebook link to app}") as URL!
FBSDKAppInviteDialog.show(from: self, with: content, delegate: self)
if result.grantedPermissions.contains("email")
{
//do work
}
}
}
func loginButtonDidLogOut(_ loginButton: FBSDKLoginButton!) {
}
}
No errors and no alerts. When i run simulator I get an empty screen. Must be doing something right cause I get the following msg:
SystemGroup/systemgroup.com.apple.configurationprofiles
2017-06-04 00:42:02.351876+0300 facebook_login[4569:144075] [MC] Reading from private effective user settings.
also, if I just paste in viewDidLoad the following lines from the code:
let loginView : FBSDKLoginButton = FBSDKLoginButton()
self.view.addSubview(loginView)
loginView.center = CGPoint(x: self.view.frame.midX, y: self.view.frame.midY + 100)
loginView.readPermissions = ["public_profile", "email"]
I get a beautiful Facebook button in simulator that of course crashes when i press it.
any help to work will be greatly appreciated
Facebook has a Swift SDK you might find easier to use than the Objective-C one (which they just call iOS). Try looking around the documentation here:
https://developers.facebook.com/docs/swift
Also, follow the steps described in the (other) iOS SDK to get started:
https://developers.facebook.com/docs/ios/getting-started/
This is the minimal app delegate I could get to work (notice the Swift SDK is missing the FB prefixes that exist in the iOS SDK):
import UIKit
import FacebookCore
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
SDKApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
return SDKApplicationDelegate.shared.application(app, open: url, options: options)
}
}
And be sure to add all the required keys in your Info.plist or else you won't get authentication to work at all.
func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith
result:FBSDKLoginManagerLoginResult!, error: Error!) {
if ((error) != nil) {
// Process error
print("Error! : \(error.localizedDescription)")
return
} else if result.isCancelled {
// Handle cancellations
print("Success! : user cancel login request")
return
} else {
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields" : "id, email, name,picture.type(large)"])
graphRequest.start(completionHandler: { (connection, result, error) -> Void in
if ((error) != nil) {
print("Error: \(error)")
} else {
// Do work in app.
let dataDict:NSDictionary = result as! NSDictionary
if let token = FBSDKAccessToken.current().tokenString {
print("tocken: \(token)")
let userDefult = UserDefaults.standard
userDefult.setValue(token, forKey: "access_tocken")
userDefult.synchronize()
}
if let user : NSString = dataDict.object(forKey: "name") as! NSString? {
print("user: \(user)")
}
if let id : NSString = dataDict.object(forKey: "id") as? NSString {
print("id: \(id)")
}
if let email : NSString = (result! as AnyObject).value(forKey: "email") as? NSString {
print("email: \(email)")
}
if let pictureData:NSDictionary = dataDict.object(forKey: "picture") as? NSDictionary{
if let data:NSDictionary = pictureData.object(forKey: "data") as? NSDictionary{
if let strPictureURL: String = data.object(forKey: "url") as? String{
self.imageviewUser.image = UIImage(data: NSData(contentsOf: NSURL(string: strPictureURL)! as URL)! as Data)
}
}
}
}
})
}
}
func loginButtonDidLogOut(_ loginButton: FBSDKLoginButton!)
{
FBSDKAccessToken.setCurrent(nil)
FBSDKProfile.setCurrent(nil)
let manager = FBSDKLoginManager()
manager.logOut()
}
Related
i am trying to implement facebook login button into my project , all good but the dialog once return from Facebook still open and not closing . i think its something related to URL app delegate its not firing since i am using SwiftUI without scenedelegate .
The login func which is work perfectly :
fileprivate func facebookLogin(){
let fbLoginManager = LoginManager()
fbLoginManager.logIn(permissions: ["public_profile", "email"], from: Tools.topViewController()! )
{ (result, error) in
if ( result != nil && (result?.isCancelled)! )
{
print("[LOGIN][FACEBOOK] cancecled by User")
return
}
if result != nil && (result?.grantedPermissions != nil)
{
print("[LOGIN][FACEBOOK] grantedPermissions")
if((AccessToken.current) != nil)
{
GraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, last_name, email"]).start(completionHandler: { (connection, result, error) -> Void in
if (error == nil)
{
print("[LOGIN][FACEBOOK SUCCESS] \(String(describing: result))")
var fb_result = result as! [String : Any]
let name = fb_result["name"] as! String
}else
{
print("[LOGIN][FACEBOOK ERROR] \(String(describing: error))")
}
})
}else
//Something went Wrong !
{
print("[LOGIN][FACEBOOK ERROR] Something went wrong! #1")
}
}
//Something went Wrong !
else{
}
}
}
App :
#main
struct LW_APP: App {
#Environment(\.scenePhase) var scenePhase
var body: some Scene {
WindowGroup {
w_landingTabs()
}
.onChange(of: scenePhase) { (newScenePhase) in
switch newScenePhase {
case .background:
print("[APP] State : Background")
case .inactive:
print("[APP] State : Inactive")
case .active:
print("[APP] State : Active")
#unknown default:
print("[APP] State : Unknown")
}
}
}
}
App delegate :
import UIKit
import FBSDKCoreKit
let APP_DELEGATE:APP_delegate = UIApplication.shared.delegate as! APP_delegate
class APP_delegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
ApplicationDelegate.shared.application(
application,
didFinishLaunchingWithOptions: launchOptions
)
print("[APP DELEGATE] MyAppDelegate didFinishLaunchingWithOptions called")
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
print("[APP DELEGATE] options: [UIApplication.OpenURLOptionsKey : Any] ")
return ApplicationDelegate.shared.application(app, open: url, options: options)
}
func applicationDidBecomeActive(_ application: UIApplication) {
print("[APP DELEGATE] applicationDidBecomeActive")
AppEvents.activateApp()
}
}
The screen i stuck on when return after login :
I've found the only solution for this issue.
This part of code :
let _ = ApplicationDelegate.shared.application(
UIApplication.shared,
open: url,
sourceApplication: nil,
annotation: [UIApplication.OpenURLOptionsKey.annotation])
must be added inside onOpenURL for SwiftUI view , in my case i did this :
#main
struct LW_APP: App {
#UIApplicationDelegateAdaptor(APP_delegate.self) private var appDelegate
#Environment(\.scenePhase) var scenePhase
var body: some Scene {
WindowGroup {
if Env.sharedInstance.openApp {
w_landingTabs()
.environmentObject(env)
.onOpenURL { (url) in
let _ = ApplicationDelegate.shared.application(
UIApplication.shared,
open: url,
sourceApplication: nil,
annotation: [UIApplication.OpenURLOptionsKey.annotation])
}
.....
I'm just going to paste in a couple of my files so that you can test this really easily and see what's going on. I'm clicking the button and it's making the shortened dynamic link. Then, I'm typing out the DynamicLink in the notes app and then I press the link. I get redirected to the app and the following error is returned:
[connection] nw_read_request_report [C1] Receive failed with error "Software caused connection abort"
Side note: all of this is being tested on an iPhone 7 (a physical device, not the simulator).
FirebaseTestApp and AppDelegate:
import SwiftUI
import Firebase
#main
struct FirebaseTestApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
var functionMaster: functions = functions()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
return true
}
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
let dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url)
if dynamicLink != nil {
print("Dynamic link : \(String(describing: dynamicLink?.url))")
return true
}
return false
}
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
print("Successful penetration")
guard let inComingURL = userActivity.webpageURL else { return false }
print("Incoming Web Page URL: \(inComingURL)")
self.functionMaster.handleIncomingDynamicLink(inComingURL)
return true
}
}
functions class:
import Foundation
import Firebase
import UIKit
class functions: ObservableObject {
func makeDynamicLink() {
var components = URLComponents()
components.scheme = "https"
components.host = "www.firebase-test.com" //this can be some random domain right? It doesn't have to actually exist yet?
components.path = "/data"
let stringifiedNumber = String(123)
components.queryItems = [stringifiedNumber]
let dynamicLinksDomainURIPrefix = "https://something.page.link"
guard let linkParameter = components.url else { return }
print("I am sharing \(linkParameter)")
guard let linkBuilder = DynamicLinkComponents(link: linkParameter, domainURIPrefix: dynamicLinksDomainURIPrefix) else { return }
if let myBundleId = Bundle.main.bundleIdentifier {
linkBuilder.iOSParameters = DynamicLinkIOSParameters(bundleID: myBundleId)
}
linkBuilder.iOSParameters?.appStoreID = "962194608"
linkBuilder.socialMetaTagParameters = DynamicLinkSocialMetaTagParameters()
linkBuilder.socialMetaTagParameters?.title = testLocation.name
linkBuilder.socialMetaTagParameters?.descriptionText = testLocation.address
linkBuilder.shorten { [weak self] (url, warnings, error) in
if let error = error{
print("Firebase encountered an error: \(error)")
return
}
if let warnings = warnings {
for warning in warnings {
print("Firebase Warning: \(warning)")
}
}
guard let url = url else { return }
print("The short URL is: \(url.absoluteString)")
self?.showShareSheet(url: url)
}
guard let longDynamicLink = linkBuilder.url else { return }
print("The long URL is: \(longDynamicLink)")
}
func showShareSheet(url: URL) {
let promoText = "Check out this thing I've marked in FirebaseTest!"
let activityVC = UIActivityViewController(activityItems: [promoText, url], applicationActivities: nil)
UIApplication.shared.windows.first?.rootViewController?.present(activityVC, animated: true)
}
func handleIncomingDynamicLink(_ dynamicLink: URL) {
_ = DynamicLinks.dynamicLinks().handleUniversalLink(dynamicLink) { (dynamiclink, error) in
guard error == nil else {
print("Found an error: \(error?.localizedDescription ?? "")")
return
}
print("Dynamic link : \(String(describing: dynamiclink?.url))")
let path = dynamiclink?.url?.path
var id = 0
if let query = dynamiclink?.url?.query {
let dataArray = query.components(separatedBy: "=")
id = Int(dataArray[1]) ?? 0
}
if path == "data" {
//Write code here
}
}
}
}
ContentView:
import SwiftUI
struct ContentView: View {
#ObservedObject var functionMaster: functions = functions()
var body: some View {
Button("Click me to run some firebase stuff") {
functionMaster.makeDynamicLink()
}
.padding()
}
}
In browser, when I navigate to https://something.page.link/apple-app-site-association, I get this:
https://i.stack.imgur.com/6Ndo0.png
Try installing the files for the the simulator you want to test on, update Xcode, delete all other versions.
I click the Facebook button but it is unresponsive on iOS 13, but on iOS 12.x it works fine. What should I do?
I am using:
1) pod 'FBSDKCoreKit', '~> 5.13.1'
2) pod 'FBSDKLoginKit', '~> 5.13.1'
3) pod 'FacebookCore'
4) pod 'FacebookLogin'
5) pod 'FacebookShare'
Your appdelegate in open url method not working because ios13 in change
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {
return
}
you have to latest xcode in work than SceneDelegate.swift file in add this method. please try it.
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else {
return
}
let _ = ApplicationDelegate.shared.application(
UIApplication.shared,
open: url,
sourceApplication: nil,
annotation: [UIApplication.OpenURLOptionsKey.annotation])
}
and not forgot to import FBSDKLoginKit in SceneDelegate
set info.plist url types
//create a function for present root view controller in AppDelegate create var window: UIWindow?
var appDelegateShared: AppDelegate {
return UIApplication.shared.delegate as? AppDelegate ?? AppDelegate()
}
create a class for Facebook login
class WFacebookManager: NSObject {
typealias FBCallback = (Bool, Any?) -> Void
var handler: FBCallback?
static let shared: WFacebookManager = WFacebookManager()
public func loginWithFacebook(completion: #escaping FBCallback ) {
handler = completion
let fbLoginManager: LoginManager = LoginManager()
fbLoginManager.logOut()
let presentView = appDelegateShared.window?.rootViewController!
fbLoginManager.logIn(permissions: ["public_profile", "email"], from: presentView) { (result, error) -> Void in
if error == nil {
if (result?.isCancelled)! {
self.handler?(false, nil)
return
} else if AccessToken.current != nil {
let values = "id, name, first_name, last_name, picture.type(large), email"
let request = GraphRequest(graphPath: "me", parameters: ["fields": values])
request.start(completionHandler: { (_, result, error) in
self.handler?(error == nil, result)
})
}
} else {
self.handler?(false, nil)
}
}
}
}
// on your action call function then get login data
#IBAction func loginWithFacebook(_ sender: Any) {
WFacebookManager.shared.loginWithFacebook { (success, data) in
if let data = data as? [String: Any], success {
print (data)
}
}
}
I am creating simple chat app using Firebase. My database has node for messages, and when user gets message from another user - cloud function monitors changes and sends firebase cloud message to user's token. UNUserNotificationCenterDelegate was properly implemented in AppDelegate.
Everything worked fine until I began to implement background location updates with CoreLocation locationManager.startUpdatingLocation() method. I instantiate locationManager also in AppDelegate.
My app saves current location to UserDefaults(), then updates location, compares it to saved location in UserDefaults and if it is far from saved location or was not saved to database for certain period of time, current location is saved to Firebase database and is updated at UserDefaults().
After running app I started to get notifications from Firebase (both in foreground and in background) which are last 5-8 (then up to 58 at a time) messages that user got new message.
I tried to understand what triggers these FCMs but only could print notification.description to console
I tried
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
}
but it didn't work
import UIKit
import Firebase
import UserNotifications
import GoogleMobileAds
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate {
var window: UIWindow?
lazy var locationManager: CLLocationManager = {
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.allowsBackgroundLocationUpdates = true
return locationManager
}()
var currentLocation: CLLocation?
var defaults = UserDefaults.standard
var geocoder = CLGeocoder()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
FirebaseApp.configure()
self.startLocationService()
locationManager.startUpdatingLocation()
self.registerForPushNotifications()
Messaging.messaging().delegate = self
return true
}
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
let dataDict:[String: String] = ["token": fcmToken]
NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
}
func applicationDidBecomeActive(_ application: UIApplication) {
UNUserNotificationCenter.current().removeAllPendingNotificationRequests( )
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
}
func registerForPushNotifications() {
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.delegate = self
notificationCenter.requestAuthorization(options: [.alert, .sound, .badge]) {
(granted, error) in
guard granted else { return }
self.getNotificationSettings()
}
}
func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let tokenParts = deviceToken.map { data -> String in
return String(format: "%02.2hhx", data)
}
print("Registered with device token \(tokenParts.joined())")
}
func application(_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register: \(error.localizedDescription)")
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("notification is \(notification.description)")
completionHandler([.sound])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("Received notification \(response.notification)")
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
if application.applicationState == .active {
application.applicationIconBadgeNumber = 0
} else {
let numberOfUnreadMessages = userInfo["count"] as! String
if let badgeNumber = Int(numberOfUnreadMessages) {
application.applicationIconBadgeNumber = badgeNumber
}
completionHandler(UIBackgroundFetchResult.newData)
}
}
}
extension AppDelegate: CLLocationManagerDelegate {
func startLocationService() {
print("Starting location services in AppDelegate")
if CLLocationManager.authorizationStatus() == .authorizedAlways || CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
activateLocationServices()
} else {
locationManager.requestAlwaysAuthorization()
}
}
private func activateLocationServices() {
if UIApplication.shared.applicationState == .active {
locationManager.startUpdatingLocation()
} else {
locationManager.startMonitoringSignificantLocationChanges()
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedAlways || status == .authorizedWhenInUse {
activateLocationServices()
} else {
print("CLAuthorizationStatus is notDetermined")
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("error happened while getting current location: \(error.localizedDescription)")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
currentLocation = locations.first
print("AppDelegate got location")
if defaults.value(forKey: "currentLatitude") == nil {
let currentLatitude = currentLocation?.coordinate.latitude
let currentLongtitude = currentLocation?.coordinate.longitude
let currentAltitude = currentLocation?.altitude
let timeStamp: Double = Double(Int(NSDate().timeIntervalSince1970))
defaults.set(currentLatitude, forKey: "currentLatitude")
defaults.set(currentLongtitude, forKey: "currentLongtitude")
defaults.set(currentAltitude, forKey: "currentAltitude")
defaults.set(timeStamp, forKey: "locationUpdated")
print("UserDefaults updated with current location")
}
saveLocationToDatabase()
}
func saveLocationToDatabase() {
var previousLocation: CLLocation?
var previuosTime: Double?
guard let currentLocation = currentLocation, let currentUserUid = defaults.string(forKey: "uid") else { return }
let previousLatitude = defaults.double(forKey: "currentLatitude")
let previousLongtitude = defaults.double(forKey: "currentLongtitude")
previuosTime = defaults.double(forKey: "locationUpdated")
previousLocation = CLLocation(latitude: previousLatitude, longitude: previousLongtitude)
let timeStamp: Double = Double(Int(NSDate().timeIntervalSince1970))
if previousLocation != nil && previuosTime != nil {
let distance = currentLocation.distance(from: previousLocation!)
let time = timeStamp - previuosTime!
if distance < 100 && time < 60 {
print("distance is \(distance), time is \(time)")
print("No need to save location")
return
} else {
self.saveChangesToDatabase()
}
} else {
self.saveChangesToDatabase()
}
}
func saveChangesToDatabase() {
guard let currentLocation = currentLocation, let currentUserUid = defaults.string(forKey: "uid") else { return }
let usersRef = Database.database().reference().child("users").child(currentUserUid)
usersRef.child("location").child("latitude").setValue(currentLocation.coordinate.latitude)
usersRef.child("location").child("longtitude").setValue(currentLocation.coordinate.longitude)
usersRef.child("location").child("altitude").setValue(currentLocation.altitude)
let timeToSet: NSNumber = NSNumber(value: Int(NSDate().timeIntervalSince1970))
usersRef.child("location").child("updated").setValue(timeToSet)
print("Location saved to database")
defaults.set(currentLocation.coordinate.latitude, forKey: "currentLatitude")
defaults.set(currentLocation.coordinate.longitude, forKey: "currentLongtitude")
defaults.set(currentLocation.altitude, forKey: "currentAltitude")
defaults.set(timeToSet, forKey: "locationUpdated")
print("UserDefaults updated with current location")
self.geocoder.reverseGeocodeLocation(currentLocation) { (placemarks, error) in
if let error = error {
print(error.localizedDescription)
return
}
guard let placemark = placemarks?.first else { return }
if let city = placemark.locality, let state = placemark.administrativeArea {
let currentPlace = "\(city), \(state)"
usersRef.child("location").child("currentPlace").setValue(currentPlace)
}
}
}
}
My console is:
AppDelegate got location
distance is 0.00017503746557926585, time is 58.0
No need to save location
AppDelegate got location
Location saved to database
UserDefaults updated with current location
notification is ,, trigger: >>
notification is ,, trigger: >>
and so on....
The problem is completely in Firebase Cloud Functions and it has nothing to do with CoreLocation services
The book states,
“An ensemble identifier is used to match stores across devices. It is
important that this be the same for each store in the ensemble.”
let ensembleFileSystem = CDEICloudFileSystem(ubiquityContainerIdentifier: "???")
Does this need to be unique for all users ? or just for my application?
If anyone has a Swift version of how the set up Ensembles that would be great.
What I have so far, is this all that is needed?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let ensembleFileSystem = CDEICloudFileSystem(ubiquityContainerIdentifier: "???")
let modelURL = NSBundle.mainBundle().URLForResource("DataModel", withExtension: "momd")!
let url = applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
let ensemble = CDEPersistentStoreEnsemble(ensembleIdentifier: "mainstore", persistentStoreURL: url, managedObjectModelURL: modelURL, cloudFileSystem: ensembleFileSystem!)
if !ensemble.leeched {
ensemble.leechPersistentStoreWithCompletion { (error) -> Void in
if error != nil {
print("cannot leech")
print(error!.localizedDescription)
}
}
}
NSNotificationCenter.defaultCenter().addObserver(self, selector: "syncWithCompletion:", name: CDEMonitoredManagedObjectContextDidSaveNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "syncWithCompletion:", name: CDEICloudFileSystemDidDownloadFilesNotification, object: nil)
return true
}
func syncWithCompletion(notification:NSNotification) {
print("synced \(notification)")
managedObjectContext.mergeChangesFromContextDidSaveNotification(notification)
}
Something is missing Im getting this error log
User is not logged into iCloud
Despite being logged in as evident
print(NSFileManager.defaultManager().ubiquityIdentityToken)
Not being nil
Got it to work in the end - found example apps in 1.0 Git
I belive I was leeching to fast - not giving enough time for the set up process to complete.
Support this framework - buy ensembles 2, if you like ver 1.
Update .. easier way
I just use the normal core data stack apple provides.
Here is the extras to get ensembles working.
var ensemble:CDEPersistentStoreEnsemble!
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let file = CDEICloudFileSystem(ubiquityContainerIdentifier: nil)
let modelURL = NSBundle.mainBundle().URLForResource("DataModel", withExtension: "momd")!
let storeurl = self.applicationDocumentsDirectory.URLByAppendingPathComponent("store.sqlite")
ensemble = CDEPersistentStoreEnsemble(ensembleIdentifier: "MyStoreName", persistentStoreURL: storeurl, managedObjectModelURL: modelURL, cloudFileSystem: file)
ensemble.delegate = self
NSNotificationCenter.defaultCenter().addObserver(self, selector: "localSaveOccurred:", name: CDEMonitoredManagedObjectContextDidSaveNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "cloudDataDidDownload:", name: CDEICloudFileSystemDidDownloadFilesNotification, object: nil)
syncWithCompletion { completed in
if completed {
print("SUCCESSS")
}
else {
print("FAIL")
}
}
return true
}
// MARK: - Sync
func applicationDidEnterBackground(application: UIApplication) {
print("Did Enter Background Save from App Delegate")
let identifier = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler(nil)
saveContext()
syncWithCompletion { (completed) -> Void in
if completed {
UIApplication.sharedApplication().endBackgroundTask(identifier)
}
}
}
func applicationWillEnterForeground(application: UIApplication) {
syncWithCompletion { (completed) -> Void in
}
}
func localSaveOccurred(note:NSNotification) {
syncWithCompletion { (completed) -> Void in
}
}
func cloudDataDidDownload(note:NSNotification) {
syncWithCompletion { (completed) -> Void in
print("items from iCloud arrived")
}
}
func syncWithCompletion(completion:(completed:Bool) -> Void) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
if !ensemble.leeched {
ensemble.leechPersistentStoreWithCompletion(nil)
}
else {
ensemble.mergeWithCompletion{ error in
if error != nil {
print("cannot merge \(error!.localizedDescription)")
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
completion(completed: false)
}
else {
print("merged")
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
completion(completed: true)
}
}
}
}
// MARK: - Ensemble Delegate Methods
func persistentStoreEnsemble(ensemble: CDEPersistentStoreEnsemble!, didSaveMergeChangesWithNotification notification: NSNotification!) {
managedObjectContext.performBlockAndWait { () -> Void in
self.managedObjectContext.mergeChangesFromContextDidSaveNotification(notification)
}
}
func persistentStoreEnsemble(ensemble: CDEPersistentStoreEnsemble!, globalIdentifiersForManagedObjects objects: [AnyObject]!) -> [AnyObject]! {
return (objects as NSArray).valueForKeyPath("uniqueIdentifier") as! [AnyObject]
}
My First Way
Here it is in Swift, with a few extras
var ensemble:CDEPersistentStoreEnsemble!
var cloudFileSystem:CDEICloudFileSystem!
var managedObjectContext: NSManagedObjectContext!
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
setUpCoreData()
let modelURL = NSBundle.mainBundle().URLForResource("YourDataModel", withExtension: "momd")!
cloudFileSystem = CDEICloudFileSystem(ubiquityContainerIdentifier:"USE_YOUR_APPS_REVERSE DOMAIN NAME HERE")
From the developer: RE ubiquityContainerIdentifier
This is not part of Ensembles per se. It is from iCloud. Every app
using iCloud has to have a ubiquity container id. You can find it in
your app settings when you enable iCloud. It is unique per app, and we
only use it if you are choosing for iCloud (eg not Dropbox).
ensemble = CDEPersistentStoreEnsemble(ensembleIdentifier: "store", persistentStoreURL: storeURL(), managedObjectModelURL: modelURL, cloudFileSystem: cloudFileSystem!)
ensemble.delegate = self
NSNotificationCenter.defaultCenter().addObserver(self, selector: "localSaveOccurred:", name: CDEMonitoredManagedObjectContextDidSaveNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "cloudDataDidDownload:", name: CDEICloudFileSystemDidDownloadFilesNotification, object: nil)
syncWithCompletion { completed in
if completed {
print("SUCCESSS")
}
else {
print("FAIL")
}
}
return true
}
// MARK: - Core Data Stack
func setUpCoreData() {
let modelURL = NSBundle.mainBundle().URLForResource("DataModel", withExtension: "momd")!
guard let model = NSManagedObjectModel(contentsOfURL: modelURL) else { fatalError("cannot use model") }
do {
try NSFileManager.defaultManager().createDirectoryAtURL(storeDirectoryURL(), withIntermediateDirectories: true, attributes: nil)
}
catch {
fatalError("cannot create dir")
}
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
//NSDictionary *options = #{NSMigratePersistentStoresAutomaticallyOption: #YES, NSInferMappingModelAutomaticallyOption: #YES};
let failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL(), options: nil)
managedObjectContext = NSManagedObjectContext.init(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
abort()
}
}
func storeDirectoryURL() -> NSURL {
let directoryURL = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
return directoryURL
}
func storeURL() -> NSURL {
let url = storeDirectoryURL().URLByAppendingPathComponent("store.sqlite")
return url
}
// MARK: - Sync
func applicationDidEnterBackground(application: UIApplication) {
print("Did Enter Background Save from App Delegate")
let identifier = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler(nil)
saveContext()
syncWithCompletion { (completed) -> Void in
if completed {
UIApplication.sharedApplication().endBackgroundTask(identifier)
}
}
}
func applicationWillEnterForeground(application: UIApplication) {
syncWithCompletion { (completed) -> Void in
}
}
func localSaveOccurred(note:NSNotification) {
syncWithCompletion { (completed) -> Void in
}
}
func cloudDataDidDownload(note:NSNotification) {
syncWithCompletion { (completed) -> Void in
}
}
func syncWithCompletion(completion:(completed:Bool) -> Void) {
if !ensemble.leeched {
ensemble.leechPersistentStoreWithCompletion { error in
if error != nil {
print("cannot leech \(error!.localizedDescription)")
completion(completed: false)
}
else {
print("leached!!")
completion(completed: true)
}
}
}
else {
ensemble.mergeWithCompletion{ error in
if error != nil {
print("cannot merge \(error!.localizedDescription)")
completion(completed: false)
}
else {
print("merged!!")
completion(completed: true)
}
}
}
}
// MARK: - Ensemble Delegate Methods
func persistentStoreEnsemble(ensemble: CDEPersistentStoreEnsemble!, didSaveMergeChangesWithNotification notification: NSNotification!) {
print("did merge changes with note")
managedObjectContext.mergeChangesFromContextDidSaveNotification(notification)
}
func persistentStoreEnsemble(ensemble: CDEPersistentStoreEnsemble!, globalIdentifiersForManagedObjects objects: [AnyObject]!) -> [AnyObject]! {
return (objects as NSArray).valueForKeyPath("uniqueIdentifier") as! [AnyObject]
}