Data sharing between apps and Extensions

Source: Internet
Author: User
Tags notification center

Data sharing between apps and Extensions

Recently, I have been playing Watch development. Currently, Watch's main logic processing is in WatchKit Extension. The real Host App, that is, the WatchKit App, is only used to display data on the interface. Therefore, the communication and data sharing between the ining app and the app extension are implemented.

  App Groups & Framework

Everyone is familiar with these two weapons. To share data, you need to enable App Groups and give the group a cool name, so that both NSUserDefaults and NSFileManager can share the persistent layer data through App Groups. Core Data also requires NSFileManager to provide storage URL support, while accessing the Data in Core Data requires a large amount of template code. After the persistent layer file is shared, the Code should also be shared, therefore, it is particularly important to package reusable code into a Framework. (Unless it is to complete the collection of code)

Taking HardChoice as an example, I created a target of the Cocoa Touch Framework type named DataKit. Create a DataAccess. swift file and transfer the Core Data template code automatically generated in AppDelegate. swift. Thanks to the improvements in Swift1.2, constructing a thread-safe Singleton mode becomes extremely simple:

Private static let instance = DataAccess ()

Public class var sharedInstance: DataAccess {

Return instance

}

Pay attention to the permission control of Swift. We need to add the public keyword to the public interfaces and attributes exposed to framework users.

To achieve Core Data persistence layer sharing, You need to modify the original applicationDocumentsDirectory attribute:

Lazy var applicationDocumentsDirectory: NSURL = {

// The directory the application uses to store the Core Data store file. This code uses a directory named "com. yxy. iCloudCoreDataTest" in the application's Support ents Application Support directory.

// Let urls = NSFileManager. defaultManager (). URLsForDirectory (. DocumentDirectory, inDomains:. UserDomainMask)

// Return urls [urls. count-1]! NSURL

Var sharedContainerURL: NSURL? = NSFileManager. defaultManager (). containerURLForSecurityApplicationGroupIdentifier (appGroupIdentifier)

Return sharedContainerURL ?? NSURL ()

}()

Here, the containerURLForSecurityApplicationGroupIdentifier method plays a crucial role.

The shared code is the Model layer, which is a subclass of NSManagedObject and used to store Data instances in Core Data. When you drag them from the original location, do not forget to change the target of the File: "File inspector"-> "Target Membership" and select DataKit.

During the process of synchronizing Data between iCloud and Core Data, I observed and processed the notifications of the three Data updates: NSPersistentStoreCoordinatorStoresWillChangeNotification, NSPersistentStoreCoordinatorStoresDidChangeNotification, and metadata, but it is written in the get method of persistentStoreCoordinator's calculation attribute. Now, lazy keywords are used for inert loading, leading to delayed observation of the three data update notifications, which can cause serious errors. So we need to move the three addObserverForName (name, object, queue, usingBlock) Methods to the init () method and observe the notification immediately.

Finally, in AppDelegate. add import DataKit in swift, replace the application (application, didfinishlaunchingwitexceptions)-> controller in Bool method. managedObjectContext = managedObjectContext is controller. managedObjectContext = DataAccess. sharedInstance. managedObjectContext: instead of using the context instance in the previous template code, the managedObjectContext in the DataAccess Singleton is used.

Similarly, the saveContext () in the applicationWillTerminate (application) method must be replaced with DataAccess. sharedInstance. saveContext ().

So we can also import DataKit in App Extensions to access Data in Core Data. In addition, the same piece of code and data are used. It's just the same world, the same dream.

  Communication between the Container app and Extension

You must know that the previously shared data can only be actively acquired, and cannot be notified in real time when the data changes. If the user changes the data on the iPhone, we need to change the display of the data on the Watch in real time. Nsicationicationcenter cannot do this because it only works within the App and does not send notifications between two apps. Similarly, KVO can't do anything about it. Don't think about anything by hand (because I tried it ). Until I found the savior in this article, the problem was solved:

  CFNotificationCenterGetDarwinNotifyCenter

This is a system-level notification center in the CoreFoundation library. Apple's system is also using it. Why did we see "Darwin? Haha! I read cficationicationcenter-related APIs, which are a bit like NSNotificationCenter. The knowledge of Toll-Bridge is required to Bridge with CoreFoundation-related classes, which is not commonly used but not difficult. Pay attention to the use of some parameters.

  MMWormhole

