I'm trying to upgrade my app to latest iOS support. I've added TwitterKit via CocoaPods, and placed header in my Bridge Header. However; I am getting an error saying:
Use of unresolved identified 'Twitter' - did you mean 'TWTRTTwitter'.
func application(_ application: UIApplication, didFinishLaunchingWithOptions lauunchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
Twitter.sharedInstance().start(withConsumerKey:"MYKEY", consumerSecret:"MYSECRET")
return true
}
This right out of the Twitter recommended code. I also get it at:
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
let handled:Bool = true
Twitter.sharedInstance().application(app, open: url, options: options)
return handled
}
Any pointers?
There’s a bit of a code change with the latest Twitter Kit release. We changed Twitter -> TWTRTwitter - but also added a macro so that you should still be able to use [Twitter sharedInstance]. Unfortunately, this currently does not work with Swift - so have a try with TWTRTwitter as suggested. We'll get this sorted out in the docs! sorry!
Thank you, Andy Piper, I fixed the issue as following:
Updated pod so that TwitterCore 3.1.0 and TwitterKit 3.3.0
In Bridging-Header.h file
imported as
TwitterKit/TWTRKit.h instead of
TwitterKit/TwitterKit.h
In didFinishLaunchingWithOptions,
Modified Twitter.sharedInstance().start(withConsumerKey: ”your consumer key”, consumerSecret:”your consumer secret”)
TWTRTwitter.sharedInstance().start(withConsumerKey: ”your consumer key”, consumerSecret:”your consumer secret”)
i.e replace Twiter with TWTRTwitter where it is used.
No need to initialize with fabric, so removed this line or removed
Fabric.with([Twitter.self]) or Fabric.with([TWTRTwitter.self])
Related
Is it possible?
I've got a new iOS 14 app set up with Core Data and Cloudkit. I had to make a few changes to my Persistence.swift file to get it working but it's working without a hitch.
I'm interested in implementing sharing with other iCloud Users, but a lot of the documentation is out of date and confusing and it seems like it might not have been possible at one point but it is now?
I think the first step is to make my database shared? I'm adding the following line to my Persistence.swift file
container.persistentStoreDescriptions.first!.cloudKitContainerOptions?.databaseScope = .shared
(Here's the whole thing):
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentCloudKitContainer
init(inMemory: Bool = false) {
container = NSPersistentCloudKitContainer(name: "Shopmatic")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
container.persistentStoreDescriptions.first!.cloudKitContainerOptions?.databaseScope = .shared
}
}
but when I run the app I get the following error
Thread 1: "CKDatabaseScopeShared is not supported with NSPersistentCloudKitContainer"
Which is not encouraging, but maybe it's possible to implement sharing with a public database scope?
I'm not really sure what the next steps are? Implementing my own NSPersistentContainer that both syncs to cloudkit and allows sharing?
I believe the answer is no based on this thread on the Apple dev forums (I'm MasonAndMuse there), but there's one Apple engineer in there who was saying yes but wouldn't elaborate. I stopped trying soon after, not finding any way around the opaque nature of the system as it is now. Nothing I've seen indicates someone else has gotten it working but I haven't looked very closely after giving up 8 months ago. Of course there might be a way - I'd love to be proven wrong!
I assume they introduce sharing in iOS 15, but that's not very helpful now... Too bad because it's so close now and it will save SO much time if/when they add it in...
The Apple engineer's answer in the forum reads quite clearly to me:
NSPersistantCloudKitContainer does not support sharing!
You need to implement CloudKit sharing through CK APIs.
I have not implemented such a feature yet, but I would approach it like this:
Get the NSPersistantCloudKitContainer implementation working.
Implement CloudKit sharing on the same container.
Start passing specific records from NSPersistantCloudKitContainer into the sharing feature implemented in step 2.
To access the shared Cloudkit database with CKDatabase.Scope = shared:
If this is the NSPersistentCloudKitContainer
:
container = NSPersistentCloudKitContainer(name: "AppName")
I would try to access the shared DB of this container like so
:
let myAppsCloudKitContainer = CKContainer(identifier: "iCloud.com.name.AppName")
let myContainersSharedDatabase = myAppsCloudKitContainer.database(with: .shared)
To access data from NSPersistantCloudKitContainer for use in the CloudKit sharing feature use NSPersistantCloudKitContainer instance method
record(for managedObjectID: NSManagedObjectID)
(as of macOS 11 / Xcode 12)
I'm trying to build the Native Messaging Test Project and it keeps failing to build with Cannot find 'SFExtensionMessageKey' in scope. How do I fix this?
The error comes from this bit of code:
SafariWebExtensionHandler.swift
/*
See LICENSE folder for this sample’s licensing information.
Abstract:
Communicates with the extension running in Safari.
*/
import SafariServices
class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling {
// Receive a message from the JavaScript component of the Web Extension.
// Unpack the message, and then wrap in an object to send as the response.
func beginRequest(with context: NSExtensionContext) {
let item = context.inputItems[0] as? NSExtensionItem
let message = item?.userInfo?[SFExtensionMessageKey] // ERROR HERE
let response = NSExtensionItem()
response.userInfo = [ SFExtensionMessageKey: [ "Response to": message ] ] // AND ERROR HERE
context.completeRequest(returningItems: [response], completionHandler: nil)
}
}
It fails because it cannot find SFExtensionMessageKey in scope, but when I look it up I see that it is supposed to be included in Safari Services.
When I inspect the Safari Services file directly in Xcode, I don't see any mention of SFExtensionMessageKey -- this could just be my confusion. But it seems like this was renamed, maybe?
Another thought was that I needed to add the SafariServices framework to the Build phases (under the section "Link Binary With Libraries") for the various app targets. This also did not solve the issue.
I'm on Xcode 12.1 & Swift 5.
As discussed in the comments, SFExtensionMessageKey is only available for macOS 11+ and not iOS or earlier macOS versions.
I am working on adding a Today extension for my app, which will need to use FirebaseAuth, so I followed the instructions on the Firebase Documentation and set up the Keychain access group by adding it in the Signing & Capabilities tab, where I wrote it in the format: {TEAM_ID}.com.{appOrg}.{appBundle}.{groupName} and initialized it using the code below in the didFinishLaunchingWithOptions of the AppDelegate of my app, and the viewDidLoad() of my today extension, called after FirebaseApp.configure() in both instances.
Here is the code:
do {
try Auth.auth().useUserAccessGroup("{TEAM_ID}.com.{appOrg}.{appBundle}.{groupName}")
]} catch let error as NSError {
print("Error changing user access group: \(error.code)")
}
Both calls give me the same error code: 0, which also translates to The operation couldn’t be completed. when I use error.localizedDescription
Any idea how to fix this? Thanks in advance!
EDIT:
Not sure if it will make any difference, but my app is a Flutter app, though the today view and code aboce is in the swift files, called upon launch of the app.
So, after several attempts under my thoughts that it was my setup of the keychain group, it ended up being that I was calling the code too early (right after configuring the FirebaseApp), so it just failed. Moving the code out of viewDidLoad and into a method called by a method channel right before login (for the app) and before I needed the user to populate the today extension worked for me.
I currently have a Swift iOS app on Apple's App Store. I have many users and I would like to make a new version and help current users migrate to the new version. FYI: the new version is an Ionic app.
Data-wise, my app is using Core Data without any iCloud or sync support. It contains JSON data and also multiple images. So I'd need to bundle the current data and find a way of bringing it to the new ionic app version.
Basically my question is: Is there a way of writing in the app's documents directory and let the new version grab that file to import its data? Is there a way of letting both apps transmit data other than AirDrop or Custom URLs?
I don't want to upload the data remotely, I'd like to do this all locally on the device and also seamlessly so the user don't have to manually do anything.
Suggestions are welcome, thanks!
I would suggest using App Groups to get a shared container. I’m not familiar with Ionic, but this is quite straightforward in native Swift. It allows multiple apps or extensions to access a shared container of data, like the image below:
(Image from https://agostini.tech/2017/08/13/sharing-data-between-applications-and-extensions-using-app-groups/ )
This would require an update to the existing app to copy data to the shared container and then users would have to install the new app while the old one was still installed, because the shared container will be deleted when there are no installed apps using it.
It can be set up like this:
1: Enable App Groups in your project's Capabilities tab (for both apps).
2: Add a new app group and name it something like "group.appDomain.appName" or similar.
3: Now that the App Group is set up, it’s shared container can be used in several ways (User Defaults, NSCoding or Core Data).
For shared User Defaults:
let defaults = UserDefaults.init(suiteName: "group.appDomain.appName")
defaults.set("Example", forKey: "exampleKey")
defaults.synchronize()
More info from Apple here.
For NSCoding:
let sharedContainerDirectory: URL = FileManager().containerURL(forSecurityApplicationGroupIdentifier: "group.appDomain.appName")!
let sharedArchiveURL: URL = sharedContainerDirectory.appendingPathComponent("whateverYouNeed")
NSKeyedArchiver.archiveRootObject(yourObject, toFile: sharedArchiveURL.path)
For Core Data:
You can set up the container as below. I have taken this code from this answer as I have not actually tried this with Core Data myself.
You use containerURL(forSecurityApplicationGroupIdentifier: "group.appDomain.appName")! to make this work in shared container.
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application.
This implementation creates and returns a container, having loaded the store for the application to it.
This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "xx")
let appName: String = "xx"
var persistentStoreDescriptions: NSPersistentStoreDescription
let storeUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.appDomain.appName")!.appendingPathComponent("xx.sqlite")
let description = NSPersistentStoreDescription()
description.shouldInferMappingModelAutomatically = true
description.shouldMigrateStoreAutomatically = true
description.url = storeUrl
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.xxx.xx.container")!.appendingPathComponent("xx.sqlite"))]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
This answer provides a way to migrate the persistent store.
As I mentioned, I am not familiar with Ionic, so I’m not sure how working in that context might change this technique.
I hope this is helpful.
I would have just left a comment but I am unable to do so.
I was able to see that after loading a native iOS application then a Ionic project with the same bundle structure that the data within Library/Application Support/DisplayName.sqlite was still there and the data within the database still intact. (Note: this is deploying using Xcode not through the App Store)
You can see this using Xcode -> Window -> Devices and Simulators -> Devices tab -> Click on your app -> settings cog -> Download container -> after saving Show package contents
I was unable to use the Ionic SQLite native plugin to open the database for some reason. That is as far as I could get. I think it might have something to do with the space of the Application Support folder.
You can do the transition without using AirDrop or Custom Url. The idea is based on how ionic works. Much of the ionic functionality depends upon the plugins developed by community like working with hardware features.
Dealing with device specific features are done in native codes then JS wrapper classes are created for making a bridge between your code and native code.
I would suggest you to write native code which will access the data and files from CoreData and then use the cordova plugin tech to setup communication between the native code and the ionic code. here is a good post on Creating Custom Plugin for ionic and a sample github project
I have implemented the Google Mobile Analytics according to steps outlined on the Google Documentation Page. I have added the pod, I have the bridging header in place, I have downloaded the configuration file and imported the plist file provided. However, when I insert the following code into my appDelegate file, I get the error Use of Unresolved Identifier 'GGLContext'.
// Configure tracker from GoogleService-Info.plist.
var configureError:NSError?
GGLContext.sharedInstance().configureWithError(&configureError)
assert(configureError == nil, "Error configuring Google services: \(configureError)")
// Optional: configure GAI options.
var gai = GAI.sharedInstance()
gai.trackUncaughtExceptions = true // report uncaught exceptions
gai.logger.logLevel = GAILogLevel.Verbose // remove before app release
I have searched many forums and cannot find any solution. I have even run pod try Google and looked at their example project and cannot see a difference. Has any experienced this before (XCode 6.4, OSX 10.10.5, Swift 1)?
Don't use CGLContext try to implement it manually with your Google Analytic ID.
I find this more reliable, as I also try to do it the Google documented way that didn't work for me as well, I believe they have to update their context (I believe this is due to swift 2 - Update).
Example: enter this in your AppDelegate.swift within didFinishLaunchingWithOptions: method.
// Init GAI.
let Tracker = GAI.sharedInstance()
//Add Publisher Track ID
Tracker.trackerWithTrackingId("UA-XXXXXXXXX-X")
Please ask if you have any other question you would like to manually implement (trackUncaughtExceptions, logger etc)
UPDATE:
Your bridge header file should look something as following, you should add whichever feat you're using in Google Analytic Framework.
//Google Analytics
#import "GAI.h"
#import "GAITracker.h"
#import "GAIFields.h"
#import "GAIDictionaryBuilder.h"
Here is a screenshot of available classes.
just do this
pod 'Google/Analytics'
and
pod install
create -Bridging-Header.h in your project root.
and then copy that to .h file
#import <Google/Analytics.h>
and drag and drop GoogleService-Info.pist to root of project.
this is so important. go to build setting and find swift compiler-code Generation and then set $(SWIFT_MODULE_NAME)-Bridging-Header.h to Objective-C Bridging Header
clean project and build and then run.
At the point in time the 'Google/Analytics' pod doesn't support Bitcode, so when you go to do a production build, you will run into problems. Especially if you want to support WatchOS or tvOS.
At this point in time, using the "GoogleAnalytics" pod seems to be the best option, and setting up the tracker manually with
// Init GAI.
let Tracker = GAI.sharedInstance()
//Add Publisher Track ID
Tracker.trackerWithTrackingId("UA-XXXXXXXXX-X")
You can also go to https://code.google.com/p/analytics-issues/issues/detail?id=671 and scroll to the bottom to vote for Google to fix the recommended path to work with Bitcode.
To import 'Google/Analytics' framework to your swift code, simply import the module using . as package separator:
import Google.Analytics
It will import all required classes without having to modify the bridging header.
You can manually import the GGLCore.framework in ViewController.swift like
import GGLCore
Like this you can manually import the GGL file