Mixpanel does not work with swift - swift

Cocoapods is not ready for Swift yet. When I implemented Mixpanel into my iOS project that uses Swift, I got several errors.
How to fix this issue and make mixpanel to work?

Add this line of code to every file that is giving an error
#import <UIKit/UIKit.h>
Also need to add these in your linked frameworks and libraries:
libicucore.dylib
CFNetwork.framework
Security.framework
Then add this to your Bridging-Header.h file:
#import "Mixpanel.h"
Then add this piece of code in your appdelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
//other stuff here
Mixpanel.sharedInstanceWithToken("yourtokennumbergoeshere")
var mixPanel = Mixpanel.sharedInstance()
mixPanel.track("Finished Launching", properties: ["name1":"property1","name2":"property2"])
return true
}

Change your #import "mixpanel.h" to #import "Mixpanel/Mixpanel.h"

Use this unofficial Swift client! Probably not as good as the real thing, but it will do for basic tracking. https://github.com/soffes/Mixpanel

Getting A/B Testing to work in Swift is not trivial as the macro is not supported. Feel free to use this gist :
https://gist.github.com/ohad7/8ce12a432773fe3b1bf3
import Foundation
import Mixpanel
public class Tweaks : NSObject, MPTweakObserver {
public static let sharedInstance = Tweaks(tweaks: [ ExecuteDiskCleanup])
public static let ExecuteDiskCleanup = Tweak.Booln(name: "Cleanup-Disk", defaultValue:false)
private var values = [String:AnyObject]()
private let tweaks: [Tweak]
public init(tweaks: [Tweak]) {
self.tweaks = tweaks
let defaults = NSUserDefaults.standardUserDefaults()
for tweak in tweaks {
if let value: AnyObject = defaults.objectForKey(Tweaks.generateKey(tweak.name)) {
self.values[tweak.name] = value
println("Initialized Tweak \(tweak.name) with value: \(value)")
}
}
}
public func setup() {
tweaks.foreach(){ tweak in
let theTweak = MPTweak(name: tweak.name, andEncoding: "UTF-8")
theTweak.defaultValue = tweak.defaultValue
theTweak.addObserver(self)
MPTweakStore.sharedInstance().addTweak(theTweak)
}
Mixpanel.sharedInstance().checkForVariantsOnActive = true
Mixpanel.sharedInstance().joinExperiments()
}
public func get(tweaks: [Tweak]) -> [String:AnyObject] {
var result = [String:AnyObject]()
synced(values) {
tweaks.foreach(){ tweak in
result[tweak.name] = self.values[tweak.name]
}
}
return result
}
public func tweakDidChange(tweak: MPTweak) {
println("tweakDidChange Tweak \(tweak.name) current value :\(tweak.currentValue)")
synced(self.values) {
let localTweak = self.tweaks.filter{ $0.name == tweak.name}.first
if let localTweak = localTweak{
switch (localTweak) {
case .Booln:
self.values[tweak.name] = Bool(tweak.currentValue as? Int == 1)
default:
self.values[tweak.name] = tweak.currentValue
}
println("Tweak \(localTweak.name) -> \(self.values[localTweak.name])")
NSUserDefaults.standardUserDefaults().setObject(self.values[localTweak.name], forKey: Tweaks.generateKey(localTweak.name))
NSUserDefaults.standardUserDefaults().synchronize()
}
}
}
private class func generateKey(key: String) -> String {
return "mp_tweak_\(key)"
}
}
public enum Tweak {
case Str(name: String, defaultValue: String)
case Booln(name: String, defaultValue: Bool)
case Integer(name: String, defaultValue: Int)
case Flt(name: String, defaultValue: Float)
var name : String {
switch (self) {
case let .Str(n, d): return n
case let .Booln(n, d): return n
case let .Integer(n, d): return n
case let .Flt(n, d): return n
}
}
var defaultValue : AnyObject {
switch (self) {
case let .Str(n, d): return d
case let .Booln(n, d): return d
case let .Integer(n, d): return d
case let .Flt(n, d): return d
}
}
public func get<T>() -> T? {
return get(self.defaultValue as? T)
}
public func get<T>(defaultValue: T?) -> T? {
var value = synced(Tweaks.sharedInstance.values){
return Tweaks.sharedInstance.values[self.name] as? T
}
return value != nil ? value : defaultValue
}
}
// Examples :
// public static let SomeIntegerZero = Tweak.Integer(name: "SomeIntegerZero", defaultValue:0)
// public static let SomeIntegerNonZero = Tweak.Integer(name: "SomeIntegerNonZero", defaultValue:6666)
// public static let SomeBooleanFalse = Tweak.Booln(name: "SomeBooleanFalse", defaultValue:false)
// public static let SomeBooleanTrue = Tweak.Booln(name: "SomeBooleanTrue", defaultValue:true)
// public static let SomeStringEmpty = Tweak.Str(name: "SomeEmptyString", defaultValue:"")
// public static let SomeStringFull = Tweak.Str(name: "SomeFullString", defaultValue:"full")
/*** Utilities - sync methods ***/
func synced<T>(lock: AnyObject, closure: () -> T) -> T {
objc_sync_enter(lock)
let result = closure()
objc_sync_exit(lock)
return result
}
func synced(lock: AnyObject, closure: () -> ()) {
objc_sync_enter(lock)
closure()
objc_sync_exit(lock)
}

