Sharing data between app and extensions communication

Source: Internet
Author: User
Tags requires notification center

Recently played watch development, and the current watch of the main logical processing are placed in the WatchKit Extension. The real host app, the WatchKit app, is just for displaying data on the interface. So the communication and data sharing between the containing app and app extension is practiced.

  APP Groups & Framework

The two weapons are familiar to everyone. To share data, you need to open the app Groups and give the group a coquettish name so that both Nsuserdefaults and Nsfilemanager can share persistent layer data through app Groups. Core data also requires Nsfilemanager to provide stored URL support, while access to the data in the core requires a lot of template code, after persistent layer file sharing, the code should also be shared, so it is particularly important to package reusable code into the framework. (unless the amount of code is set for completion)

Or take Hardchoice as an example, I created a new target of type Cocoa touch framework named Datakit. Create a new Dataaccess.swift file and transfer the core data template code that was automatically generated in the previous appdelegate.swift. Thanks to the Swift1.2 improvements, it is easy to construct a single thread-safe model:

private static Let Instance = DataAccess ()

public class Var sharedinstance:dataaccess {

return instance

}

You need to be aware of Swift's rights control issues, and we need to add the public keyword to the user's exposed interfaces and properties before they are leaked to the framework.

In order to achieve core data persistence layer sharing, the original applicationdocumentsdirectory attribute needs to be modified:

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 ' documents application Support Directory .

Let URLs = Nsfilemanager.defaultmanager (). Urlsfordirectory (. Documentdirectory, Indomains:. Userdomainmask)

return Urls[urls.count-1] as! Nsurl

var sharedcontainerurl:nsurl? = Nsfilemanager.defaultmanager (). Containerurlforsecurityapplicationgroupidentifier (AppGroupIdentifier)

Return Sharedcontainerurl?? Nsurl ()

}()

Here the Containerurlforsecurityapplicationgroupidentifier method plays a close role.

The same code that can be shared is the model layer, which is a subclass of Nsmanagedobject, used to store data instances in core data. Don't forget to change the target of the file: "File Inspector"-> "target Membership" and select Datakit when dragging them from their original position.

When I was working with icloud and core data, I nspersistentstorecoordinatorstoreswillchangenotification, The three nspersistentstorecoordinatorstoresdidchangenotification and nspersistentstoredidimportubiquitouscontentchangesnotification Notifications for data updates are observed and processed, but are written in the Get method of Persistentstorecoordinator computed properties. Now lazy loading using the lazy keyword causes a delay in the observation of these three data update notifications, which can cause serious errors. So you need to move the three addobserverforname (name, object, queue, Usingblock) methods to the Init () method to observe the notification at the first time.

Finally, add the import Datakit in the Appdelegate.swift to replace the application (application, didfinishlaunchingwithoptions)-> Controller.managedobjectcontext = Managedobjectcontext in bool method is Controller.managedobjectcontext = DataAccess.sharedInstance.managedObjectContext, which is no longer using the context instance in the previous template code, but using the Managedobjectcontext in the DataAccess single example.

Similarly, the Savecontext () in the Applicationwillterminate (application) method is replaced by DataAccess.sharedInstance.saveContext ().

So we can also import in the app extensions Datakit to access the data in the core. And it uses the same piece of code, the same data. It is the same world, the same dream.

  Container app and Extension communication

You should know that the shared data you made before can only be used to get the data actively, and you can't get notifications 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 interface in real time on the watch. This nsnotificationcenter cannot be done because it works within the app and does not send notifications between two apps. The same kvo also powerless, their own handwriting commissioned what is not to think of (because I tried). Until I find the Savior in this article, the problem is solved:

  Cfnotificationcentergetdarwinnotifycenter

This is a system-level notification Center in the Corefoundation library, and Apple's system itself is using it to see the "Darwin" has not? haha! Look at the next Cfnotificationcenter related API, It's kind of like Nsnotificationcenter. Need to use Toll-bridge knowledge and corefoundation related classes to bridge, which is not commonly used but it is not difficult. You also need to be aware of the use of individual parameters.

  Mmwormhole

