You must use [EKEvent eventWithEventStore:] to create an event - ios

I have used CalenderKit on my app. I have 2 types of users my goal is to collect calender data from targeted type and reflect it to other targeted type.
First type went quite well, I have collected the date range data and simply pulled it to Firebase. Works fine.
Second type makes me lose my mind. So idea here is to pull stored data from Firebase, generate it to EKEvent and reflect it to an empty Calendar via CalenderKit.
// EventBox Class
struct EventBox {
let startDate: String
let endDate: String
let isAllDay: Bool
let title: String
}
var userEventBox: [EventBox] = []
func getEvents() {
self.db.collection(XX).document(XX).collection("Calendar").addSnapshotListener { [self] (querySnapshot, error) in
self.userEventBox = []
if let e = error {
print("There was an issue retrieving data from Firestore. \(e)")
} else {
if let snapshotDocuments = querySnapshot?.documents {
for doc in snapshotDocuments {
let data = doc.data()
if let title = data["title"] as? String ,
let startDate = data["startDate"] as? String ,
let endDate = data["endDate"] as? String ,
let isAllDay = data["isAllDay"] as? Bool
{
let newEventBox = EventBox(startDate: startDate, endDate: endDate, isAllDay: isAllDay, title: title)
self.userEventBox.append(newEventBox)
print(newEventBox)
self.generate() //Triggers second func after data collected and stored
}
}
}
}
}
}
func generate() {
for generation in userEventBox {
// I had issues when I tried to save data to Firebase as Date and pull it back. So I decided to store dates as String and use dateFormatter when downloaded.
let isoStartDate = generation.startDate
let isoEndDate = generation.endDate
let dateFormatter = ISO8601DateFormatter()
let startDate = dateFormatter.date(from:isoStartDate)!
let endDate = dateFormatter.date(from:isoEndDate)!
//dates formatted
if let event = EKEvent() as? EKEvent {
event.title = generation.title
event.isAllDay = generation.isAllDay
event.startDate = startDate
event.endDate = endDate
event.calendar.cgColor = CGColor(red: 1, green: 1, blue: 1, alpha: 1)
self.generated = [event]
}
}
}
var generated = [EKEvent()] // This variable is where I store all events after its generated
// And finally I am triggering the first getEvents func in override
override func eventsForDate(_ date: Date) -> [EventDescriptor] {
self.getEvents()
let calenderKitEvents = generated.map(EKWrapper.init)
return calenderKitEvents
}
}
Problem is I am having this error and I couldnt figured it out for days. Any help will be appreciated.

So here is what I did and what has been achieved with it.
// created another class for generation
struct EventBoxWithDate {
var startDate: Date
var endDate: Date
var isAllDay: Bool
var title: String
var color: CGColor
}
// Than I created a custom EKWrapper
final class CustomEKWrapper: EventDescriptor {
public var dateInterval: DateInterval {
get {
DateInterval(start: ekEvent.startDate, end: ekEvent.endDate)
}
set {
ekEvent.startDate = newValue.start
ekEvent.endDate = newValue.end
}
}
public var isAllDay: Bool {
get {
ekEvent.isAllDay
}
set {
ekEvent.isAllDay = newValue
}
}
public var text: String {
get {
ekEvent.title
}
set {
ekEvent.title = newValue
}
}
public var attributedText: NSAttributedString?
public var lineBreakMode: NSLineBreakMode?
public var color: UIColor {
get {
UIColor(cgColor: ekEvent.color)
}
}
public var backgroundColor = UIColor()
public var textColor = SystemColors.label
public var font = UIFont.boldSystemFont(ofSize: 12)
public weak var editedEvent: EventDescriptor? {
didSet {
updateColors()
}
}
public private(set) var ekEvent: EventBoxWithDate
public init(eventKitEvent: EventBoxWithDate) {
self.ekEvent = eventKitEvent
applyStandardColors()
}
public func makeEditable() -> Self {
let cloned = Self(eventKitEvent: ekEvent)
cloned.editedEvent = self
return cloned
}
public func commitEditing() {
guard let edited = editedEvent else {return}
edited.dateInterval = dateInterval
}
private func updateColors() {
(editedEvent != nil) ? applyEditingColors() : applyStandardColors()
}
/// Colors used when event is not in editing mode
private func applyStandardColors() {
backgroundColor = dynamicStandardBackgroundColor()
textColor = dynamicStandardTextColor()
}
/// Colors used in editing mode
private func applyEditingColors() {
backgroundColor = color.withAlphaComponent(0.95)
textColor = .white
}
/// Dynamic color that changes depending on the user interface style (dark / light)
private func dynamicStandardBackgroundColor() -> UIColor {
let light = backgroundColorForLightTheme(baseColor: color)
let dark = backgroundColorForDarkTheme(baseColor: color)
return dynamicColor(light: light, dark: dark)
}
/// Dynamic color that changes depending on the user interface style (dark / light)
private func dynamicStandardTextColor() -> UIColor {
let light = textColorForLightTheme(baseColor: color)
return dynamicColor(light: light, dark: color)
}
private func textColorForLightTheme(baseColor: UIColor) -> UIColor {
var h: CGFloat = 0, s: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
baseColor.getHue(&h, saturation: &s, brightness: &b, alpha: &a)
return UIColor(hue: h, saturation: s, brightness: b * 0.4, alpha: a)
}
private func backgroundColorForLightTheme(baseColor: UIColor) -> UIColor {
baseColor.withAlphaComponent(0.3)
}
private func backgroundColorForDarkTheme(baseColor: UIColor) -> UIColor {
var h: CGFloat = 0, s: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
color.getHue(&h, saturation: &s, brightness: &b, alpha: &a)
return UIColor(hue: h, saturation: s, brightness: b * 0.4, alpha: a * 0.8)
}
private func dynamicColor(light: UIColor, dark: UIColor) -> UIColor {
if #available(iOS 13.0, *) {
return UIColor { traitCollection in
let interfaceStyle = traitCollection.userInterfaceStyle
switch interfaceStyle {
case .dark:
return dark
default:
return light
}
}
} else {
return light
}
}
}
// And edited the code
func getEvents() {
self.db.collection(xx).document(xx).collection("Calendar").addSnapshotListener { [self] (querySnapshot, error) in
self.userEventBox = []
if let e = error {
print("There was an issue retrieving data from Firestore. \(e)")
} else {
if let snapshotDocuments = querySnapshot?.documents {
for doc in snapshotDocuments {
// #PAUL here I use timestamp for healthier usage as suggested.
let data = doc.data()
if let title = data["title"] as? String ,
let startDate = data["startDate"] as? Timestamp ,
let endDate = data["endDate"] as? Timestamp ,
let isAllDay = data["isAllDay"] as? Bool
{
let newEventBox = EventBox(startDate: startDate.dateValue(), endDate: endDate.dateValue(), isAllDay: isAllDay, title: title)
self.userEventBox.append(newEventBox)
}
}
self.generate()
}
}
}
}
func generate() {
for generation in userEventBox {
let event = EventBoxWithDate(startDate: generation.startDate, endDate: generation.endDate, isAllDay: generation.isAllDay, title: generation.title, color: UIColor.green.cgColor)
// EDITED THIS => self.generated = [event]
// TO THIS
self.generated.append(event)
}
// AND ADDED
reloadData()
// EVERYTHING WORKS NOW
}
var generated: [EventBoxWithDate] = []
override func eventsForDate(_ date: Date) -> [EventDescriptor] {
let calenderKitEvents = generated.map(CustomEKWrapper.init)
return calenderKitEvents
}
}
//
This customization work quite well I can reflect my stored events on CalenderKit. Only thing left here is generated variable is storing all events but when code runs in override func and maps the generated variable it only shows the earliest event on the calender. When I figure it out I will update you . Maybe it will help someone else.
UPDATED THE GENERATE FUNC AND IT WORKS NOW FOR ALL EVENTS