Looks like Mixpanel has posted some official documentation on this:
https://mixpanel.com/blog/2015/08/17/community-tip-implement-mixpanel-in-swift-apps
I had trouble with podfiles and bridging-header imports, but the use_frameworks! suggested in the above link works perfectly for me.

Related

Blank constant when trying to get list of classes that have adopted a Protocol

I am trying to get a list of classes that have adopted a certain Protocol Migration: Preparation, and then to append those classes into an array. Here is the function in question:
struct Migrations {
static func getMigrations() -> [Preparation.Type] {
var migrationsList = [Preparation.Type]()
var count = UInt32(0)
let classList = objc_copyClassList(&count)!
for i in 0..<Int(count) {
let classInfo = ClassInfo(classList[i])!
if let cls = classInfo.classObject as? Migration.Type {
migrationsList.append(cls)
print(cls.description)
}
}
return migrationsList
}
}
In principle all that should work, but when debugging I note that the classInfo variable is referring to each class in the iteration, but when assigning and casting in the if let as line, the constant cls is always blank - neither a value/class nor nil, just completely blank.
Any idea what I got wrong with that code?
I am also open to suggestions for any better way to get a list of all classes that have adopted a particular protocol...
EDIT: I forgot to provide the code for ClassInfo
import Foundation
struct ClassInfo: CustomStringConvertible, Equatable {
let classObject: AnyClass
let className: String
init?(_ classObject: AnyClass?) {
guard classObject != nil else { return nil }
self.classObject = classObject!
let cName = class_getName(classObject)!
self.className = String(cString: cName)
}
var superclassInfo: ClassInfo? {
let superclassObject: AnyClass? = class_getSuperclass(self.classObject)
return ClassInfo(superclassObject)
}
var description: String {
return self.className
}
static func ==(lhs: ClassInfo, rhs: ClassInfo) -> Bool {
return lhs.className == rhs.className
}
}
I can't explain why cls is always blank, like I said in my comment it's something I run into every time I'm dealing with meta types. As for making the code work as intended, I found this q&a and updated it with Swift 3 to get this code which should cover your situation. It's important to stress that this will only work if you correctly expose Swift to the Objective-C runtime.
Drop this code anywhere and call print(Migrations.getMigrations()) from a convenient entry point.
struct Migrations {
static func getMigrations() -> [Preparation.Type] {
return getClassesImplementingProtocol(p: Preparation.self) as! [Preparation.Type]
}
static func getClassesImplementingProtocol(p: Protocol) -> [AnyClass] {
let classes = objc_getClassList()
var ret = [AnyClass]()
for cls in classes {
if class_conformsToProtocol(cls, p) {
ret.append(cls)
}
}
return ret
}
static func objc_getClassList() -> [AnyClass] {
let expectedClassCount = ObjectiveC.objc_getClassList(nil, 0)
let allClasses = UnsafeMutablePointer<AnyClass?>.allocate(capacity: Int(expectedClassCount))
let autoreleasingAllClasses = AutoreleasingUnsafeMutablePointer<AnyClass?>(allClasses)
let actualClassCount:Int32 = ObjectiveC.objc_getClassList(autoreleasingAllClasses, expectedClassCount)
var classes = [AnyClass]()
for i in 0 ..< actualClassCount {
if let currentClass: AnyClass = allClasses[Int(i)] {
classes.append(currentClass)
}
}
allClasses.deallocate(capacity: Int(expectedClassCount))
return classes
}
}
class Migration: Preparation {
}
#objc
protocol Preparation {
}