What's even more interesting is that I've also found mmwormhole this open source Library, which is designed to deliver messages between container app and extension. I read the code, although there is only one class, but still learn a lot. Although I can only use Cfnotificationcenter to notify on my hardchoice, there is absolutely no need to use mmwormhole to persist data and pass data. But I think I might use mmwormhole later, so I wrote a wormhole.swift with Swift1.2 and put it in Datakit.

  Swift and Corefoundation

The original OC wrote more than 200 lines of mmwormhole by me with 150 lines of "fresh and elegant" swift code to replace. Quotes are made because the bridge between Swift and Corefoundation is somewhat unpleasant. Because of the corefoundation in the C api,c in the pointer and type conversion is very special, there are security concerns. Swift is a secure language, but in order to invoke the api,swift of unsafe C, which is caused by historical reasons, many types have been introduced to map types in C, reference interacting with C APIs

There is no need for swift to use __bridge and type conversions, memory management transitions, as OC does, because these are all to swift: if there is a parameter type that is required by an API of type mapping to C in swift, it can be passed directly into the API. In addition, memory management is also owned by the Arc Unified management in Swift. Swift thus greatly simplifies the process of dealing with corefoundation.

Our most concern is the pointer, unsafepointer corresponds to the const CType *,unsafemutablepointer corresponding to the CType *. Of course Swifttype and CType also correspond:

More conversion rules are described in detail in the official documentation mentioned above, with only three tips:

To convert self to unsafepointer in Swift (that is, const void *) Just use this function: Unsafeaddressof (self)

Classes with the suffix "Ref" in the Corefoundation library have been removed from the suffix in swift.

The function pointer in Swift is expressed as Cfunctionpointer,type is the type of function, but it does not allow you to convert the functions or closures written by Swift into Cfunctionpointer. That is, simply not providing a way to establish a cfunctionpointer instance, except by introducing the C function externally. This involves swift and OC mixing, please stamp swift and objective-c in the Same Project

  Mixing OC in the framework

The reason why I need to do this is to destroy the purity of engineering because I have to use the following method to observe the notice:

1func cfnotificationcenteraddobserver (center:cfnotificationcenter!, Observer:unsafepointer, CallBack: Cfnotificationcallback, name:cfstring!, Object:unsafepointer, Suspensionbehavior:cfnotificationsuspensionbehavior )

In addition to the type of cfnotificationcallback parameters, the rest is said:

1typealias cfnotificationcallback = Cfunctionpointer Void) >

So he returned to cfunctionpointer the egg hurt the ground, had to write the C function in OC and then 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;

[[Nsnotificationcenter Defaultcenter] Postnotificationname:wormholenotificationname

Object:nil

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

}

@end

Then you can write it in Swift:

1CFNotificationCenterAddObserver (center, unsafeaddressof (self), helpmethod.callback, identifier, nil, cfnotificationsuspensionbehavior.deliverimmediately)

The use of OC-written classes in Swift is an easy thing to do, but it becomes unusual in the framework. I created the Helpmethod class in Datakit and created the "Datakit-bridging-header.h" file, introduced HelpMethod.h header file, and then "build Settings" under Datakit target. -> "Swift complier-code Generation"-> "objective-c bridging Header" to fill "datakit-bridging-header.h", compile error: Using Bridging headers with framework targets is unsupported.

A solution was found on the StackOverflow, so the previous "datakit-bridging-header.h" file was deleted and the reference to the bridging Header was cleared from the build Settings; add # in DataKit.h Import "HelpMethod.h" and modifies "project" to "public" on the right side of membership in the HelpMethod.h file's "file Inspector"-> "Target Datakit" ( Otherwise, there will be a compilation error for the include of Non-modular header inside Framework module ' Datakit '.

At this point, we can implement a function pointer in the Helpmethod class and use this function pointer directly in the Wormhole.swift file to pass a value for the parameter of the Cfunctionpointer type.

  Summarize

An effect chart:

This is my first time to write watch app (nonsense who is not the first), experience is not many, also because Swift1.2 has not officially released, encountered some pits. Finally overcame, but also lost chastity (after all, not pure Swift's app). There are some wrong places also please advise. As Swift continues to improve, I hope to be able to support the creation of Cfunctionpointer objects in the future so that it is good for me.

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.