Related

Realm entry corrupted immediately after write

We are running into a really strange issue with Realm (Swift). If we retrieve an entry immediately after writing it to the db it is completely different from the written one. What could possible cause this. Below is a screenshot from the debugger where it happens. The code to fetch the entry looks like this:
func fetchEntry(assetID: String) throws -> UploadInfo? {
let realm = try Realm()
return realm.objects(UploadInfo.self).filter { $0.assetID == assetID }.first
}
The entry class looks like this:
class UploadInfo: Object {
override static func primaryKey() -> String? {
return "assetID"
}
override static func indexedProperties() -> [String] {
return ["sessionID", "taskID"]
}
#objc dynamic var sessionID: String = ""
#objc dynamic var taskID: Int = 0
#objc dynamic var totalBytesSent: Int64 = 0
#objc dynamic var totalBytesExpectedToSend: Int64 = 0
#objc dynamic var downloadProgress: Float = 0.0
#objc dynamic var uploadProgress: Float = 0.0
#objc dynamic var fileURLPath: String = "" // path of file that is uploaded (i.e. multipart body)
#objc dynamic var path: String = ""
#objc dynamic var assetID: String = "" //PHAsset localIdentifier
#objc dynamic var fileModificationTime: DegooTimestamp = 0
#objc dynamic var fileSize: Int64 = 0 // unprocessedTotalFileDataLength
#objc dynamic var fileChecksumBase64: String = ""
#objc dynamic var fileChecksum: Data = Data(base64Encoded: "")!
var fileURL: URL {
return URL(fileURLWithPath: fileURLPath)
}
func makeCopy() -> UploadInfo {
let ui = UploadInfo()
ui.sessionID = sessionID
ui.taskID = taskID
ui.totalBytesSent = totalBytesSent
ui.totalBytesExpectedToSend = totalBytesExpectedToSend
ui.downloadProgress = downloadProgress
ui.uploadProgress = uploadProgress
ui.fileURLPath = fileURLPath
ui.path = path
ui.assetID = assetID
ui.fileModificationTime = fileModificationTime
ui.fileSize = fileSize
ui.fileChecksumBase64 = fileChecksumBase64
ui.fileChecksum = fileChecksum
return ui
}
}
extension UploadInfo {
var data: Data? {
do {
guard try FileManager.default.isFile(at: self.fileURL) else { return nil }
return try Data(contentsOf: self.fileURL)
} catch {
Log.error(error)
return nil
}
}
var asset: PHAsset? {
return PHAsset.fetchAssets(withLocalIdentifiers: [self.assetID], options: nil).firstObject
}
var shouldBeOptimized: Bool {
guard let asset = self.asset else { return false }
return ImageOptimizer.default.shouldBeReoptimized(width: asset.pixelWidth, height: asset.pixelHeight)
}
func cleanUpUrls() {
for url in urlsToCleanUp {
do {
if FileManager.default.fileExists(atPath: url.absoluteString) {
try FileManager.default.removeItem(at: url)
}
} catch {
Log.error(error)
}
}
}
var urlsToCleanUp: [URL] {
let localAssetPathHelper = LocalAssetPathHelper()
let assetFileName = localAssetPathHelper.fileName(for: URL(fileURLWithPath: self.path))
let multipartBodyFileName = localAssetPathHelper.fileName(for: self.fileURL)
return [
URL(fileURLWithPath: localAssetPathHelper.path(for: multipartBodyFileName)),
URL(fileURLWithPath: localAssetPathHelper.path(for: "/Photos/".appending(assetFileName))),
URL(fileURLWithPath: localAssetPathHelper.path(for: "/Videos/".appending(assetFileName)))
]
}
}
We have tried this on both Realm 3.1.1 and 3.5.0.

How do I properly segregate my View and Model in this specific use case