swift 3 downcast to dynamic class

I am trying to create a couple of objects which are dependent one to each other and they mush have a method to downcast directly the concrete class of the other object. Something like this:
protocol aProt
{
var bVar:bProt! { get set }
}
protocol bProt
{
var aVar:aProt! { get set }
}
class a: aProt
{
var bVar: bProt!
func bConcrete() -> b {
return bVar as! b
}
}
class b: bProt
{
var aVar: aProt!
func aConcrete() -> a {
return aVar as! a
}
Now, the problem is that I want this behavior (func aConcrete(),func bConcrete()) to be inherited by the subclasses of a and b. Then I thought the perfect way of doing this was using generics, but... There's no way of doing this.
class a: aProt
{
var bVar: bProt!
func bConcrete() -> T {
return bVar as! T
}
}
class b: bProt
{
var aVar: aProt!
func aConcrete<T>() -> T {
return aVar as! T
}
You can do it but when you have to use it you must downcast the variable anyway, so there is no way of doing it in a clean manner:
let aObject = a()
let bSubclassObject = a.bConcrete() // The compiler complains it cannot infer the class of T
let bSubclassObject = a.bConcrete() as! bSubclass // this works, but this is exactly which I wanted to avoid... :(
Define the generic function and add where to T:
protocol aProt {
var bVar: bProt! { get set }
}
protocol bProt {
var aVar:aProt! { get set }
}
class a: aProt {
var bVar: bProt!
func bConcrete<T: b>(_ type: T.Type) -> T? {
return bVar as? T
}
}
class b: bProt {
var aVar: aProt!
func aConcrete<T: a>(_ type: T.Type) -> T? {
return aVar as? T
}
}
class a1: a { }
class b1: b {
var fullName: String = "new object"
}
let aObj = a()
aObj.bVar = b1()
let bObj = aObj.bConcrete(b1.self)
bObj?.fullName
According to your requirement, calls bConcrete(b1.self) might still not good enough, but at least you need to know what type of data you are expecting to return.

Generic settings class in Swift 2

I'm trying to implement a class in Swift 2 for a single setting of any type that uses NSUserDefaults under the covers.
Problem: How do I define a class for storing and retrieving any type of object, including Dictionary?
I have a solution that works with AnyObject which consists of a generic protocol (Settable) and a generic class (Setting). SettingsStore is a wrapper around NSUserDefaults.
// MARK: Settable Protocol
public protocol Settable {
typealias T
init(key: String, defaultValue: T, settingsStore: SettingsStore)
var value: T { get set }
func loadCurrentValue()
}
// MARK: Settings Class
public class Setting<T: AnyObject>: Settable {
private let key: String
private let defaultValue: T
private let settingsStore: SettingsStore
private var currentValue: T?
public required init(key: String, defaultValue: T, settingsStore: SettingsStore) {
self.key = key
self.defaultValue = defaultValue
self.settingsStore = settingsStore
}
public var value: T {
get {
if self.currentValue == nil {
self.loadCurrentValue()
}
return self.currentValue!
}
set {
self.currentValue = newValue
self.settingsStore.setObject(newValue.toAnyObject(), forKey: self.key)
}
}
public func loadCurrentValue() {
let optionalValue: T? = self.settingsStore.objectForKey(key) as? T
if let value = optionalValue {
self.currentValue = value
} else {
self.currentValue = self.defaultValue
}
}
}
This allows me to create a setting like this:
let specialId: Setting<String>
init() {
self.specialId = Setting<String>(
key: "specialId",
defaultValue: "<somevalue>",
settingsStore: self.settingsStore)
}
The problem with this is that it doesn't work with value types, such as String, Bool, Int, Double, Array, or Dictionary because they are all value types and value types don't conform to the AnyObject protocol.
I've solved the problem for some of these using a protocol and extensions based on NSString and NSNumber, but a solution Dictionary is proving to be elusive (I don't need a solution for Array at the moment so I haven't spent time trying to solve that one).
// Change definition of Setting class like this:
public class Setting<T: AnyObjectRepresentable>: Settable {
...
}
public protocol AnyObjectRepresentable {
func toAnyObject() -> AnyObject
static func fromAnyObject(value: AnyObject) -> Self?
}
extension AnyObjectRepresentable where Self: AnyObject {
public func toAnyObject() -> AnyObject {
return self
}
public static func fromAnyObject(value: AnyObject) -> AnyObject? {
return value
}
}
extension String: AnyObjectRepresentable {
public func toAnyObject() -> AnyObject {
return NSString(string: self)
}
public static func fromAnyObject(value: AnyObject) -> String? {
let convertedValue = value as? String
return convertedValue
}
}
extension Bool: AnyObjectRepresentable {
public func toAnyObject() -> AnyObject {
return NSNumber(bool: self)
}
public static func fromAnyObject(value: AnyObject) -> Bool? {
let convertedValue = value as? Bool
return convertedValue
}
}
// Add extensions for Int and Double that look like the above extension for Bool.
I tried two different approaches for Dictionary. The first one is similar to the String approach:
extension Dictionary: AnyObjectRepresentable {
public func toAnyObject() -> AnyObject {
let value = self as NSDictionary
return value
}
public static func fromAnyObject(value: AnyObject) -> Dictionary? {
let convertedValue = value as? Dictionary
return convertedValue
}
}
Xcode gives me the following error on the first line of the toAnyObject() method implementation:
'Dictionary' is not convertible to 'NSDictionary'
Next I tried extending NSDictionary directly:
extension NSDictionary: AnyObjectRepresentable {
public func toAnyObject() -> AnyObject {
return NSDictionary(dictionary: self)
}
public static func fromAnyObject(value: AnyObject) -> NSDictionary? {
let convertedValue = value as? NSDictionary
return convertedValue
}
}
Xcode gives me the following error on the declaration of fromAnyObject():
Method 'fromAnyObject' in non-final class 'NSDictionary' must return Self to conform to protocol 'AnyObjectRepresentable'
I'm at my wits. Is this solvable?
Thanks,
David
UPDATED 2015-09-15 16:30
For background, here is the definition and an implementation of SettingsStore:
public protocol SettingsStore {
func objectForKey(key: String) -> AnyObject?
func setObject(value: AnyObject?, forKey key: String)
func dictionaryForKey(key: String) -> [String:AnyObject]?
}
public class UserDefaultsSettingsStore {
private let userDefaults: NSUserDefaults
public init() {
self.userDefaults = NSUserDefaults.standardUserDefaults()
}
public init(suiteName: String) {
self.userDefaults = NSUserDefaults(suiteName: suiteName)!
}
}
extension UserDefaultsSettingsStore: SettingsStore {
public func objectForKey(key: String) -> AnyObject? {
return self.userDefaults.objectForKey(key)
}
public func setObject(value: AnyObject?, forKey key: String) {
self.userDefaults.setObject(value, forKey: key)
self.userDefaults.synchronize()
}
public func dictionaryForKey(key: String) -> [String : AnyObject]? {
return self.userDefaults.dictionaryForKey(key)
}
}
If you substitute AnyObject with Any, I think you'll get the results you're looking for. Specifically, replace this line:
public class Setting<T: AnyObject>: Settable {
with this line
public class Setting<T: Any>: Settable {

How to create generic protocols in Swift?

I'd like to create a protocol with a method that takes a generic input and returns a generic value.
This is what I've tried so far, but it produces the syntax error.
Use of undeclared identifier T.
What am I doing wrong?
protocol ApiMapperProtocol {
func MapFromSource(T) -> U
}
class UserMapper: NSObject, ApiMapperProtocol {
func MapFromSource(data: NSDictionary) -> UserModel {
var user = UserModel() as UserModel
var accountsData:NSArray = data["Accounts"] as NSArray
return user
}
}
It's a little different for protocols. Look at "Associated Types" in Apple's documentation.
This is how you use it in your example
protocol ApiMapperProtocol {
associatedtype T
associatedtype U
func MapFromSource(_:T) -> U
}
class UserMapper: NSObject, ApiMapperProtocol {
typealias T = NSDictionary
typealias U = UserModel
func MapFromSource(_ data:NSDictionary) -> UserModel {
var user = UserModel()
var accountsData:NSArray = data["Accounts"] as NSArray
// For Swift 1.2, you need this line instead
// var accountsData:NSArray = data["Accounts"] as! NSArray
return user
}
}
To expound on Lou Franco's answer a bit, If you wanted to create a method that used a particular ApiMapperProtocol, you do so thusly:
protocol ApiMapperProtocol {
associatedtype T
associatedtype U
func mapFromSource(T) -> U
}
class UserMapper: NSObject, ApiMapperProtocol {
// these typealiases aren't required, but I'm including them for clarity
// Normally, you just allow swift to infer them
typealias T = NSDictionary
typealias U = UserModel
func mapFromSource(data: NSDictionary) -> UserModel {
var user = UserModel()
var accountsData: NSArray = data["Accounts"] as NSArray
// For Swift 1.2, you need this line instead
// var accountsData: NSArray = data["Accounts"] as! NSArray
return user
}
}
class UsesApiMapperProtocol {
func usesApiMapperProtocol<
SourceType,
MappedType,
ApiMapperProtocolType: ApiMapperProtocol where
ApiMapperProtocolType.T == SourceType,
ApiMapperProtocolType.U == MappedType>(
apiMapperProtocol: ApiMapperProtocolType,
source: SourceType) -> MappedType {
return apiMapperProtocol.mapFromSource(source)
}
}
UsesApiMapperProtocol is now guaranteed to only accept SourceTypes compatible with the given ApiMapperProtocol:
let dictionary: NSDictionary = ...
let uses = UsesApiMapperProtocol()
let userModel: UserModel = uses.usesApiMapperProtocol(UserMapper()
source: dictionary)
In order to achieve having generics and as well having it declare like this let userMapper: ApiMapperProtocol = UserMapper() you have to have a Generic Class conforming to the protocol which returns a generic element.
protocol ApiMapperProtocol {
associatedtype I
associatedType O
func MapFromSource(data: I) -> O
}
class ApiMapper<I, O>: ApiMapperProtocol {
func MapFromSource(data: I) -> O {
fatalError() // Should be always overridden by the class
}
}
class UserMapper: NSObject, ApiMapper<NSDictionary, UserModel> {
override func MapFromSource(data: NSDictionary) -> UserModel {
var user = UserModel() as UserModel
var accountsData:NSArray = data["Accounts"] as NSArray
return user
}
}
Now you can also refer to userMapper as an ApiMapper which have a specific implementation towards UserMapper:
let userMapper: ApiMapper = UserMapper()
let userModel: UserModel = userMapper.MapFromSource(data: ...)
How to create and use generic Protocol:
protocol Generic {
associatedtype T
associatedtype U
func operation(_ t: T) -> U
}
// Use Generic Protocol
struct Test: Generic {
typealias T = UserModel
typealias U = Any
func operation(_ t: UserModel) -> Any {
let dict = ["name":"saurabh"]
return dict
}
}
You can use templates methods with type-erasure...
protocol HeavyDelegate : class {
func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R
}
class Heavy<P, R> {
typealias Param = P
typealias Return = R
weak var delegate : HeavyDelegate?
func inject(p : P) -> R? {
if delegate != nil {
return delegate?.heavy(self, shouldReturn: p)
}
return nil
}
func callMe(r : Return) {
}
}
class Delegate : HeavyDelegate {
typealias H = Heavy<(Int, String), String>
func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R {
let h = heavy as! H
h.callMe("Hello")
print("Invoked")
return "Hello" as! R
}
}
let heavy = Heavy<(Int, String), String>()
let delegate = Delegate()
heavy.delegate = delegate
heavy.inject((5, "alive"))

Is there a way to set associated objects in Swift?

Coming from Objective-C you can call function objc_setAssociatedObject between 2 objects to have them maintain a reference, which can be handy if at runtime you don't want an object to be destroyed until its reference is removed also. Does Swift have anything similar to this?
Here is a simple but complete example derived from jckarter's answer.
It shows how to add a new property to an existing class. It does it by defining a computed property in an extension block. The computed property is stored as an associated object:
import ObjectiveC
// Declare a global var to produce a unique address as the assoc object handle
private var AssociatedObjectHandle: UInt8 = 0
extension MyClass {
var stringProperty:String {
get {
return objc_getAssociatedObject(self, &AssociatedObjectHandle) as! String
}
set {
objc_setAssociatedObject(self, &AssociatedObjectHandle, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
EDIT:
If you need to support getting the value of an uninitialized property and to avoid getting the error unexpectedly found nil while unwrapping an Optional value, you can modify the getter like this:
get {
return objc_getAssociatedObject(self, &AssociatedObjectHandle) as? String ?? ""
}
The solution supports all the value types as well, and not only those that are automagically bridged, such as String, Int, Double, etc.
Wrappers
import ObjectiveC
final class Lifted<T> {
let value: T
init(_ x: T) {
value = x
}
}
private func lift<T>(x: T) -> Lifted<T> {
return Lifted(x)
}
func setAssociatedObject<T>(object: AnyObject, value: T, associativeKey: UnsafePointer<Void>, policy: objc_AssociationPolicy) {
if let v: AnyObject = value as? AnyObject {
objc_setAssociatedObject(object, associativeKey, v, policy)
}
else {
objc_setAssociatedObject(object, associativeKey, lift(value), policy)
}
}
func getAssociatedObject<T>(object: AnyObject, associativeKey: UnsafePointer<Void>) -> T? {
if let v = objc_getAssociatedObject(object, associativeKey) as? T {
return v
}
else if let v = objc_getAssociatedObject(object, associativeKey) as? Lifted<T> {
return v.value
}
else {
return nil
}
}
A possible
Class extension (Example of usage)
extension UIView {
private struct AssociatedKey {
static var viewExtension = "viewExtension"
}
var referenceTransform: CGAffineTransform? {
get {
return getAssociatedObject(self, associativeKey: &AssociatedKey.viewExtension)
}
set {
if let value = newValue {
setAssociatedObject(self, value: value, associativeKey: &AssociatedKey.viewExtension, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
}
I wrote a modern wrapper available at https://github.com/b9swift/AssociatedObject
You may be surprised that it even supports Swift structures for free.
Obviously, this only works with Objective-C objects. After fiddling around with this a bit, here's how to make the calls in Swift:
import ObjectiveC
// Define a variable whose address we'll use as key.
// "let" doesn't work here.
var kSomeKey = "s"
…
func someFunc() {
objc_setAssociatedObject(target, &kSomeKey, value, UInt(OBJC_ASSOCIATION_RETAIN))
let value : AnyObject! = objc_getAssociatedObject(target, &kSomeKey)
}
Update in Swift 3.0
For example this is a UITextField
import Foundation
import UIKit
import ObjectiveC
// Declare a global var to produce a unique address as the assoc object handle
var AssociatedObjectHandle: UInt8 = 0
extension UITextField
{
var nextTextField:UITextField {
get {
return objc_getAssociatedObject(self, &AssociatedObjectHandle) as! UITextField
}
set {
objc_setAssociatedObject(self, &AssociatedObjectHandle, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
Klaas answer just for Swift 2.1:
import ObjectiveC
let value = NSUUID().UUIDString
var associationKey: UInt8 = 0
objc_setAssociatedObject(parentObject, &associationKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
let fetchedValue = objc_getAssociatedObject(parentObject, &associationKey) as! String
Just add #import <objc/runtime.h> on your brindging header file to access objc_setAssociatedObject under swift code
The above friend has answered your question, but if it is related to closure properties, please note:
```
import UIKit
public extension UICollectionView {
typealias XYRearrangeNewDataBlock = (_ newData: [Any]) -> Void
typealias XYRearrangeOriginaDataBlock = () -> [Any]
// MARK:- associat key
private struct xy_associatedKeys {
static var originalDataBlockKey = "xy_originalDataBlockKey"
static var newDataBlockKey = "xy_newDataBlockKey"
}
private class BlockContainer {
var rearrangeNewDataBlock: XYRearrangeNewDataBlock?
var rearrangeOriginaDataBlock: XYRearrangeOriginaDataBlock?
}
private var newDataBlock: BlockContainer? {
get {
if let newDataBlock = objc_getAssociatedObject(self, &xy_associatedKeys.newDataBlockKey) as? BlockContainer {
return newDataBlock
}
return nil
}
set(newValue) {
objc_setAssociatedObject(self, xy_associatedKeys.newDataBlockKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
}
convenience init(collectionVewFlowLayout : UICollectionViewFlowLayout, originalDataBlock: #escaping XYRearrangeOriginaDataBlock, newDataBlock: #escaping XYRearrangeNewDataBlock) {
self.init()
let blockContainer: BlockContainer = BlockContainer()
blockContainer.rearrangeNewDataBlock = newDataBlock
blockContainer.rearrangeOriginaDataBlock = originalDataBlock
self.newDataBlock = blockContainer
}
```
For 2022, now very simple:
// Utils-tags.swift
// Just a "dumb Swift trick" to add a string tag to a view controller.
// For example, with UIDocumentPickerViewController you need to know
// "which button was clicked to launch a picker"
import UIKit
private var _docPicAssociationKey: UInt8 = 0
extension UIDocumentPickerViewController {
public var tag: String {
get {
return objc_getAssociatedObject(self, &_docPicAssociationKey)
as? String ?? ""
}
set(newValue) {
objc_setAssociatedObject(self, &_docPicAssociationKey,
newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
}