Swift3 Random Extension Method
In Swift 3 there are four Range structures:
"x" ..< "y"
⇒Range<T>
"x" ... "y"
⇒ClosedRange<T>
1 ..< 5
⇒CountableRange<T>
1 ... 5
⇒CountableClosedRange<T>
(The operators ..<
and ...
are overloaded so that if the elements are stridable (random-access iterators e.g. numbers and pointers), a Countable Range will be returned. But these operators can still return plain Ranges to satisfy the type checker.)
Since Range and ClosedRange are different structures, you cannot implicitly convert a them with each other, and thus the error.
If you want Rand to accept a ClosedRange as well as Range, you must overload it:
// accepts Rand(0 ..< 5)
func Rand(_ range: Range<UInt32>) -> Int {
return Int(range.lowerBound + arc4random_uniform(range.upperBound - range.lowerBound))
}
// accepts Rand(1 ... 5)
func Rand(_ range: ClosedRange<UInt32>) -> Int {
return Int(range.lowerBound + arc4random_uniform(range.upperBound + 1 - range.lowerBound))
}
What is the equivalent of seeded random in Swift3 (Xcode8 beta 1)
You can use
srand48(seed) and drand48() in Swift3.
Shuffle array swift 3
count
returns an IndexDistance
which is the type describing
the distance between two collection indices. IndexDistance
is
required to be a SignedInteger
, but need not be an Int
and can
be different from Index
. Therefore it is not possible to create
the range 0..<count - 1
.
A solution is to use startIndex
and endIndex
instead of 0
and count
:
extension MutableCollection where Index == Int {
/// Shuffle the elements of `self` in-place.
mutating func shuffle() {
// empty and single-element collections don't shuffle
if count < 2 { return }
for i in startIndex ..< endIndex - 1 {
let j = Int(arc4random_uniform(UInt32(endIndex - i))) + i
if i != j {
swap(&self[i], &self[j])
}
}
}
}
Another advantage is that this also works correctly with array slices
(where the index of the first element is not necessarily zero).
Note that according to the new "Swift API Design Guidelines",shuffle()
is the "proper" name for a mutating shuffle method,
and shuffled()
for the non-mutating counterpart which returns an array:
extension Collection {
/// Return a copy of `self` with its elements shuffled
func shuffled() -> [Iterator.Element] {
var list = Array(self)
list.shuffle()
return list
}
}
Update: A (even more general) Swift 3 version has been added to
How do I shuffle an array in Swift? in the meantime.
For Swift 4 (Xcode 9) one has to replace the call to the swap()
function by a call to the swapAt()
method of the collection.
Also the restriction on the Index
type is no longer needed:
extension MutableCollection {
/// Shuffle the elements of `self` in-place.
mutating func shuffle() {
for i in indices.dropLast() {
let diff = distance(from: i, to: endIndex)
let j = index(i, offsetBy: numericCast(arc4random_uniform(numericCast(diff))))
swapAt(i, j)
}
}
}
See SE-0173 Add MutableCollection.swapAt(_:_:)
for more information about swapAt
.
As of Swift 4.2 (Xcode 10, currently in beta), with the implementation of
SE-0202 Random Unification,shuffle()
and shuffled()
are part of the Swift standard library.
Swift3 How to set properties created in an extension
You are probably best off creating a base class extending UIViewController
something like this:
class BaseViewController: UIViewController {
let cartCountLabel: UILabel = {
let label = UILabel(frame: CGRect(x: 0, y: -0, width: 20, height: 20))
label.textAlignment = .center
label.layer.cornerRadius = label.bounds.size.height / 2
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = .clear
label.layer.masksToBounds = true
label.textColor = .white
label.minimumScaleFactor = 10/UIFont.labelFontSize
label.adjustsFontSizeToFitWidth = true
return label
}()
func shoppingBagButtonTouched(button: UIButton) {
-----
}
func closeView() {
dismiss(animated: true, completion: nil)
}
func setupNavigationHeader(showCart: Bool? = true) {
let navigationBar: UINavigationBar = {
let navBar = UINavigationBar(frame: CGRect(0, 0, self.view.frame.size.width, Constants.HEADER_HEIGHT))
return navBar
}()
let navigationItem = UINavigationItem()
self.automaticallyAdjustsScrollViewInsets = false
UINavigationBar.appearance().barTintColor = .red
UINavigationBar.appearance().tintColor = .white
let titleLabel: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = .clear
label.layer.masksToBounds = true
label.minimumScaleFactor = 10/UIFont.labelFontSize
label.adjustsFontSizeToFitWidth = true
label.numberOfLines = 1
label.text = "not set"
label.textColor = .white
return label
}()
let fixedSpace = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
let menuBtn = UIBarButtonItem(image: UIImage(named: "closeNav"), style: .plain, target: self, action: #selector(self.closeView))
navigationItem.leftBarButtonItems = [fixedSpace, menuBtn]
navigationBar.items = [navigationItem]
if let showCart = showCart {
let shoppingBagButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 22, height: 22))
button.setBackgroundImage(UIImage(named: "shopping_bag"), for: .normal)
return button
}()
let rightBarButtonItem = UIBarButtonItem(customView: shoppingBagButton)
navigationItem.setRightBarButtonItems([rightBarButtonItem], animated: true)
shoppingBagButton.addTarget(self, action: #selector(shoppingBagButtonTouched(button:)), for: .touchUpInside)
shoppingBagButton.addSubview(cartCountLabel)
cartCountLabel.anchorCenterXToSuperview()
cartCountLabel.anchorCenterYToSuperview(constant: 2)
}
navigationBar.addSubview(titleLabel)
view.addSubview(navigationBar)
titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
titleLabel.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor, constant: -10).isActive = true
titleLabel.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
titleLabel.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width - 90).isActive = true
}
}
Then you can derive all your view controller instances from the base class like this:
class MyViewController:BaseViewController {
var titleLabelText: String = "title not set"
var cartCount: String? {
didSet {
cartCountLabel.text = cartCount
}
}
func getCartCount() {
ServerUtility.getCartCountApi { (cartCount) in
if let count = cartCount as Int? {
if count > 0 {
self.cartCount = "\(count)"
}
} else {
self.cartCount = "0"
}
}
}
}
I have not verified the above code by running it and so it might need a bit of tweaking :)
Does _ArrayType or _ArrayProtocol not available in Swift 3.1?
Type names starting with an underscore should always treated as internal.
In Swift 3.1, it is marked as internal
in the source code and therefore
not publicly visible.
Using _ArrayProtocol
was a workaround in earlier Swift versions where
you could not define an Array
extension with a "same type" requirement.
This is now possible as of Swift 3.1, as described in the
Xcode 8.3 release notes:
Constrained extensions allow same-type constraints between generic parameters and concrete types. (SR-1009)
Using the internal protocol is therefore not necessary anymore,
and you can simply define
extension Array where Element == UInt8 {
}
But note that your static func stringValue()
does not need any
restriction of the element type. What you perhaps intended is to
define an instance method like this:
extension Array where Element == UInt8 {
func stringValue() -> String {
return String(cString: self)
}
}
print([65, 66, 67, 0].stringValue()) // ABC
Also note that String(cString:)
expects a null-terminated sequence
of UTF-8 bytes.
Can't extend generic struct for specific type
The way this roadblock is commonly encountered is when attempting to extend Array. This is legal:
extension Array where Element : Comparable {
}
But this is illegal:
extension Array where Element == Int {
}
The compiler complains:
Same-type requirement makes generic parameter 'Element' non-generic
The problem is the use of ==
here in combination with Array's parameterized type Element, because Array is a generic struct.
One workaround with Array is to rise up the hierarchy of Array's inheritance to reach something that is not a generic struct:
extension Sequence where Iterator.Element == Int {
}
That's legal because Sequence and Iterator are generic protocols.
Another solution, though, is to rise up the hierarchy from the target type, namely Int. If we can find a protocol to which Int conforms, then we can use the :
operator instead of ==
. Well, there is one:
extension CountableClosedRange where Bound : Integer {
}
That's the real difference between our two attempts to implement random
on a range. The reason your attempt hits a roadblock and mine doesn't is that you are using ==
whereas I am using :
. I can do that because there's a protocol (FloatingPoint) to which Double conforms.
But, as you've been told, with luck all this trickery will soon be a thing of the past.
Swift 3: How to Calculate Random Number with Favor Towards A Bias
You can use GKGaussianDistribution
(aka normal distribution) from GameKit to do that. You will need 2 parameters: mean
(the "center" that you desire) and deviation
(how far out it should spread from the center):
import GameKit
func random(count: Int, in range: ClosedRange<Int>, mean: Int, deviation: Int) -> [Int] {
guard count > 0 else { return [] }
let randomSource = GKARC4RandomSource()
let randomDistribution = GKGaussianDistribution(randomSource: randomSource, mean: Float(mean), deviation: Float(deviation))
// Clamp the result to within the specified range
return (0..<count).map { _ in
let rnd = randomDistribution.nextInt()
if rnd < range.lowerBound {
return range.lowerBound
} else if rnd > range.upperBound {
return range.upperBound
} else {
return rnd
}
}
}
Usage and test:
let arr = random(count: 1_000_000, in: 0...100, mean: 70, deviation: 10)
let summary = NSCountedSet(array: arr)
for i in 0...100 {
print("\(i): \(summary.count(for: i))")
}
You can see that values around 70 have the highest counts
How to generate a random number in Swift?
Swift 4.2+
Swift 4.2 shipped with Xcode 10 introduces new easy-to-use random functions for many data types.
You can call the random()
method on numeric types.
let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()
Return an array of Integers within a range
The function randomNumber()
is expensive not efficient because min
and max
are extracted in each call (aka in each iteration of the loop)
This might be a bit swiftier
func randomNumbers(range: ClosedRange<Int>, count: Int) -> [Int] {
let min = range.lowerBound
let randomMax = UInt32(1 + range.upperBound - min)
return (0..<count).map {_ in return Int(arc4random_uniform(randomMax)) + min }
}
let nums = randomNumbers(range: 10...20, count: 5)
Related Topics
How to Add Followed Text to Uitextfield
Scaling Physics Bodies in Xcode Spritekit
How to Update Particular Value of Child in Firebase Db
Swift - Nsdate and Last Week of Year
Why Is Uisearchcontroller Changing the Navigation Bar Colors
Ksecattrkeytypeec Causing Encryptmessagewithpublickey() to Fail
Swift and Nsuserdefaults - Exc_Bad_Instruction When User Defaults Empty
Delegate Method to Uitableviewcell Swift
Swiftui - Scrollviewreader's Scrollto Does Not Scroll
Getting Uitableview Error "Unable to Dequeue a Cell with Identifier Cell"
How to Deal with Dynamic Sections and Rows in iOS Uitableview
Extract 4 Bits of Bluetooth Hex Data
Admob Interstitial Alway Returns False
How to Create an Image of Specific Size from Uiview
Gmsmapview Animatetocameraposition Zoom in - Zoom Out Animation
Spritekit, Swift 2.0 - Scrollview in Reverse