So I'm working through a MOOC, Stanford's "Developing iOS 11 Apps with Swift", and in the course the professor highly emphasizes how important it is to separate your View from your Model in the MVC architecture.
One of the assignments is a simple implementation of this game: Set. While I did decouple them, it feels off.
I was looking for advice on how to improve the model structure a bit more, without adding in specific view logic. For example, when I started out, instead of using Ints for the fields in my Card model, I used enums like this:
enum Symbol {
case square, circle, triangle
}
Which worked somewhat well but the professor advised against doing this because we would be updating the View layer to use different symbols. Here is the code I ended up using, but as I mentioned above it feels off and was wondering how someone more experienced would approach this (The most relevant part is Card.swift but I included everything to illustrate how it was being used):
Card.swift
import Foundation
struct Card : Hashable {
var hashValue: Int {
return self.identifier
}
static func ==(lhs: Card, rhs: Card) -> Bool {
return lhs.identifier == rhs.identifier
}
let symbol: Int
let color: Int
let shading: Int
let quantity: Int
private let identifier: Int
private static var previousIdentifier = 0
private static func generateUid() -> Int {
previousIdentifier += 1
return previousIdentifier
}
init(symbol: Int, color: Int, shading: Int, quantity: Int) {
self.symbol = symbol
self.color = color
self.shading = shading
self.quantity = quantity
self.identifier = Card.generateUid()
}
static func doesMakeSet(_ cards: [Card]) -> Bool {
let colors = cards[0].color == cards[1].color && cards[1].color == cards[2].color
let shading = cards[0].shading == cards[1].shading && cards[1].shading == cards[2].shading
let quantities = cards[0].quantity == cards[1].quantity && cards[1].quantity == cards[2].quantity
let symbols = cards[0].symbol == cards[1].symbol && cards[1].symbol == cards[2].symbol
return colors || shading || quantities || symbols
}
}
Set.swift
import Foundation
class Set {
private var deck = [Card]()
private(set) var board = [Card]() {
didSet {
assert(board.count <= 24, "Set.board: board cannot contain more than 24 cards")
}
}
var canDealMoreCards: Bool {
let matchesThatCanBeRemoved = board.filter { matchedCards.contains($0) }
let occupiedSpacesOnBoard = board.count - matchesThatCanBeRemoved.count
let freeSpaces = 24 - occupiedSpacesOnBoard
let cardsRemaining = deck.count
return cardsRemaining != 0 && freeSpaces >= 3
}
private(set) var selectedCards = [Card]()
private(set) var matchedCards = [Card]()
var currentSelectionIsMatch: Bool?
private(set) var score = 0
private func createDeck() -> [Card] {
var newDeck = [Card]()
let noOfOptions = 1...3
for quantity in noOfOptions {
for symbol in noOfOptions {
for color in noOfOptions {
for shading in noOfOptions {
newDeck.append(Card(symbol: symbol, color: color, shading: shading, quantity: quantity))
}
}
}
}
return newDeck
}
func dealCards(numberOfCards: Int = 3) {
if numberOfCards + board.count > 24 {
board = board.filter { !matchedCards.contains($0) }
}
if deck.count >= numberOfCards {
let range = 0..<numberOfCards
board.append(contentsOf: deck[range])
deck.removeSubrange(range)
assert(board.count <= 24, "Set.dealCards(): there can't be more than 24 cards on the board but \(board.count) are")
}
}
func cardChosen(index: Int) {
if index > board.count - 1 {
return
}
let card = board[index]
if matchedCards.contains(card) {
return
}
if selectedCards.count == 3 {
if Card.doesMakeSet(selectedCards) {
matchedCards += selectedCards
selectedCards.removeAll()
score += 3
currentSelectionIsMatch = true
dealCards()
} else {
currentSelectionIsMatch = false
selectedCards.removeAll()
score -= 5
}
} else {
currentSelectionIsMatch = nil
}
if selectedCards.contains(card) {
selectedCards.remove(at: selectedCards.index(of: card)!)
} else {
if !matchedCards.contains(card) {
selectedCards.append(card)
}
}
}
init() {
deck = createDeck()
var shuffled = [Card]()
for _ in deck.indices {
let randomIndex = deck.count.arc4random
shuffled.append(deck.remove(at: randomIndex))
}
deck = shuffled
assert(deck.count == 81)
dealCards(numberOfCards: 12)
}
}
extension Int {
var arc4random: Int {
if self > 0 {
return Int(arc4random_uniform(UInt32(self)))
} else if self < 0 {
return -Int(arc4random_uniform(UInt32(abs(self))))
} else {
return 0
}
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
var game = Set()
#IBOutlet var cardButtons: [UIButton]!
#IBOutlet weak var deal3CardsButton: UIButton!
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet weak var statusLabel: UILabel!
override func viewDidLoad() {
updateViewFromModel()
}
#IBAction func cardTouched(_ sender: UIButton) {
if let index = cardButtons.index(of: sender) {
game.cardChosen(index: index)
updateViewFromModel()
}
}
#IBAction func deal3CardsTouched() {
game.dealCards()
updateViewFromModel()
}
#IBAction func newGameTouched() {
game = Set()
updateViewFromModel()
}
func updateViewFromModel() {
scoreLabel.text = "Score: \(game.score)"
if let match = game.currentSelectionIsMatch {
statusLabel.text = match ? "Match!" : "Not a Match!"
} else {
statusLabel.text = ""
}
cardButtons.forEach {
$0.hide()
}
for index in game.board.indices {
let text = getAttributedString(forCard: game.board[index])
cardButtons[index].setAttributedTitle(text, for: UIControlState.normal)
if game.selectedCards.contains(game.board[index]) {
cardButtons[index].backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
} else if game.matchedCards.contains(game.board[index]) {
cardButtons[index].hide()
} else {
cardButtons[index].backgroundColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
}
}
deal3CardsButton.isUserInteractionEnabled = game.canDealMoreCards
deal3CardsButton.backgroundColor = game.canDealMoreCards ? #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) : #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
}
let symbols = ["▲", "●", "■"]
let colors = [#colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1), #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1), #colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1)]
func getAttributedString (forCard card: Card) -> NSAttributedString {
let symbol = symbols[card.symbol-1]
let string = String(repeating: symbol, count: card.quantity)
let color = colors[card.color-1]
let shading = Shading(rawValue: card.shading)!
var attributes: [NSAttributedStringKey: Any] = [
.strokeWidth: -3,
.strokeColor: color,
.foregroundColor: color
]
switch shading {
case .fill:
break
case .open:
attributes[.foregroundColor] = color.withAlphaComponent(0)
case .striped:
attributes[.foregroundColor] = color.withAlphaComponent(0.25)
}
return NSAttributedString(string: string, attributes: attributes)
}
// var circle = Symbol.circle
}
// ▲ ● ■
enum Shading: Int {
case fill = 1, striped, open
}
extension UIButton {
func hide() {
self.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 0)
self.setTitle(nil, for: UIControlState.normal)
self.setAttributedTitle(nil, for: UIControlState.normal)
}
}