What's more interesting is that I also found the MMWormhole open-source library, which is used to transmit messages between the Container app and the Extension. I read its code. Although there is only one class, I still learned a lot. Although I can only use CFNotificationCenter for notifications on my HardChoice, I do not need to use MMWormhole to persist data and transmit data. However, I think MMWormhole may be used in the future, so I used Swift1.2 to re-write a Wormhole. swift and put it in DataKit.

  Swift and CoreFoundation

In the past, the MMWormhole of over two hundred lines written by OC was replaced by 150 lines of "fresh and elegant" Swift code. The quotation marks are quoted because the bridge between Swift and CoreFoundation is unpleasant. Because CoreFoundation is a c api, pointer and type conversion in C is very good, and there is a security risk. Swift is a secure language, but in order to call APIs of insecure C caused by historical reasons, Swift introduces many types to map types in C. For details, refer to Interacting with C APIs.

In Swift, _ bridge and type conversion and memory management are not used in the same way as OC, because all of these are handed over to Swift: If Swift contains the parameter types required by APIs mapped to C, you can directly pass it into the API. In addition, the memory management is also centrally managed by the ARC in Swift. Therefore, Swift greatly simplifies the process of dealing with CoreFoundation.

We are most concerned with pointers. UnsafePointer corresponds to const CType * And UnsafeMutablePointer corresponds to CType *. Of course, SwiftType and CType also correspond:

For more conversion rules, the official document mentioned above has a detailed description. Here we only talk about three tips:

To convert self into UnsafePointer (that is, const void *) in Swift, you only need to use this function: unsafeAddressOf (self)

Classes suffixed with "Ref" in the CoreFoundation Library have been removed from Swift.

In Swift, the function pointer is expressed as CFunctionPointer, and Type is the function Type. However, you are not allowed to convert functions or closures written by Swift into CFunctionPointer, that is, the CFunctionPointer instance creation method is not provided, and the function of C can only be introduced externally. This involves the mixing of Swift and OC. Stamp Swift and Objective-C in the Same Project

  Mixing OC in Framework

The reason why I need to do this kind of destructive engineering purity is that the following method is used to observe the notification:

1 func CFNotificationCenterAddObserver (center: CFNotificationCenter !, Observer: UnsafePointer, callBack: CFNotificationCallback, name: CFString !, Object: UnsafePointer, suspensionBehavior: CFNotificationSuspensionBehavior)

Except for the CFNotificationCallback parameter, the rest can be said:

1 typealias CFNotificationCallback = CFunctionPointer Void)>

So I went back to the CFunctionPointer, and I had to write the C function in the OC and call it:

Static NSString * const WormholeNotificationName = @ "WormholeNotificationName ";

@ Implementation HelpMethod

-(Instancetype) init

{

Self = [super init];

If (self ){

_ Callback = wormholeNotificationCallback;

}

Return self;

}

Void wormholeNotificationCallback (CFNotificationCenterRef center,

Void * observer,

CFStringRef name,

Void const * object,

CFDictionaryRef userInfo ){

NSString * identifier = (_ bridge NSString *) name;

[[Nsicationcenter center defacenter center] postNotificationName: WormholeNotificationName

Object: nil

UserInfo: @ {@ "identifier": identifier}];

}

@ End

Then write in Swift as follows:

1 CFNotificationCenterAddObserver (center, unsafeAddressOf (self), helpMethod. callback, identifier, nil, CFNotificationSuspensionBehavior. DeliverImmediately)

Classes written using OC in Swift are easy, but they become unusual in the Framework. I created the HelpMethod class in DataKit and created the "DataKit-Bridging-Header.h" file to add HelpMethod. import the h Header file, and then fill in "DataKit-Bridging-Header.h" under "Build Settings"-> "Swift Complier-Code Generation"-> "Objective-C Bridging Header" under "DataKit target ", compilation error: using bridging headers with framework targets is unsupported.

The solution was found on stackoverflow, so the previous DataKit-Bridging-Header.h file was deleted and the Build Settings references about the Bridging Header was cleared; In DataKit. h add # import "HelpMethod. h ", and in HelpMethod. in the "File inspector"-> "Target Membership" File, change "project" to "public" (otherwise, include of non-modular header inside framework module "DataKit will appear. ).

So far, we can implement a function pointer in the HelpMethod class, and directly use this function pointer in the Wormhole. swift file to pass values for parameters of the CFunctionPointer type.

  Summary

Here:

This is my first time writing a Watch App (who is not the first nonsense). There are not many experiences, because Swift1.2 has not yet been officially released and has encountered some pitfalls. Finally, I overcame it, but also lost my chastity (after all, it is not a pure Swift App ). Please advise me if there is anything wrong. As Swift continues to improve, I hope it will support creating CFunctionPointer objects in the future.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.