terminating with uncaught exception of type NSException Parse server

I have a function called :'fetchPosts' that fetches data from a parse server into a UicollectionViewController, When i add this function to any other methods like view did appear or make a button to update the view i get '
terminating with uncaught exception of type NSException
' Not sure what's the problem here.
import UIKit
import AVKit
import AVFoundation
import CoreLocation
import Parse
class NewsfeedCollectionViewController : UICollectionViewController, CLLocationManagerDelegate
{
var searchController: UISearchController!
var posts: [Post]?
override func viewDidLoad() {
super.viewDidLoad()
if let user = PFUser.current() {
PFGeoPoint.geoPointForCurrentLocation { (geopoint, error) in
if geopoint != nil {
let geo = geopoint
user["location"] = geo
user.saveInBackground()
}
}
}
self.fetchPosts()
collectionView?.contentInset = UIEdgeInsets(top: 12, left: 4, bottom: 12, right: 4)
if let layout = collectionView?.collectionViewLayout as? PinterestLayout {
layout.delegate = self
}
}
func fetchPosts()
{
DispatchQueue.global(qos: .userInteractive).async {
self.posts = Post.fetchPosts(viewController: self)
DispatchQueue.main.async {
self.collectionView?.reloadData()
}
}
}
#IBAction func reloadbtnPressed(_ sender: Any) {
fetchPosts()
}
}
extension NewsfeedCollectionViewController
{
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let posts = posts {
return posts.count
} else {
return 0
}
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PostCell", for: indexPath) as! PostCollectionViewCell
cell.post = self.posts?[indexPath.item]
return cell
}
}
extension NewsfeedCollectionViewController : PinterestLayoutDelegate
{
func collectionView(collectionView: UICollectionView, heightForPhotoAt indexPath: IndexPath, with width: CGFloat) -> CGFloat
{
if let post = posts?[indexPath.item], let _ = post.image {
let boundingRect = CGRect(x: 0, y: 0, width: width, height: CGFloat(MAXFLOAT))
// do {
// try postImageView.image = UIImage(data: image!.getData())
// }
// catch {
//
// }
do {
var image: UIImage
try image = UIImage(data: post.image!.getData())!
let rect = AVMakeRect(aspectRatio: image.size, insideRect: boundingRect)
return rect.size.height
}
catch {
return 1;
}
}
return 0
}
func collectionView(collectionView: UICollectionView, heightForCaptionAt indexPath: IndexPath, with width: CGFloat) -> CGFloat
{
if let post = posts?[indexPath.item] {
let topPadding = CGFloat(8)
let bottomPadding = CGFloat(12)
let captionFont = UIFont.systemFont(ofSize: 15)
let captionHeight = self.height(for: post.caption!, with: captionFont, width: width)
let profileImageHeight = CGFloat(36)
let height = topPadding + captionHeight + topPadding + profileImageHeight + bottomPadding
return height
}
return 0.0
}
func height(for text: String, with font: UIFont, width: CGFloat) -> CGFloat
{
let nsstring = NSString(string: text)
let maxHeight = CGFloat(64.0)
let textAttributes = [NSFontAttributeName : font]
let boundingRect = nsstring.boundingRect(with: CGSize(width: width, height: maxHeight), options: .usesLineFragmentOrigin, attributes: textAttributes, context: nil)
return ceil(boundingRect.height)
}
}
extension NewsfeedCollectionViewController : PostProtocol
{
func didFinishFetch(posts: [Post]?)
{
if (posts != nil) {
self.posts = posts
}
self.collectionView?.reloadData()
}
}
ANd here is the Model Post File that i use to fetch data from server :
import UIKit
import Parse
protocol PostProtocol: class {
func didFinishFetch(posts: [Post]?)
}
struct Post
{
var ID: String
var createdBy: User
var timeAgo: String?
var caption: String?
// var image: UIImage?
var image: PFFile?
var numberOfLikes: Int?
var numberOfComments: Int?
var numberOfShares: Int?
static weak var delegate: PostProtocol?
static func fetchPosts(viewController: PostProtocol) -> [Post]
{
var posts = [Post]()
delegate = viewController
let query = PFQuery(className: "posts");
if let latitude = (PFUser.current()?["location"] as AnyObject).latitude {
if let longitude = (PFUser.current()?["location"] as AnyObject).longitude {
let geoPoint = PFGeoPoint(latitude: latitude, longitude: longitude)
query.whereKey("postLocation", nearGeoPoint: geoPoint, withinKilometers: 1)
}
}
query.findObjectsInBackground { (objects: [PFObject]?, error: Error?) in
if error == nil {
for object in objects! {
let pfObject = object as PFObject
let ID = pfObject["uuid"] as! String
let caption = pfObject["title"] as! String
let likes = pfObject["likes"] as? Int
let comments = pfObject["comments"] as? Int
let shares = pfObject["shares"] as? Int
let image = pfObject["pic"] as? PFFile
let profileImage = pfObject["ava"] as? PFFile
let username = pfObject["username"] as? String
let user = User(username: username, profileImage: profileImage);
let from = pfObject.createdAt
let now = Date()
let components : NSCalendar.Unit = [.second, .minute, .hour, .day, .weekOfMonth]
let difference = (Calendar.current as NSCalendar).components(components, from: from! as Date, to: now, options: [])
var timeAgo: String = ""
// logic what to show: seconds, minuts, hours, days or weeks
if difference.second! <= 0 {
timeAgo = "now"
}
if difference.second! > 0 && difference.minute! == 0 {
timeAgo = "\(difference.second!)s."
}
if difference.minute! > 0 && difference.hour! == 0 {
timeAgo = "\(difference.minute!)m."
}
if difference.hour! > 0 && difference.day! == 0 {
timeAgo = "\(difference.hour!)h."
}
if difference.day! > 0 && difference.weekOfMonth! == 0 {
timeAgo = "\(difference.day!)d."
}
if difference.weekOfMonth! > 0 {
timeAgo = "\(difference.weekOfMonth!)w."
}
let post = Post(ID: ID, createdBy: user, timeAgo: timeAgo, caption: caption, image: image, numberOfLikes: likes, numberOfComments: comments, numberOfShares: shares)
posts.append(post);
}
delegate?.didFinishFetch(posts: posts)
}
}
return posts
}
}
struct User
{
var username: String?
var profileImage: PFFile?
}

Create a OptionSet that is usable with IBInspectable

I'm trying to create a OptionSet which can be used in combination with #IBInspectable It seems like this was possible in Swift 2.2
I came across this library which seems to be using an OptionSet in combination with #IBInspectable (The IBInspectable is being the first one set and the structure is actually created on the bottom of the class)
I think it was possible because of the BooleanType which seems to be removed since Swift 2.3
I wrote my OptionSet like this but it doesn't work in combination with #IBInspectable because it's not supported where the BooleanType was (I think that is why it did work in the code of the earlier mentioned library)
public struct Shapes: OptionSet {
private enum Shape: Int, CustomStringConvertible {
case Circle=1, Square=2
public var description: String {
var shift = 0
while (rawValue >> shift != 1) { shift += 1 }
return ["Circle", "Square"][shift]
}
}
public let rawValue: Int
public init(rawValue: Int) { self.rawValue = rawValue }
private init(_ shape: Shape) { self.rawValue = shape.rawValue }
static let Circle = Shapes(Shape.Circle)
static let Square = Shapes(Shape.Square)
}
Does anyone know how to make sure that it will work in Swift 3
So I did find a way to be able to use it by writing some sort of adapter.
I'm pretty sure it can be done better and if anyone has a way to do so don't hesitate to provide your solution but this is how I did it right now
public struct Corners: OptionSet {
private enum Corner: Int, CustomStringConvertible {
case TopLeft=1
case TopRight=2
case BottomLeft=4
case BottomRight=8
case All=16
public var description: String {
var shift = 0
while (rawValue.hashValue >> shift != 1) { shift += 1 }
return ["topleft", "topright", "bottomleft", "bottomright", "all"][shift]
}
}
public let rawValue: Int
public init(rawValue: Int) { self.rawValue = rawValue }
private init(_ shape: Corner) { self.rawValue = shape.rawValue }
static let TopLeft = Corners(Corner.TopLeft)
static let TopRight = Corners(Corner.TopRight)
static let BottomLeft = Corners(Corner.BottomLeft)
static let BottomRight = Corners(Corner.BottomRight)
static let All = [TopLeft, TopRight, BottomLeft, BottomRight]
}
// Needed to split the string that's provided in the #IBInspectable. and remove any possible spaces the user introduced
extension String {
func getStrings() -> [String] {
var stringArray: [String] = []
let strings = self.characters.split{$0 == ","}.map(String.init)
for s in strings {
let string = s.removeSpaces()
stringArray.append(string)
}
return stringArray
}
func removeSpaces() -> String {
if self.characters.first == " " {
var copy = self
copy.characters.removeFirst()
return copy.removeSpaces()
} else {
return self
}
}
}
Then my #IBInspectable looks like this
var corners = [Corners.TopLeft]
#IBInspectable public var onCorners: String = "" {
willSet {
corners = []
for s in newValue.lowercased().getStrings() {
switch s {
case "topleft":
corners.append(Corners.TopLeft)
case "topright":
corners.append(Corners.TopRight)
case "bottomleft":
corners.append(Corners.BottomLeft)
case "bottomright":
corners.append(Corners.BottomRight)
case "all":
corners = Corners.All
default:
return
}
}
}
didSet {
// Do your logic here
}
}
Here how I did
#IBInspectable
open var cornerEdges: CGSize = CGSize(width: 20, height: 20)
#IBInspectable var topLeft: Bool = true
#IBInspectable var topRight: Bool = true
#IBInspectable var bottomLeft: Bool = true
#IBInspectable var bottomRight: Bool = true
override func awakeFromNib() {
var options = UIRectCorner()
if topLeft {
options = options.union(.topLeft)
}
if topRight {
options = options.union(.topRight)
}
if bottomLeft {
options = options.union(.bottomLeft)
}
if bottomRight {
options = options.union(.bottomRight)
}
let path = UIBezierPath(roundedRect:self.bounds,
byRoundingCorners:options,
cornerRadii: self.cornerEdges)
let maskLayer = CAShapeLayer()
maskLayer.path = path.cgPath
self.layer.mask = maskLayer
}

How to have stored properties in Swift, the same way I had on Objective-C?

I am switching an application from Objective-C to Swift, which I have a couple of categories with stored properties, for example:
#interface UIView (MyCategory)
- (void)alignToView:(UIView *)view
alignment:(UIViewRelativeAlignment)alignment;
- (UIView *)clone;
#property (strong) PFObject *xo;
#property (nonatomic) BOOL isAnimating;
#end
As Swift extensions don't accept stored properties like these, I don't know how to maintain the same structure as the Objc code. Stored properties are really important for my app and I believe Apple must have created some solution for doing it in Swift.
As said by jou, what I was looking for was actually using associated objects, so I did (in another context):
import Foundation
import QuartzCore
import ObjectiveC
extension CALayer {
var shapeLayer: CAShapeLayer? {
get {
return objc_getAssociatedObject(self, "shapeLayer") as? CAShapeLayer
}
set(newValue) {
objc_setAssociatedObject(self, "shapeLayer", newValue, UInt(OBJC_ASSOCIATION_RETAIN))
}
}
var initialPath: CGPathRef! {
get {
return objc_getAssociatedObject(self, "initialPath") as CGPathRef
}
set {
objc_setAssociatedObject(self, "initialPath", newValue, UInt(OBJC_ASSOCIATION_RETAIN))
}
}
}
But I get an EXC_BAD_ACCESS when doing:
class UIBubble : UIView {
required init(coder aDecoder: NSCoder) {
...
self.layer.shapeLayer = CAShapeLayer()
...
}
}
Any ideas?
As in Objective-C, you can't add stored property to existing classes. If you're extending an Objective-C class (UIView is definitely one), you can still use Associated Objects to emulate stored properties:
for Swift 1
import ObjectiveC
private var xoAssociationKey: UInt8 = 0
extension UIView {
var xo: PFObject! {
get {
return objc_getAssociatedObject(self, &xoAssociationKey) as? PFObject
}
set(newValue) {
objc_setAssociatedObject(self, &xoAssociationKey, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN))
}
}
}
The association key is a pointer that should be the unique for each association. For that, we create a private global variable and use it's memory address as the key with the & operator. See the Using Swift with Cocoa and Objective-C
on more details how pointers are handled in Swift.
UPDATED for Swift 2 and 3
import ObjectiveC
private var xoAssociationKey: UInt8 = 0
extension UIView {
var xo: PFObject! {
get {
return objc_getAssociatedObject(self, &xoAssociationKey) as? PFObject
}
set(newValue) {
objc_setAssociatedObject(self, &xoAssociationKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
}
UPDATED for Swift 4
In Swift 4, it's much more simple. The Holder struct will contain the private value that our computed property will expose to the world, giving the illusion of a stored property behaviour instead.
Source
extension UIViewController {
struct Holder {
static var _myComputedProperty:Bool = false
}
var myComputedProperty:Bool {
get {
return Holder._myComputedProperty
}
set(newValue) {
Holder._myComputedProperty = newValue
}
}
}
Associated objects API is a bit cumbersome to use. You can remove most of the boilerplate with a helper class.
public final class ObjectAssociation<T: AnyObject> {
private let policy: objc_AssociationPolicy
/// - Parameter policy: An association policy that will be used when linking objects.
public init(policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) {
self.policy = policy
}
/// Accesses associated object.
/// - Parameter index: An object whose associated object is to be accessed.
public subscript(index: AnyObject) -> T? {
get { return objc_getAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque()) as! T? }
set { objc_setAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque(), newValue, policy) }
}
}
Provided that you can "add" a property to objective-c class in a more readable manner:
extension SomeType {
private static let association = ObjectAssociation<NSObject>()
var simulatedProperty: NSObject? {
get { return SomeType.association[self] }
set { SomeType.association[self] = newValue }
}
}
As for the solution:
extension CALayer {
private static let initialPathAssociation = ObjectAssociation<CGPath>()
private static let shapeLayerAssociation = ObjectAssociation<CAShapeLayer>()
var initialPath: CGPath! {
get { return CALayer.initialPathAssociation[self] }
set { CALayer.initialPathAssociation[self] = newValue }
}
var shapeLayer: CAShapeLayer? {
get { return CALayer.shapeLayerAssociation[self] }
set { CALayer.shapeLayerAssociation[self] = newValue }
}
}
So I think I found a method that works cleaner than the ones above because it doesn't require any global variables. I got it from here:
http://nshipster.com/swift-objc-runtime/
The gist is that you use a struct like so:
extension UIViewController {
private struct AssociatedKeys {
static var DescriptiveName = "nsh_DescriptiveName"
}
var descriptiveName: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.DescriptiveName,
newValue as NSString?,
UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC)
)
}
}
}
}
UPDATE for Swift 2
private struct AssociatedKeys {
static var displayed = "displayed"
}
//this lets us check to see if the item is supposed to be displayed or not
var displayed : Bool {
get {
guard let number = objc_getAssociatedObject(self, &AssociatedKeys.displayed) as? NSNumber else {
return true
}
return number.boolValue
}
set(value) {
objc_setAssociatedObject(self,&AssociatedKeys.displayed,NSNumber(bool: value),objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
The solution pointed out by jou doesn't support value types,
this works fine with them as well
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)
}
}
}
}
This is really such a great solution, I wanted to add another usage example that included structs and values that are not optionals. Also, the AssociatedKey values can be simplified.
struct Crate {
var name: String
}
class Box {
var name: String
init(name: String) {
self.name = name
}
}
extension UIViewController {
private struct AssociatedKey {
static var displayed: UInt8 = 0
static var box: UInt8 = 0
static var crate: UInt8 = 0
}
var displayed: Bool? {
get {
return getAssociatedObject(self, associativeKey: &AssociatedKey.displayed)
}
set {
if let value = newValue {
setAssociatedObject(self, value: value, associativeKey: &AssociatedKey.displayed, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
var box: Box {
get {
if let result:Box = getAssociatedObject(self, associativeKey: &AssociatedKey.box) {
return result
} else {
let result = Box(name: "")
self.box = result
return result
}
}
set {
setAssociatedObject(self, value: newValue, associativeKey: &AssociatedKey.box, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
var crate: Crate {
get {
if let result:Crate = getAssociatedObject(self, associativeKey: &AssociatedKey.crate) {
return result
} else {
let result = Crate(name: "")
self.crate = result
return result
}
}
set {
setAssociatedObject(self, value: newValue, associativeKey: &AssociatedKey.crate, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
You can't define categories (Swift extensions) with new storage; any additional properties must be computed rather than stored. The syntax works for Objective C because #property in a category essentially means "I'll provide the getter and setter". In Swift, you'll need to define these yourself to get a computed property; something like:
extension String {
public var Foo : String {
get
{
return "Foo"
}
set
{
// What do you want to do here?
}
}
}
Should work fine. Remember, you can't store new values in the setter, only work with the existing available class state.
My $0.02. This code is written in Swift 2.0
extension CALayer {
private struct AssociatedKeys {
static var shapeLayer:CAShapeLayer?
}
var shapeLayer: CAShapeLayer? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.shapeLayer) as? CAShapeLayer
}
set {
if let newValue = newValue {
objc_setAssociatedObject(self, &AssociatedKeys.shapeLayer, newValue as CAShapeLayer?, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
}
I have tried many solutions, and found this is the only way to actually extend a class with extra variable parameters.
Why relying on objc runtime? I don't get the point. By using something like the following you will achieve almost the identical behaviour of a stored property, by using only a pure Swift approach:
extension UIViewController {
private static var _myComputedProperty = [String:Bool]()
var myComputedProperty:Bool {
get {
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
return UIViewController._myComputedProperty[tmpAddress] ?? false
}
set(newValue) {
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
UIViewController._myComputedProperty[tmpAddress] = newValue
}
}
}
I prefer doing code in pure Swift and not rely on Objective-C heritage. Because of this I wrote pure Swift solution with two advantages and two disadvantages.
Advantages:
Pure Swift code
Works on classes and completions or more specifically on Any object
Disadvantages:
Code should call method willDeinit() to release objects linked to specific class instance to avoid memory leaks
You cannot make extension directly to UIView for this exact example because var frame is extension to UIView, not part of class.
EDIT:
import UIKit
var extensionPropertyStorage: [NSObject: [String: Any]] = [:]
var didSetFrame_ = "didSetFrame"
extension UILabel {
override public var frame: CGRect {
get {
return didSetFrame ?? CGRectNull
}
set {
didSetFrame = newValue
}
}
var didSetFrame: CGRect? {
get {
return extensionPropertyStorage[self]?[didSetFrame_] as? CGRect
}
set {
var selfDictionary = extensionPropertyStorage[self] ?? [String: Any]()
selfDictionary[didSetFrame_] = newValue
extensionPropertyStorage[self] = selfDictionary
}
}
func willDeinit() {
extensionPropertyStorage[self] = nil
}
}
With Obj-c Categories you can only add methods, not instance variables.
In you example you have used #property as a shortcut to adding getter and setter method declarations. You still need to implement those methods.
Similarly in Swift you can add use extensions to add instance methods, computed properties etc. but not stored properties.
Notice: after further analyzing, the code below works fine, but does not release the view object, so if I can find a way around it I'll edit the answer. meanwhile, read the comments.
How about storing static map to class that is extending like this :
extension UIView {
struct Holder {
static var _padding:[UIView:UIEdgeInsets] = [:]
}
var padding : UIEdgeInsets {
get{ return UIView.Holder._padding[self] ?? .zero}
set { UIView.Holder._padding[self] = newValue }
}
}
I also get an EXC_BAD_ACCESS problem.The value in objc_getAssociatedObject() and objc_setAssociatedObject() should be an Object. And the objc_AssociationPolicy should match the Object.
I tried using objc_setAssociatedObject as mentioned in a few of the answers here, but after failing with it a few times I stepped back and realized there is no reason I need that. Borrowing from a few of the ideas here, I came up with this code which simply stores an array of whatever my extra data is (MyClass in this example) indexed by the object I want to associate it with:
class MyClass {
var a = 1
init(a: Int)
{
self.a = a
}
}
extension UIView
{
static var extraData = [UIView: MyClass]()
var myClassData: MyClass? {
get {
return UIView.extraData[self]
}
set(value) {
UIView.extraData[self] = value
}
}
}
// Test Code: (Ran in a Swift Playground)
var view1 = UIView()
var view2 = UIView()
view1.myClassData = MyClass(a: 1)
view2.myClassData = MyClass(a: 2)
print(view1.myClassData?.a)
print(view2.myClassData?.a)
Here is simplified and more expressive solution. It works for both value and reference types. The approach of lifting is taken from #HepaKKes answer.
Association code:
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 associated<T>(to base: AnyObject,
key: UnsafePointer<UInt8>,
policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN,
initialiser: () -> T) -> T {
if let v = objc_getAssociatedObject(base, key) as? T {
return v
}
if let v = objc_getAssociatedObject(base, key) as? Lifted<T> {
return v.value
}
let lifted = Lifted(initialiser())
objc_setAssociatedObject(base, key, lifted, policy)
return lifted.value
}
func associate<T>(to base: AnyObject, key: UnsafePointer<UInt8>, value: T, policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN) {
if let v: AnyObject = value as AnyObject? {
objc_setAssociatedObject(base, key, v, policy)
}
else {
objc_setAssociatedObject(base, key, lift(value), policy)
}
}
Example of usage:
1) Create extension and associate properties to it. Let's use both value and reference type properties.
extension UIButton {
struct Keys {
static fileprivate var color: UInt8 = 0
static fileprivate var index: UInt8 = 0
}
var color: UIColor {
get {
return associated(to: self, key: &Keys.color) { .green }
}
set {
associate(to: self, key: &Keys.color, value: newValue)
}
}
var index: Int {
get {
return associated(to: self, key: &Keys.index) { -1 }
}
set {
associate(to: self, key: &Keys.index, value: newValue)
}
}
}
2) Now you can use just as regular properties:
let button = UIButton()
print(button.color) // UIExtendedSRGBColorSpace 0 1 0 1 == green
button.color = .black
print(button.color) // UIExtendedGrayColorSpace 0 1 == black
print(button.index) // -1
button.index = 3
print(button.index) // 3
More details:
Lifting is needed for wrapping value types.
Default associated object behavior is retain. If you want to learn more about associated objects, I'd recommend checking this article.
if you are looking to set a custom string attribute to a UIView, this is how I did it on Swift 4
Create a UIView extension
extension UIView {
func setStringValue(value: String, key: String) {
layer.setValue(value, forKey: key)
}
func stringValueFor(key: String) -> String? {
return layer.value(forKey: key) as? String
}
}
To use this extension
let key = "COLOR"
let redView = UIView()
// To set
redView.setStringAttribute(value: "Red", key: key)
// To read
print(redView.stringValueFor(key: key)) // Optional("Red")
In PURE SWIFT with WEAK reference handling
import Foundation
import UIKit
extension CustomView {
// can make private
static let storedProperties = WeakDictionary<UIView, Properties>()
struct Properties {
var url: String = ""
var status = false
var desc: String { "url: \(url), status: \(status)" }
}
var properties: Properties {
get {
return CustomView.storedProperties.get(forKey: self) ?? Properties()
}
set {
CustomView.storedProperties.set(forKey: self, object: newValue)
}
}
}
var view: CustomView? = CustomView()
print("1 print", view?.properties.desc ?? "nil")
view?.properties.url = "abc"
view?.properties.status = true
print("2 print", view?.properties.desc ?? "nil")
view = nil
WeakDictionary.swift
import Foundation
private class WeakHolder<T: AnyObject>: Hashable {
weak var object: T?
let hash: Int
init(object: T) {
self.object = object
hash = ObjectIdentifier(object).hashValue
}
func hash(into hasher: inout Hasher) {
hasher.combine(hash)
}
static func ==(lhs: WeakHolder, rhs: WeakHolder) -> Bool {
return lhs.hash == rhs.hash
}
}
class WeakDictionary<T1: AnyObject, T2> {
private var dictionary = [WeakHolder<T1>: T2]()
func set(forKey: T1, object: T2?) {
dictionary[WeakHolder(object: forKey)] = object
}
func get(forKey: T1) -> T2? {
let obj = dictionary[WeakHolder(object: forKey)]
return obj
}
func forEach(_ handler: ((key: T1, value: T2)) -> Void) {
dictionary.forEach {
if let object = $0.key.object, let value = dictionary[$0.key] {
handler((object, value))
}
}
}
func clean() {
var removeList = [WeakHolder<T1>]()
dictionary.forEach {
if $0.key.object == nil {
removeList.append($0.key)
}
}
removeList.forEach {
dictionary[$0] = nil
}
}
}
Another example with using Objective-C associated objects and computed properties for Swift 3 and Swift 4
import CoreLocation
extension CLLocation {
private struct AssociatedKeys {
static var originAddress = "originAddress"
static var destinationAddress = "destinationAddress"
}
var originAddress: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.originAddress) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.originAddress,
newValue as NSString?,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
}
}
}
var destinationAddress: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.destinationAddress) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.destinationAddress,
newValue as NSString?,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
}
}
}
}
First, Associated Objects should be the best right solution for the extended stored properties, because it comes from the Objective-C runtime, this is a great powerful feature that we should use before there are other native features of Swift language.
You should always aware that the associated objects will be released after there are no other objects to retain them, including swift objects, so don't use custom containers to retain the target values which won't be released automatically.
Second, for those additional associated key structure definitions, the core functions just need a UnsafeRawPointer for that, actually there is another best choice for that, #function is a static string which generated when compiling the source code, it also has its own address to use.
So, here is it:
var status: Bool? {
get { objc_getAssociatedObject(self, #function) as? Bool }
set { objc_setAssociatedObject(self, #function, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)}
}
Build for swift 5.
Last, keep in mind the object type with the association policy.
I tried to store properties by using objc_getAssociatedObject, objc_setAssociatedObject, without any luck. My goal was create extension for UITextField, to validate text input characters length.
Following code works fine for me. Hope this will help someone.
private var _min: Int?
private var _max: Int?
extension UITextField {
#IBInspectable var minLength: Int {
get {
return _min ?? 0
}
set {
_min = newValue
}
}
#IBInspectable var maxLength: Int {
get {
return _max ?? 1000
}
set {
_max = newValue
}
}
func validation() -> (valid: Bool, error: String) {
var valid: Bool = true
var error: String = ""
guard let text = self.text else { return (true, "") }
if text.characters.count < minLength {
valid = false
error = "Textfield should contain at least \(minLength) characters"
}
if text.characters.count > maxLength {
valid = false
error = "Textfield should not contain more then \(maxLength) characters"
}
if (text.characters.count < minLength) && (text.characters.count > maxLength) {
valid = false
error = "Textfield should contain at least \(minLength) characters\n"
error = "Textfield should not contain more then \(maxLength) characters"
}
return (valid, error)
}
}
Why not just do something like this, i see other solutions are way out of the small need.
private var optionalID: String {
UUID().uuidString
}
Here is an alternative that works also
public final class Storage : AnyObject {
var object:Any?
public init(_ object:Any) {
self.object = object
}
}
extension Date {
private static let associationMap = NSMapTable<NSString, AnyObject>()
private struct Keys {
static var Locale:NSString = "locale"
}
public var locale:Locale? {
get {
if let storage = Date.associationMap.object(forKey: Keys.Locale) {
return (storage as! Storage).object as? Locale
}
return nil
}
set {
if newValue != nil {
Date.associationMap.setObject(Storage(newValue), forKey: Keys.Locale)
}
}
}
}
var date = Date()
date.locale = Locale(identifier: "pt_BR")
print( date.locale )
I found this solution more practical
UPDATED for Swift 3
extension UIColor {
static let graySpace = UIColor.init(red: 50/255, green: 50/255, blue: 50/255, alpha: 1.0)
static let redBlood = UIColor.init(red: 102/255, green: 0/255, blue: 0/255, alpha: 1.0)
static let redOrange = UIColor.init(red: 204/255, green: 17/255, blue: 0/255, alpha: 1.0)
func alpha(value : CGFloat) -> UIColor {
var r = CGFloat(0), g = CGFloat(0), b = CGFloat(0), a = CGFloat(0)
self.getRed(&r, green: &g, blue: &b, alpha: &a)
return UIColor(red: r, green: g, blue: b, alpha: value)
}
}
...then in your code
class gameController: UIViewController {
#IBOutlet var game: gameClass!
override func viewDidLoad() {
self.view.backgroundColor = UIColor.graySpace
}
}

